diff --git a/lib/ubix/_start.S b/lib/ubix/_start.S new file mode 100644 index 0000000..3196301 --- /dev/null +++ b/lib/ubix/_start.S @@ -0,0 +1,24 @@ + .text + .align 4 + .globl _start + .type _start, @function +_start: + .cfi_startproc + xorl %ebp,%ebp + pushl %ebp + .cfi_def_cfa_offset 4 + movl %esp,%ebp + .cfi_offset %ebp,-8 + .cfi_def_cfa_register %ebp + andl $0xfffffff0,%esp # align stack + leal 8(%ebp),%eax + subl $4,%esp + pushl %eax # argv + pushl 4(%ebp) # argc + pushl %edx # rtld cleanup + call _start1 + int3 + .cfi_endproc + .size _start, . - _start + + .section .note.GNU-stack,"",%progbits diff --git a/libexec/Makefile b/libexec/Makefile index 830ac14..96e6538 100644 --- a/libexec/Makefile +++ b/libexec/Makefile @@ -10,4 +10,4 @@ clean: (cd ld;make clean) - (cd rtld-elf;make clean) +# (cd rtld-elf;make clean) diff --git a/libexec/ld/addlibrary.c b/libexec/ld/addlibrary.c index 9081474..f591d31 100644 --- a/libexec/ld/addlibrary.c +++ b/libexec/ld/addlibrary.c @@ -26,7 +26,6 @@ } //if ((tmpLib->output = (char *)malloc((linkerFd->size+0x4000))) == 0x0) { //if ((tmpLib->output = (char *)malloc(0x111000)) == 0x0) { - printf("[%i](0x%X:0x%X)", __LINE__, linkerFd->size+0x4000, 0x111000); //if ((tmpLib->output = (char *)getPage((0x111000/0x1000),2)) == 0x0) { if ((tmpLib->output = (char *)getPage(((linkerFd->size+0x4000)/0x1000),2)) == 0x0) { printf("malloc failed: tmpLib->output\n"); @@ -35,43 +34,29 @@ sprintf(tmpLib->name,lib); } printf("Base: {0x%X}[%i]\n",tmpLib->output, __LINE__); - printf("[%i]\n", __LINE__); if (tmpLib->linkerHeader == 0x0) { - printf("[%i]", __LINE__); fseek(linkerFd,0x0,0x0); - printf("[%i]", __LINE__); if ((tmpLib->linkerHeader = (elfHeader *)malloc(sizeof(elfHeader))) == 0x0) { printf("malloc failed: tmpLib->linkerHeader\n"); exit(0x1); } - printf("[%i]", __LINE__); fread(tmpLib->linkerHeader,sizeof(elfHeader),1,linkerFd); - printf("[%i]", __LINE__); } - printf("[%i]", __LINE__); if (tmpLib->linkerProgramHeader == 0x0) { - printf("[%i]", __LINE__); if ((tmpLib->linkerProgramHeader = (elfProgramHeader *)malloc(sizeof(elfProgramHeader)*tmpLib->linkerHeader->ePhnum)) == 0x0) { printf("malloc failed: tmpLib->linkerProgramHeader\n"); exit(0x1); } - printf("[%i]", __LINE__); fseek(linkerFd,tmpLib->linkerHeader->ePhoff,0); fread(tmpLib->linkerProgramHeader,sizeof(elfProgramHeader),tmpLib->linkerHeader->ePhnum,linkerFd); - printf("[%i]", __LINE__); for (i=0;ilinkerHeader->ePhnum;i++) { - printf("[%i]", __LINE__); switch (tmpLib->linkerProgramHeader[i].phType) { case PT_LOAD: case PT_DYNAMIC: - printf("[%i](0x%X)", __LINE__, tmpLib->linkerProgramHeader[i].phVaddr + (uInt32)tmpLib->output); newLoc = (char *)tmpLib->linkerProgramHeader[i].phVaddr + (uInt32)tmpLib->output; - printf("[%i]", __LINE__); fseek(linkerFd,tmpLib->linkerProgramHeader[i].phOffset,0); - printf("[%i](0x%X)", __LINE__,tmpLib->linkerProgramHeader[i].phFilesz); fread(newLoc,tmpLib->linkerProgramHeader[i].phFilesz,1,linkerFd); - printf("[%i]", __LINE__); break; case PT_TLS: tmpLib->tlsindex = 1; @@ -79,17 +64,14 @@ tmpLib->tlsalign = tmpLib->linkerProgramHeader[i].phAlign;//ph->p_align; tmpLib->tlsinitsize = tmpLib->linkerProgramHeader[i].phFilesz;//ph->p_filesz; tmpLib->tlsinit = (void*)(tmpLib->linkerProgramHeader[i].phVaddr + (uInt32)tmpLib->output); - printf("[%i](0x%X)", __LINE__, tmpLib->linkerProgramHeader[i].phVaddr + (uInt32)tmpLib->output); +/* newLoc = (char *)tmpLib->linkerProgramHeader[i].phVaddr + (uInt32)tmpLib->output; - printf("[%i]", __LINE__); fseek(linkerFd,tmpLib->linkerProgramHeader[i].phOffset,0); - printf("[%i](0x%X)", __LINE__,tmpLib->linkerProgramHeader[i].phFilesz); fread(newLoc,tmpLib->linkerProgramHeader[i].phFilesz,1,linkerFd); - printf("[%i]", __LINE__); +*/ break; case PT_GNU_STACK: - /* Tells us if the stack should be executable. Failsafe to - executable until we add checking */ + /* Tells us if the stack should be executable. Failsafe to executable until we add checking */ printf("NOT DEF1\n"); break; case PT_PAX_FLAGS: @@ -103,7 +85,6 @@ } } - printf("[%i]", __LINE__); if (tmpLib->linkerSectionHeader == 0x0) { if ((tmpLib->linkerSectionHeader = (elfSectionHeader *)malloc(sizeof(elfSectionHeader)*tmpLib->linkerHeader->eShnum)) == 0x0) { printf("malloc failed: tmpLib->linkerSectionHeader\n"); @@ -150,6 +131,7 @@ case R_386_TLS_DTPMOD32: case R_386_TLS_DTPOFF32: *reMap += ((uInt32)tmpLib->output + tmpLib->linkerRelSymTab[rel].dynValue); + *reMap += ((uInt32)tmpLib->output + tmpLib->linkerRelSymTab[rel].dynValue) - (uInt32)reMap; break; case R_386_PC32: *reMap += ((uInt32)tmpLib->output + tmpLib->linkerRelSymTab[rel].dynValue) - (uInt32)reMap; @@ -179,10 +161,12 @@ tmpLib->sym = i; } break; + default: + printf("[SHTYPE: 0x%X]", tmpLib->linkerSectionHeader[i].shType); + break; } } } - printf("[%i]", __LINE__); if (libs != 0x0) libs->prev = tmpLib; diff --git a/libexec/ld/findfunc.c b/libexec/ld/findfunc.c index 527000c..eed92c6 100644 --- a/libexec/ld/findfunc.c +++ b/libexec/ld/findfunc.c @@ -18,7 +18,7 @@ if (!strcmp(func,(libPtr->linkerDynStr + libPtr->linkerRelSymTab[i].dynName))) { funcPtr = (uInt32 *)((uInt32)(libPtr->linkerRelSymTab[i].dynValue) + (uInt32)libPtr->output); if (funcPtr == 0x0) { - printf("[%s:0x%X]\n",func,funcPtr); + printf("[%s:0x%X]\n",func,funcPtr,*funcPtr); } return((uInt32)funcPtr); break; diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile index 9ca390d..cf9eb34 100644 --- a/libexec/rtld-elf/Makefile +++ b/libexec/rtld-elf/Makefile @@ -1,59 +1,94 @@ -# $Id: Makefile 115 2016-01-14 02:55:19Z reddawg $ -# Application Makefile (C) 2002-2004 The UbixOS Project +# $FreeBSD: releng/11.1/libexec/rtld-elf/Makefile 319016 2017-05-28 00:13:44Z rgrimes $ -# Include Global 'Source' Options -include ../../Makefile.incl -include ../Makefile.incl +# Use the following command to build local debug version of dynamic +# linker: +# make DEBUG_FLAGS=-g DEBUG=-DDEBUG WITHOUT_TESTS=yes all -INCLUDES = -I../../include.new -I./ +.include +PACKAGE= clibs +MK_SSP= no -CFLAGS+= -Wall -DFREEBSD_ELF -DIN_RTLD -DPIC -CFLAGS = -m32 -O2 -fno-strict-aliasing -pipe -Wall -DFREEBSD_ELF -DIN_RTLD -I./ -fpic -DPIC -std=gnu99 -Wformat=2 -Wno-format-extra-args -Werror -DDEBUG -#MrOlsen 2016-01-13 CFLAGS = -O2 -fno-strict-aliasing -pipe -Wall -DFREEBSD_ELF -DIN_RTLD -I./ -fpic -elf -DPIC -std=gnu99 -Wformat=2 -Wno-format-extra-args -Werror -DDEBUG +PROG?= ld-elf.so.1 +.if (${PROG:M*ld-elf32*} != "") +TAGS+= lib32 +.endif +SRCS= rtld_start.S \ + reloc.c rtld.c rtld_lock.c rtld_printf.c map_object.c \ + malloc.c xmalloc.c debug.c libmap.c +MAN= rtld.1 +CSTD?= gnu99 +CFLAGS+= -Wall -DFREEBSD_ELF -DIN_RTLD -ffreestanding +CFLAGS+= -I${SRCTOP}/lib/csu/common +.if exists(${.CURDIR}/${MACHINE_ARCH}) +RTLD_ARCH= ${MACHINE_ARCH} +.else +RTLD_ARCH= ${MACHINE_CPUARCH} +.endif +CFLAGS+= -I${.CURDIR}/${RTLD_ARCH} -I${.CURDIR} +.if ${MACHINE_ARCH} == "powerpc64" +LDFLAGS+= -nostdlib -e _rtld_start +.else +LDFLAGS+= -nostdlib -e .rtld_start +.endif +WARNS?= 2 +INSTALLFLAGS= -C -b +PRECIOUSPROG= +BINDIR= /libexec +SYMLINKS= ../..${BINDIR}/${PROG} ${LIBEXECDIR}/${PROG} +MLINKS= rtld.1 ld-elf.so.1.1 \ + rtld.1 ld.so.1 + +.if ${MACHINE_CPUARCH} == "sparc64" +CFLAGS+= -fPIC +.else +CFLAGS+= -fpic +.endif +CFLAGS+= -DPIC $(DEBUG) +.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" +CFLAGS+= -fvisibility=hidden +.endif +LDFLAGS+= -shared -Wl,-Bsymbolic -Wl,-z,defs +LIBADD= c_pic +.if ${MK_TOOLCHAIN} == "no" +LDFLAGS+= -L${LIBCDIR} +.endif + +.if ${MACHINE_CPUARCH} == "arm" +# Some of the required math functions (div & mod) are implemented in +# libcompiler_rt on ARM. The library also needs to be placed first to be +# correctly linked. As some of the functions are used before we have +# shared libraries. +LIBADD+= compiler_rt +.endif -#Linker -LD = ld -#Binary File Name -OUTPUT = ld-elf.so.1 +.if ${MK_SYMVER} == "yes" +VERSION_DEF= ${LIBCSRCDIR}/Versions.def +SYMBOL_MAPS= ${.CURDIR}/Symbol.map +VERSION_MAP= Version.map +LDFLAGS+= -Wl,--version-script=${VERSION_MAP} -#Delete Program -REMOVE = rm -f +.if exists(${.CURDIR}/${RTLD_ARCH}/Symbol.map) +SYMBOL_MAPS+= ${.CURDIR}/${RTLD_ARCH}/Symbol.map +.endif +.endif -#Objects -OBJS = rtld_start.o reloc.o rtld.o rtld_lock.o map_object.o malloc.o xmalloc.o debug.o libmap.o +.sinclude "${.CURDIR}/${RTLD_ARCH}/Makefile.inc" -LIBRARIES = #../../lib/libc/libc.so +# Since moving rtld-elf to /libexec, we need to create a symlink. +# Fixup the existing binary that's there so we can symlink over it. +beforeinstall: +.if exists(${DESTDIR}/usr/libexec/${PROG}) && ${MK_STAGING} == "no" + -chflags -h noschg ${DESTDIR}/usr/libexec/${PROG} +.endif +.PATH: ${.CURDIR}/${RTLD_ARCH} -# Link The Binary -$(OUTPUT) : $(OBJS) -# $(CC) -fpic -nostdlib -shared -Wl,-soname,$(OUTPUT) -e .rtld_start -o $(OUTPUT) $(OBJS) $(LIBRARIES) $(SUBS) -# $(CC) $(CFLAGS) -nostdlib -e .rtld_start -elf -shared -Wl,-Bsymbolic -o $(OUTPUT) $(OBJS) -lc_pic -# $(CC) $(CFLAGS) -nostdlib -e .rtld_start -elf -shared -Wl,-Bsymbolic -o $(OUTPUT) $(OBJS) ./libc_pic.a #-lc_pic - $(CC) $(CFLAGS) -nostdlib -e .rtld_start -elf -shared -Wl,-Bsymbolic -o ../../build/libexec/$(OUTPUT) $(OBJS) ../../lib/libc/pic.a #-lc_pic -# strip $(OUTPUT) +.if ${MK_TESTS} != "no" +SUBDIR+= tests +.endif -# Compile the source files -.cpp.o: - $(CXX) -Wall -O $(CFLAGS) $(INCLUDES) -c -o $@ $< - -.cc.o: - $(CXX) -Wall -O $(CFLAGS) $(INCLUDES) -c -o $@ $< - -.cc.s: - $(CXX) -Wall -O $(CFLAGS) $(INCLUDES) -S -o $@ $< - -.c.o: - $(CC) -Wall -O $(CFLAGS) $(INCLUDES) -c -o $@ $< - -.c.s: - $(CC) -Wall -O $(CFLAGS) $(INCLUDES) -S -o $@ $< - -.S.o: - $(CC) -Wall $(CFLAGS) $(INCLUDES) -c -o $@ $< - -# Clean Up The junk -clean: - $(REMOVE) $(OBJS) $(OUTPUT) +.include +${PROG_FULL}: ${VERSION_MAP} +.include diff --git a/libexec/rtld-elf/Makefile.depend b/libexec/rtld-elf/Makefile.depend new file mode 100644 index 0000000..4bf7180 --- /dev/null +++ b/libexec/rtld-elf/Makefile.depend @@ -0,0 +1,14 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/Makefile.depend 284481 2015-06-16 23:37:19Z sjg $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + lib/libc \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/libexec/rtld-elf/Symbol.map b/libexec/rtld-elf/Symbol.map new file mode 100644 index 0000000..51c7438 --- /dev/null +++ b/libexec/rtld-elf/Symbol.map @@ -0,0 +1,35 @@ +/* + * $FreeBSD: releng/11.1/libexec/rtld-elf/Symbol.map 276627 2015-01-03 18:09:53Z kib $ + */ + +FBSD_1.0 { + _rtld_error; + dlclose; + dlerror; + dlopen; + dlsym; + dlfunc; + dlvsym; + dladdr; + dllockinit; + dlinfo; + dl_iterate_phdr; + r_debug_state; + __tls_get_addr; +}; + +FBSD_1.3 { + fdlopen; +}; + +FBSDprivate_1.0 { + _rtld_thread_init; + _rtld_allocate_tls; + _rtld_free_tls; + _rtld_atfork_pre; + _rtld_atfork_post; + _rtld_addr_phdr; + _rtld_get_stack_prot; + _rtld_is_dlopened; + _r_debug_postinit; +}; diff --git a/libexec/rtld-elf/aarch64/reloc.c b/libexec/rtld-elf/aarch64/reloc.c new file mode 100644 index 0000000..6dd0109 --- /dev/null +++ b/libexec/rtld-elf/aarch64/reloc.c @@ -0,0 +1,427 @@ +/*- + * Copyright (c) 2014-2015 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Andrew Turner + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/libexec/rtld-elf/aarch64/reloc.c 317189 2017-04-20 12:48:01Z andrew $"); + +#include + +#include + +#include "debug.h" +#include "rtld.h" +#include "rtld_printf.h" + +/* + * It is possible for the compiler to emit relocations for unaligned data. + * We handle this situation with these inlines. + */ +#define RELOC_ALIGNED_P(x) \ + (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) + +/* + * This is not the correct prototype, but we only need it for + * a function pointer to a simple asm function. + */ +void *_rtld_tlsdesc(void *); +void *_rtld_tlsdesc_dynamic(void *); + +void _exit(int); + +void +init_pltgot(Obj_Entry *obj) +{ + + if (obj->pltgot != NULL) { + obj->pltgot[1] = (Elf_Addr) obj; + obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; + } +} + +int +do_copy_relocations(Obj_Entry *dstobj) +{ + const Obj_Entry *srcobj, *defobj; + const Elf_Rela *relalim; + const Elf_Rela *rela; + const Elf_Sym *srcsym; + const Elf_Sym *dstsym; + const void *srcaddr; + const char *name; + void *dstaddr; + SymLook req; + size_t size; + int res; + + /* + * COPY relocs are invalid outside of the main program + */ + assert(dstobj->mainprog); + + relalim = (const Elf_Rela *)((char *)dstobj->rela + + dstobj->relasize); + for (rela = dstobj->rela; rela < relalim; rela++) { + if (ELF_R_TYPE(rela->r_info) != R_AARCH64_COPY) + continue; + + dstaddr = (void *)(dstobj->relocbase + rela->r_offset); + dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); + name = dstobj->strtab + dstsym->st_name; + size = dstsym->st_size; + + symlook_init(&req, name); + req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); + req.flags = SYMLOOK_EARLY; + + for (srcobj = globallist_next(dstobj); srcobj != NULL; + srcobj = globallist_next(srcobj)) { + res = symlook_obj(&req, srcobj); + if (res == 0) { + srcsym = req.sym_out; + defobj = req.defobj_out; + break; + } + } + if (srcobj == NULL) { + _rtld_error( +"Undefined symbol \"%s\" referenced from COPY relocation in %s", + name, dstobj->path); + return (-1); + } + + srcaddr = (const void *)(defobj->relocbase + srcsym->st_value); + memcpy(dstaddr, srcaddr, size); + } + + return (0); +} + +struct tls_data { + int64_t index; + Obj_Entry *obj; + const Elf_Rela *rela; +}; + +static struct tls_data * +reloc_tlsdesc_alloc(Obj_Entry *obj, const Elf_Rela *rela) +{ + struct tls_data *tlsdesc; + + tlsdesc = xmalloc(sizeof(struct tls_data)); + tlsdesc->index = -1; + tlsdesc->obj = obj; + tlsdesc->rela = rela; + + return (tlsdesc); +} + +/* + * Look up the symbol to find its tls index + */ +static int64_t +rtld_tlsdesc_handle_locked(struct tls_data *tlsdesc, int flags, + RtldLockState *lockstate) +{ + const Elf_Rela *rela; + const Elf_Sym *def; + const Obj_Entry *defobj; + Obj_Entry *obj; + + rela = tlsdesc->rela; + obj = tlsdesc->obj; + + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, NULL, + lockstate); + if (def == NULL) + rtld_die(); + + tlsdesc->index = defobj->tlsoffset + def->st_value + rela->r_addend; + + return (tlsdesc->index); +} + +int64_t +rtld_tlsdesc_handle(struct tls_data *tlsdesc, int flags) +{ + RtldLockState lockstate; + + /* We have already found the index, return it */ + if (tlsdesc->index >= 0) + return (tlsdesc->index); + + wlock_acquire(rtld_bind_lock, &lockstate); + /* tlsdesc->index may have been set by another thread */ + if (tlsdesc->index == -1) + rtld_tlsdesc_handle_locked(tlsdesc, flags, &lockstate); + lock_release(rtld_bind_lock, &lockstate); + + return (tlsdesc->index); +} + +static void +reloc_tlsdesc(Obj_Entry *obj, const Elf_Rela *rela, Elf_Addr *where) +{ + if (ELF_R_SYM(rela->r_info) == 0) { + where[0] = (Elf_Addr)_rtld_tlsdesc; + where[1] = obj->tlsoffset + rela->r_addend; + } else { + where[0] = (Elf_Addr)_rtld_tlsdesc_dynamic; + where[1] = (Elf_Addr)reloc_tlsdesc_alloc(obj, rela); + } +} + +/* + * Process the PLT relocations. + */ +int +reloc_plt(Obj_Entry *obj) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + Elf_Addr *where; + + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + + switch(ELF_R_TYPE(rela->r_info)) { + case R_AARCH64_JUMP_SLOT: + *where += (Elf_Addr)obj->relocbase; + break; + case R_AARCH64_TLSDESC: + reloc_tlsdesc(obj, rela, where); + break; + default: + _rtld_error("Unknown relocation type %u in PLT", + (unsigned int)ELF_R_TYPE(rela->r_info)); + return (-1); + } + } + + return (0); +} + +/* + * LD_BIND_NOW was set - force relocation for all jump slots + */ +int +reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + const Obj_Entry *defobj; + const Elf_Rela *relalim; + const Elf_Rela *rela; + const Elf_Sym *def; + struct tls_data *tlsdesc; + + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + Elf_Addr *where; + + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + switch(ELF_R_TYPE(rela->r_info)) { + case R_AARCH64_JUMP_SLOT: + def = find_symdef(ELF_R_SYM(rela->r_info), obj, + &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); + if (def == NULL) { + dbg("reloc_jmpslots: sym not found"); + return (-1); + } + + *where = (Elf_Addr)(defobj->relocbase + def->st_value); + break; + case R_AARCH64_TLSDESC: + if (ELF_R_SYM(rela->r_info) != 0) { + tlsdesc = (struct tls_data *)where[1]; + if (tlsdesc->index == -1) + rtld_tlsdesc_handle_locked(tlsdesc, + SYMLOOK_IN_PLT | flags, lockstate); + } + break; + default: + _rtld_error("Unknown relocation type %x in jmpslot", + (unsigned int)ELF_R_TYPE(rela->r_info)); + return (-1); + } + } + + return (0); +} + +int +reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, + struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +Elf_Addr +reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj, + const Obj_Entry *obj, const Elf_Rel *rel) +{ + + assert(ELF_R_TYPE(rel->r_info) == R_AARCH64_JUMP_SLOT); + + if (*where != target && !ld_bind_not) + *where = target; + return (target); +} + +void +ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) +{ +} + +/* + * Process non-PLT relocations + */ +int +reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, + RtldLockState *lockstate) +{ + const Obj_Entry *defobj; + const Elf_Rela *relalim; + const Elf_Rela *rela; + const Elf_Sym *def; + SymCache *cache; + Elf_Addr *where; + unsigned long symnum; + + if ((flags & SYMLOOK_IFUNC) != 0) + /* XXX not implemented */ + return (0); + + /* + * The dynamic loader may be called from a thread, we have + * limited amounts of stack available so we cannot use alloca(). + */ + if (obj == obj_rtld) + cache = NULL; + else + cache = calloc(obj->dynsymcount, sizeof(SymCache)); + /* No need to check for NULL here */ + + relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); + for (rela = obj->rela; rela < relalim; rela++) { + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + symnum = ELF_R_SYM(rela->r_info); + + switch (ELF_R_TYPE(rela->r_info)) { + case R_AARCH64_ABS64: + case R_AARCH64_GLOB_DAT: + def = find_symdef(symnum, obj, &defobj, flags, cache, + lockstate); + if (def == NULL) + return (-1); + + *where = (Elf_Addr)defobj->relocbase + def->st_value + + rela->r_addend; + break; + case R_AARCH64_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_AARCH64_COPY " + "relocation in shared library", obj->path); + return (-1); + } + break; + case R_AARCH64_TLSDESC: + reloc_tlsdesc(obj, rela, where); + break; + case R_AARCH64_TLS_TPREL64: + def = find_symdef(symnum, obj, &defobj, flags, cache, + lockstate); + if (def == NULL) + return (-1); + + /* + * We lazily allocate offsets for static TLS as we + * see the first relocation that references the + * TLS block. This allows us to support (small + * amounts of) static TLS in dynamically loaded + * modules. If we run out of space, we generate an + * error. + */ + if (!defobj->tls_done) { + if (!allocate_tls_offset((Obj_Entry*) defobj)) { + _rtld_error( + "%s: No space available for static " + "Thread Local Storage", obj->path); + return (-1); + } + } + + *where = def->st_value + rela->r_addend + + defobj->tlsoffset; + break; + case R_AARCH64_RELATIVE: + *where = (Elf_Addr)(obj->relocbase + rela->r_addend); + break; + default: + rtld_printf("%s: Unhandled relocation %lu\n", + obj->path, ELF_R_TYPE(rela->r_info)); + return (-1); + } + } + + return (0); +} + +void +allocate_initial_tls(Obj_Entry *objs) +{ + Elf_Addr **tp; + + /* + * Fix the size of the static TLS block by using the maximum + * offset allocated so far and adding a bit for dynamic modules to + * use. + */ + tls_static_space = tls_last_offset + tls_last_size + + RTLD_STATIC_TLS_EXTRA; + + tp = (Elf_Addr **) allocate_tls(objs, NULL, TLS_TCB_SIZE, 16); + + asm volatile("msr tpidr_el0, %0" : : "r"(tp)); +} diff --git a/libexec/rtld-elf/aarch64/rtld_machdep.h b/libexec/rtld-elf/aarch64/rtld_machdep.h new file mode 100644 index 0000000..25b1914 --- /dev/null +++ b/libexec/rtld-elf/aarch64/rtld_machdep.h @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 1999, 2000 John D. Polstra. + * Copyright (c) 2014 the FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Andrew Turner + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/aarch64/rtld_machdep.h 316135 2017-03-29 11:03:08Z kib $ + */ + +#ifndef RTLD_MACHDEP_H +#define RTLD_MACHDEP_H 1 + +#include +#include + +struct Struct_Obj_Entry; + +/* Return the address of the .dynamic section in the dynamic linker. */ +#define rtld_dynamic(obj) \ +({ \ + Elf_Addr _dynamic_addr; \ + asm volatile("adr %0, _DYNAMIC" : "=&r"(_dynamic_addr)); \ + (const Elf_Dyn *)_dynamic_addr; \ +}) + +Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, + const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj, + const Elf_Rel *rel); + +#define make_function_pointer(def, defobj) \ + ((defobj)->relocbase + (def)->st_value) + +#define call_initfini_pointer(obj, target) \ + (((InitFunc)(target))()) + +#define call_init_pointer(obj, target) \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)) + +#define call_ifunc_resolver(ptr) \ + (((Elf_Addr (*)(void))ptr)()) + +#define round(size, align) \ + (((size) + (align) - 1) & ~((align) - 1)) +#define calculate_first_tls_offset(size, align) \ + round(16, align) +#define calculate_tls_offset(prev_offset, prev_size, size, align) \ + round(prev_offset + prev_size, align) +#define calculate_tls_end(off, size) ((off) + (size)) + +#define TLS_TCB_SIZE 16 +typedef struct { + unsigned long ti_module; + unsigned long ti_offset; +} tls_index; + +extern void *__tls_get_addr(tls_index *ti); + +#define RTLD_DEFAULT_STACK_PF_EXEC PF_X +#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC + +#define md_abi_variant_hook(x) + +#endif diff --git a/libexec/rtld-elf/aarch64/rtld_start.S b/libexec/rtld-elf/aarch64/rtld_start.S new file mode 100644 index 0000000..627ae0c --- /dev/null +++ b/libexec/rtld-elf/aarch64/rtld_start.S @@ -0,0 +1,162 @@ +/*- + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Andrew Turner under + * sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/libexec/rtld-elf/aarch64/rtld_start.S 293832 2016-01-13 15:54:17Z andrew $"); + +ENTRY(.rtld_start) + mov x19, x0 /* Put ps_strings in a callee-saved register */ + mov x20, sp /* And the stack pointer */ + + sub sp, sp, #16 /* Make room for obj_main & exit proc */ + + mov x1, sp /* exit_proc */ + add x2, x1, #8 /* obj_main */ + bl _rtld /* Call the loader */ + mov x8, x0 /* Backup the entry point */ + + ldr x2, [sp] /* Load cleanup */ + ldr x1, [sp, #8] /* Load obj_main */ + mov x0, x19 /* Restore ps_strings */ + mov sp, x20 /* Restore the stack pointer */ + br x8 /* Jump to the entry point */ +END(.rtld_start) + +/* + * sp + 0 = &GOT[x + 3] + * sp + 8 = RA + * x16 = &GOT[2] + * x17 = &_rtld_bind_start + */ +ENTRY(_rtld_bind_start) + mov x17, sp + + /* Save the arguments */ + stp x0, x1, [sp, #-16]! + stp x2, x3, [sp, #-16]! + stp x4, x5, [sp, #-16]! + stp x6, x7, [sp, #-16]! + stp x8, xzr, [sp, #-16]! + + /* Save any floating-point arguments */ + stp q0, q1, [sp, #-32]! + stp q2, q3, [sp, #-32]! + stp q4, q5, [sp, #-32]! + stp q6, q7, [sp, #-32]! + + /* Calculate reloff */ + ldr x2, [x17, #0] /* Get the address of the entry */ + sub x1, x2, x16 /* Find its offset */ + sub x1, x1, #8 /* Adjust for x16 not being at offset 0 */ + /* Each rela item has 3 entriesso we need reloff = 3 * index */ + lsl x3, x1, #1 /* x3 = 2 * offset */ + add x1, x1, x3 /* x1 = x3 + offset = 3 * offset */ + + /* Load obj */ + ldr x0, [x16, #-8] + + /* Call into rtld */ + bl _rtld_bind + + /* Restore the registers saved by the plt code */ + ldp xzr, x30, [sp, #(5 * 16 + 4 * 32)] + + /* Backup the address to branch to */ + mov x16, x0 + + /* restore the arguments */ + ldp q6, q7, [sp], #32 + ldp q4, q5, [sp], #32 + ldp q2, q3, [sp], #32 + ldp q0, q1, [sp], #32 + ldp x8, xzr, [sp], #16 + ldp x6, x7, [sp], #16 + ldp x4, x5, [sp], #16 + ldp x2, x3, [sp], #16 + ldp x0, x1, [sp], #16 + /* And the part of the stack the plt entry handled */ + add sp, sp, #16 + + /* Call into the correct function */ + br x16 +END(_rtld_bind_start) + +/* + * uint64_t _rtld_tlsdesc(struct tlsdesc *); + * + * struct tlsdesc { + * uint64_t ptr; + * uint64_t data; + * }; + * + * Returns the data. + */ +ENTRY(_rtld_tlsdesc) + ldr x0, [x0, #8] + ret +END(_rtld_tlsdesc) + +/* + * uint64_t _rtld_tlsdesc_dynamic(struct tlsdesc *); + * + * TODO: We could lookup the saved index here to skip saving the entire stack. + */ +ENTRY(_rtld_tlsdesc_dynamic) + /* Store any registers we may use in rtld_tlsdesc_handle */ + stp x29, x30, [sp, #-(10 * 16)]! + mov x29, sp + stp x1, x2, [sp, #(1 * 16)] + stp x3, x4, [sp, #(2 * 16)] + stp x5, x6, [sp, #(3 * 16)] + stp x7, x8, [sp, #(4 * 16)] + stp x9, x10, [sp, #(5 * 16)] + stp x11, x12, [sp, #(6 * 16)] + stp x13, x14, [sp, #(7 * 16)] + stp x15, x16, [sp, #(8 * 16)] + stp x17, x18, [sp, #(9 * 16)] + + /* Find the tls offset */ + ldr x0, [x0, #8] + mov x1, #1 + bl rtld_tlsdesc_handle + + /* Restore the registers */ + ldp x17, x18, [sp, #(9 * 16)] + ldp x15, x16, [sp, #(8 * 16)] + ldp x13, x14, [sp, #(7 * 16)] + ldp x11, x12, [sp, #(6 * 16)] + ldp x9, x10, [sp, #(5 * 16)] + ldp x7, x8, [sp, #(4 * 16)] + ldp x5, x6, [sp, #(3 * 16)] + ldp x3, x4, [sp, #(2 * 16)] + ldp x1, x2, [sp, #(1 * 16)] + ldp x29, x30, [sp], #(10 * 16) + + ret +END(_rtld_tlsdesc_dynamic) diff --git a/libexec/rtld-elf/amd64/Makefile.inc b/libexec/rtld-elf/amd64/Makefile.inc new file mode 100644 index 0000000..b845219 --- /dev/null +++ b/libexec/rtld-elf/amd64/Makefile.inc @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/amd64/Makefile.inc 286317 2015-08-05 12:53:55Z vangyzen $ + +CFLAGS+= ${CFLAGS_NO_SIMD} -msoft-float +# Uncomment this to build the dynamic linker as an executable instead +# of a shared library: +#LDSCRIPT= ${.CURDIR}/${MACHINE_CPUARCH}/elf_rtld.x diff --git a/libexec/rtld-elf/amd64/elf_rtld.x b/libexec/rtld-elf/amd64/elf_rtld.x new file mode 100644 index 0000000..e19168c --- /dev/null +++ b/libexec/rtld-elf/amd64/elf_rtld.x @@ -0,0 +1,131 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", + "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/obj/usr/src/tmp/usr/i386-unknown-freebsdelf/lib); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0x08000000 + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.text : + { *(.rel.text) *(.rel.gnu.linkonce.t*) } + .rela.text : + { *(.rela.text) *(.rela.gnu.linkonce.t*) } + .rel.data : + { *(.rel.data) *(.rel.gnu.linkonce.d*) } + .rela.data : + { *(.rela.data) *(.rela.gnu.linkonce.d*) } + .rel.rodata : + { *(.rel.rodata) *(.rel.gnu.linkonce.r*) } + .rela.rodata : + { *(.rela.rodata) *(.rela.gnu.linkonce.r*) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } =0x9090 + .plt : { *(.plt) } + .text : + { + *(.text) + *(.stub) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t*) + } =0x9090 + _etext = .; + PROVIDE (etext = .); + .fini : { *(.fini) } =0x9090 + .rodata : { *(.rodata) *(.gnu.linkonce.r*) } + .rodata1 : { *(.rodata1) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(0x1000) + (. & (0x1000 - 1)); + .data : + { + *(.data) + *(.gnu.linkonce.d*) + CONSTRUCTORS + } + .data1 : { *(.data1) } + .ctors : + { + *(.ctors) + } + .dtors : + { + *(.dtors) + } + .got : { *(.got.plt) *(.got) } + .dynamic : { *(.dynamic) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .sbss : { *(.sbss) *(.scommon) } + .bss : + { + *(.dynbss) + *(.bss) + *(COMMON) + } + . = ALIGN(32 / 8); + _end = . ; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* These must appear regardless of . */ +} diff --git a/libexec/rtld-elf/amd64/reloc.c b/libexec/rtld-elf/amd64/reloc.c new file mode 100644 index 0000000..40fd216 --- /dev/null +++ b/libexec/rtld-elf/amd64/reloc.c @@ -0,0 +1,507 @@ +/*- + * Copyright 1996, 1997, 1998, 1999 John D. Polstra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/amd64/reloc.c 316135 2017-03-29 11:03:08Z kib $ + */ + +/* + * Dynamic linker for ELF. + * + * John Polstra . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "rtld.h" +#include "rtld_tls.h" + +/* + * Process the special R_X86_64_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 +do_copy_relocations(Obj_Entry *dstobj) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + + assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ + + relalim = (const Elf_Rela *) ((caddr_t) dstobj->rela + dstobj->relasize); + for (rela = dstobj->rela; rela < relalim; rela++) { + if (ELF_R_TYPE(rela->r_info) == R_X86_64_COPY) { + void *dstaddr; + const Elf_Sym *dstsym; + const char *name; + size_t size; + const void *srcaddr; + const Elf_Sym *srcsym; + const Obj_Entry *srcobj, *defobj; + SymLook req; + int res; + + dstaddr = (void *) (dstobj->relocbase + rela->r_offset); + dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); + name = dstobj->strtab + dstsym->st_name; + size = dstsym->st_size; + symlook_init(&req, name); + req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); + req.flags = SYMLOOK_EARLY; + + for (srcobj = globallist_next(dstobj); srcobj != NULL; + srcobj = globallist_next(srcobj)) { + res = symlook_obj(&req, srcobj); + if (res == 0) { + srcsym = req.sym_out; + defobj = req.defobj_out; + break; + } + } + + if (srcobj == NULL) { + _rtld_error("Undefined symbol \"%s\" referenced from COPY" + " relocation in %s", name, dstobj->path); + return -1; + } + + srcaddr = (const void *) (defobj->relocbase + srcsym->st_value); + memcpy(dstaddr, srcaddr, size); + } + } + + return 0; +} + +/* Initialize the special GOT entries. */ +void +init_pltgot(Obj_Entry *obj) +{ + if (obj->pltgot != NULL) { + obj->pltgot[1] = (Elf_Addr) obj; + obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; + } +} + +/* Process the non-PLT relocations. */ +int +reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, + RtldLockState *lockstate) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + SymCache *cache; + const Elf_Sym *def; + const Obj_Entry *defobj; + Elf_Addr *where, symval; + Elf32_Addr *where32; + int r; + + r = -1; + /* + * The dynamic loader may be called from a thread, we have + * limited amounts of stack available so we cannot use alloca(). + */ + if (obj != obj_rtld) { + cache = calloc(obj->dynsymcount, sizeof(SymCache)); + /* No need to check for NULL here */ + } else + cache = NULL; + + relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); + for (rela = obj->rela; rela < relalim; rela++) { + /* + * First, resolve symbol for relocations which + * reference symbols. + */ + switch (ELF_R_TYPE(rela->r_info)) { + case R_X86_64_64: + case R_X86_64_PC32: + case R_X86_64_GLOB_DAT: + case R_X86_64_TPOFF64: + case R_X86_64_TPOFF32: + case R_X86_64_DTPMOD64: + case R_X86_64_DTPOFF64: + case R_X86_64_DTPOFF32: + def = find_symdef(ELF_R_SYM(rela->r_info), obj, + &defobj, flags, cache, lockstate); + if (def == NULL) + goto done; + /* + * If symbol is IFUNC, only perform relocation + * when caller allowed it by passing + * SYMLOOK_IFUNC flag. Skip the relocations + * otherwise. + * + * Also error out in case IFUNC relocations + * are specified for TLS, which cannot be + * usefully interpreted. + */ + if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { + switch (ELF_R_TYPE(rela->r_info)) { + case R_X86_64_64: + case R_X86_64_PC32: + case R_X86_64_GLOB_DAT: + if ((flags & SYMLOOK_IFUNC) == 0) { + obj->non_plt_gnu_ifunc = true; + continue; + } + symval = (Elf_Addr)rtld_resolve_ifunc( + defobj, def); + break; + case R_X86_64_TPOFF64: + case R_X86_64_TPOFF32: + case R_X86_64_DTPMOD64: + case R_X86_64_DTPOFF64: + case R_X86_64_DTPOFF32: + _rtld_error("%s: IFUNC for TLS reloc", + obj->path); + goto done; + } + } else { + if ((flags & SYMLOOK_IFUNC) != 0) + continue; + symval = (Elf_Addr)defobj->relocbase + + def->st_value; + } + break; + default: + if ((flags & SYMLOOK_IFUNC) != 0) + continue; + break; + } + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + where32 = (Elf32_Addr *)where; + + switch (ELF_R_TYPE(rela->r_info)) { + case R_X86_64_NONE: + break; + case R_X86_64_64: + *where = symval + rela->r_addend; + break; + case R_X86_64_PC32: + /* + * I don't think the dynamic linker should + * ever see this type of relocation. But the + * binutils-2.6 tools sometimes generate it. + */ + *where32 = (Elf32_Addr)(unsigned long)(symval + + rela->r_addend - (Elf_Addr)where); + break; + /* missing: R_X86_64_GOT32 R_X86_64_PLT32 */ + case R_X86_64_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_X86_64_COPY " + "relocation in shared library", obj->path); + goto done; + } + break; + case R_X86_64_GLOB_DAT: + *where = symval; + break; + case R_X86_64_TPOFF64: + /* + * We lazily allocate offsets for static TLS + * as we see the first relocation that + * references the TLS block. This allows us to + * support (small amounts of) static TLS in + * dynamically loaded modules. If we run out + * of space, we generate an error. + */ + if (!defobj->tls_done) { + if (!allocate_tls_offset((Obj_Entry*) defobj)) { + _rtld_error("%s: No space available " + "for static Thread Local Storage", + obj->path); + goto done; + } + } + *where = (Elf_Addr)(def->st_value - defobj->tlsoffset + + rela->r_addend); + break; + case R_X86_64_TPOFF32: + /* + * We lazily allocate offsets for static TLS + * as we see the first relocation that + * references the TLS block. This allows us to + * support (small amounts of) static TLS in + * dynamically loaded modules. If we run out + * of space, we generate an error. + */ + if (!defobj->tls_done) { + if (!allocate_tls_offset((Obj_Entry*) defobj)) { + _rtld_error("%s: No space available " + "for static Thread Local Storage", + obj->path); + goto done; + } + } + *where32 = (Elf32_Addr)(def->st_value - + defobj->tlsoffset + rela->r_addend); + break; + case R_X86_64_DTPMOD64: + *where += (Elf_Addr)defobj->tlsindex; + break; + case R_X86_64_DTPOFF64: + *where += (Elf_Addr)(def->st_value + rela->r_addend); + break; + case R_X86_64_DTPOFF32: + *where32 += (Elf32_Addr)(def->st_value + + rela->r_addend); + break; + case R_X86_64_RELATIVE: + *where = (Elf_Addr)(obj->relocbase + rela->r_addend); + break; + /* + * missing: + * R_X86_64_GOTPCREL, R_X86_64_32, R_X86_64_32S, R_X86_64_16, + * R_X86_64_PC16, R_X86_64_8, R_X86_64_PC8 + */ + default: + _rtld_error("%s: Unsupported relocation type %u" + " in non-PLT relocations\n", obj->path, + (unsigned int)ELF_R_TYPE(rela->r_info)); + goto done; + } + } + r = 0; +done: + free(cache); + return (r); +} + +/* Process the PLT relocations. */ +int +reloc_plt(Obj_Entry *obj) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + Elf_Addr *where; + + switch(ELF_R_TYPE(rela->r_info)) { + case R_X86_64_JMP_SLOT: + /* Relocate the GOT slot pointing into the PLT. */ + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + *where += (Elf_Addr)obj->relocbase; + break; + + case R_X86_64_IRELATIVE: + obj->irelative = true; + break; + + default: + _rtld_error("Unknown relocation type %x in PLT", + (unsigned int)ELF_R_TYPE(rela->r_info)); + return (-1); + } + } + return 0; +} + +/* Relocate the jump slots in an object. */ +int +reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + + if (obj->jmpslots_done) + return 0; + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + Elf_Addr *where, target; + const Elf_Sym *def; + const Obj_Entry *defobj; + + switch (ELF_R_TYPE(rela->r_info)) { + case R_X86_64_JMP_SLOT: + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + SYMLOOK_IN_PLT | flags, NULL, lockstate); + if (def == NULL) + return (-1); + if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { + obj->gnu_ifunc = true; + continue; + } + target = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend); + reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela); + break; + + case R_X86_64_IRELATIVE: + break; + + default: + _rtld_error("Unknown relocation type %x in PLT", + (unsigned int)ELF_R_TYPE(rela->r_info)); + return (-1); + } + } + obj->jmpslots_done = true; + return 0; +} + +/* Fixup the jump slot at "where" to transfer control to "target". */ +Elf_Addr +reloc_jmpslot(Elf_Addr *where, Elf_Addr target, + const struct Struct_Obj_Entry *obj, const struct Struct_Obj_Entry *refobj, + const Elf_Rel *rel) +{ +#ifdef dbg + dbg("reloc_jmpslot: *%p = %p", where, (void *)target); +#endif + if (!ld_bind_not) + *where = target; + return (target); +} + +int +reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + + if (!obj->irelative) + return (0); + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + Elf_Addr *where, target, *ptr; + + switch (ELF_R_TYPE(rela->r_info)) { + case R_X86_64_JMP_SLOT: + break; + + case R_X86_64_IRELATIVE: + ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend); + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + lock_release(rtld_bind_lock, lockstate); + target = call_ifunc_resolver(ptr); + wlock_acquire(rtld_bind_lock, lockstate); + *where = target; + break; + } + } + obj->irelative = false; + return (0); +} + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + + if (!obj->gnu_ifunc) + return (0); + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + Elf_Addr *where, target; + const Elf_Sym *def; + const Obj_Entry *defobj; + + switch (ELF_R_TYPE(rela->r_info)) { + case R_X86_64_JMP_SLOT: + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + SYMLOOK_IN_PLT | flags, NULL, lockstate); + if (def == NULL) + return (-1); + if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC) + continue; + lock_release(rtld_bind_lock, lockstate); + target = (Elf_Addr)rtld_resolve_ifunc(defobj, def); + wlock_acquire(rtld_bind_lock, lockstate); + reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela); + break; + } + } + obj->gnu_ifunc = false; + return (0); +} + +uint32_t cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2; + +void +ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) +{ + u_int p[4], cpu_high; + + do_cpuid(1, p); + cpu_feature = p[3]; + cpu_feature2 = p[2]; + do_cpuid(0, p); + cpu_high = p[0]; + if (cpu_high >= 7) { + cpuid_count(7, 0, p); + cpu_stdext_feature = p[1]; + cpu_stdext_feature2 = p[2]; + } +} + +void +allocate_initial_tls(Obj_Entry *objs) +{ + /* + * Fix the size of the static TLS block by using the maximum + * offset allocated so far and adding a bit for dynamic modules to + * use. + */ + tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA; + amd64_set_fsbase(allocate_tls(objs, 0, + 3*sizeof(Elf_Addr), sizeof(Elf_Addr))); +} + +void *__tls_get_addr(tls_index *ti) +{ + Elf_Addr** segbase; + + __asm __volatile("movq %%fs:0, %0" : "=r" (segbase)); + + return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset); +} diff --git a/libexec/rtld-elf/amd64/rtld_machdep.h b/libexec/rtld-elf/amd64/rtld_machdep.h new file mode 100644 index 0000000..47285e0 --- /dev/null +++ b/libexec/rtld-elf/amd64/rtld_machdep.h @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 1999, 2000 John D. Polstra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/amd64/rtld_machdep.h 316135 2017-03-29 11:03:08Z kib $ + */ + +#ifndef RTLD_MACHDEP_H +#define RTLD_MACHDEP_H 1 + +#include +#include + +struct Struct_Obj_Entry; + +/* Return the address of the .dynamic section in the dynamic linker. */ +Elf_Dyn *rtld_dynamic_addr(void); +#define rtld_dynamic(obj) rtld_dynamic_addr() + +Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, + const struct Struct_Obj_Entry *obj, const struct Struct_Obj_Entry *refobj, + const Elf_Rel *rel); + +#define make_function_pointer(def, defobj) \ + ((defobj)->relocbase + (def)->st_value) + +#define call_initfini_pointer(obj, target) \ + (((InitFunc)(target))()) + +#define call_init_pointer(obj, target) \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)) + +extern uint32_t cpu_feature; +extern uint32_t cpu_feature2; +extern uint32_t cpu_stdext_feature; +extern uint32_t cpu_stdext_feature2; +#define call_ifunc_resolver(ptr) \ + (((Elf_Addr (*)(uint32_t, uint32_t, uint32_t, uint32_t))ptr)( \ + cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2)) + +#define round(size, align) \ + (((size) + (align) - 1) & ~((align) - 1)) +#define calculate_first_tls_offset(size, align) \ + round(size, align) +#define calculate_tls_offset(prev_offset, prev_size, size, align) \ + round((prev_offset) + (size), align) +#define calculate_tls_end(off, size) (off) + +typedef struct { + unsigned long ti_module; + unsigned long ti_offset; +} tls_index; + +void *__tls_get_addr(tls_index *ti) __exported; + +#define RTLD_DEFAULT_STACK_PF_EXEC PF_X +#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC + +#define md_abi_variant_hook(x) + +#endif diff --git a/libexec/rtld-elf/amd64/rtld_start.S b/libexec/rtld-elf/amd64/rtld_start.S new file mode 100644 index 0000000..a3551dd --- /dev/null +++ b/libexec/rtld-elf/amd64/rtld_start.S @@ -0,0 +1,171 @@ +/*- + * Copyright 1996-1998 John D. Polstra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/amd64/rtld_start.S 312789 2017-01-26 03:05:27Z emaste $ + */ + + .text + .align 4 + .globl .rtld_start + .type .rtld_start,@function +.rtld_start: + xorq %rbp,%rbp # Clear frame pointer for good form + subq $24,%rsp # A place to store exit procedure addr + movq %rdi,%r12 + movq %rsp,%rsi # save address of exit proc + movq %rsp,%rdx # construct address of obj_main + addq $8,%rdx + call _rtld # Call rtld(sp); returns entry point + popq %rsi # Get exit procedure address + movq %r12,%rdi # *ap +/* + * At this point, %rax contains the entry point of the main program, and + * %rdx contains a pointer to a termination function that should be + * registered with atexit(). (crt1.o registers it.) + */ +.globl .rtld_goto_main +.rtld_goto_main: # This symbol exists just to make debugging easier. + jmp *%rax # Enter main program + + +/* + * Binder entry point. Control is transferred to here by code in the PLT. + * On entry, there are two arguments on the stack. In ascending address + * order, they are (1) "obj", a pointer to the calling object's Obj_Entry, + * and (2) "reloff", the byte offset of the appropriate relocation entry + * in the PLT relocation table. + * + * We are careful to preserve all registers, even the caller-save + * registers. That is because this code may be invoked by low-level + * assembly-language code that is not ABI-compliant. + * + * Stack map: + * reloff 0x60 + * obj 0x58 + * spare 0x50 + * rflags 0x48 + * rax 0x40 + * rdx 0x38 + * rcx 0x30 + * rsi 0x28 + * rdi 0x20 + * r8 0x18 + * r9 0x10 + * r10 0x8 + * r11 0x0 + */ + .align 4 + .globl _rtld_bind_start + .type _rtld_bind_start,@function +_rtld_bind_start: + .cfi_startproc + .cfi_adjust_cfa_offset 16 + subq $8,%rsp + .cfi_adjust_cfa_offset 8 + pushfq # Save rflags + .cfi_adjust_cfa_offset 8 + pushq %rax # Save %rax + .cfi_adjust_cfa_offset 8 + .cfi_offset %rax,-32 + pushq %rdx # Save %rdx + .cfi_adjust_cfa_offset 8 + .cfi_offset %rdx,-40 + pushq %rcx # Save %rcx + .cfi_adjust_cfa_offset 8 + .cfi_offset %rcx,-48 + pushq %rsi # Save %rsi + .cfi_adjust_cfa_offset 8 + .cfi_offset %rsi,-56 + pushq %rdi # Save %rdi + .cfi_adjust_cfa_offset 8 + .cfi_offset %rdi,-64 + pushq %r8 # Save %r8 + .cfi_adjust_cfa_offset 8 + .cfi_offset %r8,-72 + pushq %r9 # Save %r9 + .cfi_adjust_cfa_offset 8 + .cfi_offset %r9,-80 + pushq %r10 # Save %r10 + .cfi_adjust_cfa_offset 8 + .cfi_offset %r10,-88 + pushq %r11 # Save %r11 + .cfi_adjust_cfa_offset 8 + .cfi_offset %r11,-96 + + movq 0x58(%rsp),%rdi # Fetch obj argument + movq 0x60(%rsp),%rsi # Fetch reloff argument + leaq (%rsi,%rsi,2),%rsi # multiply by 3 + leaq (,%rsi,8),%rsi # now 8, for 24 (sizeof Elf_Rela) + + call _rtld_bind # Transfer control to the binder + /* Now %rax contains the entry point of the function being called. */ + + movq %rax,0x60(%rsp) # Store target over reloff argument + popq %r11 # Restore %r11 + .cfi_adjust_cfa_offset -8 + .cfi_restore %r11 + popq %r10 # Restore %r10 + .cfi_adjust_cfa_offset -8 + .cfi_restore %r10 + popq %r9 # Restore %r9 + .cfi_adjust_cfa_offset -8 + .cfi_restore %r9 + popq %r8 # Restore %r8 + .cfi_adjust_cfa_offset -8 + .cfi_restore %r8 + popq %rdi # Restore %rdi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rdi + popq %rsi # Restore %rsi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rsi + popq %rcx # Restore %rcx + .cfi_adjust_cfa_offset -8 + .cfi_restore %rcx + popq %rdx # Restore %rdx + .cfi_adjust_cfa_offset -8 + .cfi_restore %rdx + popq %rax # Restore %rax + .cfi_adjust_cfa_offset -8 + .cfi_restore %rax + popfq # Restore rflags + .cfi_adjust_cfa_offset -8 + leaq 16(%rsp),%rsp # Discard spare, obj, do not change rflags + ret # "Return" to target address + .cfi_endproc + .size _rtld_bind_start, . - _rtld_bind_start + + .align 4 + .globl rtld_dynamic_addr + .type rtld_dynamic_addr,@function +rtld_dynamic_addr: + .cfi_startproc + .weak _DYNAMIC + .hidden _DYNAMIC + lea _DYNAMIC(%rip),%rax + ret + .cfi_endproc + .size rtld_dynamic_addr, . - rtld_dynamic_addr + + .section .note.GNU-stack,"",%progbits diff --git a/libexec/rtld-elf/arm/Makefile.inc b/libexec/rtld-elf/arm/Makefile.inc new file mode 100644 index 0000000..dc02a7a --- /dev/null +++ b/libexec/rtld-elf/arm/Makefile.inc @@ -0,0 +1 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/arm/Makefile.inc 130646 2004-06-17 17:53:16Z cognet $ diff --git a/libexec/rtld-elf/arm/reloc.c b/libexec/rtld-elf/arm/reloc.c new file mode 100644 index 0000000..a456a8e --- /dev/null +++ b/libexec/rtld-elf/arm/reloc.c @@ -0,0 +1,524 @@ +/* $NetBSD: mdreloc.c,v 1.23 2003/07/26 15:04:38 mrg Exp $ */ + +#include +__FBSDID("$FreeBSD: releng/11.1/libexec/rtld-elf/arm/reloc.c 316135 2017-03-29 11:03:08Z kib $"); +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "machine/sysarch.h" + +#include "debug.h" +#include "rtld.h" +#include "paths.h" + +void +arm_abi_variant_hook(Elf_Auxinfo **aux_info) +{ + Elf_Word ehdr; + struct stat sb; + + /* + * If we're running an old kernel that doesn't provide any data fail + * safe by doing nothing. + */ + if (aux_info[AT_EHDRFLAGS] == NULL) + return; + ehdr = aux_info[AT_EHDRFLAGS]->a_un.a_val; + + /* + * Hard float ABI binaries are the default, and use the default paths + * and such. + */ + if ((ehdr & EF_ARM_VFP_FLOAT) != 0) + return; + + /* + * If there's no /usr/libsoft, then we don't have a system with both + * hard and soft float. In that case, hope for the best and just + * return. Such systems are required to have all soft or all hard + * float ABI binaries and libraries. This is, at best, a transition + * compatibility hack. Once we're fully hard-float, this should + * be removed. + */ + if (stat("/usr/libsoft", &sb) != 0 || !S_ISDIR(sb.st_mode)) + return; + + /* + * This is a soft float ABI binary. We need to use the soft float + * settings. + */ + ld_elf_hints_default = _PATH_SOFT_ELF_HINTS; + ld_path_libmap_conf = _PATH_SOFT_LIBMAP_CONF; + ld_path_rtld = _PATH_SOFT_RTLD; + ld_standard_library_path = SOFT_STANDARD_LIBRARY_PATH; + ld_env_prefix = LD_SOFT_; +} + +void +init_pltgot(Obj_Entry *obj) +{ + if (obj->pltgot != NULL) { + obj->pltgot[1] = (Elf_Addr) obj; + obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; + } +} + +int +do_copy_relocations(Obj_Entry *dstobj) +{ + const Elf_Rel *rellim; + const Elf_Rel *rel; + + assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ + + rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize); + for (rel = dstobj->rel; rel < rellim; rel++) { + if (ELF_R_TYPE(rel->r_info) == R_ARM_COPY) { + void *dstaddr; + const Elf_Sym *dstsym; + const char *name; + size_t size; + const void *srcaddr; + const Elf_Sym *srcsym; + const Obj_Entry *srcobj, *defobj; + SymLook req; + int res; + + dstaddr = (void *) (dstobj->relocbase + rel->r_offset); + dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info); + name = dstobj->strtab + dstsym->st_name; + size = dstsym->st_size; + + symlook_init(&req, name); + req.ventry = fetch_ventry(dstobj, + ELF_R_SYM(rel->r_info)); + req.flags = SYMLOOK_EARLY; + + for (srcobj = globallist_next(dstobj); srcobj != NULL; + srcobj = globallist_next(srcobj)) { + res = symlook_obj(&req, srcobj); + if (res == 0) { + srcsym = req.sym_out; + defobj = req.defobj_out; + break; + } + } + if (srcobj == NULL) { + _rtld_error( +"Undefined symbol \"%s\" referenced from COPY relocation in %s", + name, dstobj->path); + return (-1); + } + + srcaddr = (const void *)(defobj->relocbase + + srcsym->st_value); + memcpy(dstaddr, srcaddr, size); + } + } + return 0; +} + +void _rtld_bind_start(void); +void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); + +int open(); +int _open(); +void +_rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) +{ + const Elf_Rel *rel = NULL, *rellim; + Elf_Addr relsz = 0; + Elf_Addr *where; + uint32_t size; + + for (; dynp->d_tag != DT_NULL; dynp++) { + switch (dynp->d_tag) { + case DT_REL: + rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); + break; + case DT_RELSZ: + relsz = dynp->d_un.d_val; + break; + } + } + rellim = (const Elf_Rel *)((caddr_t)rel + relsz); + size = (rellim - 1)->r_offset - rel->r_offset; + for (; rel < rellim; rel++) { + where = (Elf_Addr *)(relocbase + rel->r_offset); + + *where += (Elf_Addr)relocbase; + } +} +/* + * It is possible for the compiler to emit relocations for unaligned data. + * We handle this situation with these inlines. + */ +#define RELOC_ALIGNED_P(x) \ + (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) + +static __inline Elf_Addr +load_ptr(void *where) +{ + Elf_Addr res; + + memcpy(&res, where, sizeof(res)); + + return (res); +} + +static __inline void +store_ptr(void *where, Elf_Addr val) +{ + + memcpy(where, &val, sizeof(val)); +} + +static int +reloc_nonplt_object(Obj_Entry *obj, const Elf_Rel *rel, SymCache *cache, + int flags, RtldLockState *lockstate) +{ + Elf_Addr *where; + const Elf_Sym *def; + const Obj_Entry *defobj; + Elf_Addr tmp; + unsigned long symnum; + + where = (Elf_Addr *)(obj->relocbase + rel->r_offset); + symnum = ELF_R_SYM(rel->r_info); + + switch (ELF_R_TYPE(rel->r_info)) { + case R_ARM_NONE: + break; + +#if 1 /* XXX should not occur */ + case R_ARM_PC24: { /* word32 S - P + A */ + Elf32_Sword addend; + + /* + * Extract addend and sign-extend if needed. + */ + addend = *where; + if (addend & 0x00800000) + addend |= 0xff000000; + + def = find_symdef(symnum, obj, &defobj, flags, cache, + lockstate); + if (def == NULL) + return -1; + tmp = (Elf_Addr)obj->relocbase + def->st_value + - (Elf_Addr)where + (addend << 2); + if ((tmp & 0xfe000000) != 0xfe000000 && + (tmp & 0xfe000000) != 0) { + _rtld_error( + "%s: R_ARM_PC24 relocation @ %p to %s failed " + "(displacement %ld (%#lx) out of range)", + obj->path, where, + obj->strtab + obj->symtab[symnum].st_name, + (long) tmp, (long) tmp); + return -1; + } + tmp >>= 2; + *where = (*where & 0xff000000) | (tmp & 0x00ffffff); + dbg("PC24 %s in %s --> %p @ %p in %s", + obj->strtab + obj->symtab[symnum].st_name, + obj->path, (void *)*where, where, defobj->path); + break; + } +#endif + + case R_ARM_ABS32: /* word32 B + S + A */ + case R_ARM_GLOB_DAT: /* word32 B + S */ + def = find_symdef(symnum, obj, &defobj, flags, cache, + lockstate); + if (def == NULL) + return -1; + if (__predict_true(RELOC_ALIGNED_P(where))) { + tmp = *where + (Elf_Addr)defobj->relocbase + + def->st_value; + *where = tmp; + } else { + tmp = load_ptr(where) + + (Elf_Addr)defobj->relocbase + + def->st_value; + store_ptr(where, tmp); + } + dbg("ABS32/GLOB_DAT %s in %s --> %p @ %p in %s", + obj->strtab + obj->symtab[symnum].st_name, + obj->path, (void *)tmp, where, defobj->path); + break; + + case R_ARM_RELATIVE: /* word32 B + A */ + if (__predict_true(RELOC_ALIGNED_P(where))) { + tmp = *where + (Elf_Addr)obj->relocbase; + *where = tmp; + } else { + tmp = load_ptr(where) + + (Elf_Addr)obj->relocbase; + store_ptr(where, tmp); + } + dbg("RELATIVE in %s --> %p", obj->path, + (void *)tmp); + break; + + case R_ARM_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; + } + dbg("COPY (avoid in main)"); + break; + + case R_ARM_TLS_DTPOFF32: + def = find_symdef(symnum, obj, &defobj, flags, cache, + lockstate); + if (def == NULL) + return -1; + + tmp = (Elf_Addr)(def->st_value); + if (__predict_true(RELOC_ALIGNED_P(where))) + *where = tmp; + else + store_ptr(where, tmp); + + dbg("TLS_DTPOFF32 %s in %s --> %p", + obj->strtab + obj->symtab[symnum].st_name, + obj->path, (void *)tmp); + + break; + case R_ARM_TLS_DTPMOD32: + def = find_symdef(symnum, obj, &defobj, flags, cache, + lockstate); + if (def == NULL) + return -1; + + tmp = (Elf_Addr)(defobj->tlsindex); + if (__predict_true(RELOC_ALIGNED_P(where))) + *where = tmp; + else + store_ptr(where, tmp); + + dbg("TLS_DTPMOD32 %s in %s --> %p", + obj->strtab + obj->symtab[symnum].st_name, + obj->path, (void *)tmp); + + break; + + case R_ARM_TLS_TPOFF32: + def = find_symdef(symnum, obj, &defobj, flags, cache, + lockstate); + if (def == NULL) + return -1; + + if (!defobj->tls_done && allocate_tls_offset(obj)) + return -1; + + /* XXX: FIXME */ + tmp = (Elf_Addr)def->st_value + defobj->tlsoffset + + TLS_TCB_SIZE; + if (__predict_true(RELOC_ALIGNED_P(where))) + *where = tmp; + else + store_ptr(where, tmp); + dbg("TLS_TPOFF32 %s in %s --> %p", + obj->strtab + obj->symtab[symnum].st_name, + obj->path, (void *)tmp); + break; + + + default: + dbg("sym = %lu, type = %lu, offset = %p, " + "contents = %p, symbol = %s", + symnum, (u_long)ELF_R_TYPE(rel->r_info), + (void *)rel->r_offset, (void *)load_ptr(where), + obj->strtab + obj->symtab[symnum].st_name); + _rtld_error("%s: Unsupported relocation type %ld " + "in non-PLT relocations\n", + obj->path, (u_long) ELF_R_TYPE(rel->r_info)); + return -1; + } + return 0; +} + +/* + * * Process non-PLT relocations + * */ +int +reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, + RtldLockState *lockstate) +{ + const Elf_Rel *rellim; + const Elf_Rel *rel; + SymCache *cache; + int r = -1; + + /* The relocation for the dynamic loader has already been done. */ + if (obj == obj_rtld) + return (0); + if ((flags & SYMLOOK_IFUNC) != 0) + /* XXX not implemented */ + return (0); + + /* + * The dynamic loader may be called from a thread, we have + * limited amounts of stack available so we cannot use alloca(). + */ + cache = calloc(obj->dynsymcount, sizeof(SymCache)); + /* No need to check for NULL here */ + + rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize); + for (rel = obj->rel; rel < rellim; rel++) { + if (reloc_nonplt_object(obj, rel, cache, flags, lockstate) < 0) + goto done; + } + r = 0; +done: + if (cache != NULL) + free(cache); + return (r); +} + +/* + * * Process the PLT relocations. + * */ +int +reloc_plt(Obj_Entry *obj) +{ + const Elf_Rel *rellim; + const Elf_Rel *rel; + + rellim = (const Elf_Rel *)((char *)obj->pltrel + + obj->pltrelsize); + for (rel = obj->pltrel; rel < rellim; rel++) { + Elf_Addr *where; + + assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT); + + where = (Elf_Addr *)(obj->relocbase + rel->r_offset); + *where += (Elf_Addr )obj->relocbase; + } + + return (0); +} + +/* + * * LD_BIND_NOW was set - force relocation for all jump slots + * */ +int +reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + const Obj_Entry *defobj; + const Elf_Rel *rellim; + const Elf_Rel *rel; + const Elf_Sym *def; + Elf_Addr *where; + Elf_Addr target; + + rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); + for (rel = obj->pltrel; rel < rellim; rel++) { + assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT); + where = (Elf_Addr *)(obj->relocbase + rel->r_offset); + def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, + SYMLOOK_IN_PLT | flags, NULL, lockstate); + if (def == NULL) { + dbg("reloc_jmpslots: sym not found"); + return (-1); + } + + target = (Elf_Addr)(defobj->relocbase + def->st_value); + reloc_jmpslot(where, target, defobj, obj, + (const Elf_Rel *) rel); + } + + obj->jmpslots_done = true; + + return (0); +} + +int +reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, + struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +Elf_Addr +reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj, + const Obj_Entry *obj, const Elf_Rel *rel) +{ + + assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT); + + if (*where != target && !ld_bind_not) + *where = target; + return (target); +} + +void +ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) +{ +} + +void +allocate_initial_tls(Obj_Entry *objs) +{ +#ifdef ARM_TP_ADDRESS + void **_tp = (void **)ARM_TP_ADDRESS; +#endif + + /* + * Fix the size of the static TLS block by using the maximum + * offset allocated so far and adding a bit for dynamic modules to + * use. + */ + + tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA; + +#ifdef ARM_TP_ADDRESS + (*_tp) = (void *) allocate_tls(objs, NULL, TLS_TCB_SIZE, 8); +#else + sysarch(ARM_SET_TP, allocate_tls(objs, NULL, TLS_TCB_SIZE, 8)); +#endif +} + +void * +__tls_get_addr(tls_index* ti) +{ + char *p; +#ifdef ARM_TP_ADDRESS + void **_tp = (void **)ARM_TP_ADDRESS; + + p = tls_get_addr_common((Elf_Addr **)(*_tp), ti->ti_module, ti->ti_offset); +#else + void *_tp; + __asm __volatile("mrc p15, 0, %0, c13, c0, 3" \ + : "=r" (_tp)); + p = tls_get_addr_common((Elf_Addr **)(_tp), ti->ti_module, ti->ti_offset); +#endif + + return (p); +} diff --git a/libexec/rtld-elf/arm/rtld_machdep.h b/libexec/rtld-elf/arm/rtld_machdep.h new file mode 100644 index 0000000..9c323db --- /dev/null +++ b/libexec/rtld-elf/arm/rtld_machdep.h @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 1999, 2000 John D. Polstra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/arm/rtld_machdep.h 319434 2017-06-01 15:12:51Z vangyzen $ + */ + +#ifndef RTLD_MACHDEP_H +#define RTLD_MACHDEP_H 1 + +#include +#include + +struct Struct_Obj_Entry; + +/* Return the address of the .dynamic section in the dynamic linker. */ +#define rtld_dynamic(obj) (&_DYNAMIC) + +Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, + const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj, + const Elf_Rel *rel); + +#define make_function_pointer(def, defobj) \ + ((defobj)->relocbase + (def)->st_value) + +#define call_initfini_pointer(obj, target) \ + (((InitFunc)(target))()) + +#define call_init_pointer(obj, target) \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)) + +#define call_ifunc_resolver(ptr) \ + (((Elf_Addr (*)(void))ptr)()) + +#define TLS_TCB_SIZE 8 +typedef struct { + unsigned long ti_module; + unsigned long ti_offset; +} tls_index; + +#define round(size, align) \ + (((size) + (align) - 1) & ~((align) - 1)) +#define calculate_first_tls_offset(size, align) \ + round(8, align) +#define calculate_tls_offset(prev_offset, prev_size, size, align) \ + round(prev_offset + prev_size, align) +#define calculate_tls_end(off, size) ((off) + (size)) + +extern void *__tls_get_addr(tls_index *ti); + +#define RTLD_DEFAULT_STACK_PF_EXEC PF_X +#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC + +extern void arm_abi_variant_hook(Elf_Auxinfo **); + +#define md_abi_variant_hook(x) arm_abi_variant_hook(x) +#define RTLD_VARIANT_ENV_NAMES + +#endif diff --git a/libexec/rtld-elf/arm/rtld_start.S b/libexec/rtld-elf/arm/rtld_start.S new file mode 100644 index 0000000..181e8a6 --- /dev/null +++ b/libexec/rtld-elf/arm/rtld_start.S @@ -0,0 +1,100 @@ +/* $NetBSD: rtld_start.S,v 1.7 2002/09/12 17:18:38 mycroft Exp $ */ + +/*- + * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matt Thomas and by Charles M. Hannum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/libexec/rtld-elf/arm/rtld_start.S 288373 2015-09-29 16:09:58Z kib $"); + + .text + .align 0 + .globl .rtld_start + .type .rtld_start,%function +.rtld_start: + mov r6, sp /* save the stack pointer */ + bic sp, sp, #7 /* align the stack pointer */ + sub sp, sp, #8 /* make room for obj_main & exit proc */ + mov r4, r0 /* save ps_strings */ + ldr sl, .L2 + ldr r5, .L2+4 + ldr r0, .L2+8 +.L1: + add sl, pc, sl + ldr r5, [sl, r5] + ldr r0, [sl, r0] + + sub r1, sl, r5 /* relocbase */ + add r0, r1, r0 /* &_DYNAMIC */ + bl _rtld_relocate_nonplt_self + mov r1, sp + add r2, sp, #4 + mov r0, r6 /* load the sp the kernel gave us */ + bl _rtld /* call the shared loader */ + mov r3, r0 /* save entry point */ + + ldr r2, [sp, #0] /* r2 = cleanup */ + ldr r1, [sp, #4] /* r1 = obj_main */ + mov sp, r6 /* restore stack */ + mov r0, r4 /* restore ps_strings */ + mov pc, r3 /* jump to the entry point */ +.L2: + .word _GLOBAL_OFFSET_TABLE_ - (.L1+8) + .word _GLOBAL_OFFSET_TABLE_(GOT) + .word _DYNAMIC(GOT) + + .align 0 + .globl _rtld_bind_start + .type _rtld_bind_start,%function +/* + * stack[0] = RA + * ip = &GOT[n+3] + * lr = &GOT[2] + */ +_rtld_bind_start: + stmdb sp!,{r0-r5,sl,fp} + + sub r1, ip, lr /* r1 = 4 * (n + 1) */ + sub r1, r1, #4 /* r1 = 4 * n */ + add r1, r1, r1 /* r1 = 8 * n */ + + ldr r0, [lr, #-4] /* get obj ptr from GOT[1] */ + mov r4, ip /* save GOT location */ + + mov r5, sp /* Save the stack pointer */ + bic sp, sp, #7 /* Align the stack pointer */ + bl _rtld_bind /* Call the binder */ + mov sp, r5 /* Restore the old stack pointer */ + + str r0, [r4] /* save address in GOT */ + mov ip, r0 /* save new address */ + + ldmia sp!,{r0-r5,sl,fp,lr} /* restore the stack */ + mov pc, ip /* jump to the new address */ + + .section .note.GNU-stack,"",%progbits diff --git a/libexec/rtld-elf/debug.c b/libexec/rtld-elf/debug.c index 3a83b66..dfca5fa 100644 --- a/libexec/rtld-elf/debug.c +++ b/libexec/rtld-elf/debug.c @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/libexec/rtld-elf/debug.c,v 1.4 2003/06/19 16:09:18 mdodd Exp $ + * $FreeBSD: releng/11.1/libexec/rtld-elf/debug.c 294373 2016-01-20 07:21:33Z kib $ */ /* @@ -34,6 +34,7 @@ #include "debug.h" #include "rtld.h" +#include "rtld_printf.h" static const char rel_header[] = " symbol name r_info r_offset st_value st_size address value\n" @@ -49,9 +50,8 @@ va_list ap; va_start(ap, format); - fflush(stdout); - vfprintf(stderr, format, ap); - putc('\n', stderr); + rtld_vfdprintf(STDERR_FILENO, format, ap); + rtld_fdputchar(STDERR_FILENO, '\n'); va_end(ap); } @@ -62,7 +62,8 @@ { Obj_Entry *obj; - for (obj = obj0; obj != NULL; obj = obj->next) { + for (obj = globallist_curr(obj0); obj != NULL; + obj = globallist_next(obj)) { dump_obj_relocations(obj); } } @@ -71,28 +72,28 @@ dump_obj_relocations (Obj_Entry *obj) { - printf("Object \"%s\", relocbase %p\n", obj->path, obj->relocbase); + rtld_printf("Object \"%s\", relocbase %p\n", obj->path, obj->relocbase); if (obj->relsize) { - printf("Non-PLT Relocations: %ld\n", + rtld_printf("Non-PLT Relocations: %ld\n", (obj->relsize / sizeof(Elf_Rel))); dump_Elf_Rel(obj, obj->rel, obj->relsize); } if (obj->relasize) { - printf("Non-PLT Relocations with Addend: %ld\n", + rtld_printf("Non-PLT Relocations with Addend: %ld\n", (obj->relasize / sizeof(Elf_Rela))); dump_Elf_Rela(obj, obj->rela, obj->relasize); } if (obj->pltrelsize) { - printf("PLT Relocations: %ld\n", + rtld_printf("PLT Relocations: %ld\n", (obj->pltrelsize / sizeof(Elf_Rel))); dump_Elf_Rel(obj, obj->pltrel, obj->pltrelsize); } if (obj->pltrelasize) { - printf("PLT Relocations with Addend: %ld\n", + rtld_printf("PLT Relocations with Addend: %ld\n", (obj->pltrelasize / sizeof(Elf_Rela))); dump_Elf_Rela(obj, obj->pltrela, obj->pltrelasize); } @@ -106,12 +107,12 @@ const Elf_Sym *sym; Elf_Addr *dstaddr; - printf("%s", rel_header); + rtld_putstr(rel_header); rellim = (const Elf_Rel *)((const char *)rel0 + relsize); for (rel = rel0; rel < rellim; rel++) { dstaddr = (Elf_Addr *)(obj->relocbase + rel->r_offset); sym = obj->symtab + ELF_R_SYM(rel->r_info); - printf(rel_format, + rtld_printf(rel_format, obj->strtab + sym->st_name, (u_long)rel->r_info, (u_long)rel->r_offset, (u_long)sym->st_value, (int)sym->st_size, @@ -128,12 +129,12 @@ const Elf_Sym *sym; Elf_Addr *dstaddr; - printf("%s", rel_header); + rtld_putstr(rel_header); relalim = (const Elf_Rela *)((const char *)rela0 + relasize); for (rela = rela0; rela < relalim; rela++) { dstaddr = (Elf_Addr *)(obj->relocbase + rela->r_offset); sym = obj->symtab + ELF_R_SYM(rela->r_info); - printf(rel_format, + rtld_printf(rel_format, obj->strtab + sym->st_name, (u_long)rela->r_info, (u_long)rela->r_offset, (u_long)sym->st_value, (int)sym->st_size, diff --git a/libexec/rtld-elf/debug.h b/libexec/rtld-elf/debug.h index 68faa0a..93fc88f 100644 --- a/libexec/rtld-elf/debug.h +++ b/libexec/rtld-elf/debug.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/libexec/rtld-elf/debug.h,v 1.6 2004/03/21 01:21:26 peter Exp $ + * $FreeBSD: releng/11.1/libexec/rtld-elf/debug.h 282551 2015-05-06 15:29:11Z emaste $ */ /* @@ -32,10 +32,6 @@ #ifndef DEBUG_H #define DEBUG_H 1 -#ifndef __GNUC__ -#error "This file must be compiled with GCC" -#endif - #include #include @@ -45,9 +41,9 @@ extern int debug; #ifdef DEBUG -#define dbg(format, args...) debug_printf(format , ## args) +#define dbg(...) debug_printf(__VA_ARGS__) #else -#define dbg(format, args...) ((void) 0) +#define dbg(...) ((void) 0) #endif #ifndef COMPAT_32BIT diff --git a/libexec/rtld-elf/i386/Makefile.inc b/libexec/rtld-elf/i386/Makefile.inc new file mode 100644 index 0000000..bb887a4 --- /dev/null +++ b/libexec/rtld-elf/i386/Makefile.inc @@ -0,0 +1,6 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/i386/Makefile.inc 286317 2015-08-05 12:53:55Z vangyzen $ + +CFLAGS+= ${CFLAGS_NO_SIMD} -msoft-float +# Uncomment this to build the dynamic linker as an executable instead +# of a shared library: +#LDSCRIPT= ${.CURDIR}/${MACHINE_CPUARCH}/elf_rtld.x diff --git a/libexec/rtld-elf/i386/Symbol.map b/libexec/rtld-elf/i386/Symbol.map new file mode 100644 index 0000000..6d66b9b --- /dev/null +++ b/libexec/rtld-elf/i386/Symbol.map @@ -0,0 +1,7 @@ +/* + * $FreeBSD: releng/11.1/libexec/rtld-elf/i386/Symbol.map 169092 2007-04-29 14:05:22Z deischen $ + */ + +FBSD_1.0 { + ___tls_get_addr; +}; diff --git a/libexec/rtld-elf/i386/elf_rtld.x b/libexec/rtld-elf/i386/elf_rtld.x new file mode 100644 index 0000000..e19168c --- /dev/null +++ b/libexec/rtld-elf/i386/elf_rtld.x @@ -0,0 +1,131 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", + "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/obj/usr/src/tmp/usr/i386-unknown-freebsdelf/lib); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0x08000000 + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.text : + { *(.rel.text) *(.rel.gnu.linkonce.t*) } + .rela.text : + { *(.rela.text) *(.rela.gnu.linkonce.t*) } + .rel.data : + { *(.rel.data) *(.rel.gnu.linkonce.d*) } + .rela.data : + { *(.rela.data) *(.rela.gnu.linkonce.d*) } + .rel.rodata : + { *(.rel.rodata) *(.rel.gnu.linkonce.r*) } + .rela.rodata : + { *(.rela.rodata) *(.rela.gnu.linkonce.r*) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } =0x9090 + .plt : { *(.plt) } + .text : + { + *(.text) + *(.stub) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t*) + } =0x9090 + _etext = .; + PROVIDE (etext = .); + .fini : { *(.fini) } =0x9090 + .rodata : { *(.rodata) *(.gnu.linkonce.r*) } + .rodata1 : { *(.rodata1) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(0x1000) + (. & (0x1000 - 1)); + .data : + { + *(.data) + *(.gnu.linkonce.d*) + CONSTRUCTORS + } + .data1 : { *(.data1) } + .ctors : + { + *(.ctors) + } + .dtors : + { + *(.dtors) + } + .got : { *(.got.plt) *(.got) } + .dynamic : { *(.dynamic) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .sbss : { *(.sbss) *(.scommon) } + .bss : + { + *(.dynbss) + *(.bss) + *(COMMON) + } + . = ALIGN(32 / 8); + _end = . ; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* These must appear regardless of . */ +} diff --git a/libexec/rtld-elf/i386/reloc.c b/libexec/rtld-elf/i386/reloc.c new file mode 100644 index 0000000..5073931 --- /dev/null +++ b/libexec/rtld-elf/i386/reloc.c @@ -0,0 +1,507 @@ +/*- + * Copyright 1996, 1997, 1998, 1999 John D. Polstra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/i386/reloc.c 316135 2017-03-29 11:03:08Z kib $ + */ + +/* + * Dynamic linker for ELF. + * + * John Polstra . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "rtld.h" +#include "rtld_tls.h" + +/* + * Process the special R_386_COPY relocations in the main program. These + * copy data from a shared object into a region in the main program's BSS + * segment. + * + * Returns 0 on success, -1 on failure. + */ +int +do_copy_relocations(Obj_Entry *dstobj) +{ + const Elf_Rel *rellim; + const Elf_Rel *rel; + + assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ + + rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize); + for (rel = dstobj->rel; rel < rellim; rel++) { + if (ELF_R_TYPE(rel->r_info) == R_386_COPY) { + void *dstaddr; + const Elf_Sym *dstsym; + const char *name; + size_t size; + const void *srcaddr; + const Elf_Sym *srcsym; + const Obj_Entry *srcobj, *defobj; + SymLook req; + int res; + + dstaddr = (void *) (dstobj->relocbase + rel->r_offset); + dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info); + name = dstobj->strtab + dstsym->st_name; + size = dstsym->st_size; + symlook_init(&req, name); + req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rel->r_info)); + req.flags = SYMLOOK_EARLY; + + for (srcobj = globallist_next(dstobj); srcobj != NULL; + srcobj = globallist_next(srcobj)) { + res = symlook_obj(&req, srcobj); + if (res == 0) { + srcsym = req.sym_out; + defobj = req.defobj_out; + break; + } + } + + if (srcobj == NULL) { + _rtld_error("Undefined symbol \"%s\" referenced from COPY" + " relocation in %s", name, dstobj->path); + return -1; + } + + srcaddr = (const void *) (defobj->relocbase + srcsym->st_value); + memcpy(dstaddr, srcaddr, size); + } + } + + return 0; +} + +/* Initialize the special GOT entries. */ +void +init_pltgot(Obj_Entry *obj) +{ + if (obj->pltgot != NULL) { + obj->pltgot[1] = (Elf_Addr) obj; + obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; + } +} + +/* Process the non-PLT relocations. */ +int +reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, + RtldLockState *lockstate) +{ + const Elf_Rel *rellim; + const Elf_Rel *rel; + SymCache *cache; + const Elf_Sym *def; + const Obj_Entry *defobj; + Elf_Addr *where, symval, add; + int r; + + r = -1; + /* + * The dynamic loader may be called from a thread, we have + * limited amounts of stack available so we cannot use alloca(). + */ + if (obj != obj_rtld) { + cache = calloc(obj->dynsymcount, sizeof(SymCache)); + /* No need to check for NULL here */ + } else + cache = NULL; + + rellim = (const Elf_Rel *)((caddr_t) obj->rel + obj->relsize); + for (rel = obj->rel; rel < rellim; rel++) { + switch (ELF_R_TYPE(rel->r_info)) { + case R_386_32: + case R_386_PC32: + case R_386_GLOB_DAT: + case R_386_TLS_TPOFF: + case R_386_TLS_TPOFF32: + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: + def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, + flags, cache, lockstate); + if (def == NULL) + goto done; + if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { + switch (ELF_R_TYPE(rel->r_info)) { + case R_386_32: + case R_386_PC32: + case R_386_GLOB_DAT: + if ((flags & SYMLOOK_IFUNC) == 0) { + obj->non_plt_gnu_ifunc = true; + continue; + } + symval = (Elf_Addr)rtld_resolve_ifunc( + defobj, def); + break; + case R_386_TLS_TPOFF: + case R_386_TLS_TPOFF32: + case R_386_TLS_DTPMOD32: + case R_386_TLS_DTPOFF32: + _rtld_error("%s: IFUNC for TLS reloc", + obj->path); + goto done; + } + } else { + if ((flags & SYMLOOK_IFUNC) != 0) + continue; + symval = (Elf_Addr)defobj->relocbase + + def->st_value; + } + break; + default: + if ((flags & SYMLOOK_IFUNC) != 0) + continue; + break; + } + where = (Elf_Addr *)(obj->relocbase + rel->r_offset); + + switch (ELF_R_TYPE(rel->r_info)) { + case R_386_NONE: + break; + case R_386_32: + *where += symval; + 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. + */ + *where += symval - (Elf_Addr)where; + break; + case R_386_COPY: + /* + * These are deferred until all other + * relocations have been done. All we do here + * is make sure that the COPY relocation is + * not in a shared library. They are allowed + * only in executable files. + */ + if (!obj->mainprog) { + _rtld_error("%s: Unexpected R_386_COPY " + "relocation in shared library", obj->path); + goto done; + } + break; + case R_386_GLOB_DAT: + *where = symval; + break; + case R_386_RELATIVE: + *where += (Elf_Addr)obj->relocbase; + break; + case R_386_TLS_TPOFF: + case R_386_TLS_TPOFF32: + /* + * We lazily allocate offsets for static TLS + * as we see the first relocation that + * references the TLS block. This allows us to + * support (small amounts of) static TLS in + * dynamically loaded modules. If we run out + * of space, we generate an error. + */ + if (!defobj->tls_done) { + if (!allocate_tls_offset((Obj_Entry*) defobj)) { + _rtld_error("%s: No space available " + "for static Thread Local Storage", + obj->path); + goto done; + } + } + add = (Elf_Addr)(def->st_value - defobj->tlsoffset); + if (ELF_R_TYPE(rel->r_info) == R_386_TLS_TPOFF) + *where += add; + else + *where -= add; + break; + case R_386_TLS_DTPMOD32: + *where += (Elf_Addr)defobj->tlsindex; + break; + case R_386_TLS_DTPOFF32: + *where += (Elf_Addr) def->st_value; + break; + default: + _rtld_error("%s: Unsupported relocation type %d" + " in non-PLT relocations\n", obj->path, + ELF_R_TYPE(rel->r_info)); + goto done; + } + } + r = 0; +done: + free(cache); + return (r); +} + +/* Process the PLT relocations. */ +int +reloc_plt(Obj_Entry *obj) +{ + const Elf_Rel *rellim; + const Elf_Rel *rel; + + rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); + for (rel = obj->pltrel; rel < rellim; rel++) { + Elf_Addr *where/*, val*/; + + switch (ELF_R_TYPE(rel->r_info)) { + case R_386_JMP_SLOT: + /* Relocate the GOT slot pointing into the PLT. */ + where = (Elf_Addr *)(obj->relocbase + rel->r_offset); + *where += (Elf_Addr)obj->relocbase; + break; + + case R_386_IRELATIVE: + obj->irelative = true; + break; + + default: + _rtld_error("Unknown relocation type %x in PLT", + ELF_R_TYPE(rel->r_info)); + return (-1); + } + } + return 0; +} + +/* Relocate the jump slots in an object. */ +int +reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + const Elf_Rel *rellim; + const Elf_Rel *rel; + + if (obj->jmpslots_done) + return 0; + rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); + for (rel = obj->pltrel; rel < rellim; rel++) { + Elf_Addr *where, target; + const Elf_Sym *def; + const Obj_Entry *defobj; + + switch (ELF_R_TYPE(rel->r_info)) { + case R_386_JMP_SLOT: + where = (Elf_Addr *)(obj->relocbase + rel->r_offset); + def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, + SYMLOOK_IN_PLT | flags, NULL, lockstate); + if (def == NULL) + return (-1); + if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { + obj->gnu_ifunc = true; + continue; + } + target = (Elf_Addr)(defobj->relocbase + def->st_value); + reloc_jmpslot(where, target, defobj, obj, rel); + break; + + case R_386_IRELATIVE: + break; + + default: + _rtld_error("Unknown relocation type %x in PLT", + ELF_R_TYPE(rel->r_info)); + return (-1); + } + } + + obj->jmpslots_done = true; + return 0; +} + +/* Fixup the jump slot at "where" to transfer control to "target". */ +Elf_Addr +reloc_jmpslot(Elf_Addr *where, Elf_Addr target, + const struct Struct_Obj_Entry *obj, const struct Struct_Obj_Entry *refobj, + const Elf_Rel *rel) +{ +#ifdef dbg + dbg("reloc_jmpslot: *%p = %p", where, (void *)target); +#endif + if (!ld_bind_not) + *where = target; + return (target); +} + +int +reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate) +{ + const Elf_Rel *rellim; + const Elf_Rel *rel; + Elf_Addr *where, target; + + if (!obj->irelative) + return (0); + rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); + for (rel = obj->pltrel; rel < rellim; rel++) { + switch (ELF_R_TYPE(rel->r_info)) { + case R_386_IRELATIVE: + where = (Elf_Addr *)(obj->relocbase + rel->r_offset); + lock_release(rtld_bind_lock, lockstate); + target = call_ifunc_resolver(obj->relocbase + *where); + wlock_acquire(rtld_bind_lock, lockstate); + *where = target; + break; + } + } + obj->irelative = false; + return (0); +} + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + const Elf_Rel *rellim; + const Elf_Rel *rel; + + if (!obj->gnu_ifunc) + return (0); + rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); + for (rel = obj->pltrel; rel < rellim; rel++) { + Elf_Addr *where, target; + const Elf_Sym *def; + const Obj_Entry *defobj; + + switch (ELF_R_TYPE(rel->r_info)) { + case R_386_JMP_SLOT: + where = (Elf_Addr *)(obj->relocbase + rel->r_offset); + def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, + SYMLOOK_IN_PLT | flags, NULL, lockstate); + if (def == NULL) + return (-1); + if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC) + continue; + lock_release(rtld_bind_lock, lockstate); + target = (Elf_Addr)rtld_resolve_ifunc(defobj, def); + wlock_acquire(rtld_bind_lock, lockstate); + reloc_jmpslot(where, target, defobj, obj, rel); + break; + } + } + + obj->gnu_ifunc = false; + return (0); +} + +uint32_t cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2; + +static void +rtld_cpuid_count(int idx, int cnt, u_int *p) +{ + + __asm __volatile( + " pushl %%ebx\n" + " cpuid\n" + " movl %%ebx,%1\n" + " popl %%ebx\n" + : "=a" (p[0]), "=r" (p[1]), "=c" (p[2]), "=d" (p[3]) + : "0" (idx), "2" (cnt)); +} + +void +ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) +{ + u_int p[4], cpu_high; + int cpuid_supported; + + __asm __volatile( + " pushfl\n" + " popl %%eax\n" + " movl %%eax,%%ecx\n" + " xorl $0x200000,%%eax\n" + " pushl %%eax\n" + " popfl\n" + " pushfl\n" + " popl %%eax\n" + " xorl %%eax,%%ecx\n" + " je 1f\n" + " movl $1,%0\n" + " jmp 2f\n" + "1: movl $0,%0\n" + "2:\n" + : "=r" (cpuid_supported) : : "eax", "ecx"); + if (!cpuid_supported) + return; + + rtld_cpuid_count(1, 0, p); + cpu_feature = p[3]; + cpu_feature2 = p[2]; + rtld_cpuid_count(0, 0, p); + cpu_high = p[0]; + if (cpu_high >= 7) { + rtld_cpuid_count(7, 0, p); + cpu_stdext_feature = p[1]; + cpu_stdext_feature2 = p[2]; + } +} + +void +allocate_initial_tls(Obj_Entry *objs) +{ + void* tls; + + /* + * Fix the size of the static TLS block by using the maximum + * offset allocated so far and adding a bit for dynamic modules to + * use. + */ + tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA; + tls = allocate_tls(objs, NULL, 3*sizeof(Elf_Addr), sizeof(Elf_Addr)); + i386_set_gsbase(tls); +} + +/* GNU ABI */ +__attribute__((__regparm__(1))) +void *___tls_get_addr(tls_index *ti) +{ + Elf_Addr** segbase; + + __asm __volatile("movl %%gs:0, %0" : "=r" (segbase)); + + return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset); +} + +/* Sun ABI */ +void *__tls_get_addr(tls_index *ti) +{ + Elf_Addr** segbase; + + __asm __volatile("movl %%gs:0, %0" : "=r" (segbase)); + + return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset); +} diff --git a/libexec/rtld-elf/i386/rtld_machdep.h b/libexec/rtld-elf/i386/rtld_machdep.h new file mode 100644 index 0000000..c78261c --- /dev/null +++ b/libexec/rtld-elf/i386/rtld_machdep.h @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 1999, 2000 John D. Polstra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/i386/rtld_machdep.h 316135 2017-03-29 11:03:08Z kib $ + */ + +#ifndef RTLD_MACHDEP_H +#define RTLD_MACHDEP_H 1 + +#include +#include + +struct Struct_Obj_Entry; + +/* Return the address of the .dynamic section in the dynamic linker. */ +#define rtld_dynamic(obj) \ + ((const Elf_Dyn *)((obj)->relocbase + (Elf_Addr)&_DYNAMIC)) + +Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, + const struct Struct_Obj_Entry *obj, const struct Struct_Obj_Entry *refobj, + const Elf_Rel *rel); + +#define make_function_pointer(def, defobj) \ + ((defobj)->relocbase + (def)->st_value) + +#define call_initfini_pointer(obj, target) \ + (((InitFunc)(target))()) + +#define call_init_pointer(obj, target) \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)) + +extern uint32_t cpu_feature; +extern uint32_t cpu_feature2; +extern uint32_t cpu_stdext_feature; +extern uint32_t cpu_stdext_feature2; +#define call_ifunc_resolver(ptr) \ + (((Elf_Addr (*)(uint32_t, uint32_t, uint32_t, uint32_t))ptr)( \ + cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2)) + +#define round(size, align) \ + (((size) + (align) - 1) & ~((align) - 1)) +#define calculate_first_tls_offset(size, align) \ + round(size, align) +#define calculate_tls_offset(prev_offset, prev_size, size, align) \ + round((prev_offset) + (size), align) +#define calculate_tls_end(off, size) (off) + +typedef struct { + unsigned long ti_module; + unsigned long ti_offset; +} tls_index; + +void *___tls_get_addr(tls_index *ti) __attribute__((__regparm__(1))) __exported; +void *__tls_get_addr(tls_index *ti) __exported; + +#define RTLD_DEFAULT_STACK_PF_EXEC PF_X +#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC + +#define md_abi_variant_hook(x) + +#endif diff --git a/libexec/rtld-elf/i386/rtld_start.S b/libexec/rtld-elf/i386/rtld_start.S new file mode 100644 index 0000000..1e7662e --- /dev/null +++ b/libexec/rtld-elf/i386/rtld_start.S @@ -0,0 +1,93 @@ +/*- + * Copyright 1996-1998 John D. Polstra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/i386/rtld_start.S 280816 2015-03-29 18:53:21Z kib $ + */ + + .text + .align 4 + .globl .rtld_start + .type .rtld_start,@function +.rtld_start: + xorl %ebp,%ebp # Clear frame pointer for good form + movl %esp,%eax # Save initial stack pointer + movl %esp,%esi # Save initial stack pointer + andl $0xfffffff0,%esp # Align stack pointer + subl $16,%esp # A place to store exit procedure addr + movl %esp,%ebx # save address of exit proc + movl %esp,%ecx # construct address of obj_main + addl $4,%ecx + subl $4,%esp # Keep stack aligned + pushl %ecx # Pass address of obj_main + pushl %ebx # Pass address of exit proc + pushl %eax # Pass initial stack pointer to rtld + call _rtld # Call rtld(sp); returns entry point + addl $16,%esp # Remove arguments from stack + popl %edx # Get exit procedure address + movl %esi,%esp # Ignore obj_main +/* + * At this point, %eax contains the entry point of the main program, and + * %edx contains a pointer to a termination function that should be + * registered with atexit(). (crt1.o registers it.) + */ +.globl .rtld_goto_main +.rtld_goto_main: # This symbol exists just to make debugging easier. + jmp *%eax # Enter main program + + +/* + * Binder entry point. Control is transferred to here by code in the PLT. + * On entry, there are two arguments on the stack. In ascending address + * order, they are (1) "obj", a pointer to the calling object's Obj_Entry, + * and (2) "reloff", the byte offset of the appropriate relocation entry + * in the PLT relocation table. + * + * We are careful to preserve all registers, even the caller-save + * registers. That is because this code may be invoked by low-level + * assembly-language code that is not ABI-compliant. + */ + .align 4 + .globl _rtld_bind_start + .type _rtld_bind_start,@function +_rtld_bind_start: + pushf # Save eflags + pushl %eax # Save %eax + pushl %edx # Save %edx + pushl %ecx # Save %ecx + pushl 20(%esp) # Copy reloff argument + pushl 20(%esp) # Copy obj argument + + call _rtld_bind # Transfer control to the binder + /* Now %eax contains the entry point of the function being called. */ + + addl $8,%esp # Discard binder arguments + movl %eax,20(%esp) # Store target over obj argument + popl %ecx # Restore %ecx + popl %edx # Restore %edx + popl %eax # Restore %eax + popf # Restore eflags + leal 4(%esp),%esp # Discard reloff, do not change eflags + ret # "Return" to target address + + .section .note.GNU-stack,"",%progbits diff --git a/libexec/rtld-elf/libmap.c b/libexec/rtld-elf/libmap.c index b78fe4c..677e29d 100644 --- a/libexec/rtld-elf/libmap.c +++ b/libexec/rtld-elf/libmap.c @@ -1,32 +1,27 @@ /* - * $FreeBSD: src/libexec/rtld-elf/libmap.c,v 1.14 2005/02/04 02:46:41 mdodd Exp $ + * $FreeBSD: releng/11.1/libexec/rtld-elf/libmap.c 290223 2015-10-31 04:39:55Z imp $ */ -#include -#include -#include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include "debug.h" #include "rtld.h" #include "libmap.h" - -#ifndef _PATH_LIBMAP_CONF -#define _PATH_LIBMAP_CONF "/etc/libmap.conf" -#endif - -#ifdef COMPAT_32BIT -#undef _PATH_LIBMAP_CONF -#define _PATH_LIBMAP_CONF "/etc/libmap32.conf" -#endif +#include "paths.h" TAILQ_HEAD(lm_list, lm); struct lm { char *f; char *t; - TAILQ_ENTRY(lm) lm_link; }; @@ -38,76 +33,198 @@ TAILQ_ENTRY(lmp) lmp_link; }; -static int lm_count; +static TAILQ_HEAD(lmc_list, lmc) lmc_head = TAILQ_HEAD_INITIALIZER(lmc_head); +struct lmc { + char *path; + TAILQ_ENTRY(lmc) next; +}; -static void lmc_parse (FILE *); -static void lm_add (const char *, const char *, const char *); -static void lm_free (struct lm_list *); -static char * lml_find (struct lm_list *, const char *); -static struct lm_list * lmp_find (const char *); -static struct lm_list * lmp_init (char *); -static const char * quickbasename (const char *); -static int readstrfn (void * cookie, char *buf, int len); -static int closestrfn (void * cookie); +static int lm_count; + +static void lmc_parse(char *, size_t); +static void lmc_parse_file(char *); +static void lmc_parse_dir(char *); +static void lm_add(const char *, const char *, const char *); +static void lm_free(struct lm_list *); +static char *lml_find(struct lm_list *, const char *); +static struct lm_list *lmp_find(const char *); +static struct lm_list *lmp_init(char *); +static const char *quickbasename(const char *); #define iseol(c) (((c) == '#') || ((c) == '\0') || \ ((c) == '\n') || ((c) == '\r')) +/* + * Do not use ctype.h macros, which rely on working TLS. It is + * too early to have thread-local variables functional. + */ +#define rtld_isspace(c) ((c) == ' ' || (c) == '\t') + int -lm_init (char *libmap_override) +lm_init(char *libmap_override) { - FILE *fp; + char *p; - dbg("%s(\"%s\")", __func__, libmap_override); - + dbg("lm_init(\"%s\")", libmap_override); TAILQ_INIT(&lmp_head); - fp = fopen(_PATH_LIBMAP_CONF, "r"); - if (fp) { - lmc_parse(fp); - fclose(fp); - } + lmc_parse_file(ld_path_libmap_conf); if (libmap_override) { - char *p; - /* do some character replacement to make $LIBMAP look like a - text file, then "open" it with funopen */ + /* + * Do some character replacement to make $LDLIBMAP look + * like a text file, then parse it. + */ libmap_override = xstrdup(libmap_override); - for (p = libmap_override; *p; p++) { switch (*p) { - case '=': - *p = ' '; break; - case ',': - *p = '\n'; break; + case '=': + *p = ' '; + break; + case ',': + *p = '\n'; + break; } } - fp = funopen(libmap_override, readstrfn, NULL, NULL, closestrfn); - if (fp) { - lmc_parse(fp); - fclose(fp); - } + lmc_parse(libmap_override, p - libmap_override); + free(libmap_override); } return (lm_count == 0); } static void -lmc_parse (FILE *fp) +lmc_parse_file(char *path) { - char *cp; - char *f, *t, *c, *p; - char prog[MAXPATHLEN]; - char line[MAXPATHLEN + 2]; + struct lmc *p; + struct stat st; + int fd; + char *rpath; + char *lm_map; - dbg("%s(%p)", __func__, fp); - + rpath = realpath(path, NULL); + if (rpath == NULL) + return; + + TAILQ_FOREACH(p, &lmc_head, next) { + if (strcmp(p->path, rpath) == 0) { + free(rpath); + return; + } + } + + fd = open(rpath, O_RDONLY | O_CLOEXEC); + if (fd == -1) { + dbg("lm_parse_file: open(\"%s\") failed, %s", rpath, + rtld_strerror(errno)); + free(rpath); + return; + } + if (fstat(fd, &st) == -1) { + close(fd); + dbg("lm_parse_file: fstat(\"%s\") failed, %s", rpath, + rtld_strerror(errno)); + free(rpath); + return; + } + lm_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (lm_map == (const char *)MAP_FAILED) { + close(fd); + dbg("lm_parse_file: mmap(\"%s\") failed, %s", rpath, + rtld_strerror(errno)); + free(rpath); + return; + } + close(fd); + p = xmalloc(sizeof(struct lmc)); + p->path = rpath; + TAILQ_INSERT_HEAD(&lmc_head, p, next); + lmc_parse(lm_map, st.st_size); + munmap(lm_map, st.st_size); +} + +static void +lmc_parse_dir(char *idir) +{ + DIR *d; + struct dirent *dp; + struct lmc *p; + char conffile[MAXPATHLEN]; + char *ext; + char *rpath; + + rpath = realpath(idir, NULL); + if (rpath == NULL) + return; + + TAILQ_FOREACH(p, &lmc_head, next) { + if (strcmp(p->path, rpath) == 0) { + free(rpath); + return; + } + } + d = opendir(idir); + if (d == NULL) { + free(rpath); + return; + } + + p = xmalloc(sizeof(struct lmc)); + p->path = rpath; + TAILQ_INSERT_HEAD(&lmc_head, p, next); + + while ((dp = readdir(d)) != NULL) { + if (dp->d_ino == 0) + continue; + if (dp->d_type != DT_REG) + continue; + ext = strrchr(dp->d_name, '.'); + if (ext == NULL) + continue; + if (strcmp(ext, ".conf") != 0) + continue; + if (strlcpy(conffile, idir, MAXPATHLEN) >= MAXPATHLEN) + continue; /* too long */ + if (strlcat(conffile, "/", MAXPATHLEN) >= MAXPATHLEN) + continue; /* too long */ + if (strlcat(conffile, dp->d_name, MAXPATHLEN) >= MAXPATHLEN) + continue; /* too long */ + lmc_parse_file(conffile); + } + closedir(d); +} + +static void +lmc_parse(char *lm_p, size_t lm_len) +{ + char *cp, *f, *t, *c, *p; + char prog[MAXPATHLEN]; + /* allow includedir + full length path */ + char line[MAXPATHLEN + 13]; + size_t cnt; + int i; + + cnt = 0; p = NULL; - while ((cp = fgets(line, MAXPATHLEN + 1, fp)) != NULL) { + while (cnt < lm_len) { + i = 0; + while (cnt < lm_len && lm_p[cnt] != '\n' && + i < sizeof(line) - 1) { + line[i] = lm_p[cnt]; + cnt++; + i++; + } + line[i] = '\0'; + while (cnt < lm_len && lm_p[cnt] != '\n') + cnt++; + /* skip over nl */ + cnt++; + + cp = &line[0]; t = f = c = NULL; /* Skip over leading space */ - while (isspace(*cp)) cp++; + while (rtld_isspace(*cp)) cp++; /* Found a comment or EOL */ if (iseol(*cp)) continue; @@ -117,7 +234,7 @@ cp++; /* Skip leading space */ - while (isspace(*cp)) cp++; + while (rtld_isspace(*cp)) cp++; /* Found comment, EOL or end of selector */ if (iseol(*cp) || *cp == ']') @@ -125,11 +242,11 @@ c = cp++; /* Skip to end of word */ - while (!isspace(*cp) && !iseol(*cp) && *cp != ']') + while (!rtld_isspace(*cp) && !iseol(*cp) && *cp != ']') cp++; /* Skip and zero out trailing space */ - while (isspace(*cp)) *cp++ = '\0'; + while (rtld_isspace(*cp)) *cp++ = '\0'; /* Check if there is a closing brace */ if (*cp != ']') continue; @@ -141,36 +258,42 @@ * There should be nothing except whitespace or comment from this point to the end of the line. */ - while(isspace(*cp)) cp++; + while(rtld_isspace(*cp)) cp++; if (!iseol(*cp)) continue; - strcpy(prog, c); + if (strlcpy(prog, c, sizeof prog) >= sizeof prog) + continue; p = prog; continue; } /* Parse the 'from' candidate. */ f = cp++; - while (!isspace(*cp) && !iseol(*cp)) cp++; + while (!rtld_isspace(*cp) && !iseol(*cp)) cp++; /* Skip and zero out the trailing whitespace */ - while (isspace(*cp)) *cp++ = '\0'; + while (rtld_isspace(*cp)) *cp++ = '\0'; /* Found a comment or EOL */ if (iseol(*cp)) continue; /* Parse 'to' mapping */ t = cp++; - while (!isspace(*cp) && !iseol(*cp)) cp++; + while (!rtld_isspace(*cp) && !iseol(*cp)) cp++; /* Skip and zero out the trailing whitespace */ - while (isspace(*cp)) *cp++ = '\0'; + while (rtld_isspace(*cp)) *cp++ = '\0'; /* Should be no extra tokens at this point */ if (!iseol(*cp)) continue; *cp = '\0'; - lm_add(p, f, t); + if (strcmp(f, "includedir") == 0) + lmc_parse_dir(t); + else if (strcmp(f, "include") == 0) + lmc_parse_file(t); + else + lm_add(p, f, t); } } @@ -195,9 +318,17 @@ lm_fini (void) { struct lmp *lmp; + struct lmc *p; dbg("%s()", __func__); + while (!TAILQ_EMPTY(&lmc_head)) { + p = TAILQ_FIRST(&lmc_head); + TAILQ_REMOVE(&lmc_head, p, next); + free(p->path); + free(p); + } + while (!TAILQ_EMPTY(&lmp_head)) { lmp = TAILQ_FIRST(&lmp_head); TAILQ_REMOVE(&lmp_head, lmp, lmp_link); @@ -257,26 +388,22 @@ /* Given a libmap translation list and a library name, return the replacement library, or NULL */ -#ifdef COMPAT_32BIT char * lm_findn (const char *p, const char *f, const int n) { char pathbuf[64], *s, *t; - if (n < sizeof(pathbuf) - 1) { - memcpy(pathbuf, f, n); - pathbuf[n] = '\0'; + if (n < sizeof(pathbuf) - 1) s = pathbuf; - } else { + else s = xmalloc(n + 1); - strcpy(s, f); - } + memcpy(s, f, n); + s[n] = '\0'; t = lm_find(p, s); if (s != pathbuf) free(s); return (t); } -#endif static char * lml_find (struct lm_list *lmh, const char *f) @@ -341,31 +468,3 @@ } return (p); } - -static int -readstrfn(void * cookie, char *buf, int len) -{ - static char *current; - static int left; - int copied; - - copied = 0; - if (!current) { - current = cookie; - left = strlen(cookie); - } - while (*current && left && len) { - *buf++ = *current++; - left--; - len--; - copied++; - } - return copied; -} - -static int -closestrfn(void * cookie) -{ - free(cookie); - return 0; -} diff --git a/libexec/rtld-elf/libmap.h b/libexec/rtld-elf/libmap.h index 6b0991d..9a7b668 100644 --- a/libexec/rtld-elf/libmap.h +++ b/libexec/rtld-elf/libmap.h @@ -1,10 +1,8 @@ /* - * $FreeBSD: src/libexec/rtld-elf/libmap.h,v 1.4 2005/02/04 02:46:41 mdodd Exp $ + * $FreeBSD: releng/11.1/libexec/rtld-elf/libmap.h 255765 2013-09-21 21:03:52Z des $ */ int lm_init (char *); void lm_fini (void); char * lm_find (const char *, const char *); -#ifdef COMPAT_32BIT char * lm_findn (const char *, const char *, const int); -#endif diff --git a/libexec/rtld-elf/malloc.c b/libexec/rtld-elf/malloc.c index 3c43e5f..1f30265 100644 --- a/libexec/rtld-elf/malloc.c +++ b/libexec/rtld-elf/malloc.c @@ -29,7 +29,7 @@ #if defined(LIBC_SCCS) && !defined(lint) /*static char *sccsid = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91";*/ -static char *rcsid = "$FreeBSD: releng/10.2/libexec/rtld-elf/malloc.c 281452 2015-04-12 06:43:13Z kib $"; +static char *rcsid = "$FreeBSD: releng/11.1/libexec/rtld-elf/malloc.c 298897 2016-05-01 19:39:23Z pfg $"; #endif /* LIBC_SCCS and not lint */ /* @@ -45,7 +45,6 @@ #include #include -#include #include #include #include @@ -225,7 +224,7 @@ * Record allocated size of block and * bound space with magic numbers. */ - op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); + op->ov_size = roundup2(nbytes, RSLOP); op->ov_rmagic = RMAGIC; *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; #endif @@ -329,7 +328,7 @@ * 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 + * to determine its bucket: 1st we make one pass through 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 @@ -389,7 +388,7 @@ } if (nbytes <= onb && nbytes > (size_t)i) { #ifdef RCHECK - op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); + op->ov_size = roundup2(nbytes, RSLOP); *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; #endif return(cp); diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index 8d5f24a..9b13a13 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/libexec/rtld-elf/map_object.c,v 1.16 2005/02/27 12:55:40 dfr Exp $ + * $FreeBSD: releng/11.1/libexec/rtld-elf/map_object.c 319134 2017-05-29 13:18:20Z kib $ */ #include @@ -38,8 +38,7 @@ #include "debug.h" #include "rtld.h" -static Elf_Ehdr *get_elf_header (int, const char *); -static int convert_prot(int); /* Elf flags -> mmap protection */ +static Elf_Ehdr *get_elf_header(int, const char *, const struct stat *); static int convert_flags(int); /* Elf flags -> mmap flags */ /* @@ -61,15 +60,14 @@ Elf_Phdr **segs; int nsegs; Elf_Phdr *phdyn; - Elf_Phdr *phphdr; Elf_Phdr *phinterp; Elf_Phdr *phtls; caddr_t mapbase; size_t mapsize; - Elf_Off base_offset; Elf_Addr base_vaddr; Elf_Addr base_vlimit; caddr_t base_addr; + int base_flags; Elf_Off data_offset; Elf_Addr data_vaddr; Elf_Addr data_vlimit; @@ -79,26 +77,41 @@ Elf_Addr clear_vaddr; caddr_t clear_addr; caddr_t clear_page; - size_t nclear; + Elf_Addr phdr_vaddr; + size_t nclear, phsize; Elf_Addr bss_vaddr; Elf_Addr bss_vlimit; caddr_t bss_addr; + Elf_Word stack_flags; + Elf_Addr relro_page; + size_t relro_size; + Elf_Addr note_start; + Elf_Addr note_end; + char *note_map; + size_t note_map_len; - hdr = get_elf_header(fd, path); + hdr = get_elf_header(fd, path, sb); if (hdr == NULL) return (NULL); /* * Scan the program header entries, and save key information. * - * We rely on there being exactly two load segments, text and data, - * in that order. + * We expect that the loadable segments are ordered by load address. */ phdr = (Elf_Phdr *) ((char *)hdr + hdr->e_phoff); + phsize = hdr->e_phnum * sizeof (phdr[0]); phlimit = phdr + hdr->e_phnum; nsegs = -1; - phdyn = phphdr = phinterp = phtls = NULL; + phdyn = phinterp = phtls = NULL; + phdr_vaddr = 0; + relro_page = 0; + relro_size = 0; + note_start = 0; + note_end = 0; + note_map = NULL; segs = alloca(sizeof(segs[0]) * hdr->e_phnum); + stack_flags = RTLD_DEFAULT_STACK_PF_EXEC | PF_R | PF_W; while (phdr < phlimit) { switch (phdr->p_type) { @@ -108,15 +121,16 @@ case PT_LOAD: segs[++nsegs] = phdr; - if (segs[nsegs]->p_align < PAGE_SIZE) { + if ((segs[nsegs]->p_align & (PAGE_SIZE - 1)) != 0) { _rtld_error("%s: PT_LOAD segment %d not page-aligned", path, nsegs); - return NULL; + goto error; } break; case PT_PHDR: - phphdr = phdr; + phdr_vaddr = phdr->p_vaddr; + phsize = phdr->p_memsz; break; case PT_DYNAMIC: @@ -126,45 +140,75 @@ case PT_TLS: phtls = phdr; break; + + case PT_GNU_STACK: + stack_flags = phdr->p_flags; + break; + + case PT_GNU_RELRO: + relro_page = phdr->p_vaddr; + relro_size = phdr->p_memsz; + break; + + case PT_NOTE: + if (phdr->p_offset > PAGE_SIZE || + phdr->p_offset + phdr->p_filesz > PAGE_SIZE) { + note_map_len = round_page(phdr->p_offset + + phdr->p_filesz) - trunc_page(phdr->p_offset); + note_map = mmap(NULL, note_map_len, PROT_READ, + MAP_PRIVATE, fd, trunc_page(phdr->p_offset)); + if (note_map == MAP_FAILED) { + _rtld_error("%s: error mapping PT_NOTE (%d)", path, errno); + goto error; + } + note_start = (Elf_Addr)(note_map + phdr->p_offset - + trunc_page(phdr->p_offset)); + } else { + note_start = (Elf_Addr)(char *)hdr + phdr->p_offset; + } + note_end = note_start + phdr->p_filesz; + break; } ++phdr; } if (phdyn == NULL) { _rtld_error("%s: object is not dynamically-linked", path); - return NULL; + goto error; } if (nsegs < 0) { _rtld_error("%s: too few PT_LOAD segments", path); - return NULL; + goto error; } /* * Map the entire address space of the object, to stake out our * contiguous region, and to establish the base address for relocation. */ - base_offset = trunc_page(segs[0]->p_offset); base_vaddr = trunc_page(segs[0]->p_vaddr); base_vlimit = round_page(segs[nsegs]->p_vaddr + segs[nsegs]->p_memsz); mapsize = base_vlimit - base_vaddr; - base_addr = hdr->e_type == ET_EXEC ? (caddr_t) base_vaddr : NULL; + base_addr = (caddr_t) base_vaddr; + base_flags = MAP_PRIVATE | MAP_ANON | MAP_NOCORE; + if (npagesizes > 1 && round_page(segs[0]->p_filesz) >= pagesizes[1]) + base_flags |= MAP_ALIGNED_SUPER; + if (base_vaddr != 0) + base_flags |= MAP_FIXED | MAP_EXCL; - mapbase = mmap(base_addr, mapsize, convert_prot(segs[0]->p_flags), - convert_flags(segs[0]->p_flags), fd, base_offset); + mapbase = mmap(base_addr, mapsize, PROT_NONE, base_flags, -1, 0); if (mapbase == (caddr_t) -1) { _rtld_error("%s: mmap of entire address space failed: %s", - path, strerror(errno)); - return NULL; + path, rtld_strerror(errno)); + goto error; } if (base_addr != NULL && mapbase != base_addr) { _rtld_error("%s: mmap returned wrong address: wanted %p, got %p", path, base_addr, mapbase); - munmap(mapbase, mapsize); - return NULL; + goto error1; } - for (i = 0; i <= nsegs; i++) { + for (i = 0; i <= nsegs; i++) { /* Overlay the segment onto the proper region. */ data_offset = trunc_page(segs[i]->p_offset); data_vaddr = trunc_page(segs[i]->p_vaddr); @@ -172,44 +216,55 @@ data_addr = mapbase + (data_vaddr - base_vaddr); data_prot = convert_prot(segs[i]->p_flags); data_flags = convert_flags(segs[i]->p_flags) | MAP_FIXED; - /* Do not call mmap on the first segment - this is redundant */ - if (i && mmap(data_addr, data_vlimit - data_vaddr, data_prot, - data_flags, fd, data_offset) == (caddr_t) -1) { - _rtld_error("%s: mmap of data failed: %s", path, strerror(errno)); - return NULL; + if (mmap(data_addr, data_vlimit - data_vaddr, data_prot, + data_flags | MAP_PREFAULT_READ, fd, data_offset) == (caddr_t) -1) { + _rtld_error("%s: mmap of data failed: %s", path, + rtld_strerror(errno)); + goto error1; } - /* Clear any BSS in the last page of the segment. */ - clear_vaddr = segs[i]->p_vaddr + segs[i]->p_filesz; - clear_addr = mapbase + (clear_vaddr - base_vaddr); - clear_page = mapbase + (trunc_page(clear_vaddr) - base_vaddr); - if ((nclear = data_vlimit - clear_vaddr) > 0) { - /* Make sure the end of the segment is writable */ - if ((data_prot & PROT_WRITE) == 0 && - -1 == mprotect(clear_page, PAGE_SIZE, data_prot|PROT_WRITE)) { + /* Do BSS setup */ + if (segs[i]->p_filesz != segs[i]->p_memsz) { + + /* Clear any BSS in the last page of the segment. */ + clear_vaddr = segs[i]->p_vaddr + segs[i]->p_filesz; + clear_addr = mapbase + (clear_vaddr - base_vaddr); + clear_page = mapbase + (trunc_page(clear_vaddr) - base_vaddr); + + if ((nclear = data_vlimit - clear_vaddr) > 0) { + /* Make sure the end of the segment is writable */ + if ((data_prot & PROT_WRITE) == 0 && -1 == + mprotect(clear_page, PAGE_SIZE, data_prot|PROT_WRITE)) { _rtld_error("%s: mprotect failed: %s", path, - strerror(errno)); - return NULL; + rtld_strerror(errno)); + goto error1; + } + + memset(clear_addr, 0, nclear); + + /* Reset the data protection back */ + if ((data_prot & PROT_WRITE) == 0) + mprotect(clear_page, PAGE_SIZE, data_prot); } - memset(clear_addr, 0, nclear); - - /* Reset the data protection back */ - if ((data_prot & PROT_WRITE) == 0) - mprotect(clear_page, PAGE_SIZE, data_prot); + /* Overlay the BSS segment onto the proper region. */ + bss_vaddr = data_vlimit; + bss_vlimit = round_page(segs[i]->p_vaddr + segs[i]->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, data_prot, + data_flags | MAP_ANON, -1, 0) == (caddr_t)-1) { + _rtld_error("%s: mmap of bss failed: %s", path, + rtld_strerror(errno)); + goto error1; + } + } } - /* Overlay the BSS segment onto the proper region. */ - bss_vaddr = data_vlimit; - bss_vlimit = round_page(segs[i]->p_vaddr + segs[i]->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, data_prot, - MAP_PRIVATE|MAP_FIXED|MAP_ANON, -1, 0) == (caddr_t) -1) { - _rtld_error("%s: mmap of bss failed: %s", path, - strerror(errno)); - return NULL; - } + if (phdr_vaddr == 0 && data_offset <= hdr->e_phoff && + (data_vlimit - data_vaddr + data_offset) >= + (hdr->e_phoff + hdr->e_phnum * sizeof (Elf_Phdr))) { + phdr_vaddr = data_vaddr + hdr->e_phoff - data_offset; } } @@ -227,10 +282,19 @@ obj->dynamic = (const Elf_Dyn *) (obj->relocbase + phdyn->p_vaddr); if (hdr->e_entry != 0) obj->entry = (caddr_t) (obj->relocbase + hdr->e_entry); - if (phphdr != NULL) { - obj->phdr = (const Elf_Phdr *) (obj->relocbase + phphdr->p_vaddr); - obj->phsize = phphdr->p_memsz; + if (phdr_vaddr != 0) { + obj->phdr = (const Elf_Phdr *) (obj->relocbase + phdr_vaddr); + } else { + obj->phdr = malloc(phsize); + if (obj->phdr == NULL) { + obj_free(obj); + _rtld_error("%s: cannot allocate program header", path); + goto error1; + } + memcpy((char *)obj->phdr, (char *)hdr + hdr->e_phoff, phsize); + obj->phdr_alloc = true; } + obj->phsize = phsize; if (phinterp != NULL) obj->interp = (const char *) (obj->relocbase + phinterp->p_vaddr); if (phtls != NULL) { @@ -241,63 +305,87 @@ obj->tlsinitsize = phtls->p_filesz; obj->tlsinit = mapbase + phtls->p_vaddr; } - return obj; + obj->stack_flags = stack_flags; + obj->relro_page = obj->relocbase + trunc_page(relro_page); + obj->relro_size = round_page(relro_size); + if (note_start < note_end) + digest_notes(obj, note_start, note_end); + if (note_map != NULL) + munmap(note_map, note_map_len); + munmap(hdr, PAGE_SIZE); + return (obj); + +error1: + munmap(mapbase, mapsize); +error: + if (note_map != NULL && note_map != MAP_FAILED) + munmap(note_map, note_map_len); + munmap(hdr, PAGE_SIZE); + return (NULL); } static Elf_Ehdr * -get_elf_header (int fd, const char *path) +get_elf_header(int fd, const char *path, const struct stat *sbp) { - static union { - Elf_Ehdr hdr; - char buf[PAGE_SIZE]; - } u; - ssize_t nbytes; + Elf_Ehdr *hdr; - if ((nbytes = read(fd, u.buf, PAGE_SIZE)) == -1) { - _rtld_error("%s: read error: %s", path, strerror(errno)); - return NULL; - } + /* Make sure file has enough data for the ELF header */ + if (sbp != NULL && sbp->st_size < sizeof(Elf_Ehdr)) { + _rtld_error("%s: invalid file format", path); + return (NULL); + } - /* Make sure the file is valid */ - if (nbytes < (ssize_t)sizeof(Elf_Ehdr) || !IS_ELF(u.hdr)) { - _rtld_error("%s: invalid file format", path); - return NULL; - } - if (u.hdr.e_ident[EI_CLASS] != ELF_TARG_CLASS - || u.hdr.e_ident[EI_DATA] != ELF_TARG_DATA) { - _rtld_error("%s: unsupported file layout", path); - return NULL; - } - if (u.hdr.e_ident[EI_VERSION] != EV_CURRENT - || u.hdr.e_version != EV_CURRENT) { - _rtld_error("%s: unsupported file version", path); - return NULL; - } - if (u.hdr.e_type != ET_EXEC && u.hdr.e_type != ET_DYN) { - _rtld_error("%s: unsupported file type", path); - return NULL; - } - if (u.hdr.e_machine != ELF_TARG_MACH) { - _rtld_error("%s: unsupported machine", path); - return NULL; - } + hdr = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE | MAP_PREFAULT_READ, + fd, 0); + if (hdr == (Elf_Ehdr *)MAP_FAILED) { + _rtld_error("%s: read error: %s", path, rtld_strerror(errno)); + 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. - */ - if (u.hdr.e_phentsize != sizeof(Elf_Phdr)) { - _rtld_error( - "%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)", path); - return NULL; - } - if (u.hdr.e_phoff + u.hdr.e_phnum * sizeof(Elf_Phdr) > (size_t)nbytes) { - _rtld_error("%s: program header too large", path); - return NULL; - } + /* Make sure the file is valid */ + if (!IS_ELF(*hdr)) { + _rtld_error("%s: invalid file format", path); + goto error; + } + if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || + hdr->e_ident[EI_DATA] != ELF_TARG_DATA) { + _rtld_error("%s: unsupported file layout", path); + goto error; + } + if (hdr->e_ident[EI_VERSION] != EV_CURRENT || + hdr->e_version != EV_CURRENT) { + _rtld_error("%s: unsupported file version", path); + goto error; + } + if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) { + _rtld_error("%s: unsupported file type", path); + goto error; + } + if (hdr->e_machine != ELF_TARG_MACH) { + _rtld_error("%s: unsupported machine", path); + goto error; + } - return (&u.hdr); + /* + * 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. + */ + if (hdr->e_phentsize != sizeof(Elf_Phdr)) { + _rtld_error( + "%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)", path); + goto error; + } + if (hdr->e_phoff + hdr->e_phnum * sizeof(Elf_Phdr) > + (size_t)PAGE_SIZE) { + _rtld_error("%s: program header too large", path); + goto error; + } + return (hdr); + +error: + munmap(hdr, PAGE_SIZE); + return (NULL); } void @@ -305,15 +393,18 @@ { Objlist_Entry *elm; - if (obj->tls_done) { + if (obj->tls_done) free_tls_offset(obj); - } - free(obj->path); while (obj->needed != NULL) { Needed_Entry *needed = obj->needed; obj->needed = needed->next; free(needed); } + while (!STAILQ_EMPTY(&obj->names)) { + Name_Entry *entry = STAILQ_FIRST(&obj->names); + STAILQ_REMOVE_HEAD(&obj->names, link); + free(entry); + } while (!STAILQ_EMPTY(&obj->dldags)) { elm = STAILQ_FIRST(&obj->dldags); STAILQ_REMOVE_HEAD(&obj->dldags, link); @@ -324,8 +415,18 @@ STAILQ_REMOVE_HEAD(&obj->dagmembers, link); free(elm); } - free(obj->origin_path); - free(obj->priv); + if (obj->vertab) + free(obj->vertab); + if (obj->origin_path) + free(obj->origin_path); + if (obj->z_origin) + free(obj->rpath); + if (obj->priv) + free(obj->priv); + if (obj->path) + free(obj->path); + if (obj->phdr_alloc) + free((void *)obj->phdr); free(obj); } @@ -337,6 +438,7 @@ obj = CNEW(Obj_Entry); STAILQ_INIT(&obj->dldags); STAILQ_INIT(&obj->dagmembers); + STAILQ_INIT(&obj->names); return obj; } @@ -344,7 +446,7 @@ * Given a set of ELF protection flags, return the corresponding protection * flags for MMAP. */ -static int +int convert_prot(int elfflags) { int prot = 0; diff --git a/libexec/rtld-elf/mips/reloc.c b/libexec/rtld-elf/mips/reloc.c new file mode 100644 index 0000000..4fefeeb --- /dev/null +++ b/libexec/rtld-elf/mips/reloc.c @@ -0,0 +1,661 @@ +/* $NetBSD: mips_reloc.c,v 1.58 2010/01/14 11:57:06 skrll Exp $ */ + +/* + * Copyright 1997 Michael L. Hitch + * Portions copyright 2002 Charles M. Hannum + * 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 +__FBSDID("$FreeBSD: releng/11.1/libexec/rtld-elf/mips/reloc.c 316135 2017-03-29 11:03:08Z kib $"); + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "debug.h" +#include "rtld.h" + +#ifdef __mips_n64 +#define GOT1_MASK 0x8000000000000000UL +#else +#define GOT1_MASK 0x80000000UL +#endif + +void +init_pltgot(Obj_Entry *obj) +{ + if (obj->pltgot != NULL) { + obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start; + if (obj->pltgot[1] & 0x80000000) + obj->pltgot[1] = (Elf_Addr) obj | GOT1_MASK; + } +} + +int +do_copy_relocations(Obj_Entry *dstobj) +{ + /* Do nothing */ + return 0; +} + +void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); + +/* + * It is possible for the compiler to emit relocations for unaligned data. + * We handle this situation with these inlines. + */ +#ifdef __mips_n64 +/* + * ELF64 MIPS encodes the relocs uniquely. The first 32-bits of info contain + * the symbol index. The top 32-bits contain three relocation types encoded + * in big-endian integer with first relocation in LSB. This means for little + * endian we have to byte swap that integer (r_type). + */ +#define Elf_Sxword Elf64_Sxword +#define ELF_R_NXTTYPE_64_P(r_type) ((((r_type) >> 8) & 0xff) == R_TYPE(64)) +#if BYTE_ORDER == LITTLE_ENDIAN +#undef ELF_R_SYM +#undef ELF_R_TYPE +#define ELF_R_SYM(r_info) ((r_info) & 0xffffffff) +#define ELF_R_TYPE(r_info) bswap32((r_info) >> 32) +#endif +#else +#define ELF_R_NXTTYPE_64_P(r_type) (0) +#define Elf_Sxword Elf32_Sword +#endif + +static __inline Elf_Sxword +load_ptr(void *where, size_t len) +{ + Elf_Sxword val; + + if (__predict_true(((uintptr_t)where & (len - 1)) == 0)) { +#ifdef __mips_n64 + if (len == sizeof(Elf_Sxword)) + return *(Elf_Sxword *)where; +#endif + return *(Elf_Sword *)where; + } + + val = 0; +#if BYTE_ORDER == LITTLE_ENDIAN + (void)memcpy(&val, where, len); +#endif +#if BYTE_ORDER == BIG_ENDIAN + (void)memcpy((uint8_t *)((&val)+1) - len, where, len); +#endif + return (len == sizeof(Elf_Sxword)) ? val : (Elf_Sword)val; +} + +static __inline void +store_ptr(void *where, Elf_Sxword val, size_t len) +{ + if (__predict_true(((uintptr_t)where & (len - 1)) == 0)) { +#ifdef __mips_n64 + if (len == sizeof(Elf_Sxword)) { + *(Elf_Sxword *)where = val; + return; + } +#endif + *(Elf_Sword *)where = val; + return; + } +#if BYTE_ORDER == LITTLE_ENDIAN + (void)memcpy(where, &val, len); +#endif +#if BYTE_ORDER == BIG_ENDIAN + (void)memcpy(where, (const uint8_t *)((&val)+1) - len, len); +#endif +} + +void +_rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) +{ + const Elf_Rel *rel = NULL, *rellim; + Elf_Addr relsz = 0; + const Elf_Sym *symtab = NULL, *sym; + Elf_Addr *where; + Elf_Addr *got = NULL; + Elf_Word local_gotno = 0, symtabno = 0, gotsym = 0; + size_t i; + + for (; dynp->d_tag != DT_NULL; dynp++) { + switch (dynp->d_tag) { + case DT_REL: + rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); + break; + case DT_RELSZ: + relsz = dynp->d_un.d_val; + break; + case DT_SYMTAB: + symtab = (const Elf_Sym *)(relocbase + dynp->d_un.d_ptr); + break; + case DT_PLTGOT: + got = (Elf_Addr *)(relocbase + dynp->d_un.d_ptr); + break; + case DT_MIPS_LOCAL_GOTNO: + local_gotno = dynp->d_un.d_val; + break; + case DT_MIPS_SYMTABNO: + symtabno = dynp->d_un.d_val; + break; + case DT_MIPS_GOTSYM: + gotsym = dynp->d_un.d_val; + break; + } + } + + i = (got[1] & GOT1_MASK) ? 2 : 1; + /* Relocate the local GOT entries */ + got += i; + for (; i < local_gotno; i++) { + *got++ += relocbase; + } + + sym = symtab + gotsym; + /* Now do the global GOT entries */ + for (i = gotsym; i < symtabno; i++) { + *got = sym->st_value + relocbase; + ++sym; + ++got; + } + + rellim = (const Elf_Rel *)((caddr_t)rel + relsz); + for (; rel < rellim; rel++) { + Elf_Word r_symndx, r_type; + + where = (void *)(relocbase + rel->r_offset); + + r_symndx = ELF_R_SYM(rel->r_info); + r_type = ELF_R_TYPE(rel->r_info); + + switch (r_type & 0xff) { + case R_TYPE(REL32): { + const size_t rlen = + ELF_R_NXTTYPE_64_P(r_type) + ? sizeof(Elf_Sxword) + : sizeof(Elf_Sword); + Elf_Sxword old = load_ptr(where, rlen); + Elf_Sxword val = old; +#ifdef __mips_n64 + assert(r_type == R_TYPE(REL32) + || r_type == (R_TYPE(REL32)|(R_TYPE(64) << 8))); +#endif + assert(r_symndx < gotsym); + sym = symtab + r_symndx; + assert(ELF_ST_BIND(sym->st_info) == STB_LOCAL); + val += relocbase; + store_ptr(where, val, sizeof(Elf_Sword)); + dbg("REL32/L(%p) %p -> %p in ", + where, (void *)old, (void *)val); + store_ptr(where, val, rlen); + break; + } + + case R_TYPE(GPREL32): + case R_TYPE(NONE): + break; + + + default: + abort(); + break; + } + } +} + +Elf_Addr +_mips_rtld_bind(Obj_Entry *obj, Elf_Size reloff) +{ + Elf_Addr *got = obj->pltgot; + const Elf_Sym *def; + const Obj_Entry *defobj; + Elf_Addr *where; + Elf_Addr target; + RtldLockState lockstate; + + rlock_acquire(rtld_bind_lock, &lockstate); + if (sigsetjmp(lockstate.env, 0) != 0) + lock_upgrade(rtld_bind_lock, &lockstate); + + where = &got[obj->local_gotno + reloff - obj->gotsym]; + def = find_symdef(reloff, obj, &defobj, SYMLOOK_IN_PLT, NULL, + &lockstate); + if (def == NULL) + rtld_die(); + + target = (Elf_Addr)(defobj->relocbase + def->st_value); + dbg("bind now/fixup at %s sym # %jd in %s --> was=%p new=%p", + obj->path, + (intmax_t)reloff, defobj->strtab + def->st_name, + (void *)*where, (void *)target); + if (!ld_bind_not) + *where = target; + lock_release(rtld_bind_lock, &lockstate); + return (Elf_Addr)target; +} + +int +reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, + RtldLockState *lockstate) +{ + const Elf_Rel *rel; + const Elf_Rel *rellim; + Elf_Addr *got = obj->pltgot; + const Elf_Sym *sym, *def; + const Obj_Entry *defobj; + Elf_Word i; +#ifdef SUPPORT_OLD_BROKEN_LD + int broken; +#endif + + /* The relocation for the dynamic loader has already been done. */ + if (obj == obj_rtld) + return (0); + + if ((flags & SYMLOOK_IFUNC) != 0) + /* XXX not implemented */ + return (0); + +#ifdef SUPPORT_OLD_BROKEN_LD + broken = 0; + sym = obj->symtab; + for (i = 1; i < 12; i++) + if (sym[i].st_info == ELF_ST_INFO(STB_LOCAL, STT_NOTYPE)) + broken = 1; + dbg("%s: broken=%d", obj->path, broken); +#endif + + i = (got[1] & GOT1_MASK) ? 2 : 1; + + /* Relocate the local GOT entries */ + got += i; + dbg("got:%p for %d entries adding %p", + got, obj->local_gotno, obj->relocbase); + for (; i < obj->local_gotno; i++) { + *got += (Elf_Addr)obj->relocbase; + got++; + } + sym = obj->symtab + obj->gotsym; + + dbg("got:%p for %d entries", + got, obj->symtabno); + /* Now do the global GOT entries */ + for (i = obj->gotsym; i < obj->symtabno; i++) { + dbg(" doing got %d sym %p (%s, %lx)", i - obj->gotsym, sym, + sym->st_name + obj->strtab, (u_long) *got); + +#ifdef SUPPORT_OLD_BROKEN_LD + if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && + broken && sym->st_shndx == SHN_UNDEF) { + /* + * XXX DANGER WILL ROBINSON! + * You might think this is stupid, as it intentionally + * defeats lazy binding -- and you'd be right. + * Unfortunately, for lazy binding to work right, we + * need to a way to force the GOT slots used for + * function pointers to be resolved immediately. This + * is supposed to be done automatically by the linker, + * by not outputting a PLT slot and setting st_value + * to 0 if there are non-PLT references, but older + * versions of GNU ld do not do this. + */ + def = find_symdef(i, obj, &defobj, flags, NULL, + lockstate); + if (def == NULL) + return -1; + *got = def->st_value + (Elf_Addr)defobj->relocbase; + } else +#endif + if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && + sym->st_value != 0 && sym->st_shndx == SHN_UNDEF) { + /* + * If there are non-PLT references to the function, + * st_value should be 0, forcing us to resolve the + * address immediately. + * + * XXX DANGER WILL ROBINSON! + * The linker is not outputting PLT slots for calls to + * functions that are defined in the same shared + * library. This is a bug, because it can screw up + * link ordering rules if the symbol is defined in + * more than one module. For now, if there is a + * definition, we fail the test above and force a full + * symbol lookup. This means that all intra-module + * calls are bound immediately. - mycroft, 2003/09/24 + */ + *got = sym->st_value + (Elf_Addr)obj->relocbase; + if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) { + dbg("Warning2, i:%d maps to relocbase address:%p", + i, obj->relocbase); + } + + } else if (sym->st_info == ELF_ST_INFO(STB_GLOBAL, STT_SECTION)) { + /* Symbols with index SHN_ABS are not relocated. */ + if (sym->st_shndx != SHN_ABS) { + *got = sym->st_value + + (Elf_Addr)obj->relocbase; + if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) { + dbg("Warning3, i:%d maps to relocbase address:%p", + i, obj->relocbase); + } + } + } else { + /* TODO: add cache here */ + def = find_symdef(i, obj, &defobj, flags, NULL, + lockstate); + if (def == NULL) { + dbg("Warning4, can't find symbole %d", i); + return -1; + } + *got = def->st_value + (Elf_Addr)defobj->relocbase; + if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) { + dbg("Warning4, i:%d maps to relocbase address:%p", + i, obj->relocbase); + dbg("via first obj symbol %s", + obj->strtab + obj->symtab[i].st_name); + dbg("found in obj %p:%s", + defobj, defobj->path); + } + } + + dbg(" --> now %lx", (u_long) *got); + ++sym; + ++got; + } + + got = obj->pltgot; + rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize); + for (rel = obj->rel; rel < rellim; rel++) { + Elf_Word r_symndx, r_type; + void *where; + + where = obj->relocbase + rel->r_offset; + r_symndx = ELF_R_SYM(rel->r_info); + r_type = ELF_R_TYPE(rel->r_info); + + switch (r_type & 0xff) { + case R_TYPE(NONE): + break; + + case R_TYPE(REL32): { + /* 32-bit PC-relative reference */ + const size_t rlen = + ELF_R_NXTTYPE_64_P(r_type) + ? sizeof(Elf_Sxword) + : sizeof(Elf_Sword); + Elf_Sxword old = load_ptr(where, rlen); + Elf_Sxword val = old; + + def = obj->symtab + r_symndx; + + if (r_symndx >= obj->gotsym) { + val += got[obj->local_gotno + r_symndx - obj->gotsym]; + dbg("REL32/G(%p) %p --> %p (%s) in %s", + where, (void *)old, (void *)val, + obj->strtab + def->st_name, + obj->path); + } else { + /* + * XXX: ABI DIFFERENCE! + * + * Old NetBSD binutils would generate shared + * libs with section-relative relocations being + * already adjusted for the start address of + * the section. + * + * New binutils, OTOH, generate shared libs + * with the same relocations being based at + * zero, so we need to add in the start address + * of the section. + * + * --rkb, Oct 6, 2001 + */ + + if (def->st_info == + ELF_ST_INFO(STB_LOCAL, STT_SECTION) +#ifdef SUPPORT_OLD_BROKEN_LD + && !broken +#endif + ) + val += (Elf_Addr)def->st_value; + + val += (Elf_Addr)obj->relocbase; + + dbg("REL32/L(%p) %p -> %p (%s) in %s", + where, (void *)old, (void *)val, + obj->strtab + def->st_name, obj->path); + } + store_ptr(where, val, rlen); + break; + } + +#ifdef __mips_n64 + case R_TYPE(TLS_DTPMOD64): +#else + case R_TYPE(TLS_DTPMOD32): +#endif + { + + const size_t rlen = sizeof(Elf_Addr); + Elf_Addr old = load_ptr(where, rlen); + Elf_Addr val = old; + + def = find_symdef(r_symndx, obj, &defobj, flags, NULL, + lockstate); + if (def == NULL) + return -1; + + val += (Elf_Addr)defobj->tlsindex; + + store_ptr(where, val, rlen); + dbg("DTPMOD %s in %s %p --> %p in %s", + obj->strtab + obj->symtab[r_symndx].st_name, + obj->path, (void *)old, (void*)val, defobj->path); + break; + } + +#ifdef __mips_n64 + case R_TYPE(TLS_DTPREL64): +#else + case R_TYPE(TLS_DTPREL32): +#endif + { + const size_t rlen = sizeof(Elf_Addr); + Elf_Addr old = load_ptr(where, rlen); + Elf_Addr val = old; + + def = find_symdef(r_symndx, obj, &defobj, flags, NULL, + lockstate); + if (def == NULL) + return -1; + + if (!defobj->tls_done && allocate_tls_offset(obj)) + return -1; + + val += (Elf_Addr)def->st_value - TLS_DTP_OFFSET; + store_ptr(where, val, rlen); + + dbg("DTPREL %s in %s %p --> %p in %s", + obj->strtab + obj->symtab[r_symndx].st_name, + obj->path, (void*)old, (void *)val, defobj->path); + break; + } + +#ifdef __mips_n64 + case R_TYPE(TLS_TPREL64): +#else + case R_TYPE(TLS_TPREL32): +#endif + { + const size_t rlen = sizeof(Elf_Addr); + Elf_Addr old = load_ptr(where, rlen); + Elf_Addr val = old; + + def = find_symdef(r_symndx, obj, &defobj, flags, NULL, + lockstate); + + if (def == NULL) + return -1; + + if (!defobj->tls_done && allocate_tls_offset(obj)) + return -1; + + val += (Elf_Addr)(def->st_value + defobj->tlsoffset + - TLS_TP_OFFSET - TLS_TCB_SIZE); + store_ptr(where, val, rlen); + + dbg("TPREL %s in %s %p --> %p in %s", + obj->strtab + obj->symtab[r_symndx].st_name, + obj->path, (void*)old, (void *)val, defobj->path); + break; + } + + + + default: + dbg("sym = %lu, type = %lu, offset = %p, " + "contents = %p, symbol = %s", + (u_long)r_symndx, (u_long)ELF_R_TYPE(rel->r_info), + (void *)rel->r_offset, + (void *)load_ptr(where, sizeof(Elf_Sword)), + obj->strtab + obj->symtab[r_symndx].st_name); + _rtld_error("%s: Unsupported relocation type %ld " + "in non-PLT relocations", + obj->path, (u_long) ELF_R_TYPE(rel->r_info)); + return -1; + } + } + + return 0; +} + +/* + * Process the PLT relocations. + */ +int +reloc_plt(Obj_Entry *obj) +{ +#if 0 + const Elf_Rel *rellim; + const Elf_Rel *rel; + + dbg("reloc_plt obj:%p pltrel:%p sz:%s", obj, obj->pltrel, (int)obj->pltrelsize); + dbg("gottable %p num syms:%s", obj->pltgot, obj->symtabno ); + dbg("*****************************************************"); + rellim = (const Elf_Rel *)((char *)obj->pltrel + + obj->pltrelsize); + for (rel = obj->pltrel; rel < rellim; rel++) { + Elf_Addr *where; + where = (Elf_Addr *)(obj->relocbase + rel->r_offset); + *where += (Elf_Addr )obj->relocbase; + } + +#endif + /* PLT fixups were done above in the GOT relocation. */ + return (0); +} + +/* + * LD_BIND_NOW was set - force relocation for all jump slots + */ +int +reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + /* Do nothing */ + obj->jmpslots_done = true; + + return (0); +} + +int +reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, + struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +Elf_Addr +reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj, + const Obj_Entry *obj, const Elf_Rel *rel) +{ + + /* Do nothing */ + + return target; +} + +void +ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) +{ +} + +void +allocate_initial_tls(Obj_Entry *objs) +{ + char *tls; + + /* + * Fix the size of the static TLS block by using the maximum + * offset allocated so far and adding a bit for dynamic modules to + * use. + */ + tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA; + + tls = (char *) allocate_tls(objs, NULL, TLS_TCB_SIZE, 8); + + sysarch(MIPS_SET_TLS, tls); +} + +void * +__tls_get_addr(tls_index* ti) +{ + Elf_Addr** tls; + char *p; + + sysarch(MIPS_GET_TLS, &tls); + + p = tls_get_addr_common(tls, ti->ti_module, ti->ti_offset + TLS_DTP_OFFSET); + + return (p); +} diff --git a/libexec/rtld-elf/mips/rtld_machdep.h b/libexec/rtld-elf/mips/rtld_machdep.h new file mode 100644 index 0000000..c838b49 --- /dev/null +++ b/libexec/rtld-elf/mips/rtld_machdep.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1999, 2000 John D. Polstra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/mips/rtld_machdep.h 319434 2017-06-01 15:12:51Z vangyzen $ + */ + +#ifndef RTLD_MACHDEP_H +#define RTLD_MACHDEP_H 1 + +#include +#include +#include + +struct Struct_Obj_Entry; + +/* Return the address of the .dynamic section in the dynamic linker. */ +#define rtld_dynamic(obj) (&_DYNAMIC) + +Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, + const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj, + const Elf_Rel *rel); + +#define make_function_pointer(def, defobj) \ + ((defobj)->relocbase + (def)->st_value) + +#define call_initfini_pointer(obj, target) \ + (((InitFunc)(target))()) + +#define call_init_pointer(obj, target) \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)) + +#define call_ifunc_resolver(ptr) \ + (((Elf_Addr (*)(void))ptr)()) + +typedef struct { + unsigned long ti_module; + unsigned long ti_offset; +} tls_index; + +#define round(size, align) \ + (((size) + (align) - 1) & ~((align) - 1)) +#define calculate_first_tls_offset(size, align) \ + round(TLS_TCB_SIZE, align) +#define calculate_tls_offset(prev_offset, prev_size, size, align) \ + round(prev_offset + prev_size, align) +#define calculate_tls_end(off, size) ((off) + (size)) + +extern void *__tls_get_addr(tls_index *ti); + +#define RTLD_DEFAULT_STACK_PF_EXEC PF_X +#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC + +#define md_abi_variant_hook(x) + +#endif diff --git a/libexec/rtld-elf/mips/rtld_start.S b/libexec/rtld-elf/mips/rtld_start.S new file mode 100644 index 0000000..8177a79 --- /dev/null +++ b/libexec/rtld-elf/mips/rtld_start.S @@ -0,0 +1,166 @@ +/* $NetBSD: rtld_start.S,v 1.10 2009/12/14 00:41:19 matt Exp $ */ + +/* + * Copyright 1997 Michael L. Hitch + * Portions copyright 2002 Charles M. Hannum + * 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/mips/rtld_start.S 233378 2012-03-23 17:54:06Z gonzo $ + */ + +#include + +.globl _C_LABEL(_rtld_relocate_nonplt_self) +.globl _C_LABEL(_rtld) + +#define PTR_SIZE (1< 1*PTR_SIZE(sp) for atexit */ + /* -> 2*PTR_SIZE(sp) for obj_main */ + move s0, a0 /* save stack pointer from a0 */ + move s3, a3 /* save ps_strings pointer */ + + PTR_LA a1, 1f + bal 1f + PTR_LA t0, _C_LABEL(_rtld_relocate_nonplt_self) +1: PTR_SUBU a1, ra, a1 /* relocbase */ + PTR_LA a0, _DYNAMIC + PTR_ADDU t9, a1, t0 + jalr t9 /* _rtld_relocate_nonplt_self(dynp, relocabase) */ + PTR_ADDU a0, a1, a0 /* &_DYNAMIC */ + + move a0, s0 /* sp */ + PTR_ADDU a1, sp, 2*PTR_SIZE /* &our atexit function */ + PTR_ADDU a2, sp, 3*PTR_SIZE /* obj_main entry */ + PTR_SUBU sp, 4*SZREG /* ABI requires to reserve memory for 4 regs */ + PTR_LA t9, _C_LABEL(_rtld) + jalr t9 /* v0 = _rtld(sp, cleanup, objp) */ + nop + PTR_ADDU sp, 4*SZREG + + PTR_L a1, 2*PTR_SIZE(sp) /* our atexit function */ + PTR_L a2, 3*PTR_SIZE(sp) /* obj_main entry */ + PTR_ADDU sp, 4*PTR_SIZE /* readjust stack */ + move a0, s0 /* stack pointer */ + move t9, v0 + PTR_SUBU sp, 4*SZREG /* ABI requires to reserve memory for 4 regs */ + move ra,t9 /* RA == PC signals backtrace routine to stop */ + j t9 /* _start(sp, cleanup, obj); */ + move a3, s3 /* restore ps_strings */ +END(rtld_start) + +#define XCALLFRAME_SIZ (12*SZREG) +#define XCALLFRAME_RA (10*SZREG) +#define XCALLFRAME_GP (9*SZREG) +#define XCALLFRAME_S0 (8*SZREG) +#define XCALLFRAME_A3 (7*SZREG) +#define XCALLFRAME_A2 (6*SZREG) +#define XCALLFRAME_A1 (5*SZREG) +#define XCALLFRAME_A0 (4*SZREG) +#if defined(__mips_n32) || defined(__mips_n64) +#define XCALLFRAME_A7 (3*SZREG) +#define XCALLFRAME_A6 (2*SZREG) +#define XCALLFRAME_A5 (1*SZREG) +#define XCALLFRAME_A4 (0*SZREG) +#endif + + .globl _rtld_bind_start + .ent _rtld_bind_start +_rtld_bind_start: + .frame sp, XCALLFRAME_SIZ, $15 + move v1, gp /* save old GP */ +#if defined(__mips_o32) || defined(__mips_o64) + PTR_ADDU t9, 8 /* modify T9 to point at .cpload */ +#endif + SETUP_GP + PTR_SUBU sp, XCALLFRAME_SIZ /* save arguments and sp value in stack */ + SETUP_GP64(XCALLFRAME_GP, _rtld_bind_start) + SAVE_GP(XCALLFRAME_GP) +#if defined(__mips_n32) || defined(__mips_n64) + REG_S a4, XCALLFRAME_A4(sp) + REG_S a5, XCALLFRAME_A5(sp) + REG_S a6, XCALLFRAME_A6(sp) + REG_S a7, XCALLFRAME_A7(sp) +#endif + REG_S a0, XCALLFRAME_A0(sp) + REG_S a1, XCALLFRAME_A1(sp) + REG_S a2, XCALLFRAME_A2(sp) + REG_S a3, XCALLFRAME_A3(sp) + REG_S $15, XCALLFRAME_RA(sp) /* ra is in t7/t3 */ + REG_S s0, XCALLFRAME_S0(sp) + move s0, sp + + move a0, v1 /* old GP */ + PTR_SUBU a0, a0, 0x7ff0 /* The offset of $gp from the */ + /* beginning of the .got section: */ + /* $gp = .got + 0x7ff0, so */ + /* .got = $gp - 0x7ff0 */ + /* Simple math as you can see. */ +#if defined(__mips_n64) + ld a0, 8(a0) /* object = pltgot[1] */ + and a0, a0, 0x7fffffffffffffff +#else + lw a0, 4(a0) /* object = pltgot[1] & 0x7fffffff */ + and a0, a0, 0x7fffffff +#endif + move a1, t8 /* symbol index */ + + PTR_LA t9, _C_LABEL(_mips_rtld_bind) + jalr t9 + nop + + move sp, s0 + REG_L ra, XCALLFRAME_RA(sp) + REG_L s0, XCALLFRAME_S0(sp) + REG_L a0, XCALLFRAME_A0(sp) + REG_L a1, XCALLFRAME_A1(sp) + REG_L a2, XCALLFRAME_A2(sp) + REG_L a3, XCALLFRAME_A3(sp) +#if defined(__mips_n32) || defined(__mips_n64) + REG_L a4, XCALLFRAME_A4(sp) + REG_L a5, XCALLFRAME_A5(sp) + REG_L a6, XCALLFRAME_A6(sp) + REG_L a7, XCALLFRAME_A7(sp) +#endif + RESTORE_GP64 + PTR_ADDU sp, XCALLFRAME_SIZ + move t9, v0 + jr t9 + nop +END(_rtld_bind_start) diff --git a/libexec/rtld-elf/paths.h b/libexec/rtld-elf/paths.h new file mode 100644 index 0000000..c49c8f4 --- /dev/null +++ b/libexec/rtld-elf/paths.h @@ -0,0 +1,74 @@ +/*- + * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra. + * Copyright 2003 Alexander Kabaev . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/paths.h 296047 2016-02-25 18:23:40Z oshogbo $ + */ + +#ifndef PATHS_H +#define PATHS_H + +#undef _PATH_ELF_HINTS + +#ifdef COMPAT_32BIT +#define _PATH_ELF_HINTS "/var/run/ld-elf32.so.hints" +#define _PATH_LIBMAP_CONF "/etc/libmap32.conf" +#define _PATH_RTLD "/libexec/ld-elf32.so.1" +#define STANDARD_LIBRARY_PATH "/lib32:/usr/lib32" +#define LD_ "LD_32_" +#endif + +#ifndef _PATH_ELF_HINTS +#define _PATH_ELF_HINTS "/var/run/ld-elf.so.hints" +#endif + +#ifndef _PATH_LIBMAP_CONF +#define _PATH_LIBMAP_CONF "/etc/libmap.conf" +#endif + +#ifndef _PATH_RTLD +#define _PATH_RTLD "/libexec/ld-elf.so.1" +#endif + +#ifndef STANDARD_LIBRARY_PATH +#define STANDARD_LIBRARY_PATH "/lib/casper:/lib:/usr/lib" +#endif + +#ifndef LD_ +#define LD_ "LD_" +#endif + +#define _PATH_SOFT_ELF_HINTS "/var/run/ld-elf-soft.so.hints" +#define _PATH_SOFT_LIBMAP_CONF "/etc/libmap-soft.conf" +#define _PATH_SOFT_RTLD "/libexec/ld-elf.so.1" +#define SOFT_STANDARD_LIBRARY_PATH "/usr/libsoft" +#define LD_SOFT_ "LD_SOFT_" + +extern char *ld_elf_hints_default; +extern char *ld_path_libmap_conf; +extern char *ld_path_rtld; +extern char *ld_standard_library_path; +extern char *ld_env_prefix; + +#endif /* PATHS_H */ diff --git a/libexec/rtld-elf/powerpc/Makefile.inc b/libexec/rtld-elf/powerpc/Makefile.inc new file mode 100644 index 0000000..4ba9b75 --- /dev/null +++ b/libexec/rtld-elf/powerpc/Makefile.inc @@ -0,0 +1 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/powerpc/Makefile.inc 107572 2002-12-04 07:32:20Z grehan $ diff --git a/libexec/rtld-elf/powerpc/reloc.c b/libexec/rtld-elf/powerpc/reloc.c new file mode 100644 index 0000000..bb24b0b --- /dev/null +++ b/libexec/rtld-elf/powerpc/reloc.c @@ -0,0 +1,665 @@ +/* $NetBSD: ppc_reloc.c,v 1.10 2001/09/10 06:09:41 mycroft Exp $ */ + +/*- + * Copyright (C) 1998 Tsubai Masanari + * 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/powerpc/reloc.c 316135 2017-03-29 11:03:08Z kib $ + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +#define _ppc_ha(x) ((((u_int32_t)(x) & 0x8000) ? \ + ((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16) +#define _ppc_la(x) ((u_int32_t)(x) & 0xffff) + +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +#define PLT_EXTENDED_BEGIN (1 << 13) +#define JMPTAB_BASE(N) (18 + N*2 + ((N > PLT_EXTENDED_BEGIN) ? \ + (N - PLT_EXTENDED_BEGIN)*2 : 0)) + +/* + * Process the R_PPC_COPY relocations + */ +int +do_copy_relocations(Obj_Entry *dstobj) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + + /* + * COPY relocs are invalid outside of the main program + */ + assert(dstobj->mainprog); + + relalim = (const Elf_Rela *) ((caddr_t) dstobj->rela + + dstobj->relasize); + for (rela = dstobj->rela; rela < relalim; rela++) { + void *dstaddr; + const Elf_Sym *dstsym; + const char *name; + size_t size; + const void *srcaddr; + const Elf_Sym *srcsym = NULL; + const Obj_Entry *srcobj, *defobj; + SymLook req; + int res; + + if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) { + continue; + } + + dstaddr = (void *) (dstobj->relocbase + rela->r_offset); + dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); + name = dstobj->strtab + dstsym->st_name; + size = dstsym->st_size; + symlook_init(&req, name); + req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); + req.flags = SYMLOOK_EARLY; + + for (srcobj = globallist_next(dstobj); srcobj != NULL; + srcobj = globallist_next(srcobj)) { + res = symlook_obj(&req, srcobj); + if (res == 0) { + srcsym = req.sym_out; + defobj = req.defobj_out; + break; + } + } + + if (srcobj == NULL) { + _rtld_error("Undefined symbol \"%s\" " + " referenced from COPY" + " relocation in %s", name, dstobj->path); + return (-1); + } + + srcaddr = (const void *) (defobj->relocbase+srcsym->st_value); + memcpy(dstaddr, srcaddr, size); + dbg("copy_reloc: src=%p,dst=%p,size=%d\n",srcaddr,dstaddr,size); + } + + return (0); +} + + +/* + * Perform early relocation of the run-time linker image + */ +void +reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase) +{ + const Elf_Rela *rela = NULL, *relalim; + Elf_Addr relasz = 0; + Elf_Addr *where; + + /* + * Extract the rela/relasz values from the dynamic section + */ + for (; dynp->d_tag != DT_NULL; dynp++) { + switch (dynp->d_tag) { + case DT_RELA: + rela = (const Elf_Rela *)(relocbase+dynp->d_un.d_ptr); + break; + case DT_RELASZ: + relasz = dynp->d_un.d_val; + break; + } + } + + /* + * Relocate these values + */ + relalim = (const Elf_Rela *)((caddr_t)rela + relasz); + for (; rela < relalim; rela++) { + where = (Elf_Addr *)(relocbase + rela->r_offset); + *where = (Elf_Addr)(relocbase + rela->r_addend); + } +} + + +/* + * Relocate a non-PLT object with addend. + */ +static int +reloc_nonplt_object(Obj_Entry *obj_rtld, Obj_Entry *obj, const Elf_Rela *rela, + SymCache *cache, int flags, RtldLockState *lockstate) +{ + Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + const Elf_Sym *def; + const Obj_Entry *defobj; + Elf_Addr tmp; + + switch (ELF_R_TYPE(rela->r_info)) { + + case R_PPC_NONE: + break; + + case R_PPC_ADDR32: /* word32 S + A */ + case R_PPC_GLOB_DAT: /* word32 S + A */ + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + flags, cache, lockstate); + if (def == NULL) { + return (-1); + } + + tmp = (Elf_Addr)(defobj->relocbase + def->st_value + + rela->r_addend); + + /* Don't issue write if unnecessary; avoid COW page fault */ + if (*where != tmp) { + *where = tmp; + } + break; + + case R_PPC_RELATIVE: /* word32 B + A */ + tmp = (Elf_Addr)(obj->relocbase + rela->r_addend); + + /* As above, don't issue write unnecessarily */ + if (*where != tmp) { + *where = tmp; + } + break; + + case R_PPC_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; + + case R_PPC_JMP_SLOT: + /* + * These will be handled by the plt/jmpslot routines + */ + break; + + case R_PPC_DTPMOD32: + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + flags, cache, lockstate); + + if (def == NULL) + return (-1); + + *where = (Elf_Addr) defobj->tlsindex; + + break; + + case R_PPC_TPREL32: + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + flags, cache, lockstate); + + if (def == NULL) + return (-1); + + /* + * We lazily allocate offsets for static TLS as we + * see the first relocation that references the + * TLS block. This allows us to support (small + * amounts of) static TLS in dynamically loaded + * modules. If we run out of space, we generate an + * error. + */ + if (!defobj->tls_done) { + if (!allocate_tls_offset((Obj_Entry*) defobj)) { + _rtld_error("%s: No space available for static " + "Thread Local Storage", obj->path); + return (-1); + } + } + + *(Elf_Addr **)where = *where * sizeof(Elf_Addr) + + (Elf_Addr *)(def->st_value + rela->r_addend + + defobj->tlsoffset - TLS_TP_OFFSET); + + break; + + case R_PPC_DTPREL32: + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + flags, cache, lockstate); + + if (def == NULL) + return (-1); + + *where += (Elf_Addr)(def->st_value + rela->r_addend + - TLS_DTV_OFFSET); + + break; + + default: + _rtld_error("%s: Unsupported relocation type %d" + " in non-PLT relocations\n", obj->path, + ELF_R_TYPE(rela->r_info)); + return (-1); + } + return (0); +} + + +/* + * Process non-PLT relocations + */ +int +reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, + RtldLockState *lockstate) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + SymCache *cache; + int r = -1; + + if ((flags & SYMLOOK_IFUNC) != 0) + /* XXX not implemented */ + return (0); + + /* + * The dynamic loader may be called from a thread, we have + * limited amounts of stack available so we cannot use alloca(). + */ + if (obj != obj_rtld) { + cache = calloc(obj->dynsymcount, sizeof(SymCache)); + /* No need to check for NULL here */ + } else + cache = NULL; + + /* + * From the SVR4 PPC ABI: + * "The PowerPC family uses only the Elf32_Rela relocation + * entries with explicit addends." + */ + relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); + for (rela = obj->rela; rela < relalim; rela++) { + if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags, + lockstate) < 0) + goto done; + } + r = 0; +done: + if (cache != NULL) + free(cache); + + /* Synchronize icache for text seg in case we made any changes */ + __syncicache(obj->mapbase, obj->textsize); + + return (r); +} + +/* + * Initialise a PLT slot to the resolving trampoline + */ +static int +reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) +{ + Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset); + Elf_Addr *pltresolve, *pltlongresolve, *jmptab; + Elf_Addr distance; + int N = obj->pltrelasize / sizeof(Elf_Rela); + int reloff; + + reloff = rela - obj->pltrela; + + if (reloff < 0) + return (-1); + + pltlongresolve = obj->pltgot + 5; + pltresolve = pltlongresolve + 5; + + distance = (Elf_Addr)pltresolve - (Elf_Addr)(where + 1); + + dbg(" reloc_plt_object: where=%p,pltres=%p,reloff=%x,distance=%x", + (void *)where, (void *)pltresolve, reloff, distance); + + if (reloff < PLT_EXTENDED_BEGIN) { + /* li r11,reloff */ + /* b pltresolve */ + where[0] = 0x39600000 | reloff; + where[1] = 0x48000000 | (distance & 0x03fffffc); + } else { + jmptab = obj->pltgot + JMPTAB_BASE(N); + jmptab[reloff] = (u_int)pltlongresolve; + + /* lis r11,jmptab[reloff]@ha */ + /* lwzu r12,jmptab[reloff]@l(r11) */ + /* mtctr r12 */ + /* bctr */ + where[0] = 0x3d600000 | _ppc_ha(&jmptab[reloff]); + where[1] = 0x858b0000 | _ppc_la(&jmptab[reloff]); + where[2] = 0x7d8903a6; + where[3] = 0x4e800420; + } + + + /* + * The icache will be sync'd in reloc_plt, which is called + * after all the slots have been updated + */ + + return (0); +} + + +/* + * Process the PLT relocations. + */ +int +reloc_plt(Obj_Entry *obj) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + int N = obj->pltrelasize / sizeof(Elf_Rela); + + if (obj->pltrelasize != 0) { + + relalim = (const Elf_Rela *)((char *)obj->pltrela + + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); + + if (reloc_plt_object(obj, rela) < 0) { + return (-1); + } + } + } + + /* + * Sync the icache for the byte range represented by the + * trampoline routines and call slots. + */ + if (obj->pltgot != NULL) + __syncicache(obj->pltgot, JMPTAB_BASE(N)*4); + + return (0); +} + + +/* + * LD_BIND_NOW was set - force relocation for all jump slots + */ +int +reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + const Obj_Entry *defobj; + const Elf_Rela *relalim; + const Elf_Rela *rela; + const Elf_Sym *def; + Elf_Addr *where; + Elf_Addr target; + + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + SYMLOOK_IN_PLT | flags, NULL, lockstate); + if (def == NULL) { + dbg("reloc_jmpslots: sym not found"); + return (-1); + } + + target = (Elf_Addr)(defobj->relocbase + def->st_value); + +#if 0 + /* PG XXX */ + dbg("\"%s\" in \"%s\" --> %p in \"%s\"", + defobj->strtab + def->st_name, basename(obj->path), + (void *)target, basename(defobj->path)); +#endif + + reloc_jmpslot(where, target, defobj, obj, + (const Elf_Rel *) rela); + } + + obj->jmpslots_done = true; + + return (0); +} + + +/* + * Update the value of a PLT jump slot. Branch directly to the target if + * it is within +/- 32Mb, otherwise go indirectly via the pltcall + * trampoline call and jump table. + */ +Elf_Addr +reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj, + const Obj_Entry *obj, const Elf_Rel *rel) +{ + Elf_Addr offset; + const Elf_Rela *rela = (const Elf_Rela *) rel; + + dbg(" reloc_jmpslot: where=%p, target=%p", + (void *)wherep, (void *)target); + + if (ld_bind_not) + goto out; + + /* + * At the PLT entry pointed at by `wherep', construct + * a direct transfer to the now fully resolved function + * address. + */ + offset = target - (Elf_Addr)wherep; + + if (abs((int)offset) < 32*1024*1024) { /* inside 32MB? */ + /* b value # branch directly */ + *wherep = 0x48000000 | (offset & 0x03fffffc); + __syncicache(wherep, 4); + } else { + Elf_Addr *pltcall, *jmptab; + int distance; + int N = obj->pltrelasize / sizeof(Elf_Rela); + int reloff = rela - obj->pltrela; + + if (reloff < 0) + return (-1); + + pltcall = obj->pltgot; + + dbg(" reloc_jmpslot: indir, reloff=%x, N=%x\n", + reloff, N); + + jmptab = obj->pltgot + JMPTAB_BASE(N); + jmptab[reloff] = target; + mb(); /* Order jmptab update before next changes */ + + if (reloff < PLT_EXTENDED_BEGIN) { + /* for extended PLT entries, we keep the old code */ + + distance = (Elf_Addr)pltcall - (Elf_Addr)(wherep + 1); + + /* li r11,reloff */ + /* b pltcall # use indirect pltcall routine */ + + /* first instruction same as before */ + wherep[1] = 0x48000000 | (distance & 0x03fffffc); + __syncicache(wherep, 8); + } + } + +out: + return (target); +} + +int +reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, + struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +/* + * Setup the plt glue routines. + */ +#define PLTCALL_SIZE 20 +#define PLTLONGRESOLVE_SIZE 20 +#define PLTRESOLVE_SIZE 24 + +void +init_pltgot(Obj_Entry *obj) +{ + Elf_Word *pltcall, *pltresolve, *pltlongresolve; + Elf_Word *jmptab; + int N = obj->pltrelasize / sizeof(Elf_Rela); + + pltcall = obj->pltgot; + + if (pltcall == NULL) { + return; + } + + /* + * From the SVR4 PPC ABI: + * + * 'The first 18 words (72 bytes) of the PLT are reserved for + * use by the dynamic linker. + * ... + * 'If the executable or shared object requires N procedure + * linkage table entries, the link editor shall reserve 3*N + * words (12*N bytes) following the 18 reserved words. The + * first 2*N of these words are the procedure linkage table + * entries themselves. The static linker directs calls to bytes + * (72 + (i-1)*8), for i between 1 and N inclusive. The remaining + * N words (4*N bytes) are reserved for use by the dynamic linker.' + */ + + /* + * Copy the absolute-call assembler stub into the first part of + * the reserved PLT area. + */ + memcpy(pltcall, _rtld_powerpc_pltcall, PLTCALL_SIZE); + + /* + * Determine the address of the jumptable, which is the dyn-linker + * reserved area after the call cells. Write the absolute address + * of the jumptable into the absolute-call assembler code so it + * can determine this address. + */ + jmptab = obj->pltgot + JMPTAB_BASE(N); + pltcall[1] |= _ppc_ha(jmptab); /* addis 11,11,jmptab@ha */ + pltcall[2] |= _ppc_la(jmptab); /* lwz 11,jmptab@l(11) */ + + /* + * Skip down 20 bytes into the initial reserved area and copy + * in the standard resolving assembler call. Into this assembler, + * insert the absolute address of the _rtld_bind_start routine + * and the address of the relocation object. + * + * We place pltlongresolve first, so it can fix up its arguments + * and then fall through to the regular PLT resolver. + */ + pltlongresolve = obj->pltgot + 5; + + memcpy(pltlongresolve, _rtld_powerpc_pltlongresolve, + PLTLONGRESOLVE_SIZE); + pltlongresolve[0] |= _ppc_ha(jmptab); /* lis 12,jmptab@ha */ + pltlongresolve[1] |= _ppc_la(jmptab); /* addi 12,12,jmptab@l */ + + pltresolve = pltlongresolve + PLTLONGRESOLVE_SIZE/sizeof(uint32_t); + memcpy(pltresolve, _rtld_powerpc_pltresolve, PLTRESOLVE_SIZE); + pltresolve[0] |= _ppc_ha(_rtld_bind_start); + pltresolve[1] |= _ppc_la(_rtld_bind_start); + pltresolve[3] |= _ppc_ha(obj); + pltresolve[4] |= _ppc_la(obj); + + /* + * The icache will be sync'd in reloc_plt, which is called + * after all the slots have been updated + */ +} + +void +ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) +{ +} + +void +allocate_initial_tls(Obj_Entry *list) +{ + Elf_Addr **tp; + + /* + * Fix the size of the static TLS block by using the maximum + * offset allocated so far and adding a bit for dynamic modules to + * use. + */ + + tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA; + + tp = (Elf_Addr **) ((char *) allocate_tls(list, NULL, TLS_TCB_SIZE, 8) + + TLS_TP_OFFSET + TLS_TCB_SIZE); + + /* + * XXX gcc seems to ignore 'tp = _tp;' + */ + + __asm __volatile("mr 2,%0" :: "r"(tp)); +} + +void* +__tls_get_addr(tls_index* ti) +{ + register Elf_Addr **tp; + char *p; + + __asm __volatile("mr %0,2" : "=r"(tp)); + p = tls_get_addr_common((Elf_Addr**)((Elf_Addr)tp - TLS_TP_OFFSET + - TLS_TCB_SIZE), ti->ti_module, ti->ti_offset); + + return (p + TLS_DTV_OFFSET); +} diff --git a/libexec/rtld-elf/powerpc/rtld_machdep.h b/libexec/rtld-elf/powerpc/rtld_machdep.h new file mode 100644 index 0000000..9dc5fba --- /dev/null +++ b/libexec/rtld-elf/powerpc/rtld_machdep.h @@ -0,0 +1,92 @@ +/*- + * Copyright (c) 1999, 2000 John D. Polstra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/powerpc/rtld_machdep.h 319434 2017-06-01 15:12:51Z vangyzen $ + */ + +#ifndef RTLD_MACHDEP_H +#define RTLD_MACHDEP_H 1 + +#include +#include + +struct Struct_Obj_Entry; + +/* Return the address of the .dynamic section in the dynamic linker. */ +#define rtld_dynamic(obj) (&_DYNAMIC) + +Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, + const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj, + const Elf_Rel *rel); + +#define make_function_pointer(def, defobj) \ + ((defobj)->relocbase + (def)->st_value) + +#define call_initfini_pointer(obj, target) \ + (((InitFunc)(target))()) + +#define call_init_pointer(obj, target) \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)) + +#define call_ifunc_resolver(ptr) \ + (((Elf_Addr (*)(void))ptr)()) + +/* + * PLT functions. Not really correct prototypes, but the + * symbol values are needed. + */ +void _rtld_powerpc_pltlongresolve(void); +void _rtld_powerpc_pltresolve(void); +void _rtld_powerpc_pltcall(void); + +/* + * TLS + */ + +#define TLS_TP_OFFSET 0x7000 +#define TLS_DTV_OFFSET 0x8000 +#define TLS_TCB_SIZE 8 + +#define round(size, align) \ + (((size) + (align) - 1) & ~((align) - 1)) +#define calculate_first_tls_offset(size, align) \ + round(8, align) +#define calculate_tls_offset(prev_offset, prev_size, size, align) \ + round(prev_offset + prev_size, align) +#define calculate_tls_end(off, size) ((off) + (size)) + +typedef struct { + unsigned long ti_module; + unsigned long ti_offset; +} tls_index; + +extern void *__tls_get_addr(tls_index* ti); + +#define RTLD_DEFAULT_STACK_PF_EXEC PF_X +#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC + +#define md_abi_variant_hook(x) + +#endif diff --git a/libexec/rtld-elf/powerpc/rtld_start.S b/libexec/rtld-elf/powerpc/rtld_start.S new file mode 100644 index 0000000..a649937 --- /dev/null +++ b/libexec/rtld-elf/powerpc/rtld_start.S @@ -0,0 +1,200 @@ +/* $NetBSD: rtld_start.S,v 1.4 2001/09/26 04:06:43 mycroft Exp $ */ + +/*- + * Copyright (C) 1998 Tsubai Masanari + * 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/powerpc/rtld_start.S 217397 2011-01-14 11:32:31Z kib $ + */ + +#include + +.extern _GLOBAL_OFFSET_TABLE_ +.extern _DYNAMIC + +_ENTRY(.rtld_start) + stwu %r1,-48(%r1) /* 16-byte aligned stack for reg saves + + exit_proc & obj _rtld args + + backchain & lrsave stack frame */ + stw %r3,16(%r1) /* argc */ + stw %r4,20(%r1) /* argv */ + stw %r5,24(%r1) /* envp */ +/* stw %r6,28(%r1) *//* obj (always 0) */ +/* stw %r7,32(%r1) *//* cleanup (always 0) */ + stw %r8,36(%r1) /* ps_strings */ + + /* + * Perform initial relocation of ld-elf.so. Not as easy as it + * sounds. + * - perform small forward branch to put PC into link reg + * - use link-time constants to determine offset to the + * _DYNAMIC section and the GOT. Add these to the PC to + * convert to absolute addresses. + * - sync icache to allow execution of the SVR4 ABI-specified + * blrl instruction preceding the GOT + * - Use this instruction to determine the GOT absolute address + * - read GOT[0], which is the SVR4 ABI-specified link-time + * value of _DYNAMIC. Subtract this value from the absolute + * value to determine the load address + * - call reloc_non_plt_self() to fix up ld-elf.so's relocations + */ + bl 1f + .long _DYNAMIC-. + .long _GLOBAL_OFFSET_TABLE_-. /* branch lr + 4 */ +1: + mflr %r3 /* PC value at .long */ + lwz %r4,4(%r3) + add %r4,%r4,%r3 /* &_GLOBAL_OFFSET_TABLE-4, blrl insn. */ + dcbst %r0,%r4 /* sync i-cache with d-cache */ + sync + icbi %r0,%r4 + isync + + lwz %r4,0(%r3) /* offset to _DYNAMIC */ + add %r3,%r4,%r3 /* r3 = &_DYNAMIC, absolute value */ + + bl _GLOBAL_OFFSET_TABLE_@local-4 + mflr %r4 /* &_GLOBAL_OFFSET_TABLE_, absolute value */ + lwz %r4,0(%r4) /* linker &_DYNAMIC, from got[0] */ + subf %r4,%r4,%r3 /* subtract to calculate relocbase */ + + bl reloc_non_plt_self@plt /* reloc_non_plt_self(&_DYNAMIC,base) */ + + /* + * The _rtld() function likes to see a stack layout containing + * { argc, argv[0], argv[1] ... argv[N], 0, env[0], ... , env[N] } + * Since the PowerPC stack was 16-byte aligned at exec time, the + * original stack layout has to be found by moving back a word + * from the argv pointer. + */ + lwz %r4,20(%r1) /* restore argv */ + addi %r3,%r4,-4 /* locate argc ptr, &argv[-1] */ + + addi %r4,%r1,8 /* &exit_proc on stack */ + addi %r5,%r1,12 /* &obj_main on stack */ + + bl _rtld@plt /* &_start = _rtld(sp, &exit_proc, &obj_main)*/ + mtlr %r3 + + /* + * Restore args, with new obj/exit proc + */ + lwz %r3,16(%r1) /* argc */ + lwz %r4,20(%r1) /* argv */ + lwz %r5,24(%r1) /* envp */ + lwz %r6,12(%r1) /* obj */ + lwz %r7,8(%r1) /* exit proc */ + lwz %r8,36(%r1) /* ps_strings */ + addi %r1,%r1,48 /* restore original stackptr */ + + blrl /* _start(argc, argv, envp, obj, cleanup, ps_strings) */ + + li %r0,1 /* _exit() */ + sc + +/* + * _rtld_bind_start() + * + * Call into the MI binder. This routine is reached via the PLT call cell, + * and then _rtld_powerpc_pltresolve(). + * On entry, %r11 contains the index of the PLT cell, and %r12 contains + * a pointer to the ELF object for the file. + * Save all registers, call into the binder to resolve and fixup the external + * routine, and then transfer to the external routine on return. + */ + .globl _rtld_bind + +_ENTRY(_rtld_bind_start) + stwu %r1,-160(%r1) # stack space for 29 regs + r0/lr/cr + stw %r0,20(%r1) # save r0 + mflr %r0 + stw %r0,16(%r1) # save lr + mfcr %r0 + stw %r0,12(%r1) # save cr + stmw %r3,24(%r1) # save r3-r31 + + mr %r3,%r12 # obj + mulli %r4,%r11,12 # rela index * sizeof(Elf_Rela) + bl _rtld_bind@PLT # target addr = _rtld_bind(obj, reloff) + mtctr %r3 # move absolute target addr into ctr + + lmw %r3,24(%r1) # restore r3-r31 + lwz %r0,12(%r1) # restore cr + mtcr %r0 + lwz %r0,16(%r1) # restore lr + mtlr %r0 + lwz %r0,20(%r1) # restore r0 + + addi %r1,%r1,160 # restore stack + bctr # jump to target + + +/* + * _rtld_powerpc_pltresolve() + * + * This routine is copied into the latter part of the 72-byte reserved + * area at the start of the PLT. The absolute address of the _rtld_bind_start + * routine, and the ELF object for the loaded file, are inserted into + * the code by the reloc.c:init_pltgot() routine. + * The first time an external routine is called, the PLT slot will + * set up %r11 to the offset of the slot, and will jump to this routine. + * The ELF object is shifted into %r11, and _rtld_bind_start is called + * to complete the binding. + */ +_ENTRY(_rtld_powerpc_pltlongresolve) + lis %r12,0 # lis 12,jmptab@ha + addi %r12,%r12,0 # addi 12,12,jmptab@l + subf %r11,%r12,%r11 # reloff + li %r12,2 + srw %r11,%r11,%r12 # index = reloff/sizeof(Elf_Addr) +_ENTRY(_rtld_powerpc_pltresolve) + lis %r12,0 # lis 12,_rtld_bind_start@ha + addi %r12,%r12,0 # addi 12,12,_rtld_bind_start@l + mtctr %r12 + lis %r12,0 # lis 12,obj@ha + addi %r12,%r12,0 # addi 12,12,obj@l + bctr + +/* + * _rtld_powerpc_pltcall() + * + * This routine is copied into the 72-byte reserved area at the + * start of the PLT. The reloc.c:init_pltgot() routine inserts + * the absolute address of the jumptable. + * Control is transferred to this routine when the binder has + * located the external routine, but determined that it is > 32Mb + * from the PLT slot. Code is inserted into the PLT slot to set up + * %r11 with the jumptable index, and jump to here, where the + * absolute address of the external routine is loaded from the + * jumptable and transferred to + */ +_ENTRY(_rtld_powerpc_pltcall) + slwi %r11,%r11,2 # jmptab offset = index * 4 + addis %r11,%r11,0 # addis 11,11,jmptab@ha + lwz %r11,0(%r11) # lwz 11,jmptab@l(11) + mtctr %r11 + bctr # (*jmptab[index])() + + .section .note.GNU-stack,"",%progbits diff --git a/libexec/rtld-elf/powerpc64/Makefile.inc b/libexec/rtld-elf/powerpc64/Makefile.inc new file mode 100644 index 0000000..249a945 --- /dev/null +++ b/libexec/rtld-elf/powerpc64/Makefile.inc @@ -0,0 +1 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/powerpc64/Makefile.inc 107572 2002-12-04 07:32:20Z grehan $ diff --git a/libexec/rtld-elf/powerpc64/reloc.c b/libexec/rtld-elf/powerpc64/reloc.c new file mode 100644 index 0000000..a3fda5e --- /dev/null +++ b/libexec/rtld-elf/powerpc64/reloc.c @@ -0,0 +1,566 @@ +/* $NetBSD: ppc_reloc.c,v 1.10 2001/09/10 06:09:41 mycroft Exp $ */ + +/*- + * Copyright (C) 1998 Tsubai Masanari + * 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/powerpc64/reloc.c 316135 2017-03-29 11:03:08Z kib $ + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +#if !defined(_CALL_ELF) || _CALL_ELF == 1 +struct funcdesc { + Elf_Addr addr; + Elf_Addr toc; + Elf_Addr env; +}; +#endif + +/* + * Process the R_PPC_COPY relocations + */ +int +do_copy_relocations(Obj_Entry *dstobj) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + + /* + * COPY relocs are invalid outside of the main program + */ + assert(dstobj->mainprog); + + relalim = (const Elf_Rela *) ((caddr_t) dstobj->rela + + dstobj->relasize); + for (rela = dstobj->rela; rela < relalim; rela++) { + void *dstaddr; + const Elf_Sym *dstsym; + const char *name; + size_t size; + const void *srcaddr; + const Elf_Sym *srcsym = NULL; + const Obj_Entry *srcobj, *defobj; + SymLook req; + int res; + + if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) { + continue; + } + + dstaddr = (void *) (dstobj->relocbase + rela->r_offset); + dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); + name = dstobj->strtab + dstsym->st_name; + size = dstsym->st_size; + symlook_init(&req, name); + req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); + req.flags = SYMLOOK_EARLY; + + for (srcobj = globallist_next(dstobj); srcobj != NULL; + srcobj = globallist_next(srcobj)) { + res = symlook_obj(&req, srcobj); + if (res == 0) { + srcsym = req.sym_out; + defobj = req.defobj_out; + break; + } + } + + if (srcobj == NULL) { + _rtld_error("Undefined symbol \"%s\" " + " referenced from COPY" + " relocation in %s", name, dstobj->path); + return (-1); + } + + srcaddr = (const void *) (defobj->relocbase+srcsym->st_value); + memcpy(dstaddr, srcaddr, size); + dbg("copy_reloc: src=%p,dst=%p,size=%zd\n",srcaddr,dstaddr,size); + } + + return (0); +} + + +/* + * Perform early relocation of the run-time linker image + */ +void +reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase) +{ + const Elf_Rela *rela = NULL, *relalim; + Elf_Addr relasz = 0; + Elf_Addr *where; + + /* + * Extract the rela/relasz values from the dynamic section + */ + for (; dynp->d_tag != DT_NULL; dynp++) { + switch (dynp->d_tag) { + case DT_RELA: + rela = (const Elf_Rela *)(relocbase+dynp->d_un.d_ptr); + break; + case DT_RELASZ: + relasz = dynp->d_un.d_val; + break; + } + } + + /* + * Relocate these values + */ + relalim = (const Elf_Rela *)((caddr_t)rela + relasz); + for (; rela < relalim; rela++) { + where = (Elf_Addr *)(relocbase + rela->r_offset); + *where = (Elf_Addr)(relocbase + rela->r_addend); + } +} + + +/* + * Relocate a non-PLT object with addend. + */ +static int +reloc_nonplt_object(Obj_Entry *obj_rtld, Obj_Entry *obj, const Elf_Rela *rela, + SymCache *cache, int flags, RtldLockState *lockstate) +{ + Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + const Elf_Sym *def; + const Obj_Entry *defobj; + Elf_Addr tmp; + + switch (ELF_R_TYPE(rela->r_info)) { + + case R_PPC_NONE: + break; + + case R_PPC64_UADDR64: /* doubleword64 S + A */ + case R_PPC64_ADDR64: + case R_PPC_GLOB_DAT: + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + flags, cache, lockstate); + if (def == NULL) { + return (-1); + } + + tmp = (Elf_Addr)(defobj->relocbase + def->st_value + + rela->r_addend); + + /* Don't issue write if unnecessary; avoid COW page fault */ + if (*where != tmp) { + *where = tmp; + } + break; + + case R_PPC_RELATIVE: /* doubleword64 B + A */ + tmp = (Elf_Addr)(obj->relocbase + rela->r_addend); + + /* As above, don't issue write unnecessarily */ + if (*where != tmp) { + *where = tmp; + } + break; + + case R_PPC_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; + + case R_PPC_JMP_SLOT: + /* + * These will be handled by the plt/jmpslot routines + */ + break; + + case R_PPC64_DTPMOD64: + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + flags, cache, lockstate); + + if (def == NULL) + return (-1); + + *where = (Elf_Addr) defobj->tlsindex; + + break; + + case R_PPC64_TPREL64: + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + flags, cache, lockstate); + + if (def == NULL) + return (-1); + + /* + * We lazily allocate offsets for static TLS as we + * see the first relocation that references the + * TLS block. This allows us to support (small + * amounts of) static TLS in dynamically loaded + * modules. If we run out of space, we generate an + * error. + */ + if (!defobj->tls_done) { + if (!allocate_tls_offset((Obj_Entry*) defobj)) { + _rtld_error("%s: No space available for static " + "Thread Local Storage", obj->path); + return (-1); + } + } + + *(Elf_Addr **)where = *where * sizeof(Elf_Addr) + + (Elf_Addr *)(def->st_value + rela->r_addend + + defobj->tlsoffset - TLS_TP_OFFSET); + + break; + + case R_PPC64_DTPREL64: + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + flags, cache, lockstate); + + if (def == NULL) + return (-1); + + *where += (Elf_Addr)(def->st_value + rela->r_addend + - TLS_DTV_OFFSET); + + break; + + default: + _rtld_error("%s: Unsupported relocation type %ld" + " in non-PLT relocations\n", obj->path, + ELF_R_TYPE(rela->r_info)); + return (-1); + } + return (0); +} + + +/* + * Process non-PLT relocations + */ +int +reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, + RtldLockState *lockstate) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + SymCache *cache; + int bytes = obj->dynsymcount * sizeof(SymCache); + int r = -1; + + if ((flags & SYMLOOK_IFUNC) != 0) + /* XXX not implemented */ + return (0); + + /* + * The dynamic loader may be called from a thread, we have + * limited amounts of stack available so we cannot use alloca(). + */ + if (obj != obj_rtld) { + cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON, + -1, 0); + if (cache == MAP_FAILED) + cache = NULL; + } else + cache = NULL; + + /* + * From the SVR4 PPC ABI: + * "The PowerPC family uses only the Elf32_Rela relocation + * entries with explicit addends." + */ + relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); + for (rela = obj->rela; rela < relalim; rela++) { + if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags, + lockstate) < 0) + goto done; + } + r = 0; +done: + if (cache) + munmap(cache, bytes); + + /* Synchronize icache for text seg in case we made any changes */ + __syncicache(obj->mapbase, obj->textsize); + + return (r); +} + + +/* + * Initialise a PLT slot to the resolving trampoline + */ +static int +reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) +{ + Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + long reloff; + + reloff = rela - obj->pltrela; + + dbg(" reloc_plt_object: where=%p,reloff=%lx,glink=%#lx", (void *)where, + reloff, obj->glink); + +#if !defined(_CALL_ELF) || _CALL_ELF == 1 + /* Glink code is 3 instructions after the first 32k, 2 before */ + *where = (Elf_Addr)obj->glink + 32 + + 8*((reloff < 0x8000) ? reloff : 0x8000) + + 12*((reloff < 0x8000) ? 0 : (reloff - 0x8000)); +#else + *where = (Elf_Addr)obj->glink + 4*reloff + 32; +#endif + + return (0); +} + + +/* + * Process the PLT relocations. + */ +int +reloc_plt(Obj_Entry *obj) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + + if (obj->pltrelasize != 0) { + relalim = (const Elf_Rela *)((char *)obj->pltrela + + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); + + if (reloc_plt_object(obj, rela) < 0) { + return (-1); + } + } + } + + return (0); +} + + +/* + * LD_BIND_NOW was set - force relocation for all jump slots + */ +int +reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + const Obj_Entry *defobj; + const Elf_Rela *relalim; + const Elf_Rela *rela; + const Elf_Sym *def; + Elf_Addr *where; + Elf_Addr target; + + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + SYMLOOK_IN_PLT | flags, NULL, lockstate); + if (def == NULL) { + dbg("reloc_jmpslots: sym not found"); + return (-1); + } + + target = (Elf_Addr)(defobj->relocbase + def->st_value); + + if (def == &sym_zero) { + /* Zero undefined weak symbols */ +#if !defined(_CALL_ELF) || _CALL_ELF == 1 + bzero(where, sizeof(struct funcdesc)); +#else + *where = 0; +#endif + } else { + reloc_jmpslot(where, target, defobj, obj, + (const Elf_Rel *) rela); + } + } + + obj->jmpslots_done = true; + + return (0); +} + + +/* + * Update the value of a PLT jump slot. + */ +Elf_Addr +reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj, + const Obj_Entry *obj, const Elf_Rel *rel) +{ + + /* + * At the PLT entry pointed at by `wherep', construct + * a direct transfer to the now fully resolved function + * address. + */ + +#if !defined(_CALL_ELF) || _CALL_ELF == 1 + dbg(" reloc_jmpslot: where=%p, target=%p (%#lx + %#lx)", + (void *)wherep, (void *)target, *(Elf_Addr *)target, + (Elf_Addr)defobj->relocbase); + + if (ld_bind_not) + goto out; + + /* + * For the trampoline, the second two elements of the function + * descriptor are unused, so we are fine replacing those at any time + * with the real ones with no thread safety implications. However, we + * need to make sure the main entry point pointer ([0]) is seen to be + * modified *after* the second two elements. This can't be done in + * general, since there are no barriers in the reading code, but put in + * some isyncs to at least make it a little better. + */ + memcpy(wherep, (void *)target, sizeof(struct funcdesc)); + wherep[2] = ((Elf_Addr *)target)[2]; + wherep[1] = ((Elf_Addr *)target)[1]; + __asm __volatile ("isync" : : : "memory"); + wherep[0] = ((Elf_Addr *)target)[0]; + __asm __volatile ("isync" : : : "memory"); + + if (((struct funcdesc *)(wherep))->addr < (Elf_Addr)defobj->relocbase) { + /* + * It is possible (LD_BIND_NOW) that the function + * descriptor we are copying has not yet been relocated. + * If this happens, fix it. Don't worry about threading in + * this case since LD_BIND_NOW makes it irrelevant. + */ + + ((struct funcdesc *)(wherep))->addr += + (Elf_Addr)defobj->relocbase; + ((struct funcdesc *)(wherep))->toc += + (Elf_Addr)defobj->relocbase; + } +out: +#else + dbg(" reloc_jmpslot: where=%p, target=%p", (void *)wherep, + (void *)target); + + if (!ld_bind_not) + *wherep = target; +#endif + + return (target); +} + +int +reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, + struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +void +init_pltgot(Obj_Entry *obj) +{ + Elf_Addr *pltcall; + + pltcall = obj->pltgot; + + if (pltcall == NULL) { + return; + } + +#if defined(_CALL_ELF) && _CALL_ELF == 2 + pltcall[0] = (Elf_Addr)&_rtld_bind_start; + pltcall[1] = (Elf_Addr)obj; +#else + memcpy(pltcall, _rtld_bind_start, sizeof(struct funcdesc)); + pltcall[2] = (Elf_Addr)obj; +#endif +} + +void +ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) +{ +} + +void +allocate_initial_tls(Obj_Entry *list) +{ + Elf_Addr **tp; + + /* + * Fix the size of the static TLS block by using the maximum + * offset allocated so far and adding a bit for dynamic modules to + * use. + */ + + tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA; + + tp = (Elf_Addr **) ((char *)allocate_tls(list, NULL, TLS_TCB_SIZE, 16) + + TLS_TP_OFFSET + TLS_TCB_SIZE); + + __asm __volatile("mr 13,%0" :: "r"(tp)); +} + +void* +__tls_get_addr(tls_index* ti) +{ + Elf_Addr **tp; + char *p; + + __asm __volatile("mr %0,13" : "=r"(tp)); + p = tls_get_addr_common((Elf_Addr**)((Elf_Addr)tp - TLS_TP_OFFSET + - TLS_TCB_SIZE), ti->ti_module, ti->ti_offset); + + return (p + TLS_DTV_OFFSET); +} diff --git a/libexec/rtld-elf/powerpc64/rtld_machdep.h b/libexec/rtld-elf/powerpc64/rtld_machdep.h new file mode 100644 index 0000000..1b33f2e --- /dev/null +++ b/libexec/rtld-elf/powerpc64/rtld_machdep.h @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 1999, 2000 John D. Polstra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/powerpc64/rtld_machdep.h 319434 2017-06-01 15:12:51Z vangyzen $ + */ + +#ifndef RTLD_MACHDEP_H +#define RTLD_MACHDEP_H 1 + +#include +#include + +struct Struct_Obj_Entry; + +/* Return the address of the .dynamic section in the dynamic linker. */ +#define rtld_dynamic(obj) (&_DYNAMIC) + +Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, + const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj, + const Elf_Rel *rel); + +#define make_function_pointer(def, defobj) \ + ((defobj)->relocbase + (def)->st_value) + +#define call_initfini_pointer(obj, target) \ + (((InitFunc)(target))()) + +#define call_init_pointer(obj, target) \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)) + +#define call_ifunc_resolver(ptr) \ + (((Elf_Addr (*)(void))ptr)()) + +/* + * TLS + */ + +#define TLS_TP_OFFSET 0x7000 +#define TLS_DTV_OFFSET 0x8000 +#define TLS_TCB_SIZE 16 + +#define round(size, align) \ + (((size) + (align) - 1) & ~((align) - 1)) +#define calculate_first_tls_offset(size, align) \ + round(16, align) +#define calculate_tls_offset(prev_offset, prev_size, size, align) \ + round(prev_offset + prev_size, align) +#define calculate_tls_end(off, size) ((off) + (size)) + +typedef struct { + unsigned long ti_module; + unsigned long ti_offset; +} tls_index; + +extern void *__tls_get_addr(tls_index* ti); + +#define RTLD_DEFAULT_STACK_PF_EXEC PF_X +#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC + +#define md_abi_variant_hook(x) + +#endif diff --git a/libexec/rtld-elf/powerpc64/rtld_start.S b/libexec/rtld-elf/powerpc64/rtld_start.S new file mode 100644 index 0000000..2757c4f --- /dev/null +++ b/libexec/rtld-elf/powerpc64/rtld_start.S @@ -0,0 +1,171 @@ +/* $NetBSD: rtld_start.S,v 1.4 2001/09/26 04:06:43 mycroft Exp $ */ + +/*- + * Copyright (C) 1998 Tsubai Masanari + * 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/powerpc64/rtld_start.S 293018 2016-01-01 00:11:29Z nwhitehorn $ + */ + +#include + +.extern _GLOBAL_OFFSET_TABLE_ +.extern _DYNAMIC + +_ENTRY(_rtld_start) + stdu %r1,-144(%r1) /* 16-byte aligned stack for reg saves + + exit_proc & obj _rtld args + + backchain & lrsave stack frame */ + std %r3,96(%r1) /* argc */ + std %r4,104(%r1) /* argv */ + std %r5,112(%r1) /* envp */ +/* std %r6,120(%r1) *//* obj (always 0) */ +/* std %r7,128(%r1) *//* cleanup (always 0) */ + std %r8,136(%r1) /* ps_strings */ + + /* + * Perform initial relocation of ld-elf.so. Not as easy as it + * sounds. + * - perform small forward branch to put PC into link reg + * - use link-time constants to determine offset to the + * _DYNAMIC section and the GOT. Add these to the PC to + * convert to absolute addresses. + * - call reloc_non_plt_self() to fix up ld-elf.so's relocations + */ + + bl 1f + .llong _DYNAMIC-. +1: + mflr %r3 /* PC value at .llong */ + ld %r4,0(%r3) /* offset to _DYNAMIC */ + add %r3,%r4,%r3 /* r3 = &_DYNAMIC, absolute value */ + + ld %r4,-0x8000(%r2) /* First TOC entry is TOC base */ + subf %r4,%r4,%r2 /* Subtract from real TOC base to get base */ + + bl reloc_non_plt_self /* reloc_non_plt_self(&_DYNAMIC,base) */ + nop + + /* + * The _rtld() function likes to see a stack layout containing + * { argc, argv[0], argv[1] ... argv[N], 0, env[0], ... , env[N] } + * Since the PowerPC stack was 16-byte aligned at exec time, the + * original stack layout has to be found by moving back a word + * from the argv pointer. + */ + ld %r4,104(%r1) + addi %r3,%r4,-8 /* locate argc ptr, &argv[-1] */ + addi %r4,%r1,128 /* &exit_proc on stack */ + addi %r5,%r1,120 /* &obj_main on stack */ + + bl _rtld /* &_start = _rtld(sp, &exit_proc, &obj_main)*/ + nop +#if !defined(_CALL_ELF) || _CALL_ELF == 1 + ld %r2,8(%r3) + ld %r11,16(%r3) + ld %r3,0(%r3) +#else + mr %r12,%r3 +#endif + mtlr %r3 + + /* + * Restore args, with new obj/exit proc + */ + ld %r3,96(%r1) /* argc */ + ld %r4,104(%r1) /* argv */ + ld %r5,112(%r1) /* envp */ + ld %r6,120(%r1) /* obj */ + ld %r7,128(%r1) /* exit proc */ + ld %r8,136(%r1) /* ps_strings */ + + blrl /* _start(argc, argv, envp, obj, cleanup, ps_strings) */ + + li %r0,1 /* _exit() */ + sc + +/* + * _rtld_bind_start() + * + * Call into the MI binder. This routine is reached via the PLT call cell + * + * On entry, %r11 contains an object pointer and %r0 contains the PLT index. + * + * Save all registers, call into the binder to resolve and fixup the external + * routine, and then transfer to the external routine on return. + */ + .globl _rtld_bind + +_ENTRY(_rtld_bind_start) + mr %r12,%r0 # save r0 (index) immediately to r12 + mflr %r0 + std %r0,16(%r1) # save lr + mfcr %r0 + std %r0,8(%r1) # save cr + + stdu %r1,-48-12*8(%r1) # stack space for 8 regs + header + # + 2 save regs + std %r3,64+0*8(%r1) # save r3-r10 (arguments) + std %r4,64+1*8(%r1) + std %r5,64+2*8(%r1) + std %r6,64+3*8(%r1) + std %r7,64+4*8(%r1) + std %r8,64+5*8(%r1) + std %r9,64+6*8(%r1) + std %r10,64+7*8(%r1) + + mr %r3,%r11 + mulli %r4,%r12,24 # Multiply index by sizeof(Elf_Rela) + + bl _rtld_bind # target addr = _rtld_bind(obj, reloff) + nop + +#if !defined(_CALL_ELF) || _CALL_ELF == 1 + ld %r2,8(%r3) + ld %r11,16(%r3) + ld %r3,0(%r3) +#else + mr %r12,%r3 +#endif + mtctr %r3 # move absolute target addr into ctr + + ld %r3,64+0*8(%r1) # restore r3-r10 + ld %r4,64+1*8(%r1) + ld %r5,64+2*8(%r1) + ld %r6,64+3*8(%r1) + ld %r7,64+4*8(%r1) + ld %r8,64+5*8(%r1) + ld %r9,64+6*8(%r1) + ld %r10,64+7*8(%r1) + + ld %r1,0(%r1) # restore stack + ld %r0,8(%r1) # restore cr + mtcr %r0 + ld %r0,16(%r1) # restore lr + mtlr %r0 + + bctr # jump to target + + .section .note.GNU-stack,"",%progbits diff --git a/libexec/rtld-elf/reloc.c b/libexec/rtld-elf/reloc.c deleted file mode 100644 index ba9be47..0000000 --- a/libexec/rtld-elf/reloc.c +++ /dev/null @@ -1,367 +0,0 @@ -/*- - * Copyright 1996, 1997, 1998, 1999 John D. Polstra. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: src/libexec/rtld-elf/i386/reloc.c,v 1.18 2005/06/29 23:15:36 peter Exp $ - */ - -/* - * Dynamic linker for ELF. - * - * John Polstra . - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "debug.h" -#include "rtld.h" - -/* - * Process the special R_386_COPY relocations in the main program. These - * copy data from a shared object into a region in the main program's BSS - * segment. - * - * Returns 0 on success, -1 on failure. - */ -int -do_copy_relocations(Obj_Entry *dstobj) -{ - const Elf_Rel *rellim; - const Elf_Rel *rel; - - assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ - - rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize); - for (rel = dstobj->rel; rel < rellim; rel++) { - if (ELF_R_TYPE(rel->r_info) == R_386_COPY) { - void *dstaddr; - const Elf_Sym *dstsym; - const char *name; - unsigned long hash; - size_t size; - const void *srcaddr; - const Elf_Sym *srcsym; - Obj_Entry *srcobj; - - dstaddr = (void *) (dstobj->relocbase + rel->r_offset); - dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info); - name = dstobj->strtab + dstsym->st_name; - hash = elf_hash(name); - size = dstsym->st_size; - - for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) - if ((srcsym = symlook_obj(name, hash, srcobj, false)) != NULL) - break; - - if (srcobj == NULL) { - _rtld_error("Undefined symbol \"%s\" referenced from COPY" - " relocation in %s", name, dstobj->path); - return -1; - } - - srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value); - memcpy(dstaddr, srcaddr, size); - } - } - - return 0; -} - -/* Initialize the special GOT entries. */ -void -init_pltgot(Obj_Entry *obj) -{ - if (obj->pltgot != NULL) { - obj->pltgot[1] = (Elf_Addr) obj; - obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; - } -} - -/* Process the non-PLT relocations. */ -int -reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) -{ - const Elf_Rel *rellim; - const Elf_Rel *rel; - SymCache *cache; - int bytes = obj->nchains * sizeof(SymCache); - int r = -1; - - /* - * The dynamic loader may be called from a thread, we have - * limited amounts of stack available so we cannot use alloca(). - */ - cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON, -1, 0); - if (cache == MAP_FAILED) - cache = NULL; - - rellim = (const Elf_Rel *) ((caddr_t) obj->rel + obj->relsize); - for (rel = obj->rel; rel < rellim; rel++) { - Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rel->r_offset); - - switch (ELF_R_TYPE(rel->r_info)) { - - case R_386_NONE: - break; - - case R_386_32: - { - const Elf_Sym *def; - const Obj_Entry *defobj; - - def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, - false, cache); - - if (def == NULL) - goto done; - *where += (Elf_Addr) (defobj->relocbase + def->st_value); - } - break; - - case R_386_PC32: - /* - * I don't think the dynamic linker should ever see this - * type of relocation. But the binutils-2.6 tools sometimes - * generate it. - */ - { - const Elf_Sym *def; - const Obj_Entry *defobj; -//dbg("A.1"); - def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, - false, cache); -//dbg("A.2"); - if (def == NULL) - goto done; - - *where += - (Elf_Addr) (defobj->relocbase + def->st_value) - - (Elf_Addr) where; - } - break; - - case R_386_COPY: - /* - * These are deferred until all other relocations have - * been done. All we do here is make sure that the COPY - * relocation is not in a shared library. They are allowed - * only in executable files. - */ -//dbg("386_COPY"); - if (!obj->mainprog) { - _rtld_error("%s: Unexpected R_386_COPY relocation" - " in shared library", obj->path); - goto done; - } - break; - - case R_386_GLOB_DAT: - { -//dbg("386_GD"); - const Elf_Sym *def; - const Obj_Entry *defobj; - - def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, - false, cache); - if (def == NULL) - goto done; - - *where = (Elf_Addr) (defobj->relocbase + def->st_value); - } - break; - - case R_386_RELATIVE: - *where += (Elf_Addr) obj->relocbase; - break; - - case R_386_TLS_TPOFF: - { - const Elf_Sym *def; - const Obj_Entry *defobj; - - def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, - false, cache); - if (def == NULL) - goto done; - - /* - * We lazily allocate offsets for static TLS as we - * see the first relocation that references the - * TLS block. This allows us to support (small - * amounts of) static TLS in dynamically loaded - * modules. If we run out of space, we generate an - * error. - */ - if (!defobj->tls_done) { - if (!allocate_tls_offset((Obj_Entry*) defobj)) { - _rtld_error("%s: No space available for static " - "Thread Local Storage", obj->path); - goto done; - } - } - - *where += (Elf_Addr) (def->st_value - defobj->tlsoffset); - } - break; - - case R_386_TLS_DTPMOD32: - { - const Elf_Sym *def; - const Obj_Entry *defobj; - - def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, - false, cache); - if (def == NULL) - goto done; - - *where += (Elf_Addr) defobj->tlsindex; - } - break; - - case R_386_TLS_DTPOFF32: - { - const Elf_Sym *def; - const Obj_Entry *defobj; - - def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, - false, cache); - if (def == NULL) - goto done; - - *where += (Elf_Addr) def->st_value; - } - break; - - default: - _rtld_error("%s: Unsupported relocation type %d" - " in non-PLT relocations\n", obj->path, - ELF_R_TYPE(rel->r_info)); - goto done; - } - } - r = 0; -done: - if (cache) - munmap(cache, bytes); - return(r); -} - -/* Process the PLT relocations. */ -int -reloc_plt(Obj_Entry *obj) -{ - const Elf_Rel *rellim; - const Elf_Rel *rel; - - rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); - for (rel = obj->pltrel; rel < rellim; rel++) { - Elf_Addr *where; - - assert(ELF_R_TYPE(rel->r_info) == R_386_JMP_SLOT); - - /* Relocate the GOT slot pointing into the PLT. */ - where = (Elf_Addr *)(obj->relocbase + rel->r_offset); - *where += (Elf_Addr)obj->relocbase; - } - return 0; -} - -/* Relocate the jump slots in an object. */ -int -reloc_jmpslots(Obj_Entry *obj) -{ - const Elf_Rel *rellim; - const Elf_Rel *rel; - - if (obj->jmpslots_done) - return 0; - rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); - for (rel = obj->pltrel; rel < rellim; rel++) { - Elf_Addr *where, target; - const Elf_Sym *def; - const Obj_Entry *defobj; - - assert(ELF_R_TYPE(rel->r_info) == R_386_JMP_SLOT); - where = (Elf_Addr *)(obj->relocbase + rel->r_offset); - def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL); - if (def == NULL) - return -1; - target = (Elf_Addr)(defobj->relocbase + def->st_value); - reloc_jmpslot(where, target, defobj, obj, rel); - } - obj->jmpslots_done = true; - return 0; -} - -void -allocate_initial_tls(Obj_Entry *objs) -{ - void* tls; - - /* - * Fix the size of the static TLS block by using the maximum - * offset allocated so far and adding a bit for dynamic modules to - * use. - */ - tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA; - tls = allocate_tls(objs, NULL, 2*sizeof(Elf_Addr), sizeof(Elf_Addr)); - i386_set_gsbase(tls); -} - -/* GNU ABI */ -__attribute__((__regparm__(1))) -void *___tls_get_addr(tls_index *ti) -{ - Elf_Addr** segbase; - Elf_Addr* dtv; - - __asm __volatile("movl %%gs:0, %0" : "=r" (segbase)); - dtv = segbase[1]; - - return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset); -} - -/* Sun ABI */ -void *__tls_get_addr(tls_index *ti) -{ - Elf_Addr** segbase; - Elf_Addr* dtv; - - __asm __volatile("movl %%gs:0, %0" : "=r" (segbase)); - dtv = segbase[1]; - - return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset); -} diff --git a/libexec/rtld-elf/riscv/reloc.c b/libexec/rtld-elf/riscv/reloc.c new file mode 100644 index 0000000..eb62b71 --- /dev/null +++ b/libexec/rtld-elf/riscv/reloc.c @@ -0,0 +1,404 @@ +/*- + * Copyright (c) 2015 Ruslan Bukin + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * This software was developed by the University of Cambridge Computer + * Laboratory as part of the CTSRD Project, with support from the UK Higher + * Education Innovation Fund (HEIF). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/libexec/rtld-elf/riscv/reloc.c 316135 2017-03-29 11:03:08Z kib $"); + +#include + +#include + +#include "debug.h" +#include "rtld.h" +#include "rtld_printf.h" + +/* + * It is possible for the compiler to emit relocations for unaligned data. + * We handle this situation with these inlines. + */ +#define RELOC_ALIGNED_P(x) \ + (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) + +void _exit(int); + +uint64_t +set_gp(Obj_Entry *obj) +{ + uint64_t old; + SymLook req; + uint64_t gp; + int res; + + __asm __volatile("mv %0, gp" : "=r"(old)); + + symlook_init(&req, "_gp"); + req.ventry = NULL; + req.flags = SYMLOOK_EARLY; + res = symlook_obj(&req, obj); + + if (res == 0) { + gp = req.sym_out->st_value; + __asm __volatile("mv gp, %0" :: "r"(gp)); + } + + return (old); +} + +void +init_pltgot(Obj_Entry *obj) +{ + + if (obj->pltgot != NULL) { + obj->pltgot[0] = (Elf_Addr)&_rtld_bind_start; + obj->pltgot[1] = (Elf_Addr)obj; + } +} + +int +do_copy_relocations(Obj_Entry *dstobj) +{ + const Obj_Entry *srcobj, *defobj; + const Elf_Rela *relalim; + const Elf_Rela *rela; + const Elf_Sym *srcsym; + const Elf_Sym *dstsym; + const void *srcaddr; + const char *name; + void *dstaddr; + SymLook req; + size_t size; + int res; + + /* + * COPY relocs are invalid outside of the main program + */ + assert(dstobj->mainprog); + + relalim = (const Elf_Rela *)((char *)dstobj->rela + + dstobj->relasize); + for (rela = dstobj->rela; rela < relalim; rela++) { + if (ELF_R_TYPE(rela->r_info) != R_RISCV_COPY) + continue; + + dstaddr = (void *)(dstobj->relocbase + rela->r_offset); + dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); + name = dstobj->strtab + dstsym->st_name; + size = dstsym->st_size; + + symlook_init(&req, name); + req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); + req.flags = SYMLOOK_EARLY; + + for (srcobj = globallist_next(dstobj); srcobj != NULL; + srcobj = globallist_next(srcobj)) { + res = symlook_obj(&req, srcobj); + if (res == 0) { + srcsym = req.sym_out; + defobj = req.defobj_out; + break; + } + } + if (srcobj == NULL) { + _rtld_error( +"Undefined symbol \"%s\" referenced from COPY relocation in %s", + name, dstobj->path); + return (-1); + } + + srcaddr = (const void *)(defobj->relocbase + srcsym->st_value); + memcpy(dstaddr, srcaddr, size); + } + + return (0); +} + +/* + * Process the PLT relocations. + */ +int +reloc_plt(Obj_Entry *obj) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + Elf_Addr *where; + + assert(ELF_R_TYPE(rela->r_info) == R_RISCV_JUMP_SLOT); + + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + *where += (Elf_Addr)obj->relocbase; + } + + return (0); +} + +/* + * LD_BIND_NOW was set - force relocation for all jump slots + */ +int +reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + const Obj_Entry *defobj; + const Elf_Rela *relalim; + const Elf_Rela *rela; + const Elf_Sym *def; + + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + Elf_Addr *where; + + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + switch(ELF_R_TYPE(rela->r_info)) { + case R_RISCV_JUMP_SLOT: + def = find_symdef(ELF_R_SYM(rela->r_info), obj, + &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); + if (def == NULL) { + dbg("reloc_jmpslots: sym not found"); + return (-1); + } + + *where = (Elf_Addr)(defobj->relocbase + def->st_value); + break; + default: + _rtld_error("Unknown relocation type %x in jmpslot", + (unsigned int)ELF_R_TYPE(rela->r_info)); + return (-1); + } + } + + return (0); +} + +int +reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, + struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +Elf_Addr +reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj, + const Obj_Entry *obj, const Elf_Rel *rel) +{ + + assert(ELF_R_TYPE(rel->r_info) == R_RISCV_JUMP_SLOT); + + if (*where != target && !ld_bind_not) + *where = target; + return (target); +} + +/* + * Process non-PLT relocations + */ +int +reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, + RtldLockState *lockstate) +{ + const Obj_Entry *defobj; + const Elf_Rela *relalim; + const Elf_Rela *rela; + const Elf_Sym *def; + SymCache *cache; + Elf_Addr *where; + unsigned long symnum; + + if ((flags & SYMLOOK_IFUNC) != 0) + /* XXX not implemented */ + return (0); + + /* + * The dynamic loader may be called from a thread, we have + * limited amounts of stack available so we cannot use alloca(). + */ + if (obj == obj_rtld) + cache = NULL; + else + cache = calloc(obj->dynsymcount, sizeof(SymCache)); + /* No need to check for NULL here */ + + relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); + for (rela = obj->rela; rela < relalim; rela++) { + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + symnum = ELF_R_SYM(rela->r_info); + + switch (ELF_R_TYPE(rela->r_info)) { + case R_RISCV_JUMP_SLOT: + /* This will be handled by the plt/jmpslot routines */ + break; + case R_RISCV_NONE: + break; + case R_RISCV_64: + def = find_symdef(symnum, obj, &defobj, flags, cache, + lockstate); + if (def == NULL) + return (-1); + + *where = (Elf_Addr)(defobj->relocbase + def->st_value + + rela->r_addend); + break; + case R_RISCV_TLS_DTPMOD64: + def = find_symdef(symnum, obj, &defobj, flags, cache, + lockstate); + if (def == NULL) + return -1; + + *where += (Elf_Addr)defobj->tlsindex; + break; + case R_RISCV_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_RISCV_COPY " + "relocation in shared library", obj->path); + return (-1); + } + break; + case R_RISCV_TLS_DTPREL64: + def = find_symdef(symnum, obj, &defobj, flags, cache, + lockstate); + if (def == NULL) + return (-1); + /* + * We lazily allocate offsets for static TLS as we + * see the first relocation that references the + * TLS block. This allows us to support (small + * amounts of) static TLS in dynamically loaded + * modules. If we run out of space, we generate an + * error. + */ + if (!defobj->tls_done) { + if (!allocate_tls_offset((Obj_Entry*) defobj)) { + _rtld_error( + "%s: No space available for static " + "Thread Local Storage", obj->path); + return (-1); + } + } + + *where += (Elf_Addr)(def->st_value + rela->r_addend + - TLS_DTV_OFFSET); + break; + case R_RISCV_TLS_TPREL64: + def = find_symdef(symnum, obj, &defobj, flags, cache, + lockstate); + if (def == NULL) + return (-1); + + /* + * We lazily allocate offsets for static TLS as we + * see the first relocation that references the + * TLS block. This allows us to support (small + * amounts of) static TLS in dynamically loaded + * modules. If we run out of space, we generate an + * error. + */ + if (!defobj->tls_done) { + if (!allocate_tls_offset((Obj_Entry*) defobj)) { + _rtld_error( + "%s: No space available for static " + "Thread Local Storage", obj->path); + return (-1); + } + } + + *where = (def->st_value + rela->r_addend + + defobj->tlsoffset - TLS_TP_OFFSET); + break; + case R_RISCV_RELATIVE: + *where = (Elf_Addr)(obj->relocbase + rela->r_addend); + break; + default: + rtld_printf("%s: Unhandled relocation %lu\n", + obj->path, ELF_R_TYPE(rela->r_info)); + return (-1); + } + } + + return (0); +} + +void +ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) +{ +} + +void +allocate_initial_tls(Obj_Entry *objs) +{ + Elf_Addr **tp; + + /* + * Fix the size of the static TLS block by using the maximum + * offset allocated so far and adding a bit for dynamic modules to + * use. + */ + tls_static_space = tls_last_offset + tls_last_size + + RTLD_STATIC_TLS_EXTRA; + + tp = (Elf_Addr **) ((char *)allocate_tls(objs, NULL, TLS_TCB_SIZE, 16) + + TLS_TP_OFFSET + TLS_TCB_SIZE); + + __asm __volatile("mv tp, %0" :: "r"(tp)); +} + +void * +__tls_get_addr(tls_index* ti) +{ + char *_tp; + void *p; + + __asm __volatile("mv %0, tp" : "=r" (_tp)); + + p = tls_get_addr_common((Elf_Addr**)((Elf_Addr)_tp - TLS_TP_OFFSET + - TLS_TCB_SIZE), ti->ti_module, ti->ti_offset); + + return (p + TLS_DTV_OFFSET); +} diff --git a/libexec/rtld-elf/riscv/rtld_machdep.h b/libexec/rtld-elf/riscv/rtld_machdep.h new file mode 100644 index 0000000..a8be9a6 --- /dev/null +++ b/libexec/rtld-elf/riscv/rtld_machdep.h @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 1999, 2000 John D. Polstra. + * Copyright (c) 2015 Ruslan Bukin + * All rights reserved. + * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Portions of this software were developed by the University of Cambridge + * Computer Laboratory as part of the CTSRD Project, with support from the + * UK Higher Education Innovation Fund (HEIF). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/riscv/rtld_machdep.h 319434 2017-06-01 15:12:51Z vangyzen $ + */ + +#ifndef RTLD_MACHDEP_H +#define RTLD_MACHDEP_H 1 + +#include +#include + +struct Struct_Obj_Entry; + +uint64_t set_gp(struct Struct_Obj_Entry *obj); + +/* Return the address of the .dynamic section in the dynamic linker. */ +#define rtld_dynamic(obj) \ +({ \ + Elf_Addr _dynamic_addr; \ + __asm __volatile("lla %0, _DYNAMIC" : "=r"(_dynamic_addr)); \ + (const Elf_Dyn *)_dynamic_addr; \ +}) + +Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, + const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj, + const Elf_Rel *rel); + +#define make_function_pointer(def, defobj) \ + ((defobj)->relocbase + (def)->st_value) + +#define call_initfini_pointer(obj, target) \ +({ \ + uint64_t old0; \ + old0 = set_gp(obj); \ + (((InitFunc)(target))()); \ + __asm __volatile("mv gp, %0" :: "r"(old0)); \ +}) + +#define call_init_pointer(obj, target) \ +({ \ + uint64_t old1; \ + old1 = set_gp(obj); \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)); \ + __asm __volatile("mv gp, %0" :: "r"(old1)); \ +}) + +#define call_ifunc_resolver(ptr) \ + (((Elf_Addr (*)(void))ptr)()) + +/* + * TLS + */ +#define TLS_TP_OFFSET 0x0 +#define TLS_DTV_OFFSET 0x800 +#define TLS_TCB_SIZE 16 + +#define round(size, align) \ + (((size) + (align) - 1) & ~((align) - 1)) +#define calculate_first_tls_offset(size, align) \ + round(16, align) +#define calculate_tls_offset(prev_offset, prev_size, size, align) \ + round(prev_offset + prev_size, align) +#define calculate_tls_end(off, size) ((off) + (size)) + +typedef struct { + unsigned long ti_module; + unsigned long ti_offset; +} tls_index; + +extern void *__tls_get_addr(tls_index* ti); + +#define RTLD_DEFAULT_STACK_PF_EXEC PF_X +#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC + +#define md_abi_variant_hook(x) + +#endif diff --git a/libexec/rtld-elf/riscv/rtld_start.S b/libexec/rtld-elf/riscv/rtld_start.S new file mode 100644 index 0000000..69f28e2 --- /dev/null +++ b/libexec/rtld-elf/riscv/rtld_start.S @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 2015 Ruslan Bukin + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * This software was developed by the University of Cambridge Computer + * Laboratory as part of the CTSRD Project, with support from the UK Higher + * Education Innovation Fund (HEIF). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/libexec/rtld-elf/riscv/rtld_start.S 292691 2015-12-24 15:47:51Z br $"); + +/* + * func_ptr_type + * _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) + */ + +ENTRY(.rtld_start) + mv s0, a0 /* Put ps_strings in a callee-saved register */ + mv s1, sp /* And the stack pointer */ + + addi sp, sp, -16 /* Make room for obj_main & exit proc */ + + mv a1, sp /* exit_proc */ + addi a2, a1, 8 /* obj_main */ + jal _rtld /* Call the loader */ + mv t0, a0 /* Backup the entry point */ + + ld a2, 0(sp) /* Load cleanup */ + ld a1, 8(sp) /* Load obj_main */ + mv a0, s0 /* Restore ps_strings */ + mv sp, s1 /* Restore the stack pointer */ + jr t0 /* Jump to the entry point */ +END(.rtld_start) + +/* + * t0 = obj pointer + * t1 = reloc offset + */ +ENTRY(_rtld_bind_start) + /* Save the arguments and ra */ + addi sp, sp, -(8 * 25) + sd a0, (8 * 0)(sp) + sd a1, (8 * 1)(sp) + sd a2, (8 * 2)(sp) + sd a3, (8 * 3)(sp) + sd a4, (8 * 4)(sp) + sd a5, (8 * 5)(sp) + sd a6, (8 * 6)(sp) + sd a7, (8 * 7)(sp) + sd ra, (8 * 8)(sp) +#if 0 + /* RISCVTODO VFP */ + /* Save any floating-point arguments */ + fsq fa0, (8 * 9)(sp) + fsq fa1, (8 * 11)(sp) + fsq fa2, (8 * 13)(sp) + fsq fa3, (8 * 15)(sp) + fsq fa4, (8 * 17)(sp) + fsq fa5, (8 * 19)(sp) + fsq fa6, (8 * 21)(sp) + fsq fa7, (8 * 23)(sp) +#endif + + /* Reloc offset is 3x of the .got.plt offset */ + slli a1, t1, 1 /* Mult items by 2 */ + add a1, a1, t1 /* Plus item */ + + /* Load obj */ + mv a0, t0 + + /* Call into rtld */ + jal _rtld_bind + + /* Backup the address to branch to */ + mv t0, a0 + + /* Restore the arguments and ra */ + ld a0, (8 * 0)(sp) + ld a1, (8 * 1)(sp) + ld a2, (8 * 2)(sp) + ld a3, (8 * 3)(sp) + ld a4, (8 * 4)(sp) + ld a5, (8 * 5)(sp) + ld a6, (8 * 6)(sp) + ld a7, (8 * 7)(sp) + ld ra, (8 * 8)(sp) +#if 0 + /* RISCVTODO VFP */ + /* Restore floating-point arguments */ + flq fa0, (8 * 9)(sp) + flq fa1, (8 * 11)(sp) + flq fa2, (8 * 13)(sp) + flq fa3, (8 * 15)(sp) + flq fa4, (8 * 17)(sp) + flq fa5, (8 * 19)(sp) + flq fa6, (8 * 21)(sp) + flq fa7, (8 * 23)(sp) +#endif + addi sp, sp, (8 * 25) + + /* Call into the correct function */ + jr t0 +END(_rtld_bind_start) diff --git a/libexec/rtld-elf/rtld.1 b/libexec/rtld-elf/rtld.1 new file mode 100644 index 0000000..75763f1 --- /dev/null +++ b/libexec/rtld-elf/rtld.1 @@ -0,0 +1,374 @@ +.\" Copyright (c) 1995 Paul Kranenburg +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgment: +.\" This product includes software developed by Paul Kranenburg. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD: releng/11.1/libexec/rtld-elf/rtld.1 319367 2017-06-01 00:50:58Z kib $ +.\" +.Dd May 20, 2017 +.Dt RTLD 1 +.Os +.Sh NAME +.Nm ld-elf.so.1 , +.Nm ld.so , +.Nm rtld +.Nd run-time link-editor +.Sh DESCRIPTION +The +.Nm +utility is a self-contained shared object providing run-time +support for loading and link-editing shared objects into a process' +address space. +It is also commonly known as the dynamic linker. +It uses the data structures +contained within dynamically linked programs to determine which shared +libraries are needed and loads them using the +.Xr mmap 2 +system call. +.Pp +After all shared libraries have been successfully loaded, +.Nm +proceeds to resolve external references from both the main program and +all objects loaded. +A mechanism is provided for initialization routines +to be called on a per-object basis, giving a shared object an opportunity +to perform any extra set-up before execution of the program proper begins. +This is useful for C++ libraries that contain static constructors. +.Pp +When resolving dependencies for the loaded objects, +.Nm +translates dynamic token strings in rpath and soname. +If the +.Fl "z origin" +option of the static linker was set when linking the binary, +the token expansion is performed at the object load time, see +.Xr ld 1 . +The following strings are recognized now: +.Bl -tag -width ".Pa $PLATFORM" +.It Pa $ORIGIN +Translated to the full path of the loaded object. +.It Pa $OSNAME +Translated to the name of the operating system implementation. +.It Pa $OSREL +Translated to the release level of the operating system. +.It Pa $PLATFORM +Translated to the machine hardware platform. +.El +.Pp +The +.Nm +utility itself is loaded by the kernel together with any dynamically-linked +program that is to be executed. +The kernel transfers control to the +dynamic linker. +After the dynamic linker has finished loading, +relocating, and initializing the program and its required shared +objects, it transfers control to the entry point of the program. +The following search order is used to locate required shared objects: +.Pp +.Bl -enum -offset indent -compact +.It +.Dv DT_RPATH +of the referencing object unless that object also contains a +.Dv DT_RUNPATH +tag +.It +.Dv DT_RPATH +of the program unless the referencing object contains a +.Dv DT_RUNPATH +tag +.It +Path indicated by +.Ev LD_LIBRARY_PATH +environment variable +.It +.Dv DT_RUNPATH +of the referencing object +.It +Hints file produced by the +.Xr ldconfig 8 +utility +.It +The +.Pa /lib +and +.Pa /usr/lib +directories, unless the referencing object was linked using the +.Dq Fl z Ar nodefaultlib +option +.El +.Pp +The +.Nm +utility +recognizes a number of environment variables that can be used to modify +its behaviour. +On 64-bit architectures, the linker for 32-bit objects recognizes +all the environment variables listed below, but is being prefixed with +.Ev LD_32_ , +for example: +.Ev LD_32_TRACE_LOADED_OBJECTS . +.Bl -tag -width ".Ev LD_LIBMAP_DISABLE" +.It Ev LD_DUMP_REL_POST +If set, +.Nm +will print a table containing all relocations after symbol +binding and relocation. +.It Ev LD_DUMP_REL_PRE +If set, +.Nm +will print a table containing all relocations before symbol +binding and relocation. +.It Ev LD_LIBMAP +A library replacement list in the same format as +.Xr libmap.conf 5 . +For convenience, the characters +.Ql = +and +.Ql \&, +can be used instead of a space and a newline. +This variable is parsed after +.Xr libmap.conf 5 , +and will override its entries. +This variable is unset for set-user-ID and set-group-ID programs. +.It Ev LD_LIBMAP_DISABLE +If set, disables the use of +.Xr libmap.conf 5 +and +.Ev LD_LIBMAP . +This variable is unset for set-user-ID and set-group-ID programs. +.It Ev LD_ELF_HINTS_PATH +This variable will override the default location of +.Dq hints +file. +This variable is unset for set-user-ID and set-group-ID programs. +.It Ev LD_LIBRARY_PATH +A colon separated list of directories, overriding the default search path +for shared libraries. +This variable is unset for set-user-ID and set-group-ID programs. +.It Ev LD_LIBRARY_PATH_RPATH +If the variable is specified and has a value starting with +any of \'y\', \'Y\' or \'1\' symbols, the path specified by +.Ev LD_LIBRARY_PATH +variable is allowed to override the path from +.Dv DT_RPATH +for binaries which does not contain +.Dv DT_RUNPATH +tag. +For such binaries, when the variable +.Ev LD_LIBRARY_PATH_RPATH +is set, +.Dq Fl z Ar nodefaultlib +link-time option is ignored as well. +.It Ev LD_PRELOAD +A list of shared libraries, separated by colons and/or white space, +to be linked in before any +other shared libraries. +If the directory is not specified then +the directories specified by +.Ev LD_LIBRARY_PATH +will be searched first +followed by the set of built-in standard directories. +This variable is unset for set-user-ID and set-group-ID programs. +.Ev LD_LIBRARY_PATH_FDS +A colon separated list of file descriptor numbers for library directories. +This is intended for use within +.Xr capsicum 4 +sandboxes, when global namespaces such as the filesystem are unavailable. +It is consulted just after LD_LIBRARY_PATH. +This variable is unset for set-user-ID and set-group-ID programs. +.It Ev LD_BIND_NOT +When set to a nonempty string, prevents modifications of the PLT slots when +doing bindings. +As result, each call of the PLT-resolved function is resolved. +In combination with debug output, this provides complete account of +all bind actions at runtime. +This variable is unset for set-user-ID and set-group-ID programs. +.It Ev LD_BIND_NOW +When set to a nonempty string, causes +.Nm +to relocate all external function calls before starting execution of the +program. +Normally, function calls are bound lazily, at the first call +of each function. +.Ev LD_BIND_NOW +increases the start-up time of a program, but it avoids run-time +surprises caused by unexpectedly undefined functions. +.It Ev LD_TRACE_LOADED_OBJECTS +When set to a nonempty string, causes +.Nm +to exit after loading the shared objects and printing a summary which includes +the absolute pathnames of all objects, to standard output. +.It Ev LD_TRACE_LOADED_OBJECTS_ALL +When set to a nonempty string, causes +.Nm +to expand the summary to indicate which objects caused each object to +be loaded. +.It Ev LD_TRACE_LOADED_OBJECTS_FMT1 +.It Ev LD_TRACE_LOADED_OBJECTS_FMT2 +When set, these variables are interpreted as format strings a la +.Xr printf 3 +to customize the trace output and are used by +.Xr ldd 1 Ns 's +.Fl f +option and allows +.Xr ldd 1 +to be operated as a filter more conveniently. +If the dependency name starts with string +.Pa lib , +.Ev LD_TRACE_LOADED_OBJECTS_FMT1 +is used, otherwise +.Ev LD_TRACE_LOADED_OBJECTS_FMT2 +is used. +The following conversions can be used: +.Bl -tag -width 4n +.It Li %a +The main program's name +(also known as +.Dq __progname ) . +.It Li \&%A +The value of the environment variable +.Ev LD_TRACE_LOADED_OBJECTS_PROGNAME . +Typically used to print both the names of programs and shared libraries +being inspected using +.Xr ldd 1 . +.It Li %o +The library name. +.It Li %p +The full pathname as determined by +.Nm rtld Ns 's +library search rules. +.It Li %x +The library's load address. +.El +.Pp +Additionally, +.Ql \en +and +.Ql \et +are recognized and have their usual meaning. +.It Ev LD_UTRACE +If set, +.Nm +will log events such as the loading and unloading of shared objects via +.Xr utrace 2 . +.It Ev LD_LOADFLTR +If set, +.Nm +will process the filtee dependencies of the loaded objects immediately, +instead of postponing it until required. +Normally, the filtees are opened at the time of the first symbol resolution +from the filter object. +.El +.Sh DIRECT EXECUTION MODE +.Nm +is typically used implicitly, loaded by the kernel as requested by the +.Dv PT_INTERP +program header of the executed binary. +.Fx +also supports a direct execution mode for the dynamic linker. +In this mode, the user explicitly executes +.Nm +and provides the path of the program to be linked and executed as +an argument. +This mode allows use of a non-standard dynamic linker for a program +activation without changing the binary or without changing +the installed dynamic linker. +Execution options may be specified. +.Pp +The syntax of the direct invocation is +.Bd -ragged -offset indent +.Pa /libexec/ld-elf.so.1 +.Op Fl f Ar fd +.Op Fl p +.Op Fl - +.Pa image_path +.Op Ar image arguments +.Ed +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl f Ar fd +File descriptor +.Ar fd +references the binary to be activated by +.Nm . +It must already be opened in the process when executing +.Nm . +If this option is specified, +.Ar image_path +is only used to provide the +.Va argv[0] +value to the program. +.It Fl p +If the +.Pa image_path +argument specifies a name which does not contain a slash +.Dq Li / +character, +.Nm +uses the search path provided by the environment variable +.Dv PATH +to find the binary to execute. +.It Fl - +Ends the +.Nm +options. +The argument following +.Fl - +is interpreted as the path of binary to execute. +.El +.Pp +To conform to user expectation to not break some naively restricted +execution environments, in the direct execution mode +.Nm +emulates verification of the binary execute permission +for current user. +The verification only uses Unix +.Dv DACs , +ignores +.Dv ACLs +and is racy by its nature. +The environments which rely on such restrictions are weak +and breakable on its own. +.Sh FILES +.Bl -tag -width ".Pa /var/run/ld-elf32.so.hints" -compact +.It Pa /var/run/ld-elf.so.hints +Hints file. +.It Pa /var/run/ld-elf32.so.hints +Hints file for 32-bit binaries on 64-bit system. +.It Pa /etc/libmap.conf +The libmap configuration file. +.It Pa /etc/libmap32.conf +The libmap configuration file for 32-bit binaries on 64-bit system. +.El +.Sh SEE ALSO +.Xr ld 1 , +.Xr ldd 1 , +.Xr capsicum 4 , +.Xr elf 5 , +.Xr libmap.conf 5 , +.Xr ldconfig 8 diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 789071b..bffcff1 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1,8 +1,14 @@ /*- * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra. * Copyright 2003 Alexander Kabaev . + * Copyright 2009-2013 Konstantin Belousov . + * Copyright 2012 John Marino . + * Copyright 2014-2017 The FreeBSD Foundation * All rights reserved. * + * Portions of this software were developed by Konstantin Belousov + * under sponsorship from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -22,8 +28,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id: rtld.c 114 2016-01-14 02:30:09Z reddawg $ */ /* @@ -32,14 +36,17 @@ * John Polstra . */ -#ifndef __GNUC__ -#error "GCC is needed to compile this file" -#endif +#include +__FBSDID("$FreeBSD: releng/11.1/libexec/rtld-elf/rtld.c 320800 2017-07-08 04:53:12Z delphij $"); #include #include #include #include +#include +#include +#include +#include #include #include @@ -54,105 +61,146 @@ #include "debug.h" #include "rtld.h" #include "libmap.h" +#include "paths.h" #include "rtld_tls.h" - -#ifndef COMPAT_32BIT -#define PATH_RTLD "/libexec/ld-elf.so.1" -#else -#define PATH_RTLD "/libexec/ld-elf32.so.1" -#endif +#include "rtld_printf.h" +#include "rtld_utrace.h" +#include "notes.h" /* Types. */ typedef void (*func_ptr_type)(); typedef void * (*path_enum_proc) (const char *path, size_t len, void *arg); /* - * This structure provides a reentrant way to keep a list of objects and - * check which ones have already been processed in some way. - */ -typedef struct Struct_DoneList { - const Obj_Entry **objs; /* Array of object pointers */ - unsigned int num_alloc; /* Allocated size of the array */ - unsigned int num_used; /* Number of array slots used */ -} DoneList; - -/* * Function declarations. */ static const char *basename(const char *); -static void die(void); +static void digest_dynamic1(Obj_Entry *, int, const Elf_Dyn **, + const Elf_Dyn **, const Elf_Dyn **); +static void digest_dynamic2(Obj_Entry *, const Elf_Dyn *, const Elf_Dyn *, + const Elf_Dyn *); static void digest_dynamic(Obj_Entry *, int); static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *); static Obj_Entry *dlcheck(void *); +static int dlclose_locked(void *, RtldLockState *); +static Obj_Entry *dlopen_object(const char *name, int fd, Obj_Entry *refobj, + int lo_flags, int mode, RtldLockState *lockstate); +static Obj_Entry *do_load_object(int, const char *, char *, struct stat *, int); static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *); static bool donelist_check(DoneList *, const Obj_Entry *); static void errmsg_restore(char *); static char *errmsg_save(void); static void *fill_search_info(const char *, size_t, void *); -static char *find_library(const char *, const Obj_Entry *); -static const char *gethints(void); +static char *find_library(const char *, const Obj_Entry *, int *); +static const char *gethints(bool); +static void hold_object(Obj_Entry *); +static void unhold_object(Obj_Entry *); static void init_dag(Obj_Entry *); -static void init_dag1(Obj_Entry *root, Obj_Entry *obj, DoneList *); -static void init_rtld(caddr_t); -static void initlist_add_neededs(Needed_Entry *needed, Objlist *list); -static void initlist_add_objects(Obj_Entry *obj, Obj_Entry **tail, - Objlist *list); -static bool is_exported(const Elf_Sym *); +static void init_marker(Obj_Entry *); +static void init_pagesizes(Elf_Auxinfo **aux_info); +static void init_rtld(caddr_t, Elf_Auxinfo **); +static void initlist_add_neededs(Needed_Entry *, Objlist *); +static void initlist_add_objects(Obj_Entry *, Obj_Entry *, Objlist *); static void linkmap_add(Obj_Entry *); static void linkmap_delete(Obj_Entry *); -static int load_needed_objects(Obj_Entry *); +static void load_filtees(Obj_Entry *, int flags, RtldLockState *); +static void unload_filtees(Obj_Entry *, RtldLockState *); +static int load_needed_objects(Obj_Entry *, int); static int load_preload_objects(void); -static Obj_Entry *load_object(char *); +static Obj_Entry *load_object(const char *, int fd, const Obj_Entry *, int); +static void map_stacks_exec(RtldLockState *); +static int obj_enforce_relro(Obj_Entry *); static Obj_Entry *obj_from_addr(const void *); -static void objlist_call_fini(Objlist *); -static void objlist_call_init(Objlist *); +static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *); +static void objlist_call_init(Objlist *, RtldLockState *); static void objlist_clear(Objlist *); static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *); static void objlist_init(Objlist *); static void objlist_push_head(Objlist *, Obj_Entry *); static void objlist_push_tail(Objlist *, Obj_Entry *); +static void objlist_put_after(Objlist *, Obj_Entry *, Obj_Entry *); static void objlist_remove(Objlist *, Obj_Entry *); -static void objlist_remove_unref(Objlist *); +static int open_binary_fd(const char *argv0, bool search_in_path); +static int parse_args(char* argv[], int argc, bool *use_pathp, int *fdp); +static int parse_integer(const char *); static void *path_enumerate(const char *, path_enum_proc, void *); -static int relocate_objects(Obj_Entry *, bool, Obj_Entry *); +static void print_usage(const char *argv0); +static void release_object(Obj_Entry *); +static int relocate_object_dag(Obj_Entry *root, bool bind_now, + Obj_Entry *rtldobj, int flags, RtldLockState *lockstate); +static int relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, + int flags, RtldLockState *lockstate); +static int relocate_objects(Obj_Entry *, bool, Obj_Entry *, int, + RtldLockState *); +static int resolve_objects_ifunc(Obj_Entry *first, bool bind_now, + int flags, RtldLockState *lockstate); static int rtld_dirname(const char *, char *); +static int rtld_dirname_abs(const char *, char *); +static void *rtld_dlopen(const char *name, int fd, int mode); static void rtld_exit(void); static char *search_library_path(const char *, const char *); -static const void **get_program_var_addr(const char *name); +static char *search_library_pathfds(const char *, const char *, int *); +static const void **get_program_var_addr(const char *, RtldLockState *); static void set_program_var(const char *, const void *); -static const Elf_Sym *symlook_default(const char *, unsigned long hash, - const Obj_Entry *refobj, const Obj_Entry **defobj_out, bool in_plt); -static const Elf_Sym *symlook_list(const char *, unsigned long, - Objlist *, const Obj_Entry **, bool in_plt, DoneList *); -static void trace_loaded_objects(Obj_Entry *obj); +static int symlook_default(SymLook *, const Obj_Entry *refobj); +static int symlook_global(SymLook *, DoneList *); +static void symlook_init_from_req(SymLook *, const SymLook *); +static int symlook_list(SymLook *, const Objlist *, DoneList *); +static int symlook_needed(SymLook *, const Needed_Entry *, DoneList *); +static int symlook_obj1_sysv(SymLook *, const Obj_Entry *); +static int symlook_obj1_gnu(SymLook *, const Obj_Entry *); +static void trace_loaded_objects(Obj_Entry *); static void unlink_object(Obj_Entry *); -static void unload_object(Obj_Entry *); +static void unload_object(Obj_Entry *, RtldLockState *lockstate); static void unref_dag(Obj_Entry *); static void ref_dag(Obj_Entry *); +static char *origin_subst_one(Obj_Entry *, char *, const char *, + const char *, bool); +static char *origin_subst(Obj_Entry *, char *); +static bool obj_resolve_origin(Obj_Entry *obj); +static void preinit_main(void); +static int rtld_verify_versions(const Objlist *); +static int rtld_verify_object_versions(Obj_Entry *); +static void object_add_name(Obj_Entry *, const char *); +static int object_match_name(const Obj_Entry *, const char *); +static void ld_utrace_log(int, void *, void *, size_t, int, const char *); +static void rtld_fill_dl_phdr_info(const Obj_Entry *obj, + struct dl_phdr_info *phdr_info); +static uint32_t gnu_hash(const char *); +static bool matched_symbol(SymLook *, const Obj_Entry *, Sym_Match_Result *, + const unsigned long); -void r_debug_state(struct r_debug*, struct link_map*); +void r_debug_state(struct r_debug *, struct link_map *) __noinline __exported; +void _r_debug_postinit(struct link_map *) __noinline __exported; + +int __sys_openat(int, const char *, int, ...); /* * Data declarations. */ static char *error_message; /* Message for dlerror(), or NULL */ -struct r_debug r_debug; /* for GDB; */ +struct r_debug r_debug __exported; /* for GDB; */ static bool libmap_disable; /* Disable libmap */ +static bool ld_loadfltr; /* Immediate filters processing */ static char *libmap_override; /* Maps to use in addition to libmap.conf */ static bool trust; /* False for setuid and setgid programs */ static bool dangerous_ld_env; /* True if environment variables have been used to affect the libraries loaded */ +bool ld_bind_not; /* Disable PLT update */ static char *ld_bind_now; /* Environment variable for immediate binding */ static char *ld_debug; /* Environment variable for debugging */ static char *ld_library_path; /* Environment variable for search path */ +static char *ld_library_dirs; /* Environment variable for library descriptors */ static char *ld_preload; /* Environment variable for libraries to load first */ +static char *ld_elf_hints_path; /* Environment variable for alternative hints path */ static char *ld_tracing; /* Called from ldd to print libs */ -static Obj_Entry *obj_list; /* Head of linked list of shared objects */ -static Obj_Entry **obj_tail; /* Link field of last object in list */ +static char *ld_utrace; /* Use utrace() to log events. */ +static struct obj_entry_q obj_list; /* Queue of all loaded objects */ static Obj_Entry *obj_main; /* The main program shared object */ static Obj_Entry obj_rtld; /* The dynamic linker shared object */ static unsigned int obj_count; /* Number of objects in obj_list */ +static unsigned int obj_loads; /* Number of loads of objects (gen count) */ static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */ STAILQ_HEAD_INITIALIZER(list_global); @@ -161,39 +209,37 @@ static Objlist list_fini = /* Objects needing fini() calls */ STAILQ_HEAD_INITIALIZER(list_fini); -static Elf_Sym sym_zero; /* For resolving undefined weak refs. */ +Elf_Sym sym_zero; /* For resolving undefined weak refs. */ #define GDB_STATE(s,m) r_debug.r_state = s; r_debug_state(&r_debug,m); extern Elf_Dyn _DYNAMIC; #pragma weak _DYNAMIC -#ifndef RTLD_IS_DYNAMIC -#define RTLD_IS_DYNAMIC() (&_DYNAMIC != NULL) -#endif -/* - * These are the functions the dynamic linker exports to application - * programs. They are the only symbols the dynamic linker is willing - * to export from itself. - */ -static func_ptr_type exports[] = { - (func_ptr_type) &_rtld_error, - (func_ptr_type) &dlclose, - (func_ptr_type) &dlerror, - (func_ptr_type) &dlopen, - (func_ptr_type) &dlsym, - (func_ptr_type) &dladdr, - (func_ptr_type) &dllockinit, - (func_ptr_type) &dlinfo, - (func_ptr_type) &_rtld_thread_init, -#ifdef __i386__ - (func_ptr_type) &___tls_get_addr, -#endif - (func_ptr_type) &__tls_get_addr, - (func_ptr_type) &_rtld_allocate_tls, - (func_ptr_type) &_rtld_free_tls, - NULL -}; +int dlclose(void *) __exported; +char *dlerror(void) __exported; +void *dlopen(const char *, int) __exported; +void *fdlopen(int, int) __exported; +void *dlsym(void *, const char *) __exported; +dlfunc_t dlfunc(void *, const char *) __exported; +void *dlvsym(void *, const char *, const char *) __exported; +int dladdr(const void *, Dl_info *) __exported; +void dllockinit(void *, void *(*)(void *), void (*)(void *), void (*)(void *), + void (*)(void *), void (*)(void *), void (*)(void *)) __exported; +int dlinfo(void *, int , void *) __exported; +int dl_iterate_phdr(__dl_iterate_hdr_callback, void *) __exported; +int _rtld_addr_phdr(const void *, struct dl_phdr_info *) __exported; +int _rtld_get_stack_prot(void) __exported; +int _rtld_is_dlopened(void *) __exported; +void _rtld_error(const char *, ...) __exported; + +int npagesizes, osreldate; +size_t *pagesizes; + +long __stack_chk_guard[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + +static int stack_prot = PROT_READ | PROT_WRITE | RTLD_DEFAULT_STACK_EXEC; +static int max_stack_flags; /* * Global declarations normally provided by crt1. The dynamic linker is @@ -203,14 +249,32 @@ char **environ; /* + * Used to pass argc, argv to init functions. + */ +int main_argc; +char **main_argv; + +/* * Globals to control TLS allocation. */ size_t tls_last_offset; /* Static TLS offset of last module */ size_t tls_last_size; /* Static TLS size of last module */ size_t tls_static_space; /* Static TLS space allocated */ +size_t tls_static_max_align; int tls_dtv_generation = 1; /* Used to detect when dtv size changes */ int tls_max_index = 1; /* Largest module index allocated */ +bool ld_library_path_rpath = false; + +/* + * Globals for path names, and such + */ +char *ld_elf_hints_default = _PATH_ELF_HINTS; +char *ld_path_libmap_conf = _PATH_LIBMAP_CONF; +char *ld_path_rtld = _PATH_RTLD; +char *ld_standard_library_path = STANDARD_LIBRARY_PATH; +char *ld_env_prefix = LD_; + /* * Fill in a DoneList with an allocation large enough to hold all of * the currently-loaded objects. Keep this as a macro since it calls @@ -222,6 +286,48 @@ (dlp)->num_alloc = obj_count, \ (dlp)->num_used = 0) +#define LD_UTRACE(e, h, mb, ms, r, n) do { \ + if (ld_utrace != NULL) \ + ld_utrace_log(e, h, mb, ms, r, n); \ +} while (0) + +static void +ld_utrace_log(int event, void *handle, void *mapbase, size_t mapsize, + int refcnt, const char *name) +{ + struct utrace_rtld ut; + static const char rtld_utrace_sig[RTLD_UTRACE_SIG_SZ] = RTLD_UTRACE_SIG; + + memcpy(ut.sig, rtld_utrace_sig, sizeof(ut.sig)); + ut.event = event; + ut.handle = handle; + ut.mapbase = mapbase; + ut.mapsize = mapsize; + ut.refcnt = refcnt; + bzero(ut.name, sizeof(ut.name)); + if (name) + strlcpy(ut.name, name, sizeof(ut.name)); + utrace(&ut, sizeof(ut)); +} + +#ifdef RTLD_VARIANT_ENV_NAMES +/* + * construct the env variable based on the type of binary that's + * running. + */ +static inline const char * +_LD(const char *var) +{ + static char buffer[128]; + + strlcpy(buffer, ld_env_prefix, sizeof(buffer)); + strlcat(buffer, var, sizeof(buffer)); + return (buffer); +} +#else +#define _LD(x) LD_ x +#endif + /* * Main entry point for dynamic linking. The first argument is the * stack pointer. The stack is expected to be laid out as described @@ -241,19 +347,20 @@ func_ptr_type _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) { - Elf_Auxinfo *aux_info[AT_COUNT]; - int i; - int argc; - char **argv; - char **env; - Elf_Auxinfo *aux; - Elf_Auxinfo *auxp; - const char *argv0; + Elf_Auxinfo *aux, *auxp, *auxpf, *aux_info[AT_COUNT]; Objlist_Entry *entry; - Obj_Entry *obj; - Obj_Entry **preload_tail; + Obj_Entry *last_interposer, *obj, *preload_tail; + const Elf_Phdr *phdr; Objlist initlist; - int lockstate; + RtldLockState lockstate; + struct stat st; + Elf_Addr *argcp; + char **argv, *argv0, **env, **envp, *kexecpath, *library_path_rpath; + caddr_t imgentry; + char buf[MAXPATHLEN]; + int argc, fd, i, mib[2], phnum, rtld_argc; + size_t len; + bool dir_enable, explicit_fd, search_in_path; /* * On entry, the dynamic linker itself has not been relocated yet. @@ -263,76 +370,209 @@ */ /* Find the auxiliary vector on the stack. */ + argcp = sp; argc = *sp++; argv = (char **) sp; - sp += argc + 1; /* Skip over arguments and NULL terminator */ env = (char **) sp; - - while (*sp++ != 0); /* Skip over environment, and NULL terminator */ - + while (*sp++ != 0) /* Skip over environment, and NULL terminator */ + ; aux = (Elf_Auxinfo *) sp; - /* Digest the auxiliary vector. */ for (i = 0; i < AT_COUNT; i++) aux_info[i] = NULL; - for (auxp = aux; auxp->a_type != AT_NULL; auxp++) { - if (auxp->a_type < AT_COUNT) { - aux_info[auxp->a_type] = auxp; - } + if (auxp->a_type < AT_COUNT) + aux_info[auxp->a_type] = auxp; } /* Initialize and relocate ourselves. */ assert(aux_info[AT_BASE] != NULL); - - init_rtld((caddr_t) aux_info[AT_BASE]->a_un.a_ptr); + init_rtld((caddr_t) aux_info[AT_BASE]->a_un.a_ptr, aux_info); __progname = obj_rtld.path; argv0 = argv[0] != NULL ? argv[0] : "(null)"; environ = env; + main_argc = argc; + main_argv = argv; + + if (aux_info[AT_CANARY] != NULL && + aux_info[AT_CANARY]->a_un.a_ptr != NULL) { + i = aux_info[AT_CANARYLEN]->a_un.a_val; + if (i > sizeof(__stack_chk_guard)) + i = sizeof(__stack_chk_guard); + memcpy(__stack_chk_guard, aux_info[AT_CANARY]->a_un.a_ptr, i); + } else { + mib[0] = CTL_KERN; + mib[1] = KERN_ARND; + + len = sizeof(__stack_chk_guard); + if (sysctl(mib, 2, __stack_chk_guard, &len, NULL, 0) == -1 || + len != sizeof(__stack_chk_guard)) { + /* If sysctl was unsuccessful, use the "terminator canary". */ + ((unsigned char *)(void *)__stack_chk_guard)[0] = 0; + ((unsigned char *)(void *)__stack_chk_guard)[1] = 0; + ((unsigned char *)(void *)__stack_chk_guard)[2] = '\n'; + ((unsigned char *)(void *)__stack_chk_guard)[3] = 255; + } + } trust = !issetugid(); - ld_bind_now = getenv(LD_ "BIND_NOW"); - if (trust) { - ld_debug = getenv(LD_ "DEBUG"); - libmap_disable = getenv(LD_ "LIBMAP_DISABLE") != NULL; - libmap_override = getenv(LD_ "LIBMAP"); - ld_library_path = "/lib";//getenv(LD_ "LIBRARY_PATH"); - ld_preload = getenv(LD_ "PRELOAD"); - dangerous_ld_env = libmap_disable || (libmap_override != NULL) || - (ld_library_path != NULL) || (ld_preload != NULL); - } else - dangerous_ld_env = 0; - ld_tracing = getenv(LD_ "TRACE_LOADED_OBJECTS"); + md_abi_variant_hook(aux_info); + fd = -1; + if (aux_info[AT_EXECFD] != NULL) { + fd = aux_info[AT_EXECFD]->a_un.a_val; + } else { + assert(aux_info[AT_PHDR] != NULL); + phdr = (const Elf_Phdr *)aux_info[AT_PHDR]->a_un.a_ptr; + if (phdr == obj_rtld.phdr) { + if (!trust) { + rtld_printf("Tainted process refusing to run binary %s\n", + argv0); + rtld_die(); + } + dbg("opening main program in direct exec mode"); + if (argc >= 2) { + rtld_argc = parse_args(argv, argc, &search_in_path, &fd); + argv0 = argv[rtld_argc]; + explicit_fd = (fd != -1); + if (!explicit_fd) + fd = open_binary_fd(argv0, search_in_path); + if (fstat(fd, &st) == -1) { + _rtld_error("failed to fstat FD %d (%s): %s", fd, + explicit_fd ? "user-provided descriptor" : argv0, + rtld_strerror(errno)); + rtld_die(); + } + + /* + * Rough emulation of the permission checks done by + * execve(2), only Unix DACs are checked, ACLs are + * ignored. Preserve the semantic of disabling owner + * to execute if owner x bit is cleared, even if + * others x bit is enabled. + * mmap(2) does not allow to mmap with PROT_EXEC if + * binary' file comes from noexec mount. We cannot + * set VV_TEXT on the binary. + */ + dir_enable = false; + if (st.st_uid == geteuid()) { + if ((st.st_mode & S_IXUSR) != 0) + dir_enable = true; + } else if (st.st_gid == getegid()) { + if ((st.st_mode & S_IXGRP) != 0) + dir_enable = true; + } else if ((st.st_mode & S_IXOTH) != 0) { + dir_enable = true; + } + if (!dir_enable) { + rtld_printf("No execute permission for binary %s\n", + argv0); + rtld_die(); + } + + /* + * For direct exec mode, argv[0] is the interpreter + * name, we must remove it and shift arguments left + * before invoking binary main. Since stack layout + * places environment pointers and aux vectors right + * after the terminating NULL, we must shift + * environment and aux as well. + */ + main_argc = argc - rtld_argc; + for (i = 0; i <= main_argc; i++) + argv[i] = argv[i + rtld_argc]; + *argcp -= rtld_argc; + environ = env = envp = argv + main_argc + 1; + do { + *envp = *(envp + rtld_argc); + envp++; + } while (*envp != NULL); + aux = auxp = (Elf_Auxinfo *)envp; + auxpf = (Elf_Auxinfo *)(envp + rtld_argc); + for (;; auxp++, auxpf++) { + *auxp = *auxpf; + if (auxp->a_type == AT_NULL) + break; + } + } else { + rtld_printf("no binary\n"); + rtld_die(); + } + } + } + + ld_bind_now = getenv(_LD("BIND_NOW")); + + /* + * If the process is tainted, then we un-set the dangerous environment + * variables. The process will be marked as tainted until setuid(2) + * is called. If any child process calls setuid(2) we do not want any + * future processes to honor the potentially un-safe variables. + */ + if (!trust) { + if (unsetenv(_LD("PRELOAD")) || unsetenv(_LD("LIBMAP")) || + unsetenv(_LD("LIBRARY_PATH")) || unsetenv(_LD("LIBRARY_PATH_FDS")) || + unsetenv(_LD("LIBMAP_DISABLE")) || unsetenv(_LD("BIND_NOT")) || + unsetenv(_LD("DEBUG")) || unsetenv(_LD("ELF_HINTS_PATH")) || + unsetenv(_LD("LOADFLTR")) || unsetenv(_LD("LIBRARY_PATH_RPATH"))) { + _rtld_error("environment corrupt; aborting"); + rtld_die(); + } + } + ld_debug = getenv(_LD("DEBUG")); + if (ld_bind_now == NULL) + ld_bind_not = getenv(_LD("BIND_NOT")) != NULL; + libmap_disable = getenv(_LD("LIBMAP_DISABLE")) != NULL; + libmap_override = getenv(_LD("LIBMAP")); + ld_library_path = getenv(_LD("LIBRARY_PATH")); + ld_library_dirs = getenv(_LD("LIBRARY_PATH_FDS")); + ld_preload = getenv(_LD("PRELOAD")); + ld_elf_hints_path = getenv(_LD("ELF_HINTS_PATH")); + ld_loadfltr = getenv(_LD("LOADFLTR")) != NULL; + library_path_rpath = getenv(_LD("LIBRARY_PATH_RPATH")); + if (library_path_rpath != NULL) { + if (library_path_rpath[0] == 'y' || + library_path_rpath[0] == 'Y' || + library_path_rpath[0] == '1') + ld_library_path_rpath = true; + else + ld_library_path_rpath = false; + } + dangerous_ld_env = libmap_disable || (libmap_override != NULL) || + (ld_library_path != NULL) || (ld_preload != NULL) || + (ld_elf_hints_path != NULL) || ld_loadfltr; + ld_tracing = getenv(_LD("TRACE_LOADED_OBJECTS")); + ld_utrace = getenv(_LD("UTRACE")); + + if ((ld_elf_hints_path == NULL) || strlen(ld_elf_hints_path) == 0) + ld_elf_hints_path = ld_elf_hints_default; if (ld_debug != NULL && *ld_debug != '\0') debug = 1; - dbg("%s is initialized, base address = %p", __progname, (caddr_t) aux_info[AT_BASE]->a_un.a_ptr); dbg("RTLD dynamic = %p", obj_rtld.dynamic); dbg("RTLD pltgot = %p", obj_rtld.pltgot); + dbg("initializing thread locks"); + lockdflt_init(); + /* * Load the main program, or process its program header if it is * already loaded. */ - if (aux_info[AT_EXECFD] != NULL) { /* Load the main program. */ - int fd = aux_info[AT_EXECFD]->a_un.a_val; + if (fd != -1) { /* Load the main program. */ dbg("loading main program"); obj_main = map_object(fd, argv0, NULL); close(fd); if (obj_main == NULL) - die(); + rtld_die(); + max_stack_flags = obj->stack_flags; } else { /* Main program already loaded. */ - const Elf_Phdr *phdr; - int phnum; - caddr_t entry; - dbg("processing main program's program header"); assert(aux_info[AT_PHDR] != NULL); phdr = (const Elf_Phdr *) aux_info[AT_PHDR]->a_un.a_ptr; @@ -341,14 +581,34 @@ assert(aux_info[AT_PHENT] != NULL); assert(aux_info[AT_PHENT]->a_un.a_val == sizeof(Elf_Phdr)); assert(aux_info[AT_ENTRY] != NULL); - entry = (caddr_t) aux_info[AT_ENTRY]->a_un.a_ptr; - if ((obj_main = digest_phdr(phdr, phnum, entry, argv0)) == NULL) - die(); + imgentry = (caddr_t) aux_info[AT_ENTRY]->a_un.a_ptr; + if ((obj_main = digest_phdr(phdr, phnum, imgentry, argv0)) == NULL) + rtld_die(); } - obj_main->path = xstrdup(argv0); + if (aux_info[AT_EXECPATH] != NULL && fd == -1) { + kexecpath = aux_info[AT_EXECPATH]->a_un.a_ptr; + dbg("AT_EXECPATH %p %s", kexecpath, kexecpath); + if (kexecpath[0] == '/') + obj_main->path = kexecpath; + else if (getcwd(buf, sizeof(buf)) == NULL || + strlcat(buf, "/", sizeof(buf)) >= sizeof(buf) || + strlcat(buf, kexecpath, sizeof(buf)) >= sizeof(buf)) + obj_main->path = xstrdup(argv0); + else + obj_main->path = xstrdup(buf); + } else { + dbg("No AT_EXECPATH or direct exec"); + obj_main->path = xstrdup(argv0); + } + dbg("obj_main path %s", obj_main->path); obj_main->mainprog = true; + if (aux_info[AT_STACKPROT] != NULL && + aux_info[AT_STACKPROT]->a_un.a_val != 0) + stack_prot = aux_info[AT_STACKPROT]->a_un.a_val; + +#ifndef COMPAT_32BIT /* * Get the actual dynamic linker pathname from the executable if * possible. (It should always be possible.) That ensures that @@ -361,54 +621,72 @@ obj_rtld.path = xstrdup(obj_main->interp); __progname = obj_rtld.path; } - +#endif digest_dynamic(obj_main, 0); + dbg("%s valid_hash_sysv %d valid_hash_gnu %d dynsymcount %d", + obj_main->path, obj_main->valid_hash_sysv, obj_main->valid_hash_gnu, + obj_main->dynsymcount); linkmap_add(obj_main); linkmap_add(&obj_rtld); /* Link the main program into the list of objects. */ - *obj_tail = obj_main; - obj_tail = &obj_main->next; + TAILQ_INSERT_HEAD(&obj_list, obj_main, next); obj_count++; - /* Make sure we don't call the main program's init and fini functions. */ - obj_main->init = obj_main->fini = (Elf_Addr)NULL; + obj_loads++; /* Initialize a fake symbol for resolving undefined weak references. */ sym_zero.st_info = ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE); sym_zero.st_shndx = SHN_UNDEF; + sym_zero.st_value = -(uintptr_t)obj_main->relocbase; if (!libmap_disable) libmap_disable = (bool)lm_init(libmap_override); dbg("loading LD_PRELOAD libraries"); if (load_preload_objects() == -1) - die(); - preload_tail = obj_tail; + rtld_die(); + preload_tail = globallist_curr(TAILQ_LAST(&obj_list, obj_entry_q)); dbg("loading needed objects"); - if (load_needed_objects(obj_main) == -1) - die(); + if (load_needed_objects(obj_main, 0) == -1) + rtld_die(); /* Make a list of all objects loaded at startup. */ - for (obj = obj_list; obj != NULL; obj = obj->next) { - objlist_push_tail(&list_main, obj); + last_interposer = obj_main; + TAILQ_FOREACH(obj, &obj_list, next) { + if (obj->marker) + continue; + if (obj->z_interpose && obj != obj_main) { + objlist_put_after(&list_main, last_interposer, obj); + last_interposer = obj; + } else { + objlist_push_tail(&list_main, obj); + } obj->refcount++; } + dbg("checking for required versions"); + if (rtld_verify_versions(&list_main) == -1 && !ld_tracing) + rtld_die(); + if (ld_tracing) { /* We're done */ trace_loaded_objects(obj_main); exit(0); } - if (getenv(LD_ "DUMP_REL_PRE") != NULL) { + if (getenv(_LD("DUMP_REL_PRE")) != NULL) { dump_relocations(obj_main); exit (0); } - /* setup TLS for main thread */ - dbg("initializing initial thread local storage"); + /* + * Processing tls relocations requires having the tls offsets + * initialized. Prepare offsets before starting initial + * relocation processing. + */ + dbg("initializing initial thread local storage offsets"); STAILQ_FOREACH(entry, &list_main, link) { /* * Allocate all the initial objects out of the static TLS @@ -417,39 +695,78 @@ allocate_tls_offset(entry->obj); } - allocate_initial_tls(obj_list); - -debug = 1; if (relocate_objects(obj_main, - ld_bind_now != NULL && *ld_bind_now != '\0', &obj_rtld) == -1) - die(); + ld_bind_now != NULL && *ld_bind_now != '\0', + &obj_rtld, SYMLOOK_EARLY, NULL) == -1) + rtld_die(); dbg("doing copy relocations"); if (do_copy_relocations(obj_main) == -1) - die(); + rtld_die(); - if (getenv(LD_ "DUMP_REL_POST") != NULL) { + dbg("enforcing main obj relro"); + if (obj_enforce_relro(obj_main) == -1) + rtld_die(); + + if (getenv(_LD("DUMP_REL_POST")) != NULL) { dump_relocations(obj_main); exit (0); } + /* + * Setup TLS for main thread. This must be done after the + * relocations are processed, since tls initialization section + * might be the subject for relocations. + */ + dbg("initializing initial thread local storage"); + allocate_initial_tls(globallist_curr(TAILQ_FIRST(&obj_list))); + dbg("initializing key program variables"); set_program_var("__progname", argv[0] != NULL ? basename(argv[0]) : ""); set_program_var("environ", env); - - dbg("initializing thread locks"); - lockdflt_init(); + set_program_var("__elf_aux_vector", aux); /* Make a list of init functions to call. */ objlist_init(&initlist); - initlist_add_objects(obj_list, preload_tail, &initlist); + initlist_add_objects(globallist_curr(TAILQ_FIRST(&obj_list)), + preload_tail, &initlist); r_debug_state(NULL, &obj_main->linkmap); /* say hello to gdb! */ - objlist_call_init(&initlist); - lockstate = wlock_acquire(rtld_bind_lock); + map_stacks_exec(NULL); + ifunc_init(aux); + + dbg("resolving ifuncs"); + if (resolve_objects_ifunc(obj_main, + ld_bind_now != NULL && *ld_bind_now != '\0', SYMLOOK_EARLY, + NULL) == -1) + rtld_die(); + + if (!obj_main->crt_no_init) { + /* + * Make sure we don't call the main program's init and fini + * functions for binaries linked with old crt1 which calls + * _init itself. + */ + obj_main->init = obj_main->fini = (Elf_Addr)NULL; + obj_main->preinit_array = obj_main->init_array = + obj_main->fini_array = (Elf_Addr)NULL; + } + + wlock_acquire(rtld_bind_lock, &lockstate); + if (obj_main->crt_no_init) + preinit_main(); + objlist_call_init(&initlist, &lockstate); + _r_debug_postinit(&obj_main->linkmap); objlist_clear(&initlist); - wlock_release(rtld_bind_lock, lockstate); + dbg("loading filtees"); + TAILQ_FOREACH(obj, &obj_list, next) { + if (obj->marker) + continue; + if (ld_loadfltr || obj->z_loadfltr) + load_filtees(obj, 0, &lockstate); + } + lock_release(rtld_bind_lock, &lockstate); dbg("transferring control to program entry point = %p", obj_main->entry); @@ -459,6 +776,21 @@ return (func_ptr_type) obj_main->entry; } +void * +rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def) +{ + void *ptr; + Elf_Addr target; + + ptr = (void *)make_function_pointer(def, obj); + target = call_ifunc_resolver(ptr); + return ((void *)target); +} + +/* + * NB: MIPS uses a private version of this function (_mips_rtld_bind). + * Changes to this function should be applied there as well. + */ Elf_Addr _rtld_bind(Obj_Entry *obj, Elf_Size reloff) { @@ -467,20 +799,25 @@ const Obj_Entry *defobj; Elf_Addr *where; Elf_Addr target; - int lockstate; + RtldLockState lockstate; - lockstate = rlock_acquire(rtld_bind_lock); + rlock_acquire(rtld_bind_lock, &lockstate); + if (sigsetjmp(lockstate.env, 0) != 0) + lock_upgrade(rtld_bind_lock, &lockstate); if (obj->pltrel) rel = (const Elf_Rel *) ((caddr_t) obj->pltrel + reloff); else rel = (const Elf_Rel *) ((caddr_t) obj->pltrela + reloff); where = (Elf_Addr *) (obj->relocbase + rel->r_offset); - def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL); + def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, SYMLOOK_IN_PLT, + NULL, &lockstate); if (def == NULL) - die(); - - target = (Elf_Addr)(defobj->relocbase + def->st_value); + rtld_die(); + if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) + target = (Elf_Addr)rtld_resolve_ifunc(defobj, def); + else + target = (Elf_Addr)(defobj->relocbase + def->st_value); dbg("\"%s\" in \"%s\" ==> %p in \"%s\"", defobj->strtab + def->st_name, basename(obj->path), @@ -494,7 +831,7 @@ * that the trampoline needs. */ target = reloc_jmpslot(where, target, defobj, obj, rel); - rlock_release(rtld_bind_lock, lockstate); + lock_release(rtld_bind_lock, &lockstate); return target; } @@ -510,7 +847,7 @@ va_list ap; va_start(ap, fmt); - vsnprintf(buf, sizeof buf, fmt, ap); + rtld_vsnprintf(buf, sizeof buf, fmt, ap); error_message = buf; va_end(ap); } @@ -546,14 +883,103 @@ return p != NULL ? p + 1 : name; } -static void -die(void) +static struct utsname uts; + +static char * +origin_subst_one(Obj_Entry *obj, char *real, const char *kw, + const char *subst, bool may_free) +{ + char *p, *p1, *res, *resp; + int subst_len, kw_len, subst_count, old_len, new_len; + + kw_len = strlen(kw); + + /* + * First, count the number of the keyword occurrences, to + * preallocate the final string. + */ + for (p = real, subst_count = 0;; p = p1 + kw_len, subst_count++) { + p1 = strstr(p, kw); + if (p1 == NULL) + break; + } + + /* + * If the keyword is not found, just return. + * + * Return non-substituted string if resolution failed. We + * cannot do anything more reasonable, the failure mode of the + * caller is unresolved library anyway. + */ + if (subst_count == 0 || (obj != NULL && !obj_resolve_origin(obj))) + return (may_free ? real : xstrdup(real)); + if (obj != NULL) + subst = obj->origin_path; + + /* + * There is indeed something to substitute. Calculate the + * length of the resulting string, and allocate it. + */ + subst_len = strlen(subst); + old_len = strlen(real); + new_len = old_len + (subst_len - kw_len) * subst_count; + res = xmalloc(new_len + 1); + + /* + * Now, execute the substitution loop. + */ + for (p = real, resp = res, *resp = '\0';;) { + p1 = strstr(p, kw); + if (p1 != NULL) { + /* Copy the prefix before keyword. */ + memcpy(resp, p, p1 - p); + resp += p1 - p; + /* Keyword replacement. */ + memcpy(resp, subst, subst_len); + resp += subst_len; + *resp = '\0'; + p = p1 + kw_len; + } else + break; + } + + /* Copy to the end of string and finish. */ + strcat(resp, p); + if (may_free) + free(real); + return (res); +} + +static char * +origin_subst(Obj_Entry *obj, char *real) +{ + char *res1, *res2, *res3, *res4; + + if (obj == NULL || !trust) + return (xstrdup(real)); + if (uts.sysname[0] == '\0') { + if (uname(&uts) != 0) { + _rtld_error("utsname failed: %d", errno); + return (NULL); + } + } + res1 = origin_subst_one(obj, real, "$ORIGIN", NULL, false); + res2 = origin_subst_one(NULL, res1, "$OSNAME", uts.sysname, true); + res3 = origin_subst_one(NULL, res2, "$OSREL", uts.release, true); + res4 = origin_subst_one(NULL, res3, "$PLATFORM", uts.machine, true); + return (res4); +} + +void +rtld_die(void) { const char *msg = dlerror(); if (msg == NULL) msg = "Fatal error"; - errx(1, "%s", msg); + rtld_fdputstr(STDERR_FILENO, msg); + rtld_fdputchar(STDERR_FILENO, '\n'); + _exit(1); } /* @@ -561,13 +987,23 @@ * information in its Obj_Entry structure. */ static void -digest_dynamic(Obj_Entry *obj, int early) +digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, + const Elf_Dyn **dyn_soname, const Elf_Dyn **dyn_runpath) { const Elf_Dyn *dynp; Needed_Entry **needed_tail = &obj->needed; - const Elf_Dyn *dyn_rpath = NULL; + Needed_Entry **needed_filtees_tail = &obj->needed_filtees; + Needed_Entry **needed_aux_filtees_tail = &obj->needed_aux_filtees; + const Elf_Hashelt *hashtab; + const Elf32_Word *hashval; + Elf32_Word bkt, nmaskwords; + int bloom_size32; int plttype = DT_REL; + *dyn_rpath = NULL; + *dyn_soname = NULL; + *dyn_runpath = NULL; + obj->bind_now = false; for (dynp = obj->dynamic; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { @@ -627,14 +1063,59 @@ obj->strsize = dynp->d_un.d_val; break; + case DT_VERNEED: + obj->verneed = (const Elf_Verneed *) (obj->relocbase + + dynp->d_un.d_val); + break; + + case DT_VERNEEDNUM: + obj->verneednum = dynp->d_un.d_val; + break; + + case DT_VERDEF: + obj->verdef = (const Elf_Verdef *) (obj->relocbase + + dynp->d_un.d_val); + break; + + case DT_VERDEFNUM: + obj->verdefnum = dynp->d_un.d_val; + break; + + case DT_VERSYM: + obj->versyms = (const Elf_Versym *)(obj->relocbase + + dynp->d_un.d_val); + break; + case DT_HASH: { - const Elf_Hashelt *hashtab = (const Elf_Hashelt *) - (obj->relocbase + dynp->d_un.d_ptr); + hashtab = (const Elf_Hashelt *)(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; + obj->valid_hash_sysv = obj->nbuckets > 0 && obj->nchains > 0 && + obj->buckets != NULL; + } + break; + + case DT_GNU_HASH: + { + hashtab = (const Elf_Hashelt *)(obj->relocbase + + dynp->d_un.d_ptr); + obj->nbuckets_gnu = hashtab[0]; + obj->symndx_gnu = hashtab[1]; + nmaskwords = hashtab[2]; + bloom_size32 = (__ELF_WORD_SIZE / 32) * nmaskwords; + obj->maskwords_bm_gnu = nmaskwords - 1; + obj->shift2_gnu = hashtab[3]; + obj->bloom_gnu = (Elf_Addr *) (hashtab + 4); + obj->buckets_gnu = hashtab + 4 + bloom_size32; + obj->chain_zero_gnu = obj->buckets_gnu + obj->nbuckets_gnu - + obj->symndx_gnu; + /* Number of bitmask words is required to be power of 2 */ + obj->valid_hash_gnu = powerof2(nmaskwords) && + obj->nbuckets_gnu > 0 && obj->buckets_gnu != NULL; } break; @@ -650,6 +1131,30 @@ } break; + case DT_FILTER: + if (!obj->rtld) { + Needed_Entry *nep = NEW(Needed_Entry); + nep->name = dynp->d_un.d_val; + nep->obj = NULL; + nep->next = NULL; + + *needed_filtees_tail = nep; + needed_filtees_tail = &nep->next; + } + break; + + case DT_AUXILIARY: + if (!obj->rtld) { + Needed_Entry *nep = NEW(Needed_Entry); + nep->name = dynp->d_un.d_val; + nep->obj = NULL; + nep->next = NULL; + + *needed_aux_filtees_tail = nep; + needed_aux_filtees_tail = &nep->next; + } + break; + case DT_PLTGOT: obj->pltgot = (Elf_Addr *) (obj->relocbase + dynp->d_un.d_ptr); break; @@ -663,47 +1168,119 @@ break; case DT_RPATH: - case DT_RUNPATH: /* XXX: process separately */ /* * 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; + *dyn_rpath = dynp; break; case DT_SONAME: - /* Not used by the dynamic linker. */ + *dyn_soname = dynp; + break; + + case DT_RUNPATH: + *dyn_runpath = dynp; break; case DT_INIT: obj->init = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr); break; + case DT_PREINIT_ARRAY: + obj->preinit_array = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr); + break; + + case DT_PREINIT_ARRAYSZ: + obj->preinit_array_num = dynp->d_un.d_val / sizeof(Elf_Addr); + break; + + case DT_INIT_ARRAY: + obj->init_array = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr); + break; + + case DT_INIT_ARRAYSZ: + obj->init_array_num = dynp->d_un.d_val / sizeof(Elf_Addr); + break; + case DT_FINI: obj->fini = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr); break; + case DT_FINI_ARRAY: + obj->fini_array = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr); + break; + + case DT_FINI_ARRAYSZ: + obj->fini_array_num = dynp->d_un.d_val / sizeof(Elf_Addr); + break; + + /* + * Don't process DT_DEBUG on MIPS as the dynamic section + * is mapped read-only. DT_MIPS_RLD_MAP is used instead. + */ + +#ifndef __mips__ case DT_DEBUG: - /* XXX - not implemented yet */ if (!early) dbg("Filling in DT_DEBUG entry"); ((Elf_Dyn*)dynp)->d_un.d_ptr = (Elf_Addr) &r_debug; break; +#endif case DT_FLAGS: - if (dynp->d_un.d_val & DF_ORIGIN) { - obj->origin_path = xmalloc(PATH_MAX); - if (rtld_dirname(obj->path, obj->origin_path) == -1) - die(); - } + if (dynp->d_un.d_val & DF_ORIGIN) + obj->z_origin = true; if (dynp->d_un.d_val & DF_SYMBOLIC) obj->symbolic = true; if (dynp->d_un.d_val & DF_TEXTREL) obj->textrel = true; if (dynp->d_un.d_val & DF_BIND_NOW) obj->bind_now = true; - if (dynp->d_un.d_val & DF_STATIC_TLS) - ; + /*if (dynp->d_un.d_val & DF_STATIC_TLS) + ;*/ + break; +#ifdef __mips__ + case DT_MIPS_LOCAL_GOTNO: + obj->local_gotno = dynp->d_un.d_val; + break; + + case DT_MIPS_SYMTABNO: + obj->symtabno = dynp->d_un.d_val; + break; + + case DT_MIPS_GOTSYM: + obj->gotsym = dynp->d_un.d_val; + break; + + case DT_MIPS_RLD_MAP: + *((Elf_Addr *)(dynp->d_un.d_ptr)) = (Elf_Addr) &r_debug; + break; +#endif + +#ifdef __powerpc64__ + case DT_PPC64_GLINK: + obj->glink = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr); + break; +#endif + + case DT_FLAGS_1: + if (dynp->d_un.d_val & DF_1_NOOPEN) + obj->z_noopen = true; + if (dynp->d_un.d_val & DF_1_ORIGIN) + obj->z_origin = true; + if (dynp->d_un.d_val & DF_1_GLOBAL) + obj->z_global = true; + if (dynp->d_un.d_val & DF_1_BIND_NOW) + obj->bind_now = true; + if (dynp->d_un.d_val & DF_1_NODELETE) + obj->z_nodelete = true; + if (dynp->d_un.d_val & DF_1_LOADFLTR) + obj->z_loadfltr = true; + if (dynp->d_un.d_val & DF_1_INTERPOSE) + obj->z_interpose = true; + if (dynp->d_un.d_val & DF_1_NODEFLIB) + obj->z_nodeflib = true; break; default: @@ -724,8 +1301,61 @@ obj->pltrelsize = 0; } - if (dyn_rpath != NULL) - obj->rpath = obj->strtab + dyn_rpath->d_un.d_val; + /* Determine size of dynsym table (equal to nchains of sysv hash) */ + if (obj->valid_hash_sysv) + obj->dynsymcount = obj->nchains; + else if (obj->valid_hash_gnu) { + obj->dynsymcount = 0; + for (bkt = 0; bkt < obj->nbuckets_gnu; bkt++) { + if (obj->buckets_gnu[bkt] == 0) + continue; + hashval = &obj->chain_zero_gnu[obj->buckets_gnu[bkt]]; + do + obj->dynsymcount++; + while ((*hashval++ & 1u) == 0); + } + obj->dynsymcount += obj->symndx_gnu; + } +} + +static bool +obj_resolve_origin(Obj_Entry *obj) +{ + + if (obj->origin_path != NULL) + return (true); + obj->origin_path = xmalloc(PATH_MAX); + return (rtld_dirname_abs(obj->path, obj->origin_path) != -1); +} + +static void +digest_dynamic2(Obj_Entry *obj, const Elf_Dyn *dyn_rpath, + const Elf_Dyn *dyn_soname, const Elf_Dyn *dyn_runpath) +{ + + if (obj->z_origin && !obj_resolve_origin(obj)) + rtld_die(); + + if (dyn_runpath != NULL) { + obj->runpath = (char *)obj->strtab + dyn_runpath->d_un.d_val; + obj->runpath = origin_subst(obj, obj->runpath); + } else if (dyn_rpath != NULL) { + obj->rpath = (char *)obj->strtab + dyn_rpath->d_un.d_val; + obj->rpath = origin_subst(obj, obj->rpath); + } + if (dyn_soname != NULL) + object_add_name(obj, obj->strtab + dyn_soname->d_un.d_val); +} + +static void +digest_dynamic(Obj_Entry *obj, int early) +{ + const Elf_Dyn *dyn_rpath; + const Elf_Dyn *dyn_soname; + const Elf_Dyn *dyn_runpath; + + digest_dynamic1(obj, early, &dyn_rpath, &dyn_soname, &dyn_runpath); + digest_dynamic2(obj, dyn_rpath, dyn_soname, dyn_runpath); } /* @@ -740,30 +1370,33 @@ Obj_Entry *obj; const Elf_Phdr *phlimit = phdr + phnum; const Elf_Phdr *ph; + Elf_Addr note_start, note_end; int nsegs = 0; obj = obj_new(); for (ph = phdr; ph < phlimit; ph++) { + if (ph->p_type != PT_PHDR) + continue; + + obj->phdr = phdr; + obj->phsize = ph->p_memsz; + obj->relocbase = (caddr_t)phdr - ph->p_vaddr; + break; + } + + obj->stack_flags = PF_X | PF_R | PF_W; + + for (ph = phdr; ph < phlimit; ph++) { switch (ph->p_type) { - case PT_PHDR: - if ((const Elf_Phdr *)ph->p_vaddr != phdr) { - _rtld_error("%s: invalid PT_PHDR", path); - return NULL; - } - obj->phdr = (const Elf_Phdr *) ph->p_vaddr; - obj->phsize = ph->p_memsz; - break; - case PT_INTERP: - obj->interp = (const char *) ph->p_vaddr; + obj->interp = (const char *)(ph->p_vaddr + obj->relocbase); break; case PT_LOAD: if (nsegs == 0) { /* First load segment */ obj->vaddrbase = trunc_page(ph->p_vaddr); - obj->mapbase = (caddr_t) obj->vaddrbase; - obj->relocbase = obj->mapbase - obj->vaddrbase; + obj->mapbase = obj->vaddrbase + obj->relocbase; obj->textsize = round_page(ph->p_vaddr + ph->p_memsz) - obj->vaddrbase; } else { /* Last load segment */ @@ -774,7 +1407,7 @@ break; case PT_DYNAMIC: - obj->dynamic = (const Elf_Dyn *) ph->p_vaddr; + obj->dynamic = (const Elf_Dyn *)(ph->p_vaddr + obj->relocbase); break; case PT_TLS: @@ -782,7 +1415,22 @@ obj->tlssize = ph->p_memsz; obj->tlsalign = ph->p_align; obj->tlsinitsize = ph->p_filesz; - obj->tlsinit = (void*) ph->p_vaddr; + obj->tlsinit = (void*)(ph->p_vaddr + obj->relocbase); + break; + + case PT_GNU_STACK: + obj->stack_flags = ph->p_flags; + break; + + case PT_GNU_RELRO: + obj->relro_page = obj->relocbase + trunc_page(ph->p_vaddr); + obj->relro_size = round_page(ph->p_memsz); + break; + + case PT_NOTE: + note_start = (Elf_Addr)obj->relocbase + ph->p_vaddr; + note_end = note_start + ph->p_filesz; + digest_notes(obj, note_start, note_end); break; } } @@ -795,14 +1443,53 @@ return obj; } +void +digest_notes(Obj_Entry *obj, Elf_Addr note_start, Elf_Addr note_end) +{ + const Elf_Note *note; + const char *note_name; + uintptr_t p; + + for (note = (const Elf_Note *)note_start; (Elf_Addr)note < note_end; + note = (const Elf_Note *)((const char *)(note + 1) + + roundup2(note->n_namesz, sizeof(Elf32_Addr)) + + roundup2(note->n_descsz, sizeof(Elf32_Addr)))) { + if (note->n_namesz != sizeof(NOTE_FREEBSD_VENDOR) || + note->n_descsz != sizeof(int32_t)) + continue; + if (note->n_type != NT_FREEBSD_ABI_TAG && + note->n_type != NT_FREEBSD_NOINIT_TAG) + continue; + note_name = (const char *)(note + 1); + if (strncmp(NOTE_FREEBSD_VENDOR, note_name, + sizeof(NOTE_FREEBSD_VENDOR)) != 0) + continue; + switch (note->n_type) { + case NT_FREEBSD_ABI_TAG: + /* FreeBSD osrel note */ + p = (uintptr_t)(note + 1); + p += roundup2(note->n_namesz, sizeof(Elf32_Addr)); + obj->osrel = *(const int32_t *)(p); + dbg("note osrel %d", obj->osrel); + break; + case NT_FREEBSD_NOINIT_TAG: + /* FreeBSD 'crt does not call init' note */ + obj->crt_no_init = true; + dbg("note crt_no_init"); + break; + } + } +} + static Obj_Entry * dlcheck(void *handle) { Obj_Entry *obj; - for (obj = obj_list; obj != NULL; obj = obj->next) + TAILQ_FOREACH(obj, &obj_list, next) { if (obj == (Obj_Entry *) handle) break; + } if (obj == NULL || obj->refcount == 0 || obj->dl_refcount == 0) { _rtld_error("Invalid shared object handle %p", handle); @@ -855,6 +1542,23 @@ } /* + * The GNU hash function is the Daniel J. Bernstein hash clipped to 32 bits + * unsigned in case it's implemented with a wider type. + */ +static uint32_t +gnu_hash(const char *s) +{ + uint32_t h; + unsigned char c; + + h = 5381; + for (c = *s; c != '\0'; c = *++s) + h = h * 33 + c; + return (h & 0xffffffff); +} + + +/* * 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. @@ -862,40 +1566,76 @@ * If the second argument is non-NULL, then it refers to an already- * loaded shared object, whose library search path will be searched. * + * If a library is successfully located via LD_LIBRARY_PATH_FDS, its + * descriptor (which is close-on-exec) will be passed out via the third + * argument. + * * The search order is: + * DT_RPATH in the referencing file _unless_ DT_RUNPATH is present (1) + * DT_RPATH of the main object if DSO without defined DT_RUNPATH (1) * LD_LIBRARY_PATH - * rpath in the referencing file - * ldconfig hints - * /lib:/usr/lib + * DT_RUNPATH in the referencing file + * ldconfig hints (if -z nodefaultlib, filter out default library directories + * from list) + * /lib:/usr/lib _unless_ the referencing file is linked with -z nodefaultlib + * + * (1) Handled in digest_dynamic2 - rpath left NULL if runpath defined. */ static char * -find_library(const char *xname, const Obj_Entry *refobj) +find_library(const char *xname, const Obj_Entry *refobj, int *fdp) { char *pathname; char *name; + bool nodeflib, objgiven; + objgiven = refobj != NULL; if (strchr(xname, '/') != NULL) { /* Hard coded pathname */ if (xname[0] != '/' && !trust) { _rtld_error("Absolute pathname required for shared object \"%s\"", xname); return NULL; } - return xstrdup(xname); + return (origin_subst(__DECONST(Obj_Entry *, refobj), + __DECONST(char *, xname))); } - if (libmap_disable || (refobj == NULL) || + if (libmap_disable || !objgiven || (name = lm_find(refobj->path, xname)) == NULL) name = (char *)xname; dbg(" Searching for \"%s\"", name); - if ((pathname = search_library_path(name, ld_library_path)) != NULL || - (refobj != NULL && - (pathname = search_library_path(name, refobj->rpath)) != NULL) || - (pathname = search_library_path(name, gethints())) != NULL || - (pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL) - return pathname; - if(refobj != NULL && refobj->path != NULL) { + /* + * If refobj->rpath != NULL, then refobj->runpath is NULL. Fall + * back to pre-conforming behaviour if user requested so with + * LD_LIBRARY_PATH_RPATH environment variable and ignore -z + * nodeflib. + */ + if (objgiven && refobj->rpath != NULL && ld_library_path_rpath) { + if ((pathname = search_library_path(name, ld_library_path)) != NULL || + (refobj != NULL && + (pathname = search_library_path(name, refobj->rpath)) != NULL) || + (pathname = search_library_pathfds(name, ld_library_dirs, fdp)) != NULL || + (pathname = search_library_path(name, gethints(false))) != NULL || + (pathname = search_library_path(name, ld_standard_library_path)) != NULL) + return (pathname); + } else { + nodeflib = objgiven ? refobj->z_nodeflib : false; + if ((objgiven && + (pathname = search_library_path(name, refobj->rpath)) != NULL) || + (objgiven && refobj->runpath == NULL && refobj != obj_main && + (pathname = search_library_path(name, obj_main->rpath)) != NULL) || + (pathname = search_library_path(name, ld_library_path)) != NULL || + (objgiven && + (pathname = search_library_path(name, refobj->runpath)) != NULL) || + (pathname = search_library_pathfds(name, ld_library_dirs, fdp)) != NULL || + (pathname = search_library_path(name, gethints(nodeflib))) != NULL || + (objgiven && !nodeflib && + (pathname = search_library_path(name, ld_standard_library_path)) != NULL)) + return (pathname); + } + + if (objgiven && refobj->path != NULL) { _rtld_error("Shared object \"%s\" not found, required by \"%s\"", name, basename(refobj->path)); } else { @@ -912,21 +1652,22 @@ */ const Elf_Sym * find_symdef(unsigned long symnum, const Obj_Entry *refobj, - const Obj_Entry **defobj_out, bool in_plt, SymCache *cache) + const Obj_Entry **defobj_out, int flags, SymCache *cache, + RtldLockState *lockstate) { const Elf_Sym *ref; const Elf_Sym *def; const Obj_Entry *defobj; + SymLook req; const char *name; - unsigned long hash; + int res; /* * If we have already found this symbol, get the information from * the cache. */ - if (symnum >= refobj->nchains) + if (symnum >= refobj->dynsymcount) return NULL; /* Bad object */ - if (cache != NULL && cache[symnum].sym != NULL) { *defobj_out = cache[symnum].obj; return cache[symnum].sym; @@ -934,6 +1675,7 @@ ref = refobj->symtab + symnum; name = refobj->strtab + ref->st_name; + def = NULL; defobj = NULL; /* @@ -949,8 +1691,15 @@ _rtld_error("%s: Bogus symbol table entry %lu", refobj->path, symnum); } - hash = elf_hash(name); - def = symlook_default(name, hash, refobj, &defobj, in_plt); + symlook_init(&req, name); + req.flags = flags; + req.ventry = fetch_ventry(refobj, symnum); + req.lockstate = lockstate; + res = symlook_default(&req, refobj); + if (res == 0) { + def = req.sym_out; + defobj = req.defobj_out; + } } else { def = ref; defobj = refobj; @@ -981,83 +1730,305 @@ /* * Return the search path from the ldconfig hints file, reading it if - * necessary. Returns NULL if there are problems with the hints file, + * necessary. If nostdlib is true, then the default search paths are + * not added to result. + * + * Returns NULL if there are problems with the hints file, * or if the search path there is empty. */ static const char * -gethints(void) +gethints(bool nostdlib) { - static char *hints; - - if (hints == NULL) { - int fd; - struct elfhints_hdr hdr; + static char *hints, *filtered_path; + static struct elfhints_hdr hdr; + struct fill_search_info_args sargs, hargs; + struct dl_serinfo smeta, hmeta, *SLPinfo, *hintinfo; + struct dl_serpath *SLPpath, *hintpath; char *p; + struct stat hint_stat; + unsigned int SLPndx, hintndx, fndx, fcount; + int fd; + size_t flen; + uint32_t dl; + bool skip; - /* Keep from trying again in case the hints file is bad. */ - hints = ""; + /* First call, read the hints file */ + if (hints == NULL) { + /* Keep from trying again in case the hints file is bad. */ + hints = ""; - if ((fd = open(_PATH_ELF_HINTS, O_RDONLY)) == -1) - return NULL; - if (read(fd, &hdr, sizeof hdr) != sizeof hdr || - hdr.magic != ELFHINTS_MAGIC || - hdr.version != 1) { - close(fd); - return NULL; + if ((fd = open(ld_elf_hints_path, O_RDONLY | O_CLOEXEC)) == -1) + return (NULL); + + /* + * Check of hdr.dirlistlen value against type limit + * intends to pacify static analyzers. Further + * paranoia leads to checks that dirlist is fully + * contained in the file range. + */ + if (read(fd, &hdr, sizeof hdr) != sizeof hdr || + hdr.magic != ELFHINTS_MAGIC || + hdr.version != 1 || hdr.dirlistlen > UINT_MAX / 2 || + fstat(fd, &hint_stat) == -1) { +cleanup1: + close(fd); + hdr.dirlistlen = 0; + return (NULL); + } + dl = hdr.strtab; + if (dl + hdr.dirlist < dl) + goto cleanup1; + dl += hdr.dirlist; + if (dl + hdr.dirlistlen < dl) + goto cleanup1; + dl += hdr.dirlistlen; + if (dl > hint_stat.st_size) + goto cleanup1; + p = xmalloc(hdr.dirlistlen + 1); + + if (lseek(fd, hdr.strtab + hdr.dirlist, SEEK_SET) == -1 || + read(fd, p, hdr.dirlistlen + 1) != + (ssize_t)hdr.dirlistlen + 1 || p[hdr.dirlistlen] != '\0') { + free(p); + goto cleanup1; + } + hints = p; + close(fd); } - p = xmalloc(hdr.dirlistlen + 1); - if (lseek(fd, hdr.strtab + hdr.dirlist, SEEK_SET) == -1 || - read(fd, p, hdr.dirlistlen + 1) != (ssize_t)hdr.dirlistlen + 1) { - free(p); - close(fd); - return NULL; + + /* + * If caller agreed to receive list which includes the default + * paths, we are done. Otherwise, if we still did not + * calculated filtered result, do it now. + */ + if (!nostdlib) + return (hints[0] != '\0' ? hints : NULL); + if (filtered_path != NULL) + goto filt_ret; + + /* + * Obtain the list of all configured search paths, and the + * list of the default paths. + * + * First estimate the size of the results. + */ + smeta.dls_size = __offsetof(struct dl_serinfo, dls_serpath); + smeta.dls_cnt = 0; + hmeta.dls_size = __offsetof(struct dl_serinfo, dls_serpath); + hmeta.dls_cnt = 0; + + sargs.request = RTLD_DI_SERINFOSIZE; + sargs.serinfo = &smeta; + hargs.request = RTLD_DI_SERINFOSIZE; + hargs.serinfo = &hmeta; + + path_enumerate(ld_standard_library_path, fill_search_info, &sargs); + path_enumerate(hints, fill_search_info, &hargs); + + SLPinfo = xmalloc(smeta.dls_size); + hintinfo = xmalloc(hmeta.dls_size); + + /* + * Next fetch both sets of paths. + */ + sargs.request = RTLD_DI_SERINFO; + sargs.serinfo = SLPinfo; + sargs.serpath = &SLPinfo->dls_serpath[0]; + sargs.strspace = (char *)&SLPinfo->dls_serpath[smeta.dls_cnt]; + + hargs.request = RTLD_DI_SERINFO; + hargs.serinfo = hintinfo; + hargs.serpath = &hintinfo->dls_serpath[0]; + hargs.strspace = (char *)&hintinfo->dls_serpath[hmeta.dls_cnt]; + + path_enumerate(ld_standard_library_path, fill_search_info, &sargs); + path_enumerate(hints, fill_search_info, &hargs); + + /* + * Now calculate the difference between two sets, by excluding + * standard paths from the full set. + */ + fndx = 0; + fcount = 0; + filtered_path = xmalloc(hdr.dirlistlen + 1); + hintpath = &hintinfo->dls_serpath[0]; + for (hintndx = 0; hintndx < hmeta.dls_cnt; hintndx++, hintpath++) { + skip = false; + SLPpath = &SLPinfo->dls_serpath[0]; + /* + * Check each standard path against current. + */ + for (SLPndx = 0; SLPndx < smeta.dls_cnt; SLPndx++, SLPpath++) { + /* matched, skip the path */ + if (!strcmp(hintpath->dls_name, SLPpath->dls_name)) { + skip = true; + break; + } + } + if (skip) + continue; + /* + * Not matched against any standard path, add the path + * to result. Separate consequtive paths with ':'. + */ + if (fcount > 0) { + filtered_path[fndx] = ':'; + fndx++; + } + fcount++; + flen = strlen(hintpath->dls_name); + strncpy((filtered_path + fndx), hintpath->dls_name, flen); + fndx += flen; } - hints = p; - close(fd); - } - return hints[0] != '\0' ? hints : NULL; + filtered_path[fndx] = '\0'; + + free(SLPinfo); + free(hintinfo); + +filt_ret: + return (filtered_path[0] != '\0' ? filtered_path : NULL); } static void init_dag(Obj_Entry *root) { + const Needed_Entry *needed; + const Objlist_Entry *elm; DoneList donelist; + if (root->dag_inited) + return; donelist_init(&donelist); - init_dag1(root, root, &donelist); + + /* Root object belongs to own DAG. */ + objlist_push_tail(&root->dldags, root); + objlist_push_tail(&root->dagmembers, root); + donelist_check(&donelist, root); + + /* + * Add dependencies of root object to DAG in breadth order + * by exploiting the fact that each new object get added + * to the tail of the dagmembers list. + */ + STAILQ_FOREACH(elm, &root->dagmembers, link) { + for (needed = elm->obj->needed; needed != NULL; needed = needed->next) { + if (needed->obj == NULL || donelist_check(&donelist, needed->obj)) + continue; + objlist_push_tail(&needed->obj->dldags, root); + objlist_push_tail(&root->dagmembers, needed->obj); + } + } + root->dag_inited = true; } static void -init_dag1(Obj_Entry *root, Obj_Entry *obj, DoneList *dlp) +init_marker(Obj_Entry *marker) { - const Needed_Entry *needed; - if (donelist_check(dlp, obj)) - return; - - obj->refcount++; - objlist_push_tail(&obj->dldags, root); - objlist_push_tail(&root->dagmembers, obj); - for (needed = obj->needed; needed != NULL; needed = needed->next) - if (needed->obj != NULL) - init_dag1(root, needed->obj, dlp); + bzero(marker, sizeof(*marker)); + marker->marker = true; } +Obj_Entry * +globallist_curr(const Obj_Entry *obj) +{ + + for (;;) { + if (obj == NULL) + return (NULL); + if (!obj->marker) + return (__DECONST(Obj_Entry *, obj)); + obj = TAILQ_PREV(obj, obj_entry_q, next); + } +} + +Obj_Entry * +globallist_next(const Obj_Entry *obj) +{ + + for (;;) { + obj = TAILQ_NEXT(obj, next); + if (obj == NULL) + return (NULL); + if (!obj->marker) + return (__DECONST(Obj_Entry *, obj)); + } +} + +/* Prevent the object from being unmapped while the bind lock is dropped. */ +static void +hold_object(Obj_Entry *obj) +{ + + obj->holdcount++; +} + +static void +unhold_object(Obj_Entry *obj) +{ + + assert(obj->holdcount > 0); + if (--obj->holdcount == 0 && obj->unholdfree) + release_object(obj); +} + +static void +process_z(Obj_Entry *root) +{ + const Objlist_Entry *elm; + Obj_Entry *obj; + + /* + * Walk over object DAG and process every dependent object + * that is marked as DF_1_NODELETE or DF_1_GLOBAL. They need + * to grow their own DAG. + * + * For DF_1_GLOBAL, DAG is required for symbol lookups in + * symlook_global() to work. + * + * For DF_1_NODELETE, the DAG should have its reference upped. + */ + STAILQ_FOREACH(elm, &root->dagmembers, link) { + obj = elm->obj; + if (obj == NULL) + continue; + if (obj->z_nodelete && !obj->ref_nodel) { + dbg("obj %s -z nodelete", obj->path); + init_dag(obj); + ref_dag(obj); + obj->ref_nodel = true; + } + if (obj->z_global && objlist_find(&list_global, obj) == NULL) { + dbg("obj %s -z global", obj->path); + objlist_push_tail(&list_global, obj); + init_dag(obj); + } + } +} /* * Initialize the dynamic linker. The argument is the address at which * the dynamic linker has been mapped into memory. The primary task of * this function is to relocate the dynamic linker. */ static void -init_rtld(caddr_t mapbase) +init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info) { Obj_Entry objtmp; /* Temporary rtld object */ + const Elf_Ehdr *ehdr; + const Elf_Dyn *dyn_rpath; + const Elf_Dyn *dyn_soname; + const Elf_Dyn *dyn_runpath; + +#ifdef RTLD_INIT_PAGESIZES_EARLY + /* The page size is required by the dynamic memory allocator. */ + init_pagesizes(aux_info); +#endif /* * Conjure up an Obj_Entry structure for the dynamic linker. * - * The "path" member can't be initialized yet because string constatns - * cannot yet be acessed. Below we will set it correctly. + * The "path" member can't be initialized yet because string constants + * cannot yet be accessed. Below we will set it correctly. */ memset(&objtmp, 0, sizeof(objtmp)); objtmp.path = NULL; @@ -1066,34 +2037,92 @@ #ifdef PIC objtmp.relocbase = mapbase; #endif - if (RTLD_IS_DYNAMIC()) { - objtmp.dynamic = rtld_dynamic(&objtmp); - digest_dynamic(&objtmp, 1); - assert(objtmp.needed == NULL); - assert(!objtmp.textrel); - /* - * Temporarily put the dynamic linker entry into the object list, so - * that symbols can be found. - */ + objtmp.dynamic = rtld_dynamic(&objtmp); + digest_dynamic1(&objtmp, 1, &dyn_rpath, &dyn_soname, &dyn_runpath); + assert(objtmp.needed == NULL); +#if !defined(__mips__) + /* MIPS has a bogus DT_TEXTREL. */ + assert(!objtmp.textrel); +#endif + /* + * Temporarily put the dynamic linker entry into the object list, so + * that symbols can be found. + */ + relocate_objects(&objtmp, true, &objtmp, 0, NULL); - relocate_objects(&objtmp, true, &objtmp); - } + ehdr = (Elf_Ehdr *)mapbase; + objtmp.phdr = (Elf_Phdr *)((char *)mapbase + ehdr->e_phoff); + objtmp.phsize = ehdr->e_phnum * sizeof(objtmp.phdr[0]); /* Initialize the object list. */ - obj_tail = &obj_list; + TAILQ_INIT(&obj_list); /* Now that non-local variables can be accesses, copy out obj_rtld. */ memcpy(&obj_rtld, &objtmp, sizeof(obj_rtld)); +#ifndef RTLD_INIT_PAGESIZES_EARLY + /* The page size is required by the dynamic memory allocator. */ + init_pagesizes(aux_info); +#endif + + if (aux_info[AT_OSRELDATE] != NULL) + osreldate = aux_info[AT_OSRELDATE]->a_un.a_val; + + digest_dynamic2(&obj_rtld, dyn_rpath, dyn_soname, dyn_runpath); + /* Replace the path with a dynamically allocated copy. */ - obj_rtld.path = xstrdup(PATH_RTLD); + obj_rtld.path = xstrdup(ld_path_rtld); r_debug.r_brk = r_debug_state; r_debug.r_state = RT_CONSISTENT; } /* + * Retrieve the array of supported page sizes. The kernel provides the page + * sizes in increasing order. + */ +static void +init_pagesizes(Elf_Auxinfo **aux_info) +{ + static size_t psa[MAXPAGESIZES]; + int mib[2]; + size_t len, size; + + if (aux_info[AT_PAGESIZES] != NULL && aux_info[AT_PAGESIZESLEN] != + NULL) { + size = aux_info[AT_PAGESIZESLEN]->a_un.a_val; + pagesizes = aux_info[AT_PAGESIZES]->a_un.a_ptr; + } else { + len = 2; + if (sysctlnametomib("hw.pagesizes", mib, &len) == 0) + size = sizeof(psa); + else { + /* As a fallback, retrieve the base page size. */ + size = sizeof(psa[0]); + if (aux_info[AT_PAGESZ] != NULL) { + psa[0] = aux_info[AT_PAGESZ]->a_un.a_val; + goto psa_filled; + } else { + mib[0] = CTL_HW; + mib[1] = HW_PAGESIZE; + len = 2; + } + } + if (sysctl(mib, len, psa, &size, NULL, 0) == -1) { + _rtld_error("sysctl for hw.pagesize(s) failed"); + rtld_die(); + } +psa_filled: + pagesizes = psa; + } + npagesizes = size / sizeof(pagesizes[0]); + /* Discard any invalid entries at the end of the array. */ + while (npagesizes > 0 && pagesizes[npagesizes - 1] == 0) + npagesizes--; +} + +/* * Add the init functions from a needed object list (and its recursive * needed objects) to "list". This is not used directly; it is a helper * function for initlist_add_objects(). The write lock must be held @@ -1108,7 +2137,7 @@ /* Process the current needed object. */ if (needed->obj != NULL) - initlist_add_objects(needed->obj, &needed->obj->next, list); + initlist_add_objects(needed->obj, needed->obj, list); } /* @@ -1121,44 +2150,108 @@ * held when this function is called. */ static void -initlist_add_objects(Obj_Entry *obj, Obj_Entry **tail, Objlist *list) +initlist_add_objects(Obj_Entry *obj, Obj_Entry *tail, Objlist *list) { - if (obj->init_done) + Obj_Entry *nobj; + + if (obj->init_scanned || obj->init_done) return; - obj->init_done = true; + obj->init_scanned = true; /* Recursively process the successor objects. */ - if (&obj->next != tail) - initlist_add_objects(obj->next, tail, list); + nobj = globallist_next(obj); + if (nobj != NULL && obj != tail) + initlist_add_objects(nobj, tail, list); /* Recursively process the needed objects. */ if (obj->needed != NULL) initlist_add_neededs(obj->needed, list); + if (obj->needed_filtees != NULL) + initlist_add_neededs(obj->needed_filtees, list); + if (obj->needed_aux_filtees != NULL) + initlist_add_neededs(obj->needed_aux_filtees, list); /* Add the object to the init list. */ - if (obj->init != (Elf_Addr)NULL) + if (obj->preinit_array != (Elf_Addr)NULL || obj->init != (Elf_Addr)NULL || + obj->init_array != (Elf_Addr)NULL) objlist_push_tail(list, obj); /* Add the object to the global fini list in the reverse order. */ - if (obj->fini != (Elf_Addr)NULL) + if ((obj->fini != (Elf_Addr)NULL || obj->fini_array != (Elf_Addr)NULL) + && !obj->on_fini_list) { objlist_push_head(&list_fini, obj); + obj->on_fini_list = true; + } } #ifndef FPTR_TARGET #define FPTR_TARGET(f) ((Elf_Addr) (f)) #endif -static bool -is_exported(const Elf_Sym *def) +static void +free_needed_filtees(Needed_Entry *n, RtldLockState *lockstate) { - Elf_Addr value; - const func_ptr_type *p; + Needed_Entry *needed, *needed1; - value = (Elf_Addr)(obj_rtld.relocbase + def->st_value); - for (p = exports; *p != NULL; p++) - if (FPTR_TARGET(*p) == value) - return true; - return false; + for (needed = n; needed != NULL; needed = needed->next) { + if (needed->obj != NULL) { + dlclose_locked(needed->obj, lockstate); + needed->obj = NULL; + } + } + for (needed = n; needed != NULL; needed = needed1) { + needed1 = needed->next; + free(needed); + } +} + +static void +unload_filtees(Obj_Entry *obj, RtldLockState *lockstate) +{ + + free_needed_filtees(obj->needed_filtees, lockstate); + obj->needed_filtees = NULL; + free_needed_filtees(obj->needed_aux_filtees, lockstate); + obj->needed_aux_filtees = NULL; + obj->filtees_loaded = false; +} + +static void +load_filtee1(Obj_Entry *obj, Needed_Entry *needed, int flags, + RtldLockState *lockstate) +{ + + for (; needed != NULL; needed = needed->next) { + needed->obj = dlopen_object(obj->strtab + needed->name, -1, obj, + flags, ((ld_loadfltr || obj->z_loadfltr) ? RTLD_NOW : RTLD_LAZY) | + RTLD_LOCAL, lockstate); + } +} + +static void +load_filtees(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + + lock_restart_for_upgrade(lockstate); + if (!obj->filtees_loaded) { + load_filtee1(obj, obj->needed_filtees, flags, lockstate); + load_filtee1(obj, obj->needed_aux_filtees, flags, lockstate); + obj->filtees_loaded = true; + } +} + +static int +process_needed(Obj_Entry *obj, Needed_Entry *needed, int flags) +{ + Obj_Entry *obj1; + + for (; needed != NULL; needed = needed->next) { + obj1 = needed->obj = load_object(obj->strtab + needed->name, -1, obj, + flags & ~RTLD_LO_NOLOAD); + if (obj1 == NULL && !ld_tracing && (flags & RTLD_LO_FILTEES) == 0) + return (-1); + } + return (0); } /* @@ -1167,36 +2260,24 @@ * returns -1 on failure. */ static int -load_needed_objects(Obj_Entry *first) +load_needed_objects(Obj_Entry *first, int flags) { Obj_Entry *obj; - for (obj = first; obj != NULL; obj = obj->next) { - Needed_Entry *needed; - - for (needed = obj->needed; needed != NULL; needed = needed->next) { - const char *name = obj->strtab + needed->name; - char *path = find_library(name, obj); - - needed->obj = NULL; - if (path == NULL && !ld_tracing) - return -1; -printf("\n\nPATH: [%s]\n",path); - if (path) { - needed->obj = load_object(path); - if (needed->obj == NULL && !ld_tracing) - return -1; /* XXX - cleanup */ - } - } + for (obj = first; obj != NULL; obj = TAILQ_NEXT(obj, next)) { + if (obj->marker) + continue; + if (process_needed(obj, obj->needed, flags) == -1) + return (-1); } - - return 0; + return (0); } static int load_preload_objects(void) { char *p = ld_preload; + Obj_Entry *obj; static const char delim[] = " \t:;"; if (p == NULL) @@ -1205,109 +2286,180 @@ p += strspn(p, delim); while (*p != '\0') { size_t len = strcspn(p, delim); - char *path; char savech; savech = p[len]; p[len] = '\0'; - if ((path = find_library(p, NULL)) == NULL) - return -1; - if (load_object(path) == NULL) + obj = load_object(p, -1, NULL, 0); + if (obj == NULL) return -1; /* XXX - cleanup */ + obj->z_interpose = true; p[len] = savech; p += len; p += strspn(p, delim); } + LD_UTRACE(UTRACE_PRELOAD_FINISHED, NULL, NULL, 0, 0, NULL); return 0; } +static const char * +printable_path(const char *path) +{ + + return (path == NULL ? "" : path); +} + /* * 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. + * object may be specified by name or by user-supplied file descriptor + * fd_u. In the later case, the fd_u descriptor is not closed, but its + * duplicate is. * * Returns a pointer to the Obj_Entry for the object. Returns NULL * on failure. */ static Obj_Entry * -load_object(char *path) +load_object(const char *name, int fd_u, const Obj_Entry *refobj, int flags) { Obj_Entry *obj; - int fd = -1; + int fd; struct stat sb; - struct statfs fs; + char *path; - for (obj = obj_list->next; obj != NULL; obj = obj->next) - if (strcmp(obj->path, path) == 0) - break; + fd = -1; + if (name != NULL) { + TAILQ_FOREACH(obj, &obj_list, next) { + if (obj->marker || obj->doomed) + continue; + if (object_match_name(obj, name)) + return (obj); + } - /* - * If we didn't find a match by pathname, open the file and check - * again by device and inode. This avoids false mismatches caused - * by multiple links or ".." in pathnames. - * - * To avoid a race, we open the file and use fstat() rather than - * using stat(). - */ - if (obj == NULL) { - if ((fd = open(path, O_RDONLY)) == -1) { + path = find_library(name, refobj, &fd); + if (path == NULL) + return (NULL); + } else + path = NULL; + + if (fd >= 0) { + /* + * search_library_pathfds() opens a fresh file descriptor for the + * library, so there is no need to dup(). + */ + } else if (fd_u == -1) { + /* + * If we didn't find a match by pathname, or the name is not + * supplied, open the file and check again by device and inode. + * This avoids false mismatches caused by multiple links or ".." + * in pathnames. + * + * To avoid a race, we open the file and use fstat() rather than + * using stat(). + */ + if ((fd = open(path, O_RDONLY | O_CLOEXEC | O_VERIFY)) == -1) { _rtld_error("Cannot open \"%s\"", path); - return NULL; + free(path); + return (NULL); } - if (fstat(fd, &sb) == -1) { - _rtld_error("Cannot fstat \"%s\"", path); - close(fd); - return NULL; - } - for (obj = obj_list->next; obj != NULL; obj = obj->next) { - if (obj->ino == sb.st_ino && obj->dev == sb.st_dev) { - close(fd); - break; - } + } else { + fd = fcntl(fd_u, F_DUPFD_CLOEXEC, 0); + if (fd == -1) { + _rtld_error("Cannot dup fd"); + free(path); + return (NULL); } } - - if (obj == NULL) { /* First use of this object, so we must map it in */ - /* - * but first, make sure that environment variables haven't been - * used to circumvent the noexec flag on a filesystem. - */ - if (dangerous_ld_env) { - if (fstatfs(fd, &fs) != 0) { - _rtld_error("Cannot fstatfs \"%s\"", path); - close(fd); - return NULL; - } - if (fs.f_flags & MNT_NOEXEC) { - _rtld_error("Cannot execute objects on %s\n", fs.f_mntonname); - close(fd); - return NULL; - } - } - dbg("loading \"%s\"", path); - printf("\n\n loading \"%s\"\n", path); - obj = map_object(fd, path, &sb); - printf("\nmapped object\n"); + if (fstat(fd, &sb) == -1) { + _rtld_error("Cannot fstat \"%s\"", printable_path(path)); close(fd); - if (obj == NULL) { - free(path); + free(path); + return NULL; + } + TAILQ_FOREACH(obj, &obj_list, next) { + if (obj->marker || obj->doomed) + continue; + if (obj->ino == sb.st_ino && obj->dev == sb.st_dev) + break; + } + if (obj != NULL && name != NULL) { + object_add_name(obj, name); + free(path); + close(fd); + return obj; + } + if (flags & RTLD_LO_NOLOAD) { + free(path); + close(fd); + return (NULL); + } + + /* First use of this object, so we must map it in */ + obj = do_load_object(fd, name, path, &sb, flags); + if (obj == NULL) + free(path); + close(fd); + + return obj; +} + +static Obj_Entry * +do_load_object(int fd, const char *name, char *path, struct stat *sbp, + int flags) +{ + Obj_Entry *obj; + struct statfs fs; + + /* + * but first, make sure that environment variables haven't been + * used to circumvent the noexec flag on a filesystem. + */ + if (dangerous_ld_env) { + if (fstatfs(fd, &fs) != 0) { + _rtld_error("Cannot fstatfs \"%s\"", printable_path(path)); return NULL; } + if (fs.f_flags & MNT_NOEXEC) { + _rtld_error("Cannot execute objects on %s\n", fs.f_mntonname); + return NULL; + } + } + dbg("loading \"%s\"", printable_path(path)); + obj = map_object(fd, printable_path(path), sbp); + if (obj == NULL) + return NULL; - obj->path = path; - digest_dynamic(obj, 0); + /* + * If DT_SONAME is present in the object, digest_dynamic2 already + * added it to the object names. + */ + if (name != NULL) + object_add_name(obj, name); + obj->path = path; + digest_dynamic(obj, 0); + dbg("%s valid_hash_sysv %d valid_hash_gnu %d dynsymcount %d", obj->path, + obj->valid_hash_sysv, obj->valid_hash_gnu, obj->dynsymcount); + if (obj->z_noopen && (flags & (RTLD_LO_DLOPEN | RTLD_LO_TRACE)) == + RTLD_LO_DLOPEN) { + dbg("refusing to load non-loadable \"%s\"", obj->path); + _rtld_error("Cannot dlopen non-loadable %s", obj->path); + munmap(obj->mapbase, obj->mapsize); + obj_free(obj); + return (NULL); + } - *obj_tail = obj; - obj_tail = &obj->next; - obj_count++; - linkmap_add(obj); /* for GDB & dlinfo() */ + obj->dlopened = (flags & RTLD_LO_DLOPEN) != 0; + TAILQ_INSERT_TAIL(&obj_list, obj, next); + obj_count++; + obj_loads++; + linkmap_add(obj); /* for GDB & dlinfo() */ + max_stack_flags |= obj->stack_flags; - dbg(" %p .. %p: %s", obj->mapbase, - obj->mapbase + obj->mapsize - 1, obj->path); - if (obj->textrel) - dbg(" WARNING: %s has impure text", obj->path); - } else - free(path); + dbg(" %p .. %p: %s", obj->mapbase, + obj->mapbase + obj->mapsize - 1, obj->path); + if (obj->textrel) + dbg(" WARNING: %s has impure text", obj->path); + LD_UTRACE(UTRACE_LOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0, + obj->path); return obj; } @@ -1317,7 +2469,9 @@ { Obj_Entry *obj; - for (obj = obj_list; obj != NULL; obj = obj->next) { + TAILQ_FOREACH(obj, &obj_list, next) { + if (obj->marker) + continue; if (addr < (void *) obj->mapbase) continue; if (addr < (void *) (obj->mapbase + obj->mapsize)) @@ -1326,29 +2480,101 @@ return NULL; } +static void +preinit_main(void) +{ + Elf_Addr *preinit_addr; + int index; + + preinit_addr = (Elf_Addr *)obj_main->preinit_array; + if (preinit_addr == NULL) + return; + + for (index = 0; index < obj_main->preinit_array_num; index++) { + if (preinit_addr[index] != 0 && preinit_addr[index] != 1) { + dbg("calling preinit function for %s at %p", obj_main->path, + (void *)preinit_addr[index]); + LD_UTRACE(UTRACE_INIT_CALL, obj_main, (void *)preinit_addr[index], + 0, 0, obj_main->path); + call_init_pointer(obj_main, preinit_addr[index]); + } + } +} + /* * Call the finalization functions for each of the objects in "list" - * which are unreferenced. All of the objects are expected to have - * non-NULL fini functions. + * belonging to the DAG of "root" and referenced once. If NULL "root" + * is specified, every finalization function will be called regardless + * of the reference count and the list elements won't be freed. All of + * the objects are expected to have non-NULL fini functions. */ static void -objlist_call_fini(Objlist *list) +objlist_call_fini(Objlist *list, Obj_Entry *root, RtldLockState *lockstate) { Objlist_Entry *elm; char *saved_msg; + Elf_Addr *fini_addr; + int index; + + assert(root == NULL || root->refcount == 1); + + if (root != NULL) + root->doomed = true; /* * Preserve the current error message since a fini function might * call into the dynamic linker and overwrite it. */ saved_msg = errmsg_save(); - STAILQ_FOREACH(elm, list, link) { - if (elm->obj->refcount == 0) { - dbg("calling fini function for %s at %p", elm->obj->path, - (void *)elm->obj->fini); - call_initfini_pointer(elm->obj, elm->obj->fini); + do { + STAILQ_FOREACH(elm, list, link) { + if (root != NULL && (elm->obj->refcount != 1 || + objlist_find(&root->dagmembers, elm->obj) == NULL)) + continue; + /* Remove object from fini list to prevent recursive invocation. */ + STAILQ_REMOVE(list, elm, Struct_Objlist_Entry, link); + /* Ensure that new references cannot be acquired. */ + elm->obj->doomed = true; + + hold_object(elm->obj); + lock_release(rtld_bind_lock, lockstate); + /* + * It is legal to have both DT_FINI and DT_FINI_ARRAY defined. + * When this happens, DT_FINI_ARRAY is processed first. + */ + fini_addr = (Elf_Addr *)elm->obj->fini_array; + if (fini_addr != NULL && elm->obj->fini_array_num > 0) { + for (index = elm->obj->fini_array_num - 1; index >= 0; + index--) { + if (fini_addr[index] != 0 && fini_addr[index] != 1) { + dbg("calling fini function for %s at %p", + elm->obj->path, (void *)fini_addr[index]); + LD_UTRACE(UTRACE_FINI_CALL, elm->obj, + (void *)fini_addr[index], 0, 0, elm->obj->path); + call_initfini_pointer(elm->obj, fini_addr[index]); + } + } + } + if (elm->obj->fini != (Elf_Addr)NULL) { + dbg("calling fini function for %s at %p", elm->obj->path, + (void *)elm->obj->fini); + LD_UTRACE(UTRACE_FINI_CALL, elm->obj, (void *)elm->obj->fini, + 0, 0, elm->obj->path); + call_initfini_pointer(elm->obj, elm->obj->fini); + } + wlock_acquire(rtld_bind_lock, lockstate); + unhold_object(elm->obj); + /* No need to free anything if process is going down. */ + if (root != NULL) + free(elm); + /* + * We must restart the list traversal after every fini call + * because a dlclose() call from the fini function or from + * another thread might have modified the reference counts. + */ + break; } - } + } while (elm != NULL); errmsg_restore(saved_msg); } @@ -1358,10 +2584,24 @@ * functions. */ static void -objlist_call_init(Objlist *list) +objlist_call_init(Objlist *list, RtldLockState *lockstate) { Objlist_Entry *elm; + Obj_Entry *obj; char *saved_msg; + Elf_Addr *init_addr; + int index; + + /* + * Clean init_scanned flag so that objects can be rechecked and + * possibly initialized earlier if any of vectors called below + * cause the change by using dlopen. + */ + TAILQ_FOREACH(obj, &obj_list, next) { + if (obj->marker) + continue; + obj->init_scanned = false; + } /* * Preserve the current error message since an init function might @@ -1369,9 +2609,42 @@ */ saved_msg = errmsg_save(); STAILQ_FOREACH(elm, list, link) { - dbg("calling init function for %s at %p", elm->obj->path, - (void *)elm->obj->init); - call_initfini_pointer(elm->obj, elm->obj->init); + if (elm->obj->init_done) /* Initialized early. */ + continue; + /* + * Race: other thread might try to use this object before current + * one completes the initialization. Not much can be done here + * without better locking. + */ + elm->obj->init_done = true; + hold_object(elm->obj); + lock_release(rtld_bind_lock, lockstate); + + /* + * It is legal to have both DT_INIT and DT_INIT_ARRAY defined. + * When this happens, DT_INIT is processed first. + */ + if (elm->obj->init != (Elf_Addr)NULL) { + dbg("calling init function for %s at %p", elm->obj->path, + (void *)elm->obj->init); + LD_UTRACE(UTRACE_INIT_CALL, elm->obj, (void *)elm->obj->init, + 0, 0, elm->obj->path); + call_initfini_pointer(elm->obj, elm->obj->init); + } + init_addr = (Elf_Addr *)elm->obj->init_array; + if (init_addr != NULL) { + for (index = 0; index < elm->obj->init_array_num; index++) { + if (init_addr[index] != 0 && init_addr[index] != 1) { + dbg("calling init function for %s at %p", elm->obj->path, + (void *)init_addr[index]); + LD_UTRACE(UTRACE_INIT_CALL, elm->obj, + (void *)init_addr[index], 0, 0, elm->obj->path); + call_init_pointer(elm->obj, init_addr[index]); + } + } + } + wlock_acquire(rtld_bind_lock, lockstate); + unhold_object(elm->obj); } errmsg_restore(saved_msg); } @@ -1426,6 +2699,23 @@ } static void +objlist_put_after(Objlist *list, Obj_Entry *listobj, Obj_Entry *obj) +{ + Objlist_Entry *elm, *listelm; + + STAILQ_FOREACH(listelm, list, link) { + if (listelm->obj == listobj) + break; + } + elm = NEW(Objlist_Entry); + elm->obj = obj; + if (listelm != NULL) + STAILQ_INSERT_AFTER(list, listelm, elm, link); + else + STAILQ_INSERT_TAIL(list, elm, link); +} + +static void objlist_remove(Objlist *list, Obj_Entry *obj) { Objlist_Entry *elm; @@ -1437,77 +2727,118 @@ } /* - * Remove all of the unreferenced objects from "list". + * Relocate dag rooted in the specified object. + * Returns 0 on success, or -1 on failure. */ -static void -objlist_remove_unref(Objlist *list) -{ - Objlist newlist; - Objlist_Entry *elm; - STAILQ_INIT(&newlist); - while (!STAILQ_EMPTY(list)) { - elm = STAILQ_FIRST(list); - STAILQ_REMOVE_HEAD(list, link); - if (elm->obj->refcount == 0) - free(elm); - else - STAILQ_INSERT_TAIL(&newlist, elm, link); - } - *list = newlist; +static int +relocate_object_dag(Obj_Entry *root, bool bind_now, Obj_Entry *rtldobj, + int flags, RtldLockState *lockstate) +{ + Objlist_Entry *elm; + int error; + + error = 0; + STAILQ_FOREACH(elm, &root->dagmembers, link) { + error = relocate_object(elm->obj, bind_now, rtldobj, flags, + lockstate); + if (error == -1) + break; + } + return (error); } /* - * 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. + * Prepare for, or clean after, relocating an object marked with + * DT_TEXTREL or DF_TEXTREL. Before relocating, all read-only + * segments are remapped read-write. After relocations are done, the + * segment's permissions are returned back to the modes specified in + * the phdrs. If any relocation happened, or always for wired + * program, COW is triggered. */ static int -relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj) +reloc_textrel_prot(Obj_Entry *obj, bool before) { - Obj_Entry *obj; + const Elf_Phdr *ph; + void *base; + size_t l, sz; + int prot; - for (obj = first; obj != NULL; obj = obj->next) { + for (l = obj->phsize / sizeof(*ph), ph = obj->phdr; l > 0; + l--, ph++) { + if (ph->p_type != PT_LOAD || (ph->p_flags & PF_W) != 0) + continue; + base = obj->relocbase + trunc_page(ph->p_vaddr); + sz = round_page(ph->p_vaddr + ph->p_filesz) - + trunc_page(ph->p_vaddr); + prot = convert_prot(ph->p_flags) | (before ? PROT_WRITE : 0); + if (mprotect(base, sz, prot) == -1) { + _rtld_error("%s: Cannot write-%sable text segment: %s", + obj->path, before ? "en" : "dis", + rtld_strerror(errno)); + return (-1); + } + } + return (0); +} + +/* + * Relocate single object. + * Returns 0 on success, or -1 on failure. + */ +static int +relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, + int flags, RtldLockState *lockstate) +{ + + if (obj->relocated) + return (0); + obj->relocated = true; if (obj != rtldobj) - dbg("relocating \"%s\"", obj->path); - 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\"", obj->path); + + if (obj->symtab == NULL || obj->strtab == NULL || + !(obj->valid_hash_sysv || obj->valid_hash_gnu)) { + _rtld_error("%s: Shared object has no run-time symbol table", + obj->path); + return (-1); } - if (obj->textrel) { - /* There are relocations to the write-protected text segment. */ - if (mprotect(obj->mapbase, obj->textsize, - PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { - _rtld_error("%s: Cannot write-enable text segment: %s", - obj->path, strerror(errno)); - return -1; - } - } + /* There are relocations to the write-protected text segment. */ + if (obj->textrel && reloc_textrel_prot(obj, true) != 0) + return (-1); - /* Process the non-PLT relocations. */ - if (reloc_non_plt(obj, rtldobj)) - return -1; + /* Process the non-PLT non-IFUNC relocations. */ + if (reloc_non_plt(obj, rtldobj, flags, lockstate)) + return (-1); - if (obj->textrel) { /* Re-protected the text segment. */ - if (mprotect(obj->mapbase, obj->textsize, - PROT_READ|PROT_EXEC) == -1) { - _rtld_error("%s: Cannot write-protect text segment: %s", - obj->path, strerror(errno)); - return -1; - } - } + /* Re-protected the text segment. */ + if (obj->textrel && reloc_textrel_prot(obj, false) != 0) + return (-1); + + /* Set the special PLT or GOT entries. */ + init_pltgot(obj); /* Process the PLT relocations. */ if (reloc_plt(obj) == -1) - return -1; + return (-1); /* Relocate the jump slots if we are doing immediate binding. */ if (obj->bind_now || bind_now) - if (reloc_jmpslots(obj) == -1) - return -1; + if (reloc_jmpslots(obj, flags, lockstate) == -1) + return (-1); + + /* + * Process the non-PLT IFUNC relocations. The relocations are + * processed in two phases, because IFUNC resolvers may + * reference other symbols, which must be readily processed + * before resolvers are called. + */ + if (obj->non_plt_gnu_ifunc && + reloc_non_plt(obj, rtldobj, flags | SYMLOOK_IFUNC, lockstate)) + return (-1); + + if (!obj->mainprog && obj_enforce_relro(obj) == -1) + return (-1); /* * Set up the magic number and version in the Obj_Entry. These @@ -1517,11 +2848,84 @@ obj->magic = RTLD_MAGIC; obj->version = RTLD_VERSION; - /* Set the special PLT or GOT entries. */ - init_pltgot(obj); - } + return (0); +} - return 0; +/* + * Relocate newly-loaded shared objects. The argument is a pointer to + * the Obj_Entry for the first such object. All objects from the first + * to the end of the list of objects are relocated. Returns 0 on success, + * or -1 on failure. + */ +static int +relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj, + int flags, RtldLockState *lockstate) +{ + Obj_Entry *obj; + int error; + + for (error = 0, obj = first; obj != NULL; + obj = TAILQ_NEXT(obj, next)) { + if (obj->marker) + continue; + error = relocate_object(obj, bind_now, rtldobj, flags, + lockstate); + if (error == -1) + break; + } + return (error); +} + +/* + * The handling of R_MACHINE_IRELATIVE relocations and jumpslots + * referencing STT_GNU_IFUNC symbols is postponed till the other + * relocations are done. The indirect functions specified as + * ifunc are allowed to call other symbols, so we need to have + * objects relocated before asking for resolution from indirects. + * + * The R_MACHINE_IRELATIVE slots are resolved in greedy fashion, + * instead of the usual lazy handling of PLT slots. It is + * consistent with how GNU does it. + */ +static int +resolve_object_ifunc(Obj_Entry *obj, bool bind_now, int flags, + RtldLockState *lockstate) +{ + if (obj->irelative && reloc_iresolve(obj, lockstate) == -1) + return (-1); + if ((obj->bind_now || bind_now) && obj->gnu_ifunc && + reloc_gnu_ifunc(obj, flags, lockstate) == -1) + return (-1); + return (0); +} + +static int +resolve_objects_ifunc(Obj_Entry *first, bool bind_now, int flags, + RtldLockState *lockstate) +{ + Obj_Entry *obj; + + for (obj = first; obj != NULL; obj = TAILQ_NEXT(obj, next)) { + if (obj->marker) + continue; + if (resolve_object_ifunc(obj, bind_now, flags, lockstate) == -1) + return (-1); + } + return (0); +} + +static int +initlist_objects_ifunc(Objlist *list, bool bind_now, int flags, + RtldLockState *lockstate) +{ + Objlist_Entry *elm; + + STAILQ_FOREACH(elm, list, link) { + if (resolve_object_ifunc(elm->obj, bind_now, flags, + lockstate) == -1) + return (-1); + } + return (0); } /* @@ -1531,24 +2935,25 @@ static void rtld_exit(void) { - Obj_Entry *obj; + RtldLockState lockstate; + wlock_acquire(rtld_bind_lock, &lockstate); dbg("rtld_exit()"); - /* Clear all the reference counts so the fini functions will be called. */ - for (obj = obj_list; obj != NULL; obj = obj->next) - obj->refcount = 0; - objlist_call_fini(&list_fini); + objlist_call_fini(&list_fini, NULL, &lockstate); /* No need to remove the items from the list, since we are exiting. */ if (!libmap_disable) lm_fini(); + lock_release(rtld_bind_lock, &lockstate); } +/* + * Iterate over a search path, translate each element, and invoke the + * callback on the result. + */ static void * path_enumerate(const char *path, path_enum_proc callback, void *arg) { -#ifdef COMPAT_32BIT const char *trans; -#endif if (path == NULL) return (NULL); @@ -1558,13 +2963,11 @@ char *res; len = strcspn(path, ":;"); -#ifdef COMPAT_32BIT trans = lm_findn(NULL, path, len); if (trans) res = callback(trans, strlen(trans), arg); else -#endif - res = callback(path, len, arg); + res = callback(path, len, arg); if (res != NULL) return (res); @@ -1618,7 +3021,7 @@ if (path == NULL) return NULL; -printf("name: [%s]\n",name); + arg.name = name; arg.namelen = strlen(name); arg.buffer = xmalloc(PATH_MAX); @@ -1631,40 +3034,115 @@ return (p); } + +/* + * Finds the library with the given name using the directory descriptors + * listed in the LD_LIBRARY_PATH_FDS environment variable. + * + * Returns a freshly-opened close-on-exec file descriptor for the library, + * or -1 if the library cannot be found. + */ +static char * +search_library_pathfds(const char *name, const char *path, int *fdp) +{ + char *envcopy, *fdstr, *found, *last_token; + size_t len; + int dirfd, fd; + + dbg("%s('%s', '%s', fdp)", __func__, name, path); + + /* Don't load from user-specified libdirs into setuid binaries. */ + if (!trust) + return (NULL); + + /* We can't do anything if LD_LIBRARY_PATH_FDS isn't set. */ + if (path == NULL) + return (NULL); + + /* LD_LIBRARY_PATH_FDS only works with relative paths. */ + if (name[0] == '/') { + dbg("Absolute path (%s) passed to %s", name, __func__); + return (NULL); + } + + /* + * Use strtok_r() to walk the FD:FD:FD list. This requires a local + * copy of the path, as strtok_r rewrites separator tokens + * with '\0'. + */ + found = NULL; + envcopy = xstrdup(path); + for (fdstr = strtok_r(envcopy, ":", &last_token); fdstr != NULL; + fdstr = strtok_r(NULL, ":", &last_token)) { + dirfd = parse_integer(fdstr); + if (dirfd < 0) { + _rtld_error("failed to parse directory FD: '%s'", + fdstr); + break; + } + fd = __sys_openat(dirfd, name, O_RDONLY | O_CLOEXEC | O_VERIFY); + if (fd >= 0) { + *fdp = fd; + len = strlen(fdstr) + strlen(name) + 3; + found = xmalloc(len); + if (rtld_snprintf(found, len, "#%d/%s", dirfd, name) < 0) { + _rtld_error("error generating '%d/%s'", + dirfd, name); + rtld_die(); + } + dbg("open('%s') => %d", found, fd); + break; + } + } + free(envcopy); + + return (found); +} + + int dlclose(void *handle) { - Obj_Entry *root; - int lockstate; + RtldLockState lockstate; + int error; - lockstate = wlock_acquire(rtld_bind_lock); + wlock_acquire(rtld_bind_lock, &lockstate); + error = dlclose_locked(handle, &lockstate); + lock_release(rtld_bind_lock, &lockstate); + return (error); +} + +static int +dlclose_locked(void *handle, RtldLockState *lockstate) +{ + Obj_Entry *root; + root = dlcheck(handle); - if (root == NULL) { - wlock_release(rtld_bind_lock, lockstate); + if (root == NULL) return -1; - } + LD_UTRACE(UTRACE_DLCLOSE_START, handle, NULL, 0, root->dl_refcount, + root->path); /* Unreference the object and its dependencies. */ root->dl_refcount--; - unref_dag(root); - - if (root->refcount == 0) { + if (root->refcount == 1) { /* - * The object is no longer referenced, so we must unload it. - * First, call the fini functions with no locks held. + * The object will be no longer referenced, so we must unload it. + * First, call the fini functions. */ - wlock_release(rtld_bind_lock, lockstate); - objlist_call_fini(&list_fini); - lockstate = wlock_acquire(rtld_bind_lock); - objlist_remove_unref(&list_fini); + objlist_call_fini(&list_fini, root, lockstate); + + unref_dag(root); /* Finish cleaning up the newly-unreferenced objects. */ GDB_STATE(RT_DELETE,&root->linkmap); - unload_object(root); + unload_object(root, lockstate); GDB_STATE(RT_CONSISTENT,NULL); - } - wlock_release(rtld_bind_lock, lockstate); + } else + unref_dag(root); + + LD_UTRACE(UTRACE_DLCLOSE_STOP, handle, NULL, 0, 0, NULL); return 0; } @@ -1701,164 +3179,370 @@ void * dlopen(const char *name, int mode) { - Obj_Entry **old_obj_tail; + + return (rtld_dlopen(name, -1, mode)); +} + +void * +fdlopen(int fd, int mode) +{ + + return (rtld_dlopen(NULL, fd, mode)); +} + +static void * +rtld_dlopen(const char *name, int fd, int mode) +{ + RtldLockState lockstate; + int lo_flags; + + LD_UTRACE(UTRACE_DLOPEN_START, NULL, NULL, 0, mode, name); + ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1"; + if (ld_tracing != NULL) { + rlock_acquire(rtld_bind_lock, &lockstate); + if (sigsetjmp(lockstate.env, 0) != 0) + lock_upgrade(rtld_bind_lock, &lockstate); + environ = (char **)*get_program_var_addr("environ", &lockstate); + lock_release(rtld_bind_lock, &lockstate); + } + lo_flags = RTLD_LO_DLOPEN; + if (mode & RTLD_NODELETE) + lo_flags |= RTLD_LO_NODELETE; + if (mode & RTLD_NOLOAD) + lo_flags |= RTLD_LO_NOLOAD; + if (ld_tracing != NULL) + lo_flags |= RTLD_LO_TRACE; + + return (dlopen_object(name, fd, obj_main, lo_flags, + mode & (RTLD_MODEMASK | RTLD_GLOBAL), NULL)); +} + +static void +dlopen_cleanup(Obj_Entry *obj, RtldLockState *lockstate) +{ + + obj->dl_refcount--; + unref_dag(obj); + if (obj->refcount == 0) + unload_object(obj, lockstate); +} + +static Obj_Entry * +dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags, + int mode, RtldLockState *lockstate) +{ + Obj_Entry *old_obj_tail; Obj_Entry *obj; Objlist initlist; - int result, lockstate; - - ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1"; - if (ld_tracing != NULL) - environ = (char **)*get_program_var_addr("environ"); + RtldLockState mlockstate; + int result; objlist_init(&initlist); - lockstate = wlock_acquire(rtld_bind_lock); + if (lockstate == NULL && !(lo_flags & RTLD_LO_EARLY)) { + wlock_acquire(rtld_bind_lock, &mlockstate); + lockstate = &mlockstate; + } GDB_STATE(RT_ADD,NULL); - old_obj_tail = obj_tail; + old_obj_tail = globallist_curr(TAILQ_LAST(&obj_list, obj_entry_q)); obj = NULL; - if (name == NULL) { + if (name == NULL && fd == -1) { obj = obj_main; obj->refcount++; } else { - char *path = find_library(name, obj_main); - if (path != NULL) - obj = load_object(path); + obj = load_object(name, fd, refobj, lo_flags); } if (obj) { obj->dl_refcount++; if (mode & RTLD_GLOBAL && objlist_find(&list_global, obj) == NULL) objlist_push_tail(&list_global, obj); - mode &= RTLD_MODEMASK; - if (*old_obj_tail != NULL) { /* We loaded something new. */ - assert(*old_obj_tail == obj); - - result = load_needed_objects(obj); + if (globallist_next(old_obj_tail) != NULL) { + /* We loaded something new. */ + assert(globallist_next(old_obj_tail) == obj); + result = load_needed_objects(obj, + lo_flags & (RTLD_LO_DLOPEN | RTLD_LO_EARLY)); + init_dag(obj); + ref_dag(obj); + if (result != -1) + result = rtld_verify_versions(&obj->dagmembers); if (result != -1 && ld_tracing) goto trace; - - if (result == -1 || - (init_dag(obj), relocate_objects(obj, mode == RTLD_NOW, - &obj_rtld)) == -1) { - obj->dl_refcount--; - unref_dag(obj); - if (obj->refcount == 0) - unload_object(obj); + if (result == -1 || relocate_object_dag(obj, + (mode & RTLD_MODEMASK) == RTLD_NOW, &obj_rtld, + (lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0, + lockstate) == -1) { + dlopen_cleanup(obj, lockstate); obj = NULL; + } else if (lo_flags & RTLD_LO_EARLY) { + /* + * Do not call the init functions for early loaded + * filtees. The image is still not initialized enough + * for them to work. + * + * Our object is found by the global object list and + * will be ordered among all init calls done right + * before transferring control to main. + */ } else { /* Make list of init functions to call. */ - initlist_add_objects(obj, &obj->next, &initlist); + initlist_add_objects(obj, obj, &initlist); } + /* + * Process all no_delete or global objects here, given + * them own DAGs to prevent their dependencies from being + * unloaded. This has to be done after we have loaded all + * of the dependencies, so that we do not miss any. + */ + if (obj != NULL) + process_z(obj); } else { - - /* Bump the reference counts for objects on this DAG. */ + /* + * Bump the reference counts for objects on this DAG. If + * this is the first dlopen() call for the object that was + * already loaded as a dependency, initialize the dag + * starting at it. + */ + init_dag(obj); ref_dag(obj); - if (ld_tracing) + if ((lo_flags & RTLD_LO_TRACE) != 0) goto trace; } + if (obj != NULL && ((lo_flags & RTLD_LO_NODELETE) != 0 || + obj->z_nodelete) && !obj->ref_nodel) { + dbg("obj %s nodelete", obj->path); + ref_dag(obj); + obj->z_nodelete = obj->ref_nodel = true; + } } + LD_UTRACE(UTRACE_DLOPEN_STOP, obj, NULL, 0, obj ? obj->dl_refcount : 0, + name); GDB_STATE(RT_CONSISTENT,obj ? &obj->linkmap : NULL); - /* Call the init functions with no locks held. */ - wlock_release(rtld_bind_lock, lockstate); - objlist_call_init(&initlist); - lockstate = wlock_acquire(rtld_bind_lock); + if (!(lo_flags & RTLD_LO_EARLY)) { + map_stacks_exec(lockstate); + } + + if (initlist_objects_ifunc(&initlist, (mode & RTLD_MODEMASK) == RTLD_NOW, + (lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0, + lockstate) == -1) { + objlist_clear(&initlist); + dlopen_cleanup(obj, lockstate); + if (lockstate == &mlockstate) + lock_release(rtld_bind_lock, lockstate); + return (NULL); + } + + if (!(lo_flags & RTLD_LO_EARLY)) { + /* Call the init functions. */ + objlist_call_init(&initlist, lockstate); + } objlist_clear(&initlist); - wlock_release(rtld_bind_lock, lockstate); + if (lockstate == &mlockstate) + lock_release(rtld_bind_lock, lockstate); return obj; trace: trace_loaded_objects(obj); - wlock_release(rtld_bind_lock, lockstate); + if (lockstate == &mlockstate) + lock_release(rtld_bind_lock, lockstate); exit(0); } +static void * +do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve, + int flags) +{ + DoneList donelist; + const Obj_Entry *obj, *defobj; + const Elf_Sym *def; + SymLook req; + RtldLockState lockstate; + tls_index ti; + void *sym; + int res; + + def = NULL; + defobj = NULL; + symlook_init(&req, name); + req.ventry = ve; + req.flags = flags | SYMLOOK_IN_PLT; + req.lockstate = &lockstate; + + LD_UTRACE(UTRACE_DLSYM_START, handle, NULL, 0, 0, name); + rlock_acquire(rtld_bind_lock, &lockstate); + if (sigsetjmp(lockstate.env, 0) != 0) + lock_upgrade(rtld_bind_lock, &lockstate); + if (handle == NULL || handle == RTLD_NEXT || + handle == RTLD_DEFAULT || handle == RTLD_SELF) { + + if ((obj = obj_from_addr(retaddr)) == NULL) { + _rtld_error("Cannot determine caller's shared object"); + lock_release(rtld_bind_lock, &lockstate); + LD_UTRACE(UTRACE_DLSYM_STOP, handle, NULL, 0, 0, name); + return NULL; + } + if (handle == NULL) { /* Just the caller's shared object. */ + res = symlook_obj(&req, obj); + if (res == 0) { + def = req.sym_out; + defobj = req.defobj_out; + } + } else if (handle == RTLD_NEXT || /* Objects after caller's */ + handle == RTLD_SELF) { /* ... caller included */ + if (handle == RTLD_NEXT) + obj = globallist_next(obj); + for (; obj != NULL; obj = TAILQ_NEXT(obj, next)) { + if (obj->marker) + continue; + res = symlook_obj(&req, obj); + if (res == 0) { + if (def == NULL || + ELF_ST_BIND(req.sym_out->st_info) != STB_WEAK) { + def = req.sym_out; + defobj = req.defobj_out; + if (ELF_ST_BIND(def->st_info) != STB_WEAK) + break; + } + } + } + /* + * Search the dynamic linker itself, and possibly resolve the + * symbol from there. This is how the application links to + * dynamic linker services such as dlopen. + */ + if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) { + res = symlook_obj(&req, &obj_rtld); + if (res == 0) { + def = req.sym_out; + defobj = req.defobj_out; + } + } + } else { + assert(handle == RTLD_DEFAULT); + res = symlook_default(&req, obj); + if (res == 0) { + defobj = req.defobj_out; + def = req.sym_out; + } + } + } else { + if ((obj = dlcheck(handle)) == NULL) { + lock_release(rtld_bind_lock, &lockstate); + LD_UTRACE(UTRACE_DLSYM_STOP, handle, NULL, 0, 0, name); + return NULL; + } + + donelist_init(&donelist); + if (obj->mainprog) { + /* Handle obtained by dlopen(NULL, ...) implies global scope. */ + res = symlook_global(&req, &donelist); + if (res == 0) { + def = req.sym_out; + defobj = req.defobj_out; + } + /* + * Search the dynamic linker itself, and possibly resolve the + * symbol from there. This is how the application links to + * dynamic linker services such as dlopen. + */ + if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) { + res = symlook_obj(&req, &obj_rtld); + if (res == 0) { + def = req.sym_out; + defobj = req.defobj_out; + } + } + } + else { + /* Search the whole DAG rooted at the given object. */ + res = symlook_list(&req, &obj->dagmembers, &donelist); + if (res == 0) { + def = req.sym_out; + defobj = req.defobj_out; + } + } + } + + if (def != NULL) { + lock_release(rtld_bind_lock, &lockstate); + + /* + * The value required by the caller is derived from the value + * of the symbol. this is simply the relocated value of the + * symbol. + */ + if (ELF_ST_TYPE(def->st_info) == STT_FUNC) + sym = make_function_pointer(def, defobj); + else if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) + sym = rtld_resolve_ifunc(defobj, def); + else if (ELF_ST_TYPE(def->st_info) == STT_TLS) { + ti.ti_module = defobj->tlsindex; + ti.ti_offset = def->st_value; + sym = __tls_get_addr(&ti); + } else + sym = defobj->relocbase + def->st_value; + LD_UTRACE(UTRACE_DLSYM_STOP, handle, sym, 0, 0, name); + return (sym); + } + + _rtld_error("Undefined symbol \"%s\"", name); + lock_release(rtld_bind_lock, &lockstate); + LD_UTRACE(UTRACE_DLSYM_STOP, handle, NULL, 0, 0, name); + return NULL; +} + void * dlsym(void *handle, const char *name) { + return do_dlsym(handle, name, __builtin_return_address(0), NULL, + SYMLOOK_DLSYM); +} + +dlfunc_t +dlfunc(void *handle, const char *name) +{ + union { + void *d; + dlfunc_t f; + } rv; + + rv.d = do_dlsym(handle, name, __builtin_return_address(0), NULL, + SYMLOOK_DLSYM); + return (rv.f); +} + +void * +dlvsym(void *handle, const char *name, const char *version) +{ + Ver_Entry ventry; + + ventry.name = version; + ventry.file = NULL; + ventry.hash = elf_hash(version); + ventry.flags= 0; + return do_dlsym(handle, name, __builtin_return_address(0), &ventry, + SYMLOOK_DLSYM); +} + +int +_rtld_addr_phdr(const void *addr, struct dl_phdr_info *phdr_info) +{ const Obj_Entry *obj; - unsigned long hash; - const Elf_Sym *def; - const Obj_Entry *defobj; - int lockstate; + RtldLockState lockstate; - hash = elf_hash(name); - def = NULL; - defobj = NULL; - - lockstate = rlock_acquire(rtld_bind_lock); - if (handle == NULL || handle == RTLD_NEXT || - handle == RTLD_DEFAULT || handle == RTLD_SELF) { - void *retaddr; - - retaddr = __builtin_return_address(0); /* __GNUC__ only */ - if ((obj = obj_from_addr(retaddr)) == NULL) { - _rtld_error("Cannot determine caller's shared object"); - rlock_release(rtld_bind_lock, lockstate); - return NULL; - } - if (handle == NULL) { /* Just the caller's shared object. */ - def = symlook_obj(name, hash, obj, true); - defobj = obj; - } else if (handle == RTLD_NEXT || /* Objects after caller's */ - handle == RTLD_SELF) { /* ... caller included */ - if (handle == RTLD_NEXT) - obj = obj->next; - for (; obj != NULL; obj = obj->next) { - if ((def = symlook_obj(name, hash, obj, true)) != NULL) { - defobj = obj; - break; - } - } - } else { - assert(handle == RTLD_DEFAULT); - def = symlook_default(name, hash, obj, &defobj, true); - } - } else { - if ((obj = dlcheck(handle)) == NULL) { - rlock_release(rtld_bind_lock, lockstate); - return NULL; - } - - if (obj->mainprog) { - DoneList donelist; - - /* Search main program and all libraries loaded by it. */ - donelist_init(&donelist); - def = symlook_list(name, hash, &list_main, &defobj, true, - &donelist); - } else { - /* - * XXX - This isn't correct. The search should include the whole - * DAG rooted at the given object. - */ - def = symlook_obj(name, hash, obj, true); - defobj = obj; - } + rlock_acquire(rtld_bind_lock, &lockstate); + obj = obj_from_addr(addr); + if (obj == NULL) { + _rtld_error("No shared object contains address"); + lock_release(rtld_bind_lock, &lockstate); + return (0); } - - if (def != NULL) { - rlock_release(rtld_bind_lock, lockstate); - - /* - * The value required by the caller is derived from the value - * of the symbol. For the ia64 architecture, we need to - * construct a function descriptor which the caller can use to - * call the function with the right 'gp' value. For other - * architectures and for non-functions, the value is simply - * the relocated value of the symbol. - */ - if (ELF_ST_TYPE(def->st_info) == STT_FUNC) - return make_function_pointer(def, defobj); - else - return defobj->relocbase + def->st_value; - } - - _rtld_error("Undefined symbol \"%s\"", name); - rlock_release(rtld_bind_lock, lockstate); - return NULL; + rtld_fill_dl_phdr_info(obj, phdr_info); + lock_release(rtld_bind_lock, &lockstate); + return (1); } int @@ -1868,13 +3552,13 @@ const Elf_Sym *def; void *symbol_addr; unsigned long symoffset; - int lockstate; + RtldLockState lockstate; - lockstate = rlock_acquire(rtld_bind_lock); + rlock_acquire(rtld_bind_lock, &lockstate); obj = obj_from_addr(addr); if (obj == NULL) { _rtld_error("No shared object contains address"); - rlock_release(rtld_bind_lock, lockstate); + lock_release(rtld_bind_lock, &lockstate); return 0; } info->dli_fname = obj->path; @@ -1886,7 +3570,7 @@ * Walk the symbol list looking for the symbol whose address is * closest to the address sent in. */ - for (symoffset = 0; symoffset < obj->nchains; symoffset++) { + for (symoffset = 0; symoffset < obj->dynsymcount; symoffset++) { def = obj->symtab + symoffset; /* @@ -1913,7 +3597,7 @@ if (info->dli_saddr == addr) break; } - rlock_release(rtld_bind_lock, lockstate); + lock_release(rtld_bind_lock, &lockstate); return 1; } @@ -1921,9 +3605,10 @@ dlinfo(void *handle, int request, void *p) { const Obj_Entry *obj; - int error, lockstate; + RtldLockState lockstate; + int error; - lockstate = rlock_acquire(rtld_bind_lock); + rlock_acquire(rtld_bind_lock, &lockstate); if (handle == NULL || handle == RTLD_SELF) { void *retaddr; @@ -1935,7 +3620,7 @@ obj = dlcheck(handle); if (obj == NULL) { - rlock_release(rtld_bind_lock, lockstate); + lock_release(rtld_bind_lock, &lockstate); return (-1); } @@ -1958,18 +3643,65 @@ error = -1; } - rlock_release(rtld_bind_lock, lockstate); + lock_release(rtld_bind_lock, &lockstate); return (error); } -struct fill_search_info_args { - int request; - unsigned int flags; - Dl_serinfo *serinfo; - Dl_serpath *serpath; - char *strspace; -}; +static void +rtld_fill_dl_phdr_info(const Obj_Entry *obj, struct dl_phdr_info *phdr_info) +{ + + phdr_info->dlpi_addr = (Elf_Addr)obj->relocbase; + phdr_info->dlpi_name = obj->path; + phdr_info->dlpi_phdr = obj->phdr; + phdr_info->dlpi_phnum = obj->phsize / sizeof(obj->phdr[0]); + phdr_info->dlpi_tls_modid = obj->tlsindex; + phdr_info->dlpi_tls_data = obj->tlsinit; + phdr_info->dlpi_adds = obj_loads; + phdr_info->dlpi_subs = obj_loads - obj_count; +} + +int +dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param) +{ + struct dl_phdr_info phdr_info; + Obj_Entry *obj, marker; + RtldLockState bind_lockstate, phdr_lockstate; + int error; + + init_marker(&marker); + error = 0; + + wlock_acquire(rtld_phdr_lock, &phdr_lockstate); + wlock_acquire(rtld_bind_lock, &bind_lockstate); + for (obj = globallist_curr(TAILQ_FIRST(&obj_list)); obj != NULL;) { + TAILQ_INSERT_AFTER(&obj_list, obj, &marker, next); + rtld_fill_dl_phdr_info(obj, &phdr_info); + hold_object(obj); + lock_release(rtld_bind_lock, &bind_lockstate); + + error = callback(&phdr_info, sizeof phdr_info, param); + + wlock_acquire(rtld_bind_lock, &bind_lockstate); + unhold_object(obj); + obj = globallist_next(&marker); + TAILQ_REMOVE(&obj_list, &marker, next); + if (error != 0) { + lock_release(rtld_bind_lock, &bind_lockstate); + lock_release(rtld_phdr_lock, &phdr_lockstate); + return (error); + } + } + + if (error == 0) { + rtld_fill_dl_phdr_info(&obj_rtld, &phdr_info); + lock_release(rtld_bind_lock, &bind_lockstate); + error = callback(&phdr_info, sizeof(phdr_info), param); + } + lock_release(rtld_phdr_lock, &phdr_lockstate); + return (error); +} static void * fill_search_info(const char *dir, size_t dirlen, void *param) @@ -1980,7 +3712,7 @@ if (arg->request == RTLD_DI_SERINFOSIZE) { arg->serinfo->dls_cnt ++; - arg->serinfo->dls_size += sizeof(Dl_serpath) + dirlen + 1; + arg->serinfo->dls_size += sizeof(struct dl_serpath) + dirlen + 1; } else { struct dl_serpath *s_entry; @@ -2010,10 +3742,12 @@ _info.dls_size = __offsetof(struct dl_serinfo, dls_serpath); _info.dls_cnt = 0; - path_enumerate(ld_library_path, fill_search_info, &args); path_enumerate(obj->rpath, fill_search_info, &args); - path_enumerate(gethints(), fill_search_info, &args); - path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &args); + path_enumerate(ld_library_path, fill_search_info, &args); + path_enumerate(obj->runpath, fill_search_info, &args); + path_enumerate(gethints(obj->z_nodeflib), fill_search_info, &args); + if (!obj->z_nodeflib) + path_enumerate(ld_standard_library_path, fill_search_info, &args); if (request == RTLD_DI_SERINFOSIZE) { @@ -2032,20 +3766,26 @@ args.serpath = &info->dls_serpath[0]; args.strspace = (char *)&info->dls_serpath[_info.dls_cnt]; + args.flags = LA_SER_RUNPATH; + if (path_enumerate(obj->rpath, fill_search_info, &args) != NULL) + return (-1); + args.flags = LA_SER_LIBPATH; if (path_enumerate(ld_library_path, fill_search_info, &args) != NULL) return (-1); args.flags = LA_SER_RUNPATH; - if (path_enumerate(obj->rpath, fill_search_info, &args) != NULL) + if (path_enumerate(obj->runpath, fill_search_info, &args) != NULL) return (-1); args.flags = LA_SER_CONFIG; - if (path_enumerate(gethints(), fill_search_info, &args) != NULL) + if (path_enumerate(gethints(obj->z_nodeflib), fill_search_info, &args) + != NULL) return (-1); args.flags = LA_SER_DEFAULT; - if (path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &args) != NULL) + if (!obj->z_nodeflib && + path_enumerate(ld_standard_library_path, fill_search_info, &args) != NULL) return (-1); return (0); } @@ -2093,6 +3833,22 @@ return (0); } +static int +rtld_dirname_abs(const char *path, char *base) +{ + char *last; + + if (realpath(path, base) == NULL) + return (-1); + dbg("%s -> %s", path, base); + last = strrchr(base, '/'); + if (last == NULL) + return (-1); + if (last != base) + *last = '\0'; + return (0); +} + static void linkmap_add(Obj_Entry *obj) { @@ -2160,29 +3916,65 @@ void r_debug_state(struct r_debug* rd, struct link_map *m) { + /* + * The following is a hack to force the compiler to emit calls to + * this function, even when optimizing. If the function is empty, + * the compiler is not obliged to emit any code for calls to it, + * even when marked __noinline. However, gdb depends on those + * calls being made. + */ + __compiler_membar(); +} + +/* + * A function called after init routines have completed. This can be used to + * break before a program's entry routine is called, and can be used when + * main is not available in the symbol table. + */ +void +_r_debug_postinit(struct link_map *m) +{ + + /* See r_debug_state(). */ + __compiler_membar(); +} + +static void +release_object(Obj_Entry *obj) +{ + + if (obj->holdcount > 0) { + obj->unholdfree = true; + return; + } + munmap(obj->mapbase, obj->mapsize); + linkmap_delete(obj); + obj_free(obj); } /* * Get address of the pointer variable in the main program. + * Prefer non-weak symbol over the weak one. */ static const void ** -get_program_var_addr(const char *name) +get_program_var_addr(const char *name, RtldLockState *lockstate) { - const Obj_Entry *obj; - unsigned long hash; + SymLook req; + DoneList donelist; - hash = elf_hash(name); - for (obj = obj_main; obj != NULL; obj = obj->next) { - const Elf_Sym *def; - - if ((def = symlook_obj(name, hash, obj, false)) != NULL) { - const void **addr; - - addr = (const void **)(obj->relocbase + def->st_value); - return addr; - } - } - return NULL; + symlook_init(&req, name); + req.lockstate = lockstate; + donelist_init(&donelist); + if (symlook_global(&req, &donelist) != 0) + return (NULL); + if (ELF_ST_TYPE(req.sym_out->st_info) == STT_FUNC) + return ((const void **)make_function_pointer(req.sym_out, + req.defobj_out)); + else if (ELF_ST_TYPE(req.sym_out->st_info) == STT_GNU_IFUNC) + return ((const void **)rtld_resolve_ifunc(req.defobj_out, req.sym_out)); + else + return ((const void **)(req.defobj_out->relocbase + + req.sym_out->st_value)); } /* @@ -2195,157 +3987,437 @@ { const void **addr; - if ((addr = get_program_var_addr(name)) != NULL) { + if ((addr = get_program_var_addr(name, NULL)) != NULL) { dbg("\"%s\": *%p <-- %p", name, addr, value); *addr = value; } } /* + * Search the global objects, including dependencies and main object, + * for the given symbol. + */ +static int +symlook_global(SymLook *req, DoneList *donelist) +{ + SymLook req1; + const Objlist_Entry *elm; + int res; + + symlook_init_from_req(&req1, req); + + /* Search all objects loaded at program start up. */ + if (req->defobj_out == NULL || + ELF_ST_BIND(req->sym_out->st_info) == STB_WEAK) { + res = symlook_list(&req1, &list_main, donelist); + if (res == 0 && (req->defobj_out == NULL || + ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) { + req->sym_out = req1.sym_out; + req->defobj_out = req1.defobj_out; + assert(req->defobj_out != NULL); + } + } + + /* Search all DAGs whose roots are RTLD_GLOBAL objects. */ + STAILQ_FOREACH(elm, &list_global, link) { + if (req->defobj_out != NULL && + ELF_ST_BIND(req->sym_out->st_info) != STB_WEAK) + break; + res = symlook_list(&req1, &elm->obj->dagmembers, donelist); + if (res == 0 && (req->defobj_out == NULL || + ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) { + req->sym_out = req1.sym_out; + req->defobj_out = req1.defobj_out; + assert(req->defobj_out != NULL); + } + } + + return (req->sym_out != NULL ? 0 : ESRCH); +} + +/* * Given a symbol name in a referencing object, find the corresponding * definition of the symbol. Returns a pointer to the symbol, or NULL if * no definition was found. Returns a pointer to the Obj_Entry of the * defining object via the reference parameter DEFOBJ_OUT. */ -static const Elf_Sym * -symlook_default(const char *name, unsigned long hash, - const Obj_Entry *refobj, const Obj_Entry **defobj_out, bool in_plt) +static int +symlook_default(SymLook *req, const Obj_Entry *refobj) { DoneList donelist; - const Elf_Sym *def; - const Elf_Sym *symp; - const Obj_Entry *obj; - const Obj_Entry *defobj; const Objlist_Entry *elm; - def = NULL; - defobj = NULL; + SymLook req1; + int res; + donelist_init(&donelist); + symlook_init_from_req(&req1, req); - /* Look first in the referencing object if linked symbolically. */ - if (refobj->symbolic && !donelist_check(&donelist, refobj)) { - symp = symlook_obj(name, hash, refobj, in_plt); - if (symp != NULL) { - def = symp; - defobj = refobj; - } + /* + * Look first in the referencing object if linked symbolically, + * and similarly handle protected symbols. + */ + res = symlook_obj(&req1, refobj); + if (res == 0 && (refobj->symbolic || + ELF_ST_VISIBILITY(req1.sym_out->st_other) == STV_PROTECTED)) { + req->sym_out = req1.sym_out; + req->defobj_out = req1.defobj_out; + assert(req->defobj_out != NULL); } + if (refobj->symbolic || req->defobj_out != NULL) + donelist_check(&donelist, refobj); - /* Search all objects loaded at program start up. */ - if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) { - symp = symlook_list(name, hash, &list_main, &obj, in_plt, &donelist); - if (symp != NULL && - (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) { - def = symp; - defobj = obj; - } - } - - /* Search all DAGs whose roots are RTLD_GLOBAL objects. */ - STAILQ_FOREACH(elm, &list_global, link) { - if (def != NULL && ELF_ST_BIND(def->st_info) != STB_WEAK) - break; - symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, in_plt, - &donelist); - if (symp != NULL && - (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) { - def = symp; - defobj = obj; - } - } + symlook_global(req, &donelist); /* Search all dlopened DAGs containing the referencing object. */ STAILQ_FOREACH(elm, &refobj->dldags, link) { - if (def != NULL && ELF_ST_BIND(def->st_info) != STB_WEAK) + if (req->sym_out != NULL && + ELF_ST_BIND(req->sym_out->st_info) != STB_WEAK) break; - symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, in_plt, - &donelist); - if (symp != NULL && - (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) { - def = symp; - defobj = obj; + res = symlook_list(&req1, &elm->obj->dagmembers, &donelist); + if (res == 0 && (req->sym_out == NULL || + ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) { + req->sym_out = req1.sym_out; + req->defobj_out = req1.defobj_out; + assert(req->defobj_out != NULL); } } /* * Search the dynamic linker itself, and possibly resolve the * symbol from there. This is how the application links to - * dynamic linker services such as dlopen. Only the values listed - * in the "exports" array can be resolved from the dynamic linker. + * dynamic linker services such as dlopen. */ - if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) { - symp = symlook_obj(name, hash, &obj_rtld, in_plt); - if (symp != NULL && is_exported(symp)) { - def = symp; - defobj = &obj_rtld; + if (req->sym_out == NULL || + ELF_ST_BIND(req->sym_out->st_info) == STB_WEAK) { + res = symlook_obj(&req1, &obj_rtld); + if (res == 0) { + req->sym_out = req1.sym_out; + req->defobj_out = req1.defobj_out; + assert(req->defobj_out != NULL); } } - if (def != NULL) - *defobj_out = defobj; - return def; + return (req->sym_out != NULL ? 0 : ESRCH); } -static const Elf_Sym * -symlook_list(const char *name, unsigned long hash, Objlist *objlist, - const Obj_Entry **defobj_out, bool in_plt, DoneList *dlp) +static int +symlook_list(SymLook *req, const Objlist *objlist, DoneList *dlp) { - const Elf_Sym *symp; const Elf_Sym *def; const Obj_Entry *defobj; const Objlist_Entry *elm; + SymLook req1; + int res; def = NULL; defobj = NULL; STAILQ_FOREACH(elm, objlist, link) { if (donelist_check(dlp, elm->obj)) continue; - if ((symp = symlook_obj(name, hash, elm->obj, in_plt)) != NULL) { - if (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK) { - def = symp; - defobj = elm->obj; + symlook_init_from_req(&req1, req); + if ((res = symlook_obj(&req1, elm->obj)) == 0) { + if (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK) { + def = req1.sym_out; + defobj = req1.defobj_out; if (ELF_ST_BIND(def->st_info) != STB_WEAK) break; } } } - if (def != NULL) - *defobj_out = defobj; - return def; + if (def != NULL) { + req->sym_out = def; + req->defobj_out = defobj; + return (0); + } + return (ESRCH); +} + +/* + * Search the chain of DAGS cointed to by the given Needed_Entry + * for a symbol of the given name. Each DAG is scanned completely + * before advancing to the next one. Returns a pointer to the symbol, + * or NULL if no definition was found. + */ +static int +symlook_needed(SymLook *req, const Needed_Entry *needed, DoneList *dlp) +{ + const Elf_Sym *def; + const Needed_Entry *n; + const Obj_Entry *defobj; + SymLook req1; + int res; + + def = NULL; + defobj = NULL; + symlook_init_from_req(&req1, req); + for (n = needed; n != NULL; n = n->next) { + if (n->obj == NULL || + (res = symlook_list(&req1, &n->obj->dagmembers, dlp)) != 0) + continue; + if (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK) { + def = req1.sym_out; + defobj = req1.defobj_out; + if (ELF_ST_BIND(def->st_info) != STB_WEAK) + break; + } + } + if (def != NULL) { + req->sym_out = def; + req->defobj_out = defobj; + return (0); + } + return (ESRCH); } /* * 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 given name and version, if requested. Returns a pointer to the + * symbol, or NULL if no definition was found. If the object is + * filter, return filtered symbol from filtee. * * The symbol's hash value is passed in for efficiency reasons; that * eliminates many recomputations of the hash value. */ -const Elf_Sym * -symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj, - bool in_plt) +int +symlook_obj(SymLook *req, const Obj_Entry *obj) { - if (obj->buckets != NULL) { - unsigned long symnum = obj->buckets[hash % obj->nbuckets]; + DoneList donelist; + SymLook req1; + int flags, res, mres; - while (symnum != STN_UNDEF) { - const Elf_Sym *symp; - const char *strp; + /* + * If there is at least one valid hash at this point, we prefer to + * use the faster GNU version if available. + */ + if (obj->valid_hash_gnu) + mres = symlook_obj1_gnu(req, obj); + else if (obj->valid_hash_sysv) + mres = symlook_obj1_sysv(req, obj); + else + return (EINVAL); - if (symnum >= obj->nchains) - return NULL; /* Bad object */ - symp = obj->symtab + symnum; - strp = obj->strtab + symp->st_name; - - if (name[0] == strp[0] && strcmp(name, strp) == 0) - return symp->st_shndx != SHN_UNDEF || - (!in_plt && symp->st_value != 0 && - ELF_ST_TYPE(symp->st_info) == STT_FUNC) ? symp : NULL; - - symnum = obj->chains[symnum]; + if (mres == 0) { + if (obj->needed_filtees != NULL) { + flags = (req->flags & SYMLOOK_EARLY) ? RTLD_LO_EARLY : 0; + load_filtees(__DECONST(Obj_Entry *, obj), flags, req->lockstate); + donelist_init(&donelist); + symlook_init_from_req(&req1, req); + res = symlook_needed(&req1, obj->needed_filtees, &donelist); + if (res == 0) { + req->sym_out = req1.sym_out; + req->defobj_out = req1.defobj_out; + } + return (res); + } + if (obj->needed_aux_filtees != NULL) { + flags = (req->flags & SYMLOOK_EARLY) ? RTLD_LO_EARLY : 0; + load_filtees(__DECONST(Obj_Entry *, obj), flags, req->lockstate); + donelist_init(&donelist); + symlook_init_from_req(&req1, req); + res = symlook_needed(&req1, obj->needed_aux_filtees, &donelist); + if (res == 0) { + req->sym_out = req1.sym_out; + req->defobj_out = req1.defobj_out; + return (res); + } } } - return NULL; + return (mres); +} + +/* Symbol match routine common to both hash functions */ +static bool +matched_symbol(SymLook *req, const Obj_Entry *obj, Sym_Match_Result *result, + const unsigned long symnum) +{ + Elf_Versym verndx; + const Elf_Sym *symp; + const char *strp; + + symp = obj->symtab + symnum; + strp = obj->strtab + symp->st_name; + + switch (ELF_ST_TYPE(symp->st_info)) { + case STT_FUNC: + case STT_NOTYPE: + case STT_OBJECT: + case STT_COMMON: + case STT_GNU_IFUNC: + if (symp->st_value == 0) + return (false); + /* fallthrough */ + case STT_TLS: + if (symp->st_shndx != SHN_UNDEF) + break; +#ifndef __mips__ + else if (((req->flags & SYMLOOK_IN_PLT) == 0) && + (ELF_ST_TYPE(symp->st_info) == STT_FUNC)) + break; + /* fallthrough */ +#endif + default: + return (false); + } + if (req->name[0] != strp[0] || strcmp(req->name, strp) != 0) + return (false); + + if (req->ventry == NULL) { + if (obj->versyms != NULL) { + verndx = VER_NDX(obj->versyms[symnum]); + if (verndx > obj->vernum) { + _rtld_error( + "%s: symbol %s references wrong version %d", + obj->path, obj->strtab + symnum, verndx); + return (false); + } + /* + * If we are not called from dlsym (i.e. this + * is a normal relocation from unversioned + * binary), accept the symbol immediately if + * it happens to have first version after this + * shared object became versioned. Otherwise, + * if symbol is versioned and not hidden, + * remember it. If it is the only symbol with + * this name exported by the shared object, it + * will be returned as a match by the calling + * function. If symbol is global (verndx < 2) + * accept it unconditionally. + */ + if ((req->flags & SYMLOOK_DLSYM) == 0 && + verndx == VER_NDX_GIVEN) { + result->sym_out = symp; + return (true); + } + else if (verndx >= VER_NDX_GIVEN) { + if ((obj->versyms[symnum] & VER_NDX_HIDDEN) + == 0) { + if (result->vsymp == NULL) + result->vsymp = symp; + result->vcount++; + } + return (false); + } + } + result->sym_out = symp; + return (true); + } + if (obj->versyms == NULL) { + if (object_match_name(obj, req->ventry->name)) { + _rtld_error("%s: object %s should provide version %s " + "for symbol %s", obj_rtld.path, obj->path, + req->ventry->name, obj->strtab + symnum); + return (false); + } + } else { + verndx = VER_NDX(obj->versyms[symnum]); + if (verndx > obj->vernum) { + _rtld_error("%s: symbol %s references wrong version %d", + obj->path, obj->strtab + symnum, verndx); + return (false); + } + if (obj->vertab[verndx].hash != req->ventry->hash || + strcmp(obj->vertab[verndx].name, req->ventry->name)) { + /* + * Version does not match. Look if this is a + * global symbol and if it is not hidden. If + * global symbol (verndx < 2) is available, + * use it. Do not return symbol if we are + * called by dlvsym, because dlvsym looks for + * a specific version and default one is not + * what dlvsym wants. + */ + if ((req->flags & SYMLOOK_DLSYM) || + (verndx >= VER_NDX_GIVEN) || + (obj->versyms[symnum] & VER_NDX_HIDDEN)) + return (false); + } + } + result->sym_out = symp; + return (true); +} + +/* + * Search for symbol using SysV hash function. + * obj->buckets is known not to be NULL at this point; the test for this was + * performed with the obj->valid_hash_sysv assignment. + */ +static int +symlook_obj1_sysv(SymLook *req, const Obj_Entry *obj) +{ + unsigned long symnum; + Sym_Match_Result matchres; + + matchres.sym_out = NULL; + matchres.vsymp = NULL; + matchres.vcount = 0; + + for (symnum = obj->buckets[req->hash % obj->nbuckets]; + symnum != STN_UNDEF; symnum = obj->chains[symnum]) { + if (symnum >= obj->nchains) + return (ESRCH); /* Bad object */ + + if (matched_symbol(req, obj, &matchres, symnum)) { + req->sym_out = matchres.sym_out; + req->defobj_out = obj; + return (0); + } + } + if (matchres.vcount == 1) { + req->sym_out = matchres.vsymp; + req->defobj_out = obj; + return (0); + } + return (ESRCH); +} + +/* Search for symbol using GNU hash function */ +static int +symlook_obj1_gnu(SymLook *req, const Obj_Entry *obj) +{ + Elf_Addr bloom_word; + const Elf32_Word *hashval; + Elf32_Word bucket; + Sym_Match_Result matchres; + unsigned int h1, h2; + unsigned long symnum; + + matchres.sym_out = NULL; + matchres.vsymp = NULL; + matchres.vcount = 0; + + /* Pick right bitmask word from Bloom filter array */ + bloom_word = obj->bloom_gnu[(req->hash_gnu / __ELF_WORD_SIZE) & + obj->maskwords_bm_gnu]; + + /* Calculate modulus word size of gnu hash and its derivative */ + h1 = req->hash_gnu & (__ELF_WORD_SIZE - 1); + h2 = ((req->hash_gnu >> obj->shift2_gnu) & (__ELF_WORD_SIZE - 1)); + + /* Filter out the "definitely not in set" queries */ + if (((bloom_word >> h1) & (bloom_word >> h2) & 1) == 0) + return (ESRCH); + + /* Locate hash chain and corresponding value element*/ + bucket = obj->buckets_gnu[req->hash_gnu % obj->nbuckets_gnu]; + if (bucket == 0) + return (ESRCH); + hashval = &obj->chain_zero_gnu[bucket]; + do { + if (((*hashval ^ req->hash_gnu) >> 1) == 0) { + symnum = hashval - obj->chain_zero_gnu; + if (matched_symbol(req, obj, &matchres, symnum)) { + req->sym_out = matchres.sym_out; + req->defobj_out = obj; + return (0); + } + } + } while ((*hashval++ & 1) == 0); + if (matchres.vcount == 1) { + req->sym_out = matchres.vsymp; + req->defobj_out = obj; + return (0); + } + return (ESRCH); } static void @@ -2354,24 +4426,26 @@ char *fmt1, *fmt2, *fmt, *main_local, *list_containers; int c; - if ((main_local = getenv(LD_ "TRACE_LOADED_OBJECTS_PROGNAME")) == NULL) + if ((main_local = getenv(_LD("TRACE_LOADED_OBJECTS_PROGNAME"))) == NULL) main_local = ""; - if ((fmt1 = getenv(LD_ "TRACE_LOADED_OBJECTS_FMT1")) == NULL) + if ((fmt1 = getenv(_LD("TRACE_LOADED_OBJECTS_FMT1"))) == NULL) fmt1 = "\t%o => %p (%x)\n"; - if ((fmt2 = getenv(LD_ "TRACE_LOADED_OBJECTS_FMT2")) == NULL) + if ((fmt2 = getenv(_LD("TRACE_LOADED_OBJECTS_FMT2"))) == NULL) fmt2 = "\t%o (%x)\n"; - list_containers = getenv(LD_ "TRACE_LOADED_OBJECTS_ALL"); + list_containers = getenv(_LD("TRACE_LOADED_OBJECTS_ALL")); - for (; obj; obj = obj->next) { + for (; obj != NULL; obj = TAILQ_NEXT(obj, next)) { Needed_Entry *needed; char *name, *path; bool is_lib; + if (obj->marker) + continue; if (list_containers && obj->needed != NULL) - printf("%s:\n", obj->path); + rtld_printf("%s:\n", obj->path); for (needed = obj->needed; needed; needed = needed->next) { if (needed->obj != NULL) { if (needed->obj->traced && !list_containers) @@ -2388,17 +4462,17 @@ while ((c = *fmt++) != '\0') { switch (c) { default: - putchar(c); + rtld_putchar(c); continue; case '\\': switch (c = *fmt) { case '\0': continue; case 'n': - putchar('\n'); + rtld_putchar('\n'); break; case 't': - putchar('\t'); + rtld_putchar('\t'); break; } break; @@ -2408,30 +4482,31 @@ continue; case '%': default: - putchar(c); + rtld_putchar(c); break; case 'A': - printf("%s", main_local); + rtld_putstr(main_local); break; case 'a': - printf("%s", obj_main->path); + rtld_putstr(obj_main->path); break; case 'o': - printf("%s", name); + rtld_putstr(name); break; #if 0 case 'm': - printf("%d", sodp->sod_major); + rtld_printf("%d", sodp->sod_major); break; case 'n': - printf("%d", sodp->sod_minor); + rtld_printf("%d", sodp->sod_minor); break; #endif case 'p': - printf("%s", path); + rtld_putstr(path); break; case 'x': - printf("%p", needed->obj ? needed->obj->mapbase : 0); + rtld_printf("%p", needed->obj ? needed->obj->mapbase : + 0); break; } break; @@ -2449,33 +4524,46 @@ * reference count of 0. */ static void -unload_object(Obj_Entry *root) +unload_object(Obj_Entry *root, RtldLockState *lockstate) { - Obj_Entry *obj; - Obj_Entry **linkp; + Obj_Entry marker, *obj, *next; - assert(root->refcount == 0); + assert(root->refcount == 0); - /* - * Pass over the DAG removing unreferenced objects from - * appropriate lists. - */ - unlink_object(root); + /* + * Pass over the DAG removing unreferenced objects from + * appropriate lists. + */ + unlink_object(root); - /* Unmap all objects that are no longer referenced. */ - linkp = &obj_list->next; - while ((obj = *linkp) != NULL) { - if (obj->refcount == 0) { - dbg("unloading \"%s\"", obj->path); - munmap(obj->mapbase, obj->mapsize); - linkmap_delete(obj); - *linkp = obj->next; - obj_count--; - obj_free(obj); - } else - linkp = &obj->next; - } - obj_tail = linkp; + /* Unmap all objects that are no longer referenced. */ + for (obj = TAILQ_FIRST(&obj_list); obj != NULL; obj = next) { + next = TAILQ_NEXT(obj, next); + if (obj->marker || obj->refcount != 0) + continue; + LD_UTRACE(UTRACE_UNLOAD_OBJECT, obj, obj->mapbase, + obj->mapsize, 0, obj->path); + dbg("unloading \"%s\"", obj->path); + /* + * Unlink the object now to prevent new references from + * being acquired while the bind lock is dropped in + * recursive dlclose() invocations. + */ + TAILQ_REMOVE(&obj_list, obj, next); + obj_count--; + + if (obj->filtees_loaded) { + if (next != NULL) { + init_marker(&marker); + TAILQ_INSERT_BEFORE(next, &marker, next); + unload_filtees(obj, lockstate); + next = TAILQ_NEXT(&marker, next); + TAILQ_REMOVE(&obj_list, &marker, next); + } else + unload_filtees(obj, lockstate); + } + release_object(obj); + } } static void @@ -2488,7 +4576,7 @@ objlist_remove(&list_global, root); /* Remove the object from all objects' DAG lists. */ - STAILQ_FOREACH(elm, &root->dagmembers , link) { + STAILQ_FOREACH(elm, &root->dagmembers, link) { objlist_remove(&elm->obj->dldags, root); if (elm->obj != root) unlink_object(elm->obj); @@ -2501,7 +4589,8 @@ { Objlist_Entry *elm; - STAILQ_FOREACH(elm, &root->dagmembers , link) + assert(root->dag_inited); + STAILQ_FOREACH(elm, &root->dagmembers, link) elm->obj->refcount++; } @@ -2510,26 +4599,27 @@ { Objlist_Entry *elm; - STAILQ_FOREACH(elm, &root->dagmembers , link) + assert(root->dag_inited); + STAILQ_FOREACH(elm, &root->dagmembers, link) elm->obj->refcount--; } /* * Common code for MD __tls_get_addr(). */ -void * -tls_get_addr_common(Elf_Addr** dtvp, int index, size_t offset) +static void *tls_get_addr_slow(Elf_Addr **, int, size_t) __noinline; +static void * +tls_get_addr_slow(Elf_Addr **dtvp, int index, size_t offset) { - Elf_Addr* dtv = *dtvp; - int lockstate; + Elf_Addr *newdtv, *dtv; + RtldLockState lockstate; + int to_copy; + dtv = *dtvp; /* Check dtv generation in case new modules have arrived */ if (dtv[0] != tls_dtv_generation) { - Elf_Addr* newdtv; - int to_copy; - - lockstate = wlock_acquire(rtld_bind_lock); - newdtv = calloc(1, (tls_max_index + 2) * sizeof(Elf_Addr)); + wlock_acquire(rtld_bind_lock, &lockstate); + newdtv = xcalloc(tls_max_index + 2, sizeof(Elf_Addr)); to_copy = dtv[1]; if (to_copy > tls_max_index) to_copy = tls_max_index; @@ -2537,24 +4627,36 @@ newdtv[0] = tls_dtv_generation; newdtv[1] = tls_max_index; free(dtv); - wlock_release(rtld_bind_lock, lockstate); - *dtvp = newdtv; + lock_release(rtld_bind_lock, &lockstate); + dtv = *dtvp = newdtv; } /* Dynamically allocate module TLS if necessary */ - if (!dtv[index + 1]) { + if (dtv[index + 1] == 0) { /* Signal safe, wlock will block out signals. */ - lockstate = wlock_acquire(rtld_bind_lock); + wlock_acquire(rtld_bind_lock, &lockstate); if (!dtv[index + 1]) dtv[index + 1] = (Elf_Addr)allocate_module_tls(index); - wlock_release(rtld_bind_lock, lockstate); + lock_release(rtld_bind_lock, &lockstate); } - return (void*) (dtv[index + 1] + offset); + return ((void *)(dtv[index + 1] + offset)); } -/* XXX not sure what variants to use for arm. */ +void * +tls_get_addr_common(Elf_Addr **dtvp, int index, size_t offset) +{ + Elf_Addr *dtv; -#if defined(__ia64__) || defined(__alpha__) || defined(__powerpc__) + dtv = *dtvp; + /* Check dtv generation in case new modules have arrived */ + if (__predict_true(dtv[0] == tls_dtv_generation && + dtv[index + 1] != 0)) + return ((void *)(dtv[index + 1] + offset)); + return (tls_get_addr_slow(dtvp, index, offset)); +} + +#if defined(__aarch64__) || defined(__arm__) || defined(__mips__) || \ + defined(__powerpc__) || defined(__riscv__) /* * Allocate Static TLS using the Variant I method. @@ -2573,7 +4675,7 @@ return (oldtcb); assert(tcbsize >= TLS_TCB_SIZE); - tcb = calloc(1, tls_static_space - TLS_TCB_SIZE + tcbsize); + tcb = xcalloc(1, tls_static_space - TLS_TCB_SIZE + tcbsize); tls = (Elf_Addr **)(tcb + tcbsize - TLS_TCB_SIZE); if (oldtcb != NULL) { @@ -2589,19 +4691,20 @@ } } } else { - dtv = calloc(tls_max_index + 2, sizeof(Elf_Addr)); + dtv = xcalloc(tls_max_index + 2, sizeof(Elf_Addr)); tls[0] = dtv; dtv[0] = tls_dtv_generation; dtv[1] = tls_max_index; - for (obj = objs; obj; obj = obj->next) { - if (obj->tlsoffset) { + for (obj = globallist_curr(objs); obj != NULL; + obj = globallist_next(obj)) { + if (obj->tlsoffset > 0) { addr = (Elf_Addr)tls + obj->tlsoffset; - memset((void*) (addr + obj->tlsinitsize), - 0, obj->tlssize - obj->tlsinitsize); - if (obj->tlsinit) - memcpy((void*) addr, obj->tlsinit, - obj->tlsinitsize); + if (obj->tlsinitsize > 0) + memcpy((void*) addr, obj->tlsinit, obj->tlsinitsize); + if (obj->tlssize > obj->tlsinitsize) + memset((void*) (addr + obj->tlsinitsize), 0, + obj->tlssize - obj->tlsinitsize); dtv[obj->tlsindex + 1] = addr; } } @@ -2635,8 +4738,7 @@ #endif -#if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) || \ - defined(__arm__) +#if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) /* * Allocate Static TLS using the Variant II method. @@ -2645,19 +4747,22 @@ allocate_tls(Obj_Entry *objs, void *oldtls, size_t tcbsize, size_t tcbalign) { Obj_Entry *obj; - size_t size; + size_t size, ralign; char *tls; Elf_Addr *dtv, *olddtv; Elf_Addr segbase, oldsegbase, addr; int i; - size = round(tls_static_space, tcbalign); + ralign = tcbalign; + if (tls_static_max_align > ralign) + ralign = tls_static_max_align; + size = round(tls_static_space, ralign) + round(tcbsize, ralign); assert(tcbsize >= 2*sizeof(Elf_Addr)); - tls = malloc(size + tcbsize); - dtv = calloc(1, (tls_max_index + 2) * sizeof(Elf_Addr)); + tls = malloc_aligned(size, ralign); + dtv = xcalloc(tls_max_index + 2, sizeof(Elf_Addr)); - segbase = (Elf_Addr)(tls + size); + segbase = (Elf_Addr)(tls + round(tls_static_space, ralign)); ((Elf_Addr*)segbase)[0] = segbase; ((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv; @@ -2691,15 +4796,15 @@ */ free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr)); } else { - for (obj = objs; obj; obj = obj->next) { - if (obj->tlsoffset) { + for (obj = objs; obj != NULL; obj = TAILQ_NEXT(obj, next)) { + if (obj->marker || obj->tlsoffset == 0) + continue; addr = segbase - obj->tlsoffset; memset((void*) (addr + obj->tlsinitsize), 0, obj->tlssize - obj->tlsinitsize); if (obj->tlsinit) memcpy((void*) addr, obj->tlsinit, obj->tlsinitsize); dtv[obj->tlsindex + 1] = addr; - } } } @@ -2709,8 +4814,8 @@ void free_tls(void *tls, size_t tcbsize, size_t tcbalign) { - size_t size; Elf_Addr* dtv; + size_t size, ralign; int dtvsize, i; Elf_Addr tlsstart, tlsend; @@ -2718,19 +4823,23 @@ * Figure out the size of the initial TLS block so that we can * find stuff which ___tls_get_addr() allocated dynamically. */ - size = round(tls_static_space, tcbalign); + ralign = tcbalign; + if (tls_static_max_align > ralign) + ralign = tls_static_max_align; + size = round(tls_static_space, ralign); dtv = ((Elf_Addr**)tls)[1]; dtvsize = dtv[1]; tlsend = (Elf_Addr) tls; tlsstart = tlsend - size; for (i = 0; i < dtvsize; i++) { - if (dtv[i+2] && (dtv[i+2] < tlsstart || dtv[i+2] > tlsend)) { - free((void*) dtv[i+2]); + if (dtv[i + 2] != 0 && (dtv[i + 2] < tlsstart || dtv[i + 2] > tlsend)) { + free_aligned((void *)dtv[i + 2]); } } - free((void*) tlsstart); + free_aligned((void *)tlsstart); + free((void*) dtv); } #endif @@ -2744,16 +4853,18 @@ Obj_Entry* obj; char* p; - for (obj = obj_list; obj; obj = obj->next) { + TAILQ_FOREACH(obj, &obj_list, next) { + if (obj->marker) + continue; if (obj->tlsindex == index) break; } if (!obj) { _rtld_error("Can't find module with TLS index %d", index); - die(); + rtld_die(); } - p = malloc(obj->tlssize); + p = malloc_aligned(obj->tlssize, obj->tlsalign); memcpy(p, obj->tlsinit, obj->tlsinitsize); memset(p + obj->tlsinitsize, 0, obj->tlssize - obj->tlsinitsize); @@ -2773,7 +4884,7 @@ return true; } - if (obj->tlsindex == 1) + if (tls_last_offset == 0) off = calculate_first_tls_offset(obj->tlssize, obj->tlsalign); else off = calculate_tls_offset(tls_last_offset, tls_last_size, @@ -2785,9 +4896,11 @@ * leave a small amount of space spare to be used for dynamically * loading modules which use static TLS. */ - if (tls_static_space) { + if (tls_static_space != 0) { if (calculate_tls_end(off, obj->tlssize) > tls_static_space) return false; + } else if (obj->tlsalign > tls_static_max_align) { + tls_static_max_align = obj->tlsalign; } tls_last_offset = obj->tlsoffset = off; @@ -2800,41 +4913,595 @@ void free_tls_offset(Obj_Entry *obj) { -#if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) || \ - defined(__arm__) + /* * If we were the last thing to allocate out of the static TLS * block, we give our space back to the 'allocator'. This is a * simplistic workaround to allow libGL.so.1 to be loaded and - * unloaded multiple times. We only handle the Variant II - * mechanism for now - this really needs a proper allocator. + * unloaded multiple times. */ if (calculate_tls_end(obj->tlsoffset, obj->tlssize) == calculate_tls_end(tls_last_offset, tls_last_size)) { tls_last_offset -= obj->tlssize; tls_last_size = 0; } -#endif } void * _rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign) { void *ret; - int lockstate; + RtldLockState lockstate; - lockstate = wlock_acquire(rtld_bind_lock); - ret = allocate_tls(obj_list, oldtls, tcbsize, tcbalign); - wlock_release(rtld_bind_lock, lockstate); + wlock_acquire(rtld_bind_lock, &lockstate); + ret = allocate_tls(globallist_curr(TAILQ_FIRST(&obj_list)), oldtls, + tcbsize, tcbalign); + lock_release(rtld_bind_lock, &lockstate); return (ret); } void _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign) { - int lockstate; + RtldLockState lockstate; - lockstate = wlock_acquire(rtld_bind_lock); + wlock_acquire(rtld_bind_lock, &lockstate); free_tls(tcb, tcbsize, tcbalign); - wlock_release(rtld_bind_lock, lockstate); + lock_release(rtld_bind_lock, &lockstate); +} + +static void +object_add_name(Obj_Entry *obj, const char *name) +{ + Name_Entry *entry; + size_t len; + + len = strlen(name); + entry = malloc(sizeof(Name_Entry) + len); + + if (entry != NULL) { + strcpy(entry->name, name); + STAILQ_INSERT_TAIL(&obj->names, entry, link); + } +} + +static int +object_match_name(const Obj_Entry *obj, const char *name) +{ + Name_Entry *entry; + + STAILQ_FOREACH(entry, &obj->names, link) { + if (strcmp(name, entry->name) == 0) + return (1); + } + return (0); +} + +static Obj_Entry * +locate_dependency(const Obj_Entry *obj, const char *name) +{ + const Objlist_Entry *entry; + const Needed_Entry *needed; + + STAILQ_FOREACH(entry, &list_main, link) { + if (object_match_name(entry->obj, name)) + return entry->obj; + } + + for (needed = obj->needed; needed != NULL; needed = needed->next) { + if (strcmp(obj->strtab + needed->name, name) == 0 || + (needed->obj != NULL && object_match_name(needed->obj, name))) { + /* + * If there is DT_NEEDED for the name we are looking for, + * we are all set. Note that object might not be found if + * dependency was not loaded yet, so the function can + * return NULL here. This is expected and handled + * properly by the caller. + */ + return (needed->obj); + } + } + _rtld_error("%s: Unexpected inconsistency: dependency %s not found", + obj->path, name); + rtld_die(); +} + +static int +check_object_provided_version(Obj_Entry *refobj, const Obj_Entry *depobj, + const Elf_Vernaux *vna) +{ + const Elf_Verdef *vd; + const char *vername; + + vername = refobj->strtab + vna->vna_name; + vd = depobj->verdef; + if (vd == NULL) { + _rtld_error("%s: version %s required by %s not defined", + depobj->path, vername, refobj->path); + return (-1); + } + for (;;) { + if (vd->vd_version != VER_DEF_CURRENT) { + _rtld_error("%s: Unsupported version %d of Elf_Verdef entry", + depobj->path, vd->vd_version); + return (-1); + } + if (vna->vna_hash == vd->vd_hash) { + const Elf_Verdaux *aux = (const Elf_Verdaux *) + ((char *)vd + vd->vd_aux); + if (strcmp(vername, depobj->strtab + aux->vda_name) == 0) + return (0); + } + if (vd->vd_next == 0) + break; + vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next); + } + if (vna->vna_flags & VER_FLG_WEAK) + return (0); + _rtld_error("%s: version %s required by %s not found", + depobj->path, vername, refobj->path); + return (-1); +} + +static int +rtld_verify_object_versions(Obj_Entry *obj) +{ + const Elf_Verneed *vn; + const Elf_Verdef *vd; + const Elf_Verdaux *vda; + const Elf_Vernaux *vna; + const Obj_Entry *depobj; + int maxvernum, vernum; + + if (obj->ver_checked) + return (0); + obj->ver_checked = true; + + maxvernum = 0; + /* + * Walk over defined and required version records and figure out + * max index used by any of them. Do very basic sanity checking + * while there. + */ + vn = obj->verneed; + while (vn != NULL) { + if (vn->vn_version != VER_NEED_CURRENT) { + _rtld_error("%s: Unsupported version %d of Elf_Verneed entry", + obj->path, vn->vn_version); + return (-1); + } + vna = (const Elf_Vernaux *) ((char *)vn + vn->vn_aux); + for (;;) { + vernum = VER_NEED_IDX(vna->vna_other); + if (vernum > maxvernum) + maxvernum = vernum; + if (vna->vna_next == 0) + break; + vna = (const Elf_Vernaux *) ((char *)vna + vna->vna_next); + } + if (vn->vn_next == 0) + break; + vn = (const Elf_Verneed *) ((char *)vn + vn->vn_next); + } + + vd = obj->verdef; + while (vd != NULL) { + if (vd->vd_version != VER_DEF_CURRENT) { + _rtld_error("%s: Unsupported version %d of Elf_Verdef entry", + obj->path, vd->vd_version); + return (-1); + } + vernum = VER_DEF_IDX(vd->vd_ndx); + if (vernum > maxvernum) + maxvernum = vernum; + if (vd->vd_next == 0) + break; + vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next); + } + + if (maxvernum == 0) + return (0); + + /* + * Store version information in array indexable by version index. + * Verify that object version requirements are satisfied along the + * way. + */ + obj->vernum = maxvernum + 1; + obj->vertab = xcalloc(obj->vernum, sizeof(Ver_Entry)); + + vd = obj->verdef; + while (vd != NULL) { + if ((vd->vd_flags & VER_FLG_BASE) == 0) { + vernum = VER_DEF_IDX(vd->vd_ndx); + assert(vernum <= maxvernum); + vda = (const Elf_Verdaux *)((char *)vd + vd->vd_aux); + obj->vertab[vernum].hash = vd->vd_hash; + obj->vertab[vernum].name = obj->strtab + vda->vda_name; + obj->vertab[vernum].file = NULL; + obj->vertab[vernum].flags = 0; + } + if (vd->vd_next == 0) + break; + vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next); + } + + vn = obj->verneed; + while (vn != NULL) { + depobj = locate_dependency(obj, obj->strtab + vn->vn_file); + if (depobj == NULL) + return (-1); + vna = (const Elf_Vernaux *) ((char *)vn + vn->vn_aux); + for (;;) { + if (check_object_provided_version(obj, depobj, vna)) + return (-1); + vernum = VER_NEED_IDX(vna->vna_other); + assert(vernum <= maxvernum); + obj->vertab[vernum].hash = vna->vna_hash; + obj->vertab[vernum].name = obj->strtab + vna->vna_name; + obj->vertab[vernum].file = obj->strtab + vn->vn_file; + obj->vertab[vernum].flags = (vna->vna_other & VER_NEED_HIDDEN) ? + VER_INFO_HIDDEN : 0; + if (vna->vna_next == 0) + break; + vna = (const Elf_Vernaux *) ((char *)vna + vna->vna_next); + } + if (vn->vn_next == 0) + break; + vn = (const Elf_Verneed *) ((char *)vn + vn->vn_next); + } + return 0; +} + +static int +rtld_verify_versions(const Objlist *objlist) +{ + Objlist_Entry *entry; + int rc; + + rc = 0; + STAILQ_FOREACH(entry, objlist, link) { + /* + * Skip dummy objects or objects that have their version requirements + * already checked. + */ + if (entry->obj->strtab == NULL || entry->obj->vertab != NULL) + continue; + if (rtld_verify_object_versions(entry->obj) == -1) { + rc = -1; + if (ld_tracing == NULL) + break; + } + } + if (rc == 0 || ld_tracing != NULL) + rc = rtld_verify_object_versions(&obj_rtld); + return rc; +} + +const Ver_Entry * +fetch_ventry(const Obj_Entry *obj, unsigned long symnum) +{ + Elf_Versym vernum; + + if (obj->vertab) { + vernum = VER_NDX(obj->versyms[symnum]); + if (vernum >= obj->vernum) { + _rtld_error("%s: symbol %s has wrong verneed value %d", + obj->path, obj->strtab + symnum, vernum); + } else if (obj->vertab[vernum].hash != 0) { + return &obj->vertab[vernum]; + } + } + return NULL; +} + +int +_rtld_get_stack_prot(void) +{ + + return (stack_prot); +} + +int +_rtld_is_dlopened(void *arg) +{ + Obj_Entry *obj; + RtldLockState lockstate; + int res; + + rlock_acquire(rtld_bind_lock, &lockstate); + obj = dlcheck(arg); + if (obj == NULL) + obj = obj_from_addr(arg); + if (obj == NULL) { + _rtld_error("No shared object contains address"); + lock_release(rtld_bind_lock, &lockstate); + return (-1); + } + res = obj->dlopened ? 1 : 0; + lock_release(rtld_bind_lock, &lockstate); + return (res); +} + +int +obj_enforce_relro(Obj_Entry *obj) +{ + + if (obj->relro_size > 0 && mprotect(obj->relro_page, obj->relro_size, + PROT_READ) == -1) { + _rtld_error("%s: Cannot enforce relro protection: %s", + obj->path, rtld_strerror(errno)); + return (-1); + } + return (0); +} + +static void +map_stacks_exec(RtldLockState *lockstate) +{ + void (*thr_map_stacks_exec)(void); + + if ((max_stack_flags & PF_X) == 0 || (stack_prot & PROT_EXEC) != 0) + return; + thr_map_stacks_exec = (void (*)(void))(uintptr_t) + get_program_var_addr("__pthread_map_stacks_exec", lockstate); + if (thr_map_stacks_exec != NULL) { + stack_prot |= PROT_EXEC; + thr_map_stacks_exec(); + } +} + +void +symlook_init(SymLook *dst, const char *name) +{ + + bzero(dst, sizeof(*dst)); + dst->name = name; + dst->hash = elf_hash(name); + dst->hash_gnu = gnu_hash(name); +} + +static void +symlook_init_from_req(SymLook *dst, const SymLook *src) +{ + + dst->name = src->name; + dst->hash = src->hash; + dst->hash_gnu = src->hash_gnu; + dst->ventry = src->ventry; + dst->flags = src->flags; + dst->defobj_out = NULL; + dst->sym_out = NULL; + dst->lockstate = src->lockstate; +} + +static int +open_binary_fd(const char *argv0, bool search_in_path) +{ + char *pathenv, *pe, binpath[PATH_MAX]; + int fd; + + if (search_in_path && strchr(argv0, '/') == NULL) { + pathenv = getenv("PATH"); + if (pathenv == NULL) { + rtld_printf("-p and no PATH environment variable\n"); + rtld_die(); + } + pathenv = strdup(pathenv); + if (pathenv == NULL) { + rtld_printf("Cannot allocate memory\n"); + rtld_die(); + } + fd = -1; + errno = ENOENT; + while ((pe = strsep(&pathenv, ":")) != NULL) { + if (strlcpy(binpath, pe, sizeof(binpath)) >= + sizeof(binpath)) + continue; + if (binpath[0] != '\0' && + strlcat(binpath, "/", sizeof(binpath)) >= + sizeof(binpath)) + continue; + if (strlcat(binpath, argv0, sizeof(binpath)) >= + sizeof(binpath)) + continue; + fd = open(binpath, O_RDONLY | O_CLOEXEC | O_VERIFY); + if (fd != -1 || errno != ENOENT) + break; + } + free(pathenv); + } else { + fd = open(argv0, O_RDONLY | O_CLOEXEC | O_VERIFY); + } + + if (fd == -1) { + rtld_printf("Opening %s: %s\n", argv0, + rtld_strerror(errno)); + rtld_die(); + } + return (fd); +} + +/* + * Parse a set of command-line arguments. + */ +static int +parse_args(char* argv[], int argc, bool *use_pathp, int *fdp) +{ + const char *arg; + int fd, i, j, arglen; + char opt; + + dbg("Parsing command-line arguments"); + *use_pathp = false; + *fdp = -1; + + for (i = 1; i < argc; i++ ) { + arg = argv[i]; + dbg("argv[%d]: '%s'", i, arg); + + /* + * rtld arguments end with an explicit "--" or with the first + * non-prefixed argument. + */ + if (strcmp(arg, "--") == 0) { + i++; + break; + } + if (arg[0] != '-') + break; + + /* + * All other arguments are single-character options that can + * be combined, so we need to search through `arg` for them. + */ + arglen = strlen(arg); + for (j = 1; j < arglen; j++) { + opt = arg[j]; + if (opt == 'h') { + print_usage(argv[0]); + rtld_die(); + } else if (opt == 'f') { + /* + * -f XX can be used to specify a descriptor for the + * binary named at the command line (i.e., the later + * argument will specify the process name but the + * descriptor is what will actually be executed) + */ + if (j != arglen - 1) { + /* -f must be the last option in, e.g., -abcf */ + _rtld_error("invalid options: %s", arg); + rtld_die(); + } + i++; + fd = parse_integer(argv[i]); + if (fd == -1) { + _rtld_error("invalid file descriptor: '%s'", + argv[i]); + rtld_die(); + } + *fdp = fd; + break; + } else if (opt == 'p') { + *use_pathp = true; + } else { + rtld_printf("invalid argument: '%s'\n", arg); + print_usage(argv[0]); + rtld_die(); + } + } + } + + return (i); +} + +/* + * Parse a file descriptor number without pulling in more of libc (e.g. atoi). + */ +static int +parse_integer(const char *str) +{ + static const int RADIX = 10; /* XXXJA: possibly support hex? */ + const char *orig; + int n; + char c; + + orig = str; + n = 0; + for (c = *str; c != '\0'; c = *++str) { + if (c < '0' || c > '9') + return (-1); + + n *= RADIX; + n += c - '0'; + } + + /* Make sure we actually parsed something. */ + if (str == orig) + return (-1); + return (n); +} + +static void +print_usage(const char *argv0) +{ + + rtld_printf("Usage: %s [-h] [-f ] [--] []\n" + "\n" + "Options:\n" + " -h Display this help message\n" + " -p Search in PATH for named binary\n" + " -f Execute instead of searching for \n" + " -- End of RTLD options\n" + " Name of process to execute\n" + " Arguments to the executed process\n", argv0); +} + +/* + * Overrides for libc_pic-provided functions. + */ + +int +__getosreldate(void) +{ + size_t len; + int oid[2]; + int error, osrel; + + if (osreldate != 0) + return (osreldate); + + oid[0] = CTL_KERN; + oid[1] = KERN_OSRELDATE; + osrel = 0; + len = sizeof(osrel); + error = sysctl(oid, 2, &osrel, &len, NULL, 0); + if (error == 0 && osrel > 0 && len == sizeof(osrel)) + osreldate = osrel; + return (osreldate); +} + +void +exit(int status) +{ + + _exit(status); +} + +void (*__cleanup)(void); +int __isthreaded = 0; +int _thread_autoinit_dummy_decl = 1; + +/* + * No unresolved symbols for rtld. + */ +void +__pthread_cxa_finalize(struct dl_phdr_info *a) +{ +} + +void +__stack_chk_fail(void) +{ + + _rtld_error("stack overflow detected; terminated"); + rtld_die(); +} +__weak_reference(__stack_chk_fail, __stack_chk_fail_local); + +void +__chk_fail(void) +{ + + _rtld_error("buffer overflow detected; terminated"); + rtld_die(); +} + +const char * +rtld_strerror(int errnum) +{ + + if (errnum < 0 || errnum >= sys_nerr) + return ("Unknown error"); + return (sys_errlist[errnum]); } diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 890c5e5..858342b 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: releng/10.2/libexec/rtld-elf/rtld.h 282118 2015-04-28 01:15:17Z emaste $ + * $FreeBSD: releng/11.1/libexec/rtld-elf/rtld.h 316135 2017-03-29 11:03:08Z kib $ */ #ifndef RTLD_H /* { */ @@ -41,22 +41,6 @@ #include "rtld_lock.h" #include "rtld_machdep.h" -#ifdef COMPAT_32BIT -#undef STANDARD_LIBRARY_PATH -#undef _PATH_ELF_HINTS -#define _PATH_ELF_HINTS "/var/run/ld-elf32.so.hints" -/* For running 32 bit binaries */ -#define STANDARD_LIBRARY_PATH "/lib32:/usr/lib32" -#define LD_ "LD_32_" -#endif - -#ifndef STANDARD_LIBRARY_PATH -#define STANDARD_LIBRARY_PATH "/lib:/usr/lib" -#endif -#ifndef LD_ -#define LD_ "LD_" -#endif - #define NEW(type) ((type *) xmalloc(sizeof(type))) #define CNEW(type) ((type *) xcalloc(1, sizeof(type))) @@ -155,10 +139,11 @@ Elf_Size magic; /* Magic number (sanity check) */ Elf_Size version; /* Version number of struct format */ - struct Struct_Obj_Entry *next; + TAILQ_ENTRY(Struct_Obj_Entry) next; char *path; /* Pathname of underlying file (%) */ char *origin_path; /* Directory path of origin file */ - int refcount; + int refcount; /* DAG references */ + int holdcount; /* Count of transient references */ int dl_refcount; /* Number of times loaded by dlopen */ /* These items are computed by map_object() or by digest_phdr(). */ @@ -203,6 +188,9 @@ Elf_Word symtabno; /* Number of dynamic symbols */ Elf_Word gotsym; /* First dynamic symbol in GOT */ #endif +#ifdef __powerpc64__ + Elf_Addr glink; /* GLINK PLT call stub section */ +#endif const Elf_Verneed *verneed; /* Required versions. */ Elf_Word verneednum; /* Number of entries in verneed table */ @@ -277,6 +265,9 @@ bool valid_hash_sysv : 1; /* A valid System V hash hash tag is available */ bool valid_hash_gnu : 1; /* A valid GNU hash tag is available */ bool dlopened : 1; /* dlopen()-ed (vs. load statically) */ + bool marker : 1; /* marker on the global obj list */ + bool unholdfree : 1; /* unmap upon last unhold */ + bool doomed : 1; /* Object cannot be referenced */ struct link_map linkmap; /* For GDB and dlinfo() */ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */ @@ -289,6 +280,8 @@ #define RTLD_MAGIC 0xd550b87a #define RTLD_VERSION 1 +TAILQ_HEAD(obj_entry_q, Struct_Obj_Entry); + #define RTLD_STATIC_TLS_EXTRA 128 /* Flags to be passed into symlook_ family of functions. */ @@ -365,6 +358,7 @@ void free_aligned(void *ptr); extern Elf_Addr _GLOBAL_OFFSET_TABLE_[]; extern Elf_Sym sym_zero; /* For resolving undefined weak refs. */ +extern bool ld_bind_not; void dump_relocations(Obj_Entry *); void dump_obj_relocations(Obj_Entry *); @@ -377,9 +371,12 @@ unsigned long elf_hash(const char *); const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *, const Obj_Entry **, int, SymCache *, struct Struct_RtldLockState *); +void ifunc_init(Elf_Auxinfo[__min_size(AT_COUNT)]); void init_pltgot(Obj_Entry *); void lockdflt_init(void); void digest_notes(Obj_Entry *, Elf_Addr, Elf_Addr); +Obj_Entry *globallist_curr(const Obj_Entry *obj); +Obj_Entry *globallist_next(const Obj_Entry *obj); void obj_free(Obj_Entry *); Obj_Entry *obj_new(void); void _rtld_bind_start(void); @@ -393,6 +390,7 @@ bool allocate_tls_offset(Obj_Entry *obj); void free_tls_offset(Obj_Entry *obj); const Ver_Entry *fetch_ventry(const Obj_Entry *obj, unsigned long); +int convert_prot(int elfflags); /* * MD function declarations. diff --git a/libexec/rtld-elf/rtld_lock.c b/libexec/rtld-elf/rtld_lock.c index 0e4babf..336b560 100644 --- a/libexec/rtld-elf/rtld_lock.c +++ b/libexec/rtld-elf/rtld_lock.c @@ -23,7 +23,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * from: FreeBSD: src/libexec/rtld-elf/sparc64/lockdflt.c,v 1.3 2002/10/09 - * $FreeBSD: releng/10.2/libexec/rtld-elf/rtld_lock.c 281453 2015-04-12 06:45:40Z kib $ + * $FreeBSD: releng/11.1/libexec/rtld-elf/rtld_lock.c 312693 2017-01-24 11:13:41Z kib $ */ /* @@ -38,8 +38,8 @@ * In this algorithm the lock is a single word. Its low-order bit is * set when a writer holds the lock. The remaining high-order bits * contain a count of readers desiring the lock. The algorithm requires - * atomic "compare_and_store" and "add" operations, which we implement - * using assembly language sequences in "rtld_start.S". + * atomic "compare_and_store" and "add" operations, which we take + * from machine/atomic.h. */ #include @@ -64,10 +64,10 @@ } Lock; static sigset_t fullsigmask, oldsigmask; -static int thread_flag; +static int thread_flag, wnested; static void * -def_lock_create() +def_lock_create(void) { void *base; char *p; @@ -117,29 +117,34 @@ static void def_wlock_acquire(void *lock) { - Lock *l = (Lock *)lock; - sigset_t tmp_oldsigmask; + Lock *l; + sigset_t tmp_oldsigmask; - for ( ; ; ) { - sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask); - if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG)) - break; - sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL); - } - oldsigmask = tmp_oldsigmask; + l = (Lock *)lock; + for (;;) { + sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask); + if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG)) + break; + sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL); + } + if (atomic_fetchadd_int(&wnested, 1) == 0) + oldsigmask = tmp_oldsigmask; } static void def_lock_release(void *lock) { - Lock *l = (Lock *)lock; + Lock *l; - if ((l->lock & WAFLAG) == 0) - atomic_add_rel_int(&l->lock, -RC_INCR); - else { - atomic_add_rel_int(&l->lock, -WAFLAG); - sigprocmask(SIG_SETMASK, &oldsigmask, NULL); - } + l = (Lock *)lock; + if ((l->lock & WAFLAG) == 0) + atomic_add_rel_int(&l->lock, -RC_INCR); + else { + assert(wnested > 0); + atomic_add_rel_int(&l->lock, -WAFLAG); + if (atomic_fetchadd_int(&wnested, -1) == 1) + sigprocmask(SIG_SETMASK, &oldsigmask, NULL); + } } static int @@ -269,7 +274,7 @@ } void -lockdflt_init() +lockdflt_init(void) { int i; @@ -373,12 +378,12 @@ return; /* - * Warning: this does not work with the rtld compat locks - * above, since the thread signal mask is corrupted (set to - * all signals blocked) if two locks are taken in write mode. - * The caller of the _rtld_atfork_pre() must provide the - * working implementation of the locks, and libthr locks are - * fine. + * Warning: this did not worked well with the rtld compat + * locks above, when the thread signal mask was corrupted (set + * to all signals blocked) if two locks were taken + * simultaneously in the write mode. The caller of the + * _rtld_atfork_pre() must provide the working implementation + * of the locks anyway, and libthr locks are fine. */ wlock_acquire(rtld_phdr_lock, &ls[0]); wlock_acquire(rtld_bind_lock, &ls[1]); diff --git a/libexec/rtld-elf/rtld_lock.h b/libexec/rtld-elf/rtld_lock.h index f30c63f..29a57fb 100644 --- a/libexec/rtld-elf/rtld_lock.h +++ b/libexec/rtld-elf/rtld_lock.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: releng/10.2/libexec/rtld-elf/rtld_lock.h 281453 2015-04-12 06:45:40Z kib $ + * $FreeBSD: releng/11.1/libexec/rtld-elf/rtld_lock.h 280816 2015-03-29 18:53:21Z kib $ */ #ifndef _RTLD_LOCK_H_ diff --git a/libexec/rtld-elf/rtld_machdep.h b/libexec/rtld-elf/rtld_machdep.h deleted file mode 100644 index 3b2ccb9..0000000 --- a/libexec/rtld-elf/rtld_machdep.h +++ /dev/null @@ -1,83 +0,0 @@ -/*- - * Copyright (c) 1999, 2000 John D. Polstra. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - * - * $FreeBSD: releng/10.2/libexec/rtld-elf/i386/rtld_machdep.h 281453 2015-04-12 06:45:40Z kib $ - */ - -#ifndef RTLD_MACHDEP_H -#define RTLD_MACHDEP_H 1 - -#include -#include - -struct Struct_Obj_Entry; - -/* Return the address of the .dynamic section in the dynamic linker. */ -#define rtld_dynamic(obj) \ - ((const Elf_Dyn *)((obj)->relocbase + (Elf_Addr)&_DYNAMIC)) - -/* Fixup the jump slot at "where" to transfer control to "target". */ -static inline Elf_Addr -reloc_jmpslot(Elf_Addr *where, Elf_Addr target, - const struct Struct_Obj_Entry *obj, - const struct Struct_Obj_Entry *refobj, const Elf_Rel *rel) -{ -#ifdef dbg - dbg("reloc_jmpslot: *%p = %p", (void *)(where), - (void *)(target)); -#endif - (*(Elf_Addr *)(where) = (Elf_Addr)(target)); - return target; -} - -#define make_function_pointer(def, defobj) \ - ((defobj)->relocbase + (def)->st_value) - -#define call_initfini_pointer(obj, target) \ - (((InitFunc)(target))()) - -#define call_init_pointer(obj, target) \ - (((InitArrFunc)(target))(main_argc, main_argv, environ)) - -#define round(size, align) \ - (((size) + (align) - 1) & ~((align) - 1)) -#define calculate_first_tls_offset(size, align) \ - round(size, align) -#define calculate_tls_offset(prev_offset, prev_size, size, align) \ - round((prev_offset) + (size), align) -#define calculate_tls_end(off, size) (off) - -typedef struct { - unsigned long ti_module; - unsigned long ti_offset; -} tls_index; - -void *___tls_get_addr(tls_index *ti) __attribute__((__regparm__(1))) __exported; -void *__tls_get_addr(tls_index *ti) __exported; - -#define RTLD_DEFAULT_STACK_PF_EXEC PF_X -#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC - -#endif diff --git a/libexec/rtld-elf/rtld_printf.c b/libexec/rtld-elf/rtld_printf.c new file mode 100644 index 0000000..4c3f705 --- /dev/null +++ b/libexec/rtld-elf/rtld_printf.c @@ -0,0 +1,501 @@ +/*- + * Copyright (c) 1986, 1988, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * Copyright (c) 2011 Konstantin Belousov + * + * 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. + * 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/rtld_printf.c 267678 2014-06-20 17:08:32Z jonathan $ + */ + +#include +#include +#include +#include +#include +#include +#include "rtld_printf.h" + +#define MAXNBUF (sizeof(intmax_t) * NBBY + 1) + +#define PRINT_METHOD_SNPRINTF 1 +#define PRINT_METHOD_WRITE 2 + +struct snprintf_arg { + int method; + char *str; + char *buf; + size_t remain; + size_t buf_total; + int fd; +}; + +static void +printf_out(struct snprintf_arg *info) +{ + + if (info->remain == info->buf_total) + return; + write(info->fd, info->buf, info->buf_total - info->remain); + info->str = info->buf; + info->remain = info->buf_total; +} + +static void +snprintf_func(int ch, struct snprintf_arg *const info) +{ + + switch (info->method) { + case PRINT_METHOD_SNPRINTF: + if (info->remain >= 2) { + *info->str++ = ch; + info->remain--; + } + break; + case PRINT_METHOD_WRITE: + if (info->remain > 0) { + *info->str++ = ch; + info->remain--; + } else + printf_out(info); + break; + } +} + +static char const hex2ascii_lower[] = "0123456789abcdefghijklmnopqrstuvwxyz"; +static char const hex2ascii_upper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#define hex2ascii(hex) (hex2ascii_lower[hex]) +#define hex2ascii_upper(hex) (hex2ascii_upper[hex]) + +static __inline int +imax(int a, int b) +{ + + return (a > b ? a : b); +} + +static char * +ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) +{ + char *p, c; + + p = nbuf; + *p = '\0'; + do { + c = upper ? hex2ascii_upper(num % base) : + hex2ascii(num % base); + *++p = c; + } while (num /= base); + if (lenp) + *lenp = p - nbuf; + return (p); +} + +static int +kvprintf(char const *fmt, struct snprintf_arg *arg, int radix, va_list ap) +{ +#define PCHAR(c) snprintf_func((c), arg) + char nbuf[MAXNBUF]; + const char *p, *percent, *q; + u_char *up; + int ch, n; + uintmax_t num; + int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; + int cflag, hflag, jflag, tflag, zflag; + int dwidth, upper; + char padc; + int stop = 0, retval = 0; + + num = 0; + + if (fmt == NULL) + fmt = "(fmt null)\n"; + + if (radix < 2 || radix > 36) + radix = 10; + + for (;;) { + padc = ' '; + width = 0; + while ((ch = (u_char)*fmt++) != '%' || stop) { + if (ch == '\0') + return (retval); + PCHAR(ch); + } + percent = fmt - 1; + qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; + sign = 0; dot = 0; dwidth = 0; upper = 0; + cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; +reswitch: switch (ch = (u_char)*fmt++) { + case '.': + dot = 1; + goto reswitch; + case '#': + sharpflag = 1; + goto reswitch; + case '+': + sign = 1; + goto reswitch; + case '-': + ladjust = 1; + goto reswitch; + case '%': + PCHAR(ch); + break; + case '*': + if (!dot) { + width = va_arg(ap, int); + if (width < 0) { + ladjust = !ladjust; + width = -width; + } + } else { + dwidth = va_arg(ap, int); + } + goto reswitch; + case '0': + if (!dot) { + padc = '0'; + goto reswitch; + } + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + for (n = 0;; ++fmt) { + n = n * 10 + ch - '0'; + ch = *fmt; + if (ch < '0' || ch > '9') + break; + } + if (dot) + dwidth = n; + else + width = n; + goto reswitch; + case 'b': + num = (u_int)va_arg(ap, int); + p = va_arg(ap, char *); + for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;) + PCHAR(*q--); + + if (num == 0) + break; + + for (tmp = 0; *p;) { + n = *p++; + if (num & (1 << (n - 1))) { + PCHAR(tmp ? ',' : '<'); + for (; (n = *p) > ' '; ++p) + PCHAR(n); + tmp = 1; + } else + for (; *p > ' '; ++p) + continue; + } + if (tmp) + PCHAR('>'); + break; + case 'c': + PCHAR(va_arg(ap, int)); + break; + case 'D': + up = va_arg(ap, u_char *); + p = va_arg(ap, char *); + if (!width) + width = 16; + while(width--) { + PCHAR(hex2ascii(*up >> 4)); + PCHAR(hex2ascii(*up & 0x0f)); + up++; + if (width) + for (q=p;*q;q++) + PCHAR(*q); + } + break; + case 'd': + case 'i': + base = 10; + sign = 1; + goto handle_sign; + case 'h': + if (hflag) { + hflag = 0; + cflag = 1; + } else + hflag = 1; + goto reswitch; + case 'j': + jflag = 1; + goto reswitch; + case 'l': + if (lflag) { + lflag = 0; + qflag = 1; + } else + lflag = 1; + goto reswitch; + case 'n': + if (jflag) + *(va_arg(ap, intmax_t *)) = retval; + else if (qflag) + *(va_arg(ap, quad_t *)) = retval; + else if (lflag) + *(va_arg(ap, long *)) = retval; + else if (zflag) + *(va_arg(ap, size_t *)) = retval; + else if (hflag) + *(va_arg(ap, short *)) = retval; + else if (cflag) + *(va_arg(ap, char *)) = retval; + else + *(va_arg(ap, int *)) = retval; + break; + case 'o': + base = 8; + goto handle_nosign; + case 'p': + base = 16; + sharpflag = (width == 0); + sign = 0; + num = (uintptr_t)va_arg(ap, void *); + goto number; + case 'q': + qflag = 1; + goto reswitch; + case 'r': + base = radix; + if (sign) + goto handle_sign; + goto handle_nosign; + case 's': + p = va_arg(ap, char *); + if (p == NULL) + p = "(null)"; + if (!dot) + n = strlen (p); + else + for (n = 0; n < dwidth && p[n]; n++) + continue; + + width -= n; + + if (!ladjust && width > 0) + while (width--) + PCHAR(padc); + while (n--) + PCHAR(*p++); + if (ladjust && width > 0) + while (width--) + PCHAR(padc); + break; + case 't': + tflag = 1; + goto reswitch; + case 'u': + base = 10; + goto handle_nosign; + case 'X': + upper = 1; + case 'x': + base = 16; + goto handle_nosign; + case 'y': + base = 16; + sign = 1; + goto handle_sign; + case 'z': + zflag = 1; + goto reswitch; +handle_nosign: + sign = 0; + if (jflag) + num = va_arg(ap, uintmax_t); + else if (qflag) + num = va_arg(ap, u_quad_t); + else if (tflag) + num = va_arg(ap, ptrdiff_t); + else if (lflag) + num = va_arg(ap, u_long); + else if (zflag) + num = va_arg(ap, size_t); + else if (hflag) + num = (u_short)va_arg(ap, int); + else if (cflag) + num = (u_char)va_arg(ap, int); + else + num = va_arg(ap, u_int); + goto number; +handle_sign: + if (jflag) + num = va_arg(ap, intmax_t); + else if (qflag) + num = va_arg(ap, quad_t); + else if (tflag) + num = va_arg(ap, ptrdiff_t); + else if (lflag) + num = va_arg(ap, long); + else if (zflag) + num = va_arg(ap, ssize_t); + else if (hflag) + num = (short)va_arg(ap, int); + else if (cflag) + num = (char)va_arg(ap, int); + else + num = va_arg(ap, int); +number: + if (sign && (intmax_t)num < 0) { + neg = 1; + num = -(intmax_t)num; + } + p = ksprintn(nbuf, num, base, &n, upper); + tmp = 0; + if (sharpflag && num != 0) { + if (base == 8) + tmp++; + else if (base == 16) + tmp += 2; + } + if (neg) + tmp++; + + if (!ladjust && padc == '0') + dwidth = width - tmp; + width -= tmp + imax(dwidth, n); + dwidth -= n; + if (!ladjust) + while (width-- > 0) + PCHAR(' '); + if (neg) + PCHAR('-'); + if (sharpflag && num != 0) { + if (base == 8) { + PCHAR('0'); + } else if (base == 16) { + PCHAR('0'); + PCHAR('x'); + } + } + while (dwidth-- > 0) + PCHAR('0'); + + while (*p) + PCHAR(*p--); + + if (ladjust) + while (width-- > 0) + PCHAR(' '); + + break; + default: + while (percent < fmt) + PCHAR(*percent++); + /* + * Since we ignore an formatting argument it is no + * longer safe to obey the remaining formatting + * arguments as the arguments will no longer match + * the format specs. + */ + stop = 1; + break; + } + } +#undef PCHAR +} + +int +rtld_snprintf(char *buf, size_t bufsize, const char *fmt, ...) +{ + va_list ap; + int retval; + + va_start(ap, fmt); + retval = rtld_vsnprintf(buf, bufsize, fmt, ap); + va_end(ap); + return (retval); +} + +int +rtld_vsnprintf(char *buf, size_t bufsize, const char *fmt, va_list ap) +{ + struct snprintf_arg info; + int retval; + + info.method = PRINT_METHOD_SNPRINTF; + info.buf = info.str = buf; + info.buf_total = info.remain = bufsize; + info.fd = -1; + retval = kvprintf(fmt, &info, 10, ap); + if (info.remain >= 1) + *info.str++ = '\0'; + return (retval); +} + +int +rtld_vfdprintf(int fd, const char *fmt, va_list ap) +{ + char buf[512]; + struct snprintf_arg info; + int retval; + + info.method = PRINT_METHOD_WRITE; + info.buf = info.str = buf; + info.buf_total = info.remain = sizeof(buf); + info.fd = fd; + retval = kvprintf(fmt, &info, 10, ap); + printf_out(&info); + return (retval); +} + +int +rtld_fdprintf(int fd, const char *fmt, ...) +{ + va_list ap; + int retval; + + va_start(ap, fmt); + retval = rtld_vfdprintf(fd, fmt, ap); + va_end(ap); + return (retval); +} + +void +rtld_fdputstr(int fd, const char *str) +{ + + write(fd, str, strlen(str)); +} + +void +rtld_fdputchar(int fd, int c) +{ + char c1; + + c1 = c; + write(fd, &c1, 1); +} diff --git a/libexec/rtld-elf/rtld_printf.h b/libexec/rtld-elf/rtld_printf.h index b37bee6..f82158c 100644 --- a/libexec/rtld-elf/rtld_printf.h +++ b/libexec/rtld-elf/rtld_printf.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: releng/10.2/libexec/rtld-elf/rtld_printf.h 225152 2011-08-24 20:05:13Z kib $ + * $FreeBSD: releng/11.1/libexec/rtld-elf/rtld_printf.h 267678 2014-06-20 17:08:32Z jonathan $ */ #ifndef RTLD_PRINTF_H @@ -31,6 +31,8 @@ #include #include +int rtld_snprintf(char *buf, size_t bufsize, const char *fmt, ...) + __printflike(3, 4); int rtld_vsnprintf(char *buf, size_t bufsize, const char *fmt, va_list ap); int rtld_vfdprintf(int fd, const char *fmt, va_list ap); int rtld_fdprintf(int fd, const char *fmt, ...) __printflike(2, 3); diff --git a/libexec/rtld-elf/rtld_start.S b/libexec/rtld-elf/rtld_start.S deleted file mode 100644 index 65d900d..0000000 --- a/libexec/rtld-elf/rtld_start.S +++ /dev/null @@ -1,91 +0,0 @@ -/*- - * Copyright 1996-1998 John D. Polstra. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: src/libexec/rtld-elf/i386/rtld_start.S,v 1.4 2005/05/19 07:32:42 dfr Exp $ - */ - - .text - .align 4 - .globl .rtld_start - .type .rtld_start,@function -.rtld_start: - xorl %ebp,%ebp # Clear frame pointer for good form - movl %esp,%eax # Save initial stack pointer - movl %esp,%esi # Save initial stack pointer - andl $0xfffffff0,%esp # Align stack pointer - subl $16,%esp # A place to store exit procedure addr - movl %esp,%ebx # save address of exit proc - movl %esp,%ecx # construct address of obj_main - addl $4,%ecx - subl $4,%esp # Keep stack aligned - pushl %ecx # Pass address of obj_main - pushl %ebx # Pass address of exit proc - pushl %eax # Pass initial stack pointer to rtld - call _rtld@PLT # Call rtld(sp); returns entry point - addl $16,%esp # Remove arguments from stack - popl %edx # Get exit procedure address - movl %esi,%esp # Ignore obj_main -/* - * At this point, %eax contains the entry point of the main program, and - * %edx contains a pointer to a termination function that should be - * registered with atexit(). (crt1.o registers it.) - */ -.globl .rtld_goto_main -.rtld_goto_main: # This symbol exists just to make debugging easier. - jmp *%eax # Enter main program - - -/* - * Binder entry point. Control is transferred to here by code in the PLT. - * On entry, there are two arguments on the stack. In ascending address - * order, they are (1) "obj", a pointer to the calling object's Obj_Entry, - * and (2) "reloff", the byte offset of the appropriate relocation entry - * in the PLT relocation table. - * - * We are careful to preserve all registers, even the the caller-save - * registers. That is because this code may be invoked by low-level - * assembly-language code that is not ABI-compliant. - */ - .align 4 - .globl _rtld_bind_start - .type _rtld_bind_start,@function -_rtld_bind_start: - pushf # Save eflags - pushl %eax # Save %eax - pushl %edx # Save %edx - pushl %ecx # Save %ecx - pushl 20(%esp) # Copy reloff argument - pushl 20(%esp) # Copy obj argument - - call _rtld_bind@PLT # Transfer control to the binder - /* Now %eax contains the entry point of the function being called. */ - - addl $8,%esp # Discard binder arguments - movl %eax,20(%esp) # Store target over obj argument - popl %ecx # Restore %ecx - popl %edx # Restore %edx - popl %eax # Restore %eax - popf # Restore eflags - leal 4(%esp),%esp # Discard reloff, do not change eflags - ret # "Return" to target address diff --git a/libexec/rtld-elf/rtld_tls.h b/libexec/rtld-elf/rtld_tls.h index 4e17934..ba00a30 100644 --- a/libexec/rtld-elf/rtld_tls.h +++ b/libexec/rtld-elf/rtld_tls.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/libexec/rtld-elf/rtld_tls.h,v 1.1 2004/08/03 08:50:58 dfr Exp $ + * $FreeBSD: releng/11.1/libexec/rtld-elf/rtld_tls.h 280816 2015-03-29 18:53:21Z kib $ */ /* @@ -57,13 +57,14 @@ * The value returned from this function is suitable for installing * directly into the thread pointer register. */ -extern void *_rtld_allocate_tls(void* oldtls, size_t tcbsize, size_t tcbalign); +void *_rtld_allocate_tls(void* oldtls, size_t tcbsize, size_t tcbalign) + __exported; /* * Free a TLS block allocated using _rtld_allocate_tls(). The tcbsize * and tcbalign parameters must be the same as those used to allocate * the block. */ -extern void _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign); +void _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign) __exported; #endif diff --git a/libexec/rtld-elf/rtld_utrace.h b/libexec/rtld-elf/rtld_utrace.h new file mode 100644 index 0000000..58d4da1 --- /dev/null +++ b/libexec/rtld-elf/rtld_utrace.h @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 2007 John Baldwin + * + * 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. + * 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/rtld_utrace.h 304453 2016-08-19 08:00:48Z kib $ + */ + +#ifndef RTLD_UTRACE_H +#define RTLD_UTRACE_H + +#include + +#define UTRACE_DLOPEN_START 1 +#define UTRACE_DLOPEN_STOP 2 +#define UTRACE_DLCLOSE_START 3 +#define UTRACE_DLCLOSE_STOP 4 +#define UTRACE_LOAD_OBJECT 5 +#define UTRACE_UNLOAD_OBJECT 6 +#define UTRACE_ADD_RUNDEP 7 +#define UTRACE_PRELOAD_FINISHED 8 +#define UTRACE_INIT_CALL 9 +#define UTRACE_FINI_CALL 10 +#define UTRACE_DLSYM_START 11 +#define UTRACE_DLSYM_STOP 12 + +#define RTLD_UTRACE_SIG_SZ 4 +#define RTLD_UTRACE_SIG "RTLD" + +struct utrace_rtld { + char sig[RTLD_UTRACE_SIG_SZ]; + int event; + void *handle; + void *mapbase; /* Used for 'parent' and 'init/fini' */ + size_t mapsize; + int refcnt; /* Used for 'mode' */ + char name[MAXPATHLEN]; +}; + +#endif diff --git a/libexec/rtld-elf/sparc64/Makefile.inc b/libexec/rtld-elf/sparc64/Makefile.inc new file mode 100644 index 0000000..60d9bbb --- /dev/null +++ b/libexec/rtld-elf/sparc64/Makefile.inc @@ -0,0 +1 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/sparc64/Makefile.inc 92195 2002-03-13 02:40:39Z jake $ diff --git a/libexec/rtld-elf/sparc64/reloc.c b/libexec/rtld-elf/sparc64/reloc.c new file mode 100644 index 0000000..1efaa11 --- /dev/null +++ b/libexec/rtld-elf/sparc64/reloc.c @@ -0,0 +1,862 @@ +/* $NetBSD: mdreloc.c,v 1.42 2008/04/28 20:23:04 martin Exp $ */ + +/*- + * Copyright (c) 2000 Eduardo Horvath. + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Kranenburg. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include +__FBSDID("$FreeBSD: releng/11.1/libexec/rtld-elf/sparc64/reloc.c 316135 2017-03-29 11:03:08Z kib $"); + +#include +#include + +#include +#include +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +/* + * The following table holds for each relocation type: + * - the width in bits of the memory location the relocation + * applies to (not currently used) + * - the number of bits the relocation value must be shifted to the + * right (i.e. discard least significant bits) to fit into + * the appropriate field in the instruction word. + * - flags indicating whether + * * the relocation involves a symbol + * * the relocation is relative to the current position + * * the relocation is for a GOT entry + * * the relocation is relative to the load address + * + */ +#define _RF_S 0x80000000 /* Resolve symbol */ +#define _RF_A 0x40000000 /* Use addend */ +#define _RF_P 0x20000000 /* Location relative */ +#define _RF_G 0x10000000 /* GOT offset */ +#define _RF_B 0x08000000 /* Load address relative */ +#define _RF_U 0x04000000 /* Unaligned */ +#define _RF_X 0x02000000 /* Bare symbols, needs proc */ +#define _RF_D 0x01000000 /* Use dynamic TLS offset */ +#define _RF_O 0x00800000 /* Use static TLS offset */ +#define _RF_I 0x00400000 /* Use TLS object ID */ +#define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */ +#define _RF_RS(s) ( (s) & 0xff) /* right shift */ +static const int reloc_target_flags[] = { + 0, /* NONE */ + _RF_S|_RF_A| _RF_SZ(8) | _RF_RS(0), /* 8 */ + _RF_S|_RF_A| _RF_SZ(16) | _RF_RS(0), /* 16 */ + _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 32 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(8) | _RF_RS(0), /* DISP_8 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(16) | _RF_RS(0), /* DISP_16 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* DISP_32 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_30 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_22 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* HI22 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 22 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 13 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* LO10 */ + _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT10 */ + _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT13 */ + _RF_G| _RF_SZ(32) | _RF_RS(10), /* GOT22 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PC10 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC22 */ + _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WPLT30 */ + _RF_SZ(32) | _RF_RS(0), /* COPY */ + _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* GLOB_DAT */ + _RF_SZ(32) | _RF_RS(0), /* JMP_SLOT */ + _RF_A| _RF_B| _RF_SZ(64) | _RF_RS(0), /* RELATIVE */ + _RF_S|_RF_A| _RF_U| _RF_SZ(32) | _RF_RS(0), /* UA_32 */ + + _RF_A| _RF_SZ(32) | _RF_RS(0), /* PLT32 */ + _RF_A| _RF_SZ(32) | _RF_RS(10), /* HIPLT22 */ + _RF_A| _RF_SZ(32) | _RF_RS(0), /* LOPLT10 */ + _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PCPLT32 */ + _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PCPLT22 */ + _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PCPLT10 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 10 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 11 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(64) | _RF_RS(0), /* 64 */ + _RF_S|_RF_A|/*extra*/ _RF_SZ(32) | _RF_RS(0), /* OLO10 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(42), /* HH22 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(32), /* HM10 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* LM22 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(42), /* PC_HH22 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(32), /* PC_HM10 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC_LM22 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP16 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP19 */ + _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GLOB_JMP */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 7 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 5 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 6 */ + _RF_S|_RF_A|_RF_P| _RF_SZ(64) | _RF_RS(0), /* DISP64 */ + _RF_A| _RF_SZ(64) | _RF_RS(0), /* PLT64 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* HIX22 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* LOX10 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(22), /* H44 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(12), /* M44 */ + _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* L44 */ + _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* REGISTER */ + _RF_S|_RF_A| _RF_U| _RF_SZ(64) | _RF_RS(0), /* UA64 */ + _RF_S|_RF_A| _RF_U| _RF_SZ(16) | _RF_RS(0), /* UA16 */ + + /* TLS */ + _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* GD_HI22 */ + _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GD_LO10 */ + 0, /* GD_ADD */ + _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* GD_CALL */ + _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* LDM_HI22 */ + _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LDM_LO10 */ + 0, /* LDM_ADD */ + _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* LDM_CALL */ + _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* LDO_HIX22 */ + _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LDO_LOX10 */ + 0, /* LDO_ADD */ + _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* IE_HI22 */ + _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* IE_LO10 */ + 0, /* IE_LD */ + 0, /* IE_LDX */ + 0, /* IE_ADD */ + _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(10), /* LE_HIX22 */ + _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(0), /* LE_LOX10 */ + _RF_S| _RF_I| _RF_SZ(32) | _RF_RS(0), /* DTPMOD32 */ + _RF_S| _RF_I| _RF_SZ(64) | _RF_RS(0), /* DTPMOD64 */ + _RF_S|_RF_A| _RF_D| _RF_SZ(32) | _RF_RS(0), /* DTPOFF32 */ + _RF_S|_RF_A| _RF_D| _RF_SZ(64) | _RF_RS(0), /* DTPOFF64 */ + _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(0), /* TPOFF32 */ + _RF_S|_RF_A| _RF_O| _RF_SZ(64) | _RF_RS(0) /* TPOFF64 */ +}; + +#if 0 +static const char *const reloc_names[] = { + "NONE", "8", "16", "32", "DISP_8", "DISP_16", "DISP_32", "WDISP_30", + "WDISP_22", "HI22", "22", "13", "LO10", "GOT10", "GOT13", "GOT22", + "PC10", "PC22", "WPLT30", "COPY", "GLOB_DAT", "JMP_SLOT", "RELATIVE", + "UA_32", "PLT32", "HIPLT22", "LOPLT10", "LOPLT10", "PCPLT22", + "PCPLT32", "10", "11", "64", "OLO10", "HH22", "HM10", "LM22", + "PC_HH22", "PC_HM10", "PC_LM22", "WDISP16", "WDISP19", "GLOB_JMP", + "7", "5", "6", "DISP64", "PLT64", "HIX22", "LOX10", "H44", "M44", + "L44", "REGISTER", "UA64", "UA16", "GD_HI22", "GD_LO10", "GD_ADD", + "GD_CALL", "LDM_HI22", "LDMO10", "LDM_ADD", "LDM_CALL", "LDO_HIX22", + "LDO_LOX10", "LDO_ADD", "IE_HI22", "IE_LO10", "IE_LD", "IE_LDX", + "IE_ADD", "LE_HIX22", "LE_LOX10", "DTPMOD32", "DTPMOD64", "DTPOFF32", + "DTPOFF64", "TPOFF32", "TPOFF64" +}; +#endif + +#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0) +#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0) +#define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0) +#define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0) +#define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0) +#define RELOC_BARE_SYMBOL(t) ((reloc_target_flags[t] & _RF_X) != 0) +#define RELOC_USE_TLS_DOFF(t) ((reloc_target_flags[t] & _RF_D) != 0) +#define RELOC_USE_TLS_OFF(t) ((reloc_target_flags[t] & _RF_O) != 0) +#define RELOC_USE_TLS_ID(t) ((reloc_target_flags[t] & _RF_I) != 0) +#define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff) +#define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff) + +static const long reloc_target_bitmask[] = { +#define _BM(x) (~(-(1ULL << (x)))) + 0, /* NONE */ + _BM(8), _BM(16), _BM(32), /* 8, 16, 32 */ + _BM(8), _BM(16), _BM(32), /* DISP8, DISP16, DISP32 */ + _BM(30), _BM(22), /* WDISP30, WDISP22 */ + _BM(22), _BM(22), /* HI22, 22 */ + _BM(13), _BM(10), /* 13, LO10 */ + _BM(10), _BM(13), _BM(22), /* GOT10, GOT13, GOT22 */ + _BM(10), _BM(22), /* PC10, PC22 */ + _BM(30), 0, /* WPLT30, COPY */ + _BM(32), _BM(32), _BM(32), /* GLOB_DAT, JMP_SLOT, RELATIVE */ + _BM(32), _BM(32), /* UA32, PLT32 */ + _BM(22), _BM(10), /* HIPLT22, LOPLT10 */ + _BM(32), _BM(22), _BM(10), /* PCPLT32, PCPLT22, PCPLT10 */ + _BM(10), _BM(11), -1, /* 10, 11, 64 */ + _BM(13), _BM(22), /* OLO10, HH22 */ + _BM(10), _BM(22), /* HM10, LM22 */ + _BM(22), _BM(10), _BM(22), /* PC_HH22, PC_HM10, PC_LM22 */ + _BM(16), _BM(19), /* WDISP16, WDISP19 */ + -1, /* GLOB_JMP */ + _BM(7), _BM(5), _BM(6), /* 7, 5, 6 */ + -1, -1, /* DISP64, PLT64 */ + _BM(22), _BM(13), /* HIX22, LOX10 */ + _BM(22), _BM(10), _BM(13), /* H44, M44, L44 */ + -1, -1, _BM(16), /* REGISTER, UA64, UA16 */ + _BM(22), _BM(10), 0, _BM(30), /* GD_HI22, GD_LO10, GD_ADD, GD_CALL */ + _BM(22), _BM(10), 0, /* LDM_HI22, LDMO10, LDM_ADD */ + _BM(30), /* LDM_CALL */ + _BM(22), _BM(10), 0, /* LDO_HIX22, LDO_LOX10, LDO_ADD */ + _BM(22), _BM(10), 0, 0, /* IE_HI22, IE_LO10, IE_LD, IE_LDX */ + 0, /* IE_ADD */ + _BM(22), _BM(13), /* LE_HIX22, LE_LOX10 */ + _BM(32), -1, /* DTPMOD32, DTPMOD64 */ + _BM(32), -1, /* DTPOFF32, DTPOFF64 */ + _BM(32), -1 /* TPOFF32, TPOFF64 */ +#undef _BM +}; +#define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t]) + +#undef flush +#define flush(va, offs) \ + __asm __volatile("flush %0 + %1" : : "r" (va), "I" (offs)); + +static int reloc_nonplt_object(Obj_Entry *obj, const Elf_Rela *rela, + SymCache *cache, int flags, RtldLockState *lockstate); +static void install_plt(Elf_Word *pltgot, Elf_Addr proc); + +extern char _rtld_bind_start_0[]; +extern char _rtld_bind_start_1[]; + +int +do_copy_relocations(Obj_Entry *dstobj) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + const Elf_Sym *dstsym; + const Elf_Sym *srcsym; + void *dstaddr; + const void *srcaddr; + const Obj_Entry *srcobj, *defobj; + SymLook req; + const char *name; + size_t size; + int res; + + assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ + + relalim = (const Elf_Rela *)((caddr_t)dstobj->rela + dstobj->relasize); + for (rela = dstobj->rela; rela < relalim; rela++) { + if (ELF_R_TYPE(rela->r_info) == R_SPARC_COPY) { + dstaddr = (void *)(dstobj->relocbase + rela->r_offset); + dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); + name = dstobj->strtab + dstsym->st_name; + size = dstsym->st_size; + symlook_init(&req, name); + req.ventry = fetch_ventry(dstobj, + ELF_R_SYM(rela->r_info)); + req.flags = SYMLOOK_EARLY; + + for (srcobj = globallist_next(dstobj); srcobj != NULL; + srcobj = globallist_next(srcobj)) { + res = symlook_obj(&req, srcobj); + if (res == 0) { + srcsym = req.sym_out; + defobj = req.defobj_out; + break; + } + } + if (srcobj == NULL) { + _rtld_error("Undefined symbol \"%s\"" + "referenced from COPY relocation" + "in %s", name, dstobj->path); + return (-1); + } + + srcaddr = (const void *)(defobj->relocbase + + srcsym->st_value); + memcpy(dstaddr, srcaddr, size); + } + } + + return (0); +} + +int +reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, + RtldLockState *lockstate) +{ + const Elf_Rela *relalim; + const Elf_Rela *rela; + SymCache *cache; + int r = -1; + + if ((flags & SYMLOOK_IFUNC) != 0) + /* XXX not implemented */ + return (0); + + /* + * The dynamic loader may be called from a thread, we have + * limited amounts of stack available so we cannot use alloca(). + */ + if (obj != obj_rtld) { + cache = calloc(obj->dynsymcount, sizeof(SymCache)); + /* No need to check for NULL here */ + } else + cache = NULL; + + relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); + for (rela = obj->rela; rela < relalim; rela++) { + if (reloc_nonplt_object(obj, rela, cache, flags, lockstate) < 0) + goto done; + } + r = 0; +done: + if (cache != NULL) + free(cache); + return (r); +} + +static int +reloc_nonplt_object(Obj_Entry *obj, const Elf_Rela *rela, SymCache *cache, + int flags, RtldLockState *lockstate) +{ + const Obj_Entry *defobj; + const Elf_Sym *def; + Elf_Addr *where; + Elf_Word *where32; + Elf_Word type; + Elf_Addr value; + Elf_Addr mask; + + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + where32 = (Elf_Word *)where; + defobj = NULL; + def = NULL; + + type = ELF64_R_TYPE_ID(rela->r_info); + if (type == R_SPARC_NONE) + return (0); + + /* We do JMP_SLOTs below. */ + if (type == R_SPARC_JMP_SLOT) + return (0); + + /* COPY relocs are also handled elsewhere. */ + if (type == R_SPARC_COPY) + return (0); + + /* Ignore ADD and CALL relocations for dynamic TLS references. */ + if (type == R_SPARC_TLS_GD_ADD || type == R_SPARC_TLS_GD_CALL || + type == R_SPARC_TLS_LDM_ADD || type == R_SPARC_TLS_LDM_CALL || + type == R_SPARC_TLS_LDO_ADD) + return (0); + + /* + * Note: R_SPARC_TLS_TPOFF64 must be the numerically largest + * relocation type. + */ + if (type >= sizeof(reloc_target_bitmask) / + sizeof(*reloc_target_bitmask)) { + _rtld_error("%s: Unsupported relocation type %d in non-PLT " + "object\n", obj->path, type); + return (-1); + } + + value = rela->r_addend; + + /* + * Handle relative relocs here, because we might not be able to access + * globals yet. + */ + if (type == R_SPARC_RELATIVE) { + /* XXXX -- apparently we ignore the preexisting value. */ + *where = (Elf_Addr)(obj->relocbase + value); + return (0); + } + + /* + * If we get here while relocating rtld itself, we will crash because + * a non-local variable is accessed. + */ + if (RELOC_RESOLVE_SYMBOL(type)) { + /* Find the symbol. */ + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + flags, cache, lockstate); + if (def == NULL) + return (-1); + + if (RELOC_USE_TLS_ID(type)) + value = (Elf_Addr)defobj->tlsindex; + else if (RELOC_USE_TLS_DOFF(type)) + value += (Elf_Addr)def->st_value; + else if (RELOC_USE_TLS_OFF(type)) { + /* + * We lazily allocate offsets for static TLS as we + * see the first relocation that references the TLS + * block. This allows us to support (small amounts + * of) static TLS in dynamically loaded modules. If + * we run out of space, we generate an error. + */ + if (!defobj->tls_done && + !allocate_tls_offset((Obj_Entry*)defobj)) { + _rtld_error("%s: No space available for " + "static Thread Local Storage", obj->path); + return (-1); + } + value += (Elf_Addr)(def->st_value - + defobj->tlsoffset); + } else { + /* Add in the symbol's absolute address. */ + value += (Elf_Addr)(def->st_value + + defobj->relocbase); + } + } + + if (type == R_SPARC_OLO10) + value = (value & 0x3ff) + ELF64_R_TYPE_DATA(rela->r_info); + + if (type == R_SPARC_HIX22 || type == R_SPARC_TLS_LE_HIX22) + value ^= 0xffffffffffffffff; + + if (RELOC_PC_RELATIVE(type)) + value -= (Elf_Addr)where; + + if (RELOC_BASE_RELATIVE(type)) { + /* + * Note that even though sparcs use `Elf_rela' exclusively + * we still need the implicit memory addend in relocations + * referring to GOT entries. Undoubtedly, someone f*cked + * this up in the distant past, and now we're stuck with + * it in the name of compatibility for all eternity ... + * + * In any case, the implicit and explicit should be mutually + * exclusive. We provide a check for that here. + */ + /* XXXX -- apparently we ignore the preexisting value */ + value += (Elf_Addr)(obj->relocbase); + } + + mask = RELOC_VALUE_BITMASK(type); + value >>= RELOC_VALUE_RIGHTSHIFT(type); + value &= mask; + + if (type == R_SPARC_LOX10 || type == R_SPARC_TLS_LE_LOX10) + value |= 0x1c00; + + if (RELOC_UNALIGNED(type)) { + /* Handle unaligned relocations. */ + Elf_Addr tmp; + char *ptr; + int size; + int i; + + size = RELOC_TARGET_SIZE(type) / 8; + ptr = (char *)where; + tmp = 0; + + /* Read it in one byte at a time. */ + for (i = 0; i < size; i++) + tmp = (tmp << 8) | ptr[i]; + + tmp &= ~mask; + tmp |= value; + + /* Write it back out. */ + for (i = 0; i < size; i++) + ptr[i] = ((tmp >> ((size - i - 1) * 8)) & 0xff); + } else if (RELOC_TARGET_SIZE(type) > 32) { + *where &= ~mask; + *where |= value; + } else { + *where32 &= ~mask; + *where32 |= value; + } + + return (0); +} + +int +reloc_plt(Obj_Entry *obj) +{ +#if 0 + const Obj_Entry *defobj; + const Elf_Rela *relalim; + const Elf_Rela *rela; + const Elf_Sym *def; + Elf_Addr *where; + Elf_Addr value; + + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + if (rela->r_addend == 0) + continue; + assert(ELF64_R_TYPE_ID(rela->r_info) == R_SPARC_JMP_SLOT); + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + SYMLOOK_IN_PLT, NULL, lockstate); + value = (Elf_Addr)(defobj->relocbase + def->st_value); + *where = value; + } +#endif + return (0); +} + +/* + * Instruction templates: + */ +#define BAA 0x10400000 /* ba,a %xcc, 0 */ +#define SETHI 0x03000000 /* sethi %hi(0), %g1 */ +#define JMP 0x81c06000 /* jmpl %g1+%lo(0), %g0 */ +#define NOP 0x01000000 /* sethi %hi(0), %g0 */ +#define OR 0x82806000 /* or %g1, 0, %g1 */ +#define XOR 0x82c06000 /* xor %g1, 0, %g1 */ +#define MOV71 0x8283a000 /* or %o7, 0, %g1 */ +#define MOV17 0x9c806000 /* or %g1, 0, %o7 */ +#define CALL 0x40000000 /* call 0 */ +#define SLLX 0x8b407000 /* sllx %g1, 0, %g1 */ +#define SETHIG5 0x0b000000 /* sethi %hi(0), %g5 */ +#define ORG5 0x82804005 /* or %g1, %g5, %g1 */ + +/* %hi(v) with variable shift */ +#define HIVAL(v, s) (((v) >> (s)) & 0x003fffff) +#define LOVAL(v) ((v) & 0x000003ff) + +int +reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) +{ + const Obj_Entry *defobj; + const Elf_Rela *relalim; + const Elf_Rela *rela; + const Elf_Sym *def; + Elf_Addr *where; + Elf_Addr target; + + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + assert(ELF64_R_TYPE_ID(rela->r_info) == R_SPARC_JMP_SLOT); + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + SYMLOOK_IN_PLT | flags, NULL, lockstate); + if (def == NULL) + return -1; + target = (Elf_Addr)(defobj->relocbase + def->st_value); + reloc_jmpslot(where, target, defobj, obj, (Elf_Rel *)rela); + } + obj->jmpslots_done = true; + return (0); +} + +int +reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +int +reloc_gnu_ifunc(Obj_Entry *obj, int flags, + struct Struct_RtldLockState *lockstate) +{ + + /* XXX not implemented */ + return (0); +} + +Elf_Addr +reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *obj, + const Obj_Entry *refobj, const Elf_Rel *rel) +{ + const Elf_Rela *rela = (const Elf_Rela *)rel; + Elf_Addr offset; + Elf_Word *where; + + if (ld_bind_not) { + /* Skip any PLT modifications */ + } else if (rela - refobj->pltrela < 32764) { + /* + * At the PLT entry pointed at by `where', we now construct + * a direct transfer to the now fully resolved function + * address. + * + * A PLT entry is supposed to start by looking like this: + * + * sethi (. - .PLT0), %g1 + * ba,a %xcc, .PLT1 + * nop + * nop + * nop + * nop + * nop + * nop + * + * When we replace these entries we start from the second + * entry and do it in reverse order so the last thing we + * do is replace the branch. That allows us to change this + * atomically. + * + * We now need to find out how far we need to jump. We + * have a choice of several different relocation techniques + * which are increasingly expensive. + */ + where = (Elf_Word *)wherep; + offset = ((Elf_Addr)where) - target; + if (offset <= (1L<<20) && offset >= -(1L<<20)) { + /* + * We're within 1MB -- we can use a direct branch + * instruction. + * + * We can generate this pattern: + * + * sethi %hi(. - .PLT0), %g1 + * ba,a %xcc, addr + * nop + * nop + * nop + * nop + * nop + * nop + * + */ + where[1] = BAA | ((offset >> 2) &0x3fffff); + flush(where, 4); + } else if (target >= 0 && target < (1L<<32)) { + /* + * We're within 32-bits of address zero. + * + * The resulting code in the jump slot is: + * + * sethi %hi(. - .PLT0), %g1 + * sethi %hi(addr), %g1 + * jmp %g1+%lo(addr) + * nop + * nop + * nop + * nop + * nop + * + */ + where[2] = JMP | LOVAL(target); + flush(where, 8); + where[1] = SETHI | HIVAL(target, 10); + flush(where, 4); + } else if (target <= 0 && target > -(1L<<32)) { + /* + * We're within 32-bits of address -1. + * + * The resulting code in the jump slot is: + * + * sethi %hi(. - .PLT0), %g1 + * sethi %hix(addr), %g1 + * xor %g1, %lox(addr), %g1 + * jmp %g1 + * nop + * nop + * nop + * nop + * + */ + where[3] = JMP; + flush(where, 12); + where[2] = XOR | ((~target) & 0x00001fff); + flush(where, 8); + where[1] = SETHI | HIVAL(~target, 10); + flush(where, 4); + } else if (offset <= (1L<<32) && offset >= -((1L<<32) - 4)) { + /* + * We're within 32-bits -- we can use a direct call + * insn + * + * The resulting code in the jump slot is: + * + * sethi %hi(. - .PLT0), %g1 + * mov %o7, %g1 + * call (.+offset) + * mov %g1, %o7 + * nop + * nop + * nop + * nop + * + */ + where[3] = MOV17; + flush(where, 12); + where[2] = CALL | ((offset >> 4) & 0x3fffffff); + flush(where, 8); + where[1] = MOV71; + flush(where, 4); + } else if (offset >= 0 && offset < (1L<<44)) { + /* + * We're within 44 bits. We can generate this + * pattern: + * + * The resulting code in the jump slot is: + * + * sethi %hi(. - .PLT0), %g1 + * sethi %h44(addr), %g1 + * or %g1, %m44(addr), %g1 + * sllx %g1, 12, %g1 + * jmp %g1+%l44(addr) + * nop + * nop + * nop + * + */ + where[4] = JMP | LOVAL(offset); + flush(where, 16); + where[3] = SLLX | 12; + flush(where, 12); + where[2] = OR | (((offset) >> 12) & 0x00001fff); + flush(where, 8); + where[1] = SETHI | HIVAL(offset, 22); + flush(where, 4); + } else if (offset < 0 && offset > -(1L<<44)) { + /* + * We're within 44 bits. We can generate this + * pattern: + * + * The resulting code in the jump slot is: + * + * sethi %hi(. - .PLT0), %g1 + * sethi %h44(-addr), %g1 + * xor %g1, %m44(-addr), %g1 + * sllx %g1, 12, %g1 + * jmp %g1+%l44(addr) + * nop + * nop + * nop + * + */ + where[4] = JMP | LOVAL(offset); + flush(where, 16); + where[3] = SLLX | 12; + flush(where, 12); + where[2] = XOR | (((~offset) >> 12) & 0x00001fff); + flush(where, 8); + where[1] = SETHI | HIVAL(~offset, 22); + flush(where, 4); + } else { + /* + * We need to load all 64-bits + * + * The resulting code in the jump slot is: + * + * sethi %hi(. - .PLT0), %g1 + * sethi %hh(addr), %g1 + * sethi %lm(addr), %g5 + * or %g1, %hm(addr), %g1 + * sllx %g1, 32, %g1 + * or %g1, %g5, %g1 + * jmp %g1+%lo(addr) + * nop + * + */ + where[6] = JMP | LOVAL(target); + flush(where, 24); + where[5] = ORG5; + flush(where, 20); + where[4] = SLLX | 32; + flush(where, 16); + where[3] = OR | LOVAL((target) >> 32); + flush(where, 12); + where[2] = SETHIG5 | HIVAL(target, 10); + flush(where, 8); + where[1] = SETHI | HIVAL(target, 42); + flush(where, 4); + } + } else { + /* + * This is a high PLT slot; the relocation offset specifies a + * pointer that needs to be frobbed; no actual code needs to + * be modified. The pointer to be calculated needs the addend + * added and the reference object relocation base subtraced. + */ + *wherep = target + rela->r_addend - + (Elf_Addr)refobj->relocbase; + } + + return (target); +} + +void +ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) +{ +} + +/* + * Install rtld function call into this PLT slot. + */ +#define SAVE 0x9de3bf50 +#define SETHI_l0 0x21000000 +#define SETHI_l1 0x23000000 +#define OR_l0_l0 0xa0142000 +#define SLLX_l0_32_l0 0xa12c3020 +#define OR_l0_l1_l0 0xa0140011 +#define JMPL_l0_o1 0x93c42000 +#define MOV_g1_o0 0x90100001 + +void +init_pltgot(Obj_Entry *obj) +{ + Elf_Word *entry; + + if (obj->pltgot != NULL) { + entry = (Elf_Word *)obj->pltgot; + install_plt(&entry[0], (Elf_Addr)_rtld_bind_start_0); + install_plt(&entry[8], (Elf_Addr)_rtld_bind_start_1); + obj->pltgot[8] = (Elf_Addr)obj; + } +} + +static void +install_plt(Elf_Word *pltgot, Elf_Addr proc) +{ + pltgot[0] = SAVE; + flush(pltgot, 0); + pltgot[1] = SETHI_l0 | HIVAL(proc, 42); + flush(pltgot, 4); + pltgot[2] = SETHI_l1 | HIVAL(proc, 10); + flush(pltgot, 8); + pltgot[3] = OR_l0_l0 | LOVAL((proc) >> 32); + flush(pltgot, 12); + pltgot[4] = SLLX_l0_32_l0; + flush(pltgot, 16); + pltgot[5] = OR_l0_l1_l0; + flush(pltgot, 20); + pltgot[6] = JMPL_l0_o1 | LOVAL(proc); + flush(pltgot, 24); + pltgot[7] = MOV_g1_o0; + flush(pltgot, 28); +} + +void +allocate_initial_tls(Obj_Entry *objs) +{ + Elf_Addr* tpval; + + /* + * Fix the size of the static TLS block by using the maximum offset + * allocated so far and adding a bit for dynamic modules to use. + */ + tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA; + tpval = allocate_tls(objs, NULL, 3 * sizeof(Elf_Addr), + sizeof(Elf_Addr)); + __asm __volatile("mov %0, %%g7" : : "r" (tpval)); +} + +void *__tls_get_addr(tls_index *ti) +{ + register Elf_Addr** tp __asm__("%g7"); + + return (tls_get_addr_common(tp, ti->ti_module, ti->ti_offset)); +} diff --git a/libexec/rtld-elf/sparc64/rtld_machdep.h b/libexec/rtld-elf/sparc64/rtld_machdep.h new file mode 100644 index 0000000..140a0a2 --- /dev/null +++ b/libexec/rtld-elf/sparc64/rtld_machdep.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1999, 2000 John D. Polstra. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/sparc64/rtld_machdep.h 316135 2017-03-29 11:03:08Z kib $ + */ + +#ifndef RTLD_MACHDEP_H +#define RTLD_MACHDEP_H 1 + +#include +#include + +struct Struct_Obj_Entry; + +/* Return the address of the .dynamic section in the dynamic linker. */ +Elf_Dyn *rtld_dynamic_addr(void); +#define rtld_dynamic(obj) rtld_dynamic_addr() + +Elf_Addr reloc_jmpslot(Elf_Addr *, Elf_Addr, + const struct Struct_Obj_Entry *, const struct Struct_Obj_Entry *, + const Elf_Rel *); + +#define make_function_pointer(def, defobj) \ + ((defobj)->relocbase + (def)->st_value) + +#define call_initfini_pointer(obj, target) \ + (((InitFunc)(target))()) + +#define call_init_pointer(obj, target) \ + (((InitArrFunc)(target))(main_argc, main_argv, environ)) + +#define call_ifunc_resolver(ptr) \ + (((Elf_Addr (*)(void))ptr)()) + +#define round(size, align) \ + (((size) + (align) - 1) & ~((align) - 1)) +#define calculate_first_tls_offset(size, align) \ + round(size, align) +#define calculate_tls_offset(prev_offset, prev_size, size, align) \ + round((prev_offset) + (size), align) +#define calculate_tls_end(off, size) ((off) + (size)) + +typedef struct { + unsigned long ti_module; + unsigned long ti_offset; +} tls_index; + +extern void *__tls_get_addr(tls_index *ti); + +#define RTLD_DEFAULT_STACK_PF_EXEC 0 +#define RTLD_DEFAULT_STACK_EXEC 0 + +#define md_abi_variant_hook(x) + +#endif diff --git a/libexec/rtld-elf/sparc64/rtld_start.S b/libexec/rtld-elf/sparc64/rtld_start.S new file mode 100644 index 0000000..48871ae --- /dev/null +++ b/libexec/rtld-elf/sparc64/rtld_start.S @@ -0,0 +1,170 @@ +/* $NetBSD: rtld_start.S,v 1.5 2001/08/14 22:17:48 eeh Exp $ */ + +/*- + * Copyright (c) 2001 Jake Burkholder. + * Copyright (c) 2000 Eduardo Horvath. + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas and Paul Kranenburg. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/sparc64/rtld_start.S 130661 2004-06-18 02:01:37Z tmm $ + */ + +#include + +/* + * ELF: + * On startup the stack should contain 16 extended word register save + * area, followed by the arg count, etc. + */ + +ENTRY(.rtld_start) + clr %fp + mov %o0, %l0 + mov %o3, %l1 + + sub %sp, 16, %sp + add %sp, SPOFF + CCFSZ + 0x0, %o1 + call _rtld + add %sp, SPOFF + CCFSZ + 0x8, %o2 + + ldx [%sp + SPOFF + CCFSZ + 0x0], %o1 + ldx [%sp + SPOFF + CCFSZ + 0x8], %o2 + add %sp, 16, %sp + + mov %l1, %o3 + jmp %o0 + mov %l0, %o0 +END(.rtld_start) + +/* + * Find the address of _DYNAMIC by disassembling a call instruction to it. + * Binutils may not fill in the GOT as expected on other architectures. + */ +.weak _DYNAMIC + +ENTRY(rtld_dynamic_addr) + save %sp, -CCFSZ, %sp + call 1f + nop + call _DYNAMIC + 8 +1: lduw [%o7 + 8], %o0 + sll %o0, 2, %o0 + sra %o0, 0, %o0 + ret + restore %o0, %o7, %o0 +END(rtld_dynamic_addr) + + /* + * We have two separate entry points to the runtime linker. + * I'm implementing this following the SPARC v9 ABI spec. + * + * _rtld_bind_start_0(x, y) is called from .PLT0, and is used for + * PLT entries above 32768. + * + * _rtld_bind_start_1(x, y) is called from .PLT1, and is used for + * PLT entries below 32768. + * + * The first two entries of PLT2 contain the xword object pointer. + * + * These routines are called with two longword arguments, + * x and y. To calculate the address of the entry, + * _rtld_bind_start_1(x, y) does: + * + * n = x >> 15; + * + * and _rtld_bind_start_0(x, y) should do, according to the SCD: + * + * i = x - y - 1048596; + * n = 32768 + (i/5120)*160 + (i%5120)/24; + * + * Note that the number of 1048596 from above is incorrect; rather, + * we need to use HIPLTOFFS as defined below. + * + * Neither routine needs to issue a save since it's already been + * done in the PLT entry. + */ + +#define NPLTLOSLOTS 32768 +#define PLTSLOTSZ 32 +/* + * - 16 to compensate for the difference of the positions of the jumps that + * generate the arguments in .PLT0 and the high plt entry. + */ +#define HIPLTOFFS (NPLTLOSLOTS * PLTSLOTSZ - 16) + +ENTRY(_rtld_bind_start_0) + sethi %hi(HIPLTOFFS), %l1 + or %l1, %lo(HIPLTOFFS), %l1 + sub %o0, %o1, %l0 /* x - y */ + sub %l0, %l1, %l0 /* i = x - y - HIPLTOFFS */ + sethi %hi(5120), %l7 + sdivx %l0, %l7, %l1 /* Calculate i / 5120 */ + mulx %l1, %l7, %l3 + sub %l0, %l3, %l2 /* And i % 5120 */ + mulx %l1, 160, %l5 /* (i / 5120) * 160 */ + sdivx %l2, 24, %l4 /* (i % 5120) / 24 */ + sethi %hi(NPLTLOSLOTS), %l6 + add %l4, %l5, %l4 /* (i / 5120) * 160 + (i % 5120) / 24 */ + add %l4, %l6, %l4 /* + NPLTLOSLOTS */ + sub %l4, 4, %l4 /* XXX: 4 entries are reserved */ + + sllx %l4, 1, %l5 /* Each element is an Elf_Rela which */ + add %l5, %l4, %l4 /* is 3 longwords or 24 bytes. */ + sllx %l4, 3, %l4 /* So multiply by 24. */ + + ldx [%o1 + (10*4)], %o0 /* Load object pointer from PLT2 */ + + call _rtld_bind /* Call _rtld_bind(obj, offset) */ + mov %l4, %o1 + + jmp %o0 /* return value == function address */ + restore /* Dump our stack frame */ +END(_rtld_bind_start_0) + +ENTRY(_rtld_bind_start_1) + srax %o0, 15, %o2 /* %o0 is the index to our PLT slot */ + sub %o2, 4, %o2 /* XXX: 4 entries are reserved */ + + sllx %o2, 1, %o3 /* Each element is an Elf_Rela which */ + add %o3, %o2, %o2 /* is 3 longwords or 24 bytes. */ + sllx %o2, 3, %o2 /* So multiply by 24. */ + + ldx [%o1 + 8], %o0 /* The object pointer is at [%o1 + 8] */ + + call _rtld_bind /* Call _rtld_bind(obj, offset) */ + mov %o2, %o1 + + jmp %o0 /* return value == function address */ + restore /* Dump our stack frame */ +END(_rtld_bind_start_1) diff --git a/libexec/rtld-elf/tests/Makefile b/libexec/rtld-elf/tests/Makefile new file mode 100644 index 0000000..29f3b6c --- /dev/null +++ b/libexec/rtld-elf/tests/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/tests/Makefile 316388 2017-04-02 02:16:01Z asomers $ + +SUBDIR+= libpythagoras target + +SUBDIR_DEPEND_target= libpythagoras +ATF_TESTS_C= ld_library_pathfds +WARNS?= 3 + +.include diff --git a/libexec/rtld-elf/tests/Makefile.depend b/libexec/rtld-elf/tests/Makefile.depend new file mode 100644 index 0000000..e30c64b --- /dev/null +++ b/libexec/rtld-elf/tests/Makefile.depend @@ -0,0 +1,19 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/tests/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/atf/libatf-c \ + lib/libc \ + lib/libcompiler_rt \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/libexec/rtld-elf/tests/ld_library_pathfds.c b/libexec/rtld-elf/tests/ld_library_pathfds.c new file mode 100644 index 0000000..38cf8a2 --- /dev/null +++ b/libexec/rtld-elf/tests/ld_library_pathfds.c @@ -0,0 +1,221 @@ +/*- + * Copyright 2014 Jonathan Anderson. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/tests/ld_library_pathfds.c 269504 2014-08-04 05:46:10Z ngie $ + */ + +#include +#include +#include + + +struct descriptors { + int binary; + int testdir; + int root; + int etc; + int usr; +}; + +static void setup(struct descriptors *, const atf_tc_t *); +static void expect_success(int binary, char *pathfds); +static void expect_missing_library(int binary, char *pathfds); + +static void try_to_run(int binary, int expected_exit_status, + char * const *env, const char *expected_out, const char *expected_err); +static int opendir(const char *name); +static int opendirat(int parent, const char *name); + + +ATF_TC_WITHOUT_HEAD(missing_library); +ATF_TC_BODY(missing_library, tc) +{ + struct descriptors files; + + setup(&files, tc); + expect_missing_library(files.binary, NULL); +} + + +ATF_TC_WITHOUT_HEAD(wrong_library_directories); +ATF_TC_BODY(wrong_library_directories, tc) +{ + struct descriptors files; + char *pathfds; + + setup(&files, tc); + ATF_REQUIRE( + asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d", files.etc) > 0); + + expect_missing_library(files.binary, pathfds); +} + + +ATF_TC_WITHOUT_HEAD(bad_library_directories); +ATF_TC_BODY(bad_library_directories, tc) +{ + struct descriptors files; + char *pathfds; + + setup(&files, tc); + ATF_REQUIRE(asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=::") > 0); + + expect_missing_library(files.binary, pathfds); +} + + +ATF_TC_WITHOUT_HEAD(single_library_directory); +ATF_TC_BODY(single_library_directory, tc) +{ + struct descriptors files; + char *pathfds; + + setup(&files, tc); + ATF_REQUIRE( + asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d", files.testdir) > 0); + + expect_success(files.binary, pathfds); +} + + +ATF_TC_WITHOUT_HEAD(first_library_directory); +ATF_TC_BODY(first_library_directory, tc) +{ + struct descriptors files; + char *pathfds; + + setup(&files, tc); + ATF_REQUIRE( + asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d:%d", + files.testdir, files.etc) > 0); + + expect_success(files.binary, pathfds); +} + + +ATF_TC_WITHOUT_HEAD(middle_library_directory); +ATF_TC_BODY(middle_library_directory, tc) +{ + struct descriptors files; + char *pathfds; + + setup(&files, tc); + ATF_REQUIRE( + asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d:%d:%d", + files.root, files.testdir, files.usr) > 0); + + expect_success(files.binary, pathfds); +} + + +ATF_TC_WITHOUT_HEAD(last_library_directory); +ATF_TC_BODY(last_library_directory, tc) +{ + struct descriptors files; + char *pathfds; + + setup(&files, tc); + ATF_REQUIRE( + asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d:%d", + files.root, files.testdir) > 0); + + expect_success(files.binary, pathfds); +} + + + +/* Register test cases with ATF. */ +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, missing_library); + ATF_TP_ADD_TC(tp, wrong_library_directories); + ATF_TP_ADD_TC(tp, bad_library_directories); + ATF_TP_ADD_TC(tp, single_library_directory); + ATF_TP_ADD_TC(tp, first_library_directory); + ATF_TP_ADD_TC(tp, middle_library_directory); + ATF_TP_ADD_TC(tp, last_library_directory); + + return atf_no_error(); +} + + +static void +setup(struct descriptors *dp, const atf_tc_t *tc) +{ + + dp->testdir = opendir(atf_tc_get_config_var(tc, "srcdir")); + ATF_REQUIRE(dp->testdir >= 0); + ATF_REQUIRE( + (dp->binary = openat(dp->testdir, "target", O_RDONLY)) >= 0); + + ATF_REQUIRE((dp->root = opendir("/")) >= 0); + ATF_REQUIRE((dp->etc = opendirat(dp->root, "etc")) >= 0); + ATF_REQUIRE((dp->usr = opendirat(dp->root, "usr")) >= 0); +} + +static void +expect_success(int binary, char *pathfds) +{ + char * const env[] = { pathfds, NULL }; + try_to_run(binary, 0, env, "the hypotenuse of 3 and 4 is 5\n", ""); +} + +static void +expect_missing_library(int binary, char *pathfds) +{ + char * const env[] = { pathfds, NULL }; + try_to_run(binary, 1, env, "", + "Shared object \"libpythagoras.so.0\" not found," + " required by \"target\"\n"); +} + + +static void +try_to_run(int binary, int exit_status, char * const *env, + const char *expected_out, const char *expected_err) +{ + pid_t child = atf_utils_fork(); + + if (child == 0) { + char * const args[] = { "target", NULL }; + + fexecve(binary, args, env); + atf_tc_fail("fexecve() failed"); + } + + atf_utils_wait(child, exit_status, expected_out, expected_err); +} + + +static int +opendir(const char *name) +{ + return open(name, O_RDONLY | O_DIRECTORY); +} + +static int +opendirat(int parent, const char *name) +{ + return openat(parent, name, O_RDONLY | O_DIRECTORY); +} diff --git a/libexec/rtld-elf/tests/libpythagoras/Makefile b/libexec/rtld-elf/tests/libpythagoras/Makefile new file mode 100644 index 0000000..5995add --- /dev/null +++ b/libexec/rtld-elf/tests/libpythagoras/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/tests/libpythagoras/Makefile 316388 2017-04-02 02:16:01Z asomers $ + +.include + +LIB= pythagoras +SHLIB_MAJOR= 0 + +LIBDIR= ${TESTSBASE}/libexec/rtld-elf +SHLIBDIR= ${TESTSBASE}/libexec/rtld-elf + +SRCS= pythagoras.c + +WARNS?= 6 +LIBADD= m + +.include diff --git a/libexec/rtld-elf/tests/libpythagoras/Makefile.depend b/libexec/rtld-elf/tests/libpythagoras/Makefile.depend new file mode 100644 index 0000000..fb50b4b --- /dev/null +++ b/libexec/rtld-elf/tests/libpythagoras/Makefile.depend @@ -0,0 +1,18 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/tests/libpythagoras/Makefile.depend 311183 2017-01-03 22:51:40Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/msun \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/libexec/rtld-elf/tests/libpythagoras/pythagoras.c b/libexec/rtld-elf/tests/libpythagoras/pythagoras.c new file mode 100644 index 0000000..0df422f --- /dev/null +++ b/libexec/rtld-elf/tests/libpythagoras/pythagoras.c @@ -0,0 +1,42 @@ +/*- + * Copyright 2014 Jonathan Anderson. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/tests/libpythagoras/pythagoras.c 267679 2014-06-20 17:14:59Z jonathan $ + */ + +#include +#include + +#include "pythagoras.h" + +double +pythagorean_theorem(double a, double b) +{ + + if (a <= 0 || b <= 0) { + errno = ERANGE; + return (-1.0); + } + return (sqrt(pow(a, 2) + pow(b, 2))); +} diff --git a/libexec/rtld-elf/tests/libpythagoras/pythagoras.h b/libexec/rtld-elf/tests/libpythagoras/pythagoras.h new file mode 100644 index 0000000..e0487c4 --- /dev/null +++ b/libexec/rtld-elf/tests/libpythagoras/pythagoras.h @@ -0,0 +1,28 @@ +/*- + * Copyright 2014 Jonathan Anderson. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/tests/libpythagoras/pythagoras.h 267679 2014-06-20 17:14:59Z jonathan $ + */ + +double pythagorean_theorem(double, double); diff --git a/libexec/rtld-elf/tests/target/Makefile b/libexec/rtld-elf/tests/target/Makefile new file mode 100644 index 0000000..6af3acb --- /dev/null +++ b/libexec/rtld-elf/tests/target/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/tests/target/Makefile 316388 2017-04-02 02:16:01Z asomers $ + +.include + +PROG= target +BINDIR= ${TESTSBASE}/libexec/rtld-elf + +WARNS?= 3 +CFLAGS+= -I${.CURDIR}/../libpythagoras + +LDFLAGS+= -L${.OBJDIR}/../libpythagoras +DPADD+= ${.OBJDIR}/../libpythagoras/libpythagoras.a +LDADD= -lpythagoras + +MAN= + +.include diff --git a/libexec/rtld-elf/tests/target/Makefile.depend b/libexec/rtld-elf/tests/target/Makefile.depend new file mode 100644 index 0000000..119e47a --- /dev/null +++ b/libexec/rtld-elf/tests/target/Makefile.depend @@ -0,0 +1,18 @@ +# $FreeBSD: releng/11.1/libexec/rtld-elf/tests/target/Makefile.depend 311183 2017-01-03 22:51:40Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + libexec/rtld-elf/tests/libpythagoras \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/libexec/rtld-elf/tests/target/target.c b/libexec/rtld-elf/tests/target/target.c new file mode 100644 index 0000000..af883e9 --- /dev/null +++ b/libexec/rtld-elf/tests/target/target.c @@ -0,0 +1,39 @@ +/*- + * Copyright 2014 Jonathan Anderson. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: releng/11.1/libexec/rtld-elf/tests/target/target.c 267679 2014-06-20 17:14:59Z jonathan $ + */ + +#include "pythagoras.h" + +#include + +int +main(int argc, char *argv[]) +{ + float hypotenuse = pythagorean_theorem(3, 4); + printf("the hypotenuse of 3 and 4 is %d\n", (int) hypotenuse); + + return 0; +} diff --git a/libexec/rtld-elf/xmalloc.c b/libexec/rtld-elf/xmalloc.c index 785e162..c11084b 100644 --- a/libexec/rtld-elf/xmalloc.c +++ b/libexec/rtld-elf/xmalloc.c @@ -22,38 +22,76 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/libexec/rtld-elf/xmalloc.c,v 1.3 2003/06/19 05:28:26 mdodd Exp $ + * $FreeBSD: releng/11.1/libexec/rtld-elf/xmalloc.c 262334 2014-02-22 11:06:48Z davidxu $ */ -#include #include #include #include - -void *xcalloc(size_t); -void *xmalloc(size_t); -char *xstrdup(const char *); +#include +#include "rtld.h" +#include "rtld_printf.h" void * -xcalloc(size_t size) +xcalloc(size_t number, size_t size) { - return memset(xmalloc(size), 0, size); + void *p; + + p = calloc(number, size); + if (p == NULL) { + rtld_fdputstr(STDERR_FILENO, "Out of memory\n"); + _exit(1); + } + return (p); } void * xmalloc(size_t size) { void *p = malloc(size); - if (p == NULL) - err(1, "Out of memory"); + if (p == NULL) { + rtld_fdputstr(STDERR_FILENO, "Out of memory\n"); + _exit(1); + } return p; } char * -xstrdup(const char *s) +xstrdup(const char *str) { - char *p = strdup(s); - if (p == NULL) - err(1, "Out of memory"); - return p; + char *copy; + size_t len; + + len = strlen(str) + 1; + copy = xmalloc(len); + memcpy(copy, str, len); + return (copy); +} + +void * +malloc_aligned(size_t size, size_t align) +{ + void *mem, *res; + + if (align < sizeof(void *)) + align = sizeof(void *); + + mem = xmalloc(size + sizeof(void *) + align - 1); + res = (void *)round((uintptr_t)mem + sizeof(void *), align); + *(void **)((uintptr_t)res - sizeof(void *)) = mem; + return (res); +} + +void +free_aligned(void *ptr) +{ + void *mem; + uintptr_t x; + + if (ptr == NULL) + return; + x = (uintptr_t)ptr; + x -= sizeof(void *); + mem = *(void **)x; + free(mem); } diff --git a/sys/i386/i386_exec.c b/sys/i386/i386_exec.c index ae70b23..536ef29 100644 --- a/sys/i386/i386_exec.c +++ b/sys/i386/i386_exec.c @@ -321,7 +321,7 @@ //*tmp++ = 0x0; // Stack EIP Return Addr //*tmp++ = tmp + 1; // Pointer To AP *tmp++ = 0x1; // ARGC - *tmp++ = 0x0; // ARGV + *tmp++ = 0x100; // ARGV *tmp++ = 0x0; // ARGV TERM *tmp++ = 0x0; // ENV *tmp++ = 0x0; // ENV TERM @@ -329,7 +329,7 @@ *tmp++ = 0x0; // AUX 1.B *tmp++ = 0x0; // AUX TERM *tmp++ = 0x0; // AUX TERM - *tmp++ = 0x0; // TERM + *tmp++ = 0x1; // TERM /* Switch Back To The Kernels VM Space */ asm volatile( @@ -380,6 +380,7 @@ elfProgramHeader *programHeader = 0x0; elfSectionHeader *sectionHeader = 0x0; elfDynamic *elfDynamicS = 0x0; + elfDynamic *dynp = 0x0; u_long text_addr = 0, text_size = 0; u_long data_addr = 0, data_size = 0; @@ -551,22 +552,52 @@ /* What is this doing? 11/23/06 */ if ( elfDynamicS != 0x0 ) { - for ( i = 0; i < 12; i++ ) { - if ( elfDynamicS[i].dynVal == 0x3 ) { - tmp = (void *) elfDynamicS[i].dynPtr; - if ( tmp == 0x0 ) - kpanic( "tmp: NULL\n" ); - else - kprintf("[0x%X]", tmp); - tmp[2] = (uInt32) ldAddr; - tmp[1] = (uInt32) fd; + for ( dynp = elfDynamicS ; dynp->d_tag != 0x0; dynp++) { + switch (dynp->d_tag) { + case DT_NEEDED: + break; + case DT_INIT: + break; + case DT_FINI: + break; + case DT_HASH: + kprintf("COM BACK TO THIS"); + break; + case DT_STRTAB: + break; + case DT_SYMTAB: + break; + case DT_STRSZ: + break; + case DT_SYMENT: + break; + case DT_DEBUG: + break; + case DT_PLTRE: + break; + case DT_JMPREL: + break; + case DT_REL: + break; + case DT_RELSZ: + break; + case DT_RELENT: + if (dynp->d_un.d_val != sizeof(Elf_Rel)) + kpanic("NOEXEC"); + break; + case DT_PLTGOT: + tmp = (void *) dynp->d_un.d_ptr;//elfDynamicS[i].dynPtr; + if ( tmp == 0x0 ) + kpanic( "tmp: NULL\n" ); + else + kprintf("[0x%X]", tmp); + tmp[2] = (uInt32) ldAddr; + tmp[1] = (uInt32) fd; + break; + default: + kprintf("t_tag: 0x%X>", dynp->d_tag); break; } - /* - else { - kprintf("dyn_val: %i",elfDynamicS[i].dynVal); - } - */ } } /* @@ -611,9 +642,9 @@ //kprintf( "EBP-1(%i): EBP: [0x%X], EIP: [0x%X], ESP: [0x%X]\n", _current->id, iFrame->ebp, iFrame->eip, iFrame->user_esp ); - argc = 2; + argc = 1; - iFrame->ebp = STACK_ADDR; + iFrame->ebp = 0x0;//STACK_ADDR; iFrame->eip = binaryHeader->e_entry; //iFrame->user_ebp = 0x0; iFrame->edx = 0x0; @@ -622,10 +653,12 @@ tmp = (void *) iFrame->user_esp; //MrOlsen 2017-11-14 iFrame->user_ebp; + kprintf("[0x%X][0x%X]", iFrame->user_esp,tmp); + //memset(tmp,0x0,((sizeof(uint32_t) * (argc + 8 + 1)) + (sizeof(Elf32_Auxinfo) * 2))); memset((char *)(STACK_ADDR - 128),0x0,128); - *tmp++ = argc; // ARGC + tmp[0] = argc; // ARGC /* if ( argc == 1 ) { @@ -637,18 +670,19 @@ } } */ + tmp[1] = 0x0; // ARGV + tmp[2] = 0x0; // ARGV Terminator - *tmp++ = 0x0; // ARGV Terminator - *tmp++ = 0x0; // ENV - *tmp++ = 0x0; // ENV Terminator + tmp[3] = 0x0; // ENV + tmp[4] = 0x0; // ENV Terminator - *tmp++ = 0x0; - *tmp++ = 0x0; + tmp[5] = 0x0; + tmp[6] = 0x0; - *tmp++ = 0x0; // AUX VECTOR 8 Bytes - *tmp++ = 0x0; // Terminator + tmp[7] = 0x0; // AUX VECTOR 8 Bytes + tmp[8] = 0x0; // Terminator - *tmp++ = 0x0; // End Marker + tmp[9] = 0xDEADBEEF; // End Marker kfree( argvNew ); diff --git a/sys/i386/trap.c b/sys/i386/trap.c index f98c210..a30c2f7 100644 --- a/sys/i386/trap.c +++ b/sys/i386/trap.c @@ -32,6 +32,11 @@ #include #include #include +#include + +int kmain(uint32_t); + +#define KERNEL_STACK 0x2000 static void trap_end_task(char *string, struct trapframe *regs, long error_code); @@ -83,28 +88,68 @@ int i; unsigned long esp; unsigned short ss; + unsigned long *stack, addr, module_start, module_end; + char *_etext = 0x300a0; esp = (unsigned long) ®s->tf_esp; - //ss = KERNEL_DS; - ss = 0x10; - if ((regs->tf_eflags & VM_MASK) || (3 & regs->tf_cs) == 3) - return; - if (regs->tf_cs & 3) { + + ss = 0x10; //KERNEL_DS + + //if ((regs->tf_eflags & VM_MASK) || (3 & regs->tf_cs) == 3) + // return; + + // if ((regs->tf_cs & 3) == 3) { esp = regs->tf_esp; ss = regs->tf_ss; - } + kprintf("USER TASK!"); + //} - kprintf("%s: %04lx\n", str, err & 0xffff); - kprintf("EIP: %04x:%08lx\nEFLAGS: %08lx\n", 0xffff & regs->tf_cs, regs->tf_eip, regs->tf_eflags); + ss = 0x30; + + kprintf("%s: %04lx(%i:%i)[0x%X]\n", str, err & 0xffff, regs->tf_trapno, regs->tf_err, regs->tf_ss); + kprintf("CPU: %d\n", 0); + kprintf("EIP: %04x:[<%08lx>]\nEFLAGS: %08lx\n", 0xffff & regs->tf_cs, regs->tf_eip, regs->tf_eflags); kprintf("eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n", regs->tf_eax, regs->tf_ebx, regs->tf_ecx, regs->tf_edx); kprintf("esi: %08lx edi: %08lx ebp: %08lx esp: %08lx\n", regs->tf_esi, regs->tf_edi, regs->tf_ebp, esp); - kprintf("ds: %04x es: %04x fs: %04x gs: %04x ss: %04x\n", regs->tf_ds, regs->tf_es, regs->tf_fs, regs->tf_gs, ss); + kprintf("cs: 0x%X ds: 0x%X es: 0x%X fs: 0x%X gs: 0x%X ss: 0x%X\n", regs->tf_cs, regs->tf_ds, regs->tf_es, regs->tf_fs, regs->tf_gs, ss); store_TR(i); - //kprintf("Pid: %d, process nr: %d (%s)\nStack: ", _current->id, 0xffff & i, _current->comm); - kprintf("Pid: %d, process nr: %d ()\nStack: ", _current->id, 0xffff & i); - for (i = 0; i < 5; i++) - kprintf("%08lx ", get_seg_long(ss, (i+(unsigned long *)esp))); - kprintf("\nCode: "); + kprintf("Process %s (pid: %i, process nr: %d, stackpage=%08lx)\nStack:", _current->name, _current->id, 0xffff & i, KERNEL_STACK); + + stack = (unsigned long *)esp; + for (i = 0; i < 16; i++) { + if (i && ((i % 8) == 0)) + kprintf("\n "); + kprintf("%08lx ", get_seg_long(ss, stack++)); + } + kprintf("\nCall Trace: "); + stack = (unsigned long *)esp; + i = 1; + +#define VMALLOC_OFFSET (8*1024*1024) +#define MODULE_RANGE (8*1024*1024) + + module_start = ((numPages + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)); + module_end = module_start + MODULE_RANGE; + + //while (((long) stack & 4095) != 0) { + while (i < 12) { + addr = get_seg_long(ss, stack++); + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (((addr >= (unsigned long) &kmain) && (addr <= (unsigned long) &_etext)) || ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + kprintf("\n "); + kprintf("[<%08lx>] ", addr); + i++; + } + } + for (i = 0; i < 20; i++) kprintf("%02x ", 0xff & get_seg_byte(regs->tf_cs, (i+(char *)regs->tf_eip))); kprintf("\n"); @@ -131,6 +176,10 @@ cr2 = rcr2(); kprintf("trap_code: %i(0x%X), EIP: 0x%X, CR2: 0x%X\n", frame->tf_trapno, frame->tf_trapno, frame->tf_eip, cr2); + die_if_kernel("trapCode", frame, frame->tf_trapno); + endTask( _current->id ); + sched_yield(); + /* switch (trap_code) { diff --git a/sys/include/i386/elf.h b/sys/include/i386/elf.h index 907e734..0f92ee6 100644 --- a/sys/include/i386/elf.h +++ b/sys/include/i386/elf.h @@ -45,6 +45,14 @@ typedef struct { uInt32 dynVal; uInt32 dynPtr; +} elfDynamic_Old; + +typedef struct { + uint32_t d_tag; + union { + uint32_t d_val; + uint32_t d_ptr; + } d_un; } elfDynamic; typedef struct { diff --git a/sys/include/i386/signal.h b/sys/include/i386/signal.h index 1481c5b..bc08ad9 100644 --- a/sys/include/i386/signal.h +++ b/sys/include/i386/signal.h @@ -1,41 +1,41 @@ -#ifndef _I386_SIGNAL_H_ -#define _i386_SIGNAL_H_ - -#define _NSIG 32 -#define NSIG _NSIG - -#define SIGHUP 1 -#define SIGINT 2 -#define SIGQUIT 3 -#define SIGILL 4 -#define SIGTRAP 5 -#define SIGABRT 6 -#define SIGIOT 6 -#define SIGBUS 7 -#define SIGFPE 8 -#define SIGKILL 9 -#define SIGUSR1 10 -#define SIGSEGV 11 -#define SIGUSR2 12 -#define SIGPIPE 13 -#define SIGALRM 14 -#define SIGTERM 15 -#define SIGSTKFLT 16 -#define SIGCHLD 17 -#define SIGCONT 18 -#define SIGSTOP 19 -#define SIGTSTP 20 -#define SIGTTIN 21 -#define SIGTTOU 22 -#define SIGURG 23 -#define SIGXCPU 24 -#define SIGXFSZ 25 -#define SIGVTALRM 26 -#define SIGPROF 27 -#define SIGWINCH 28 -#define SIGIO 29 -#define SIGPOLL SIGIO -#define SIGPWR 30 -#define SIGUNUSED 31 - -#endif +#ifndef _I386_SIGNAL_H_ +#define _I386_SIGNAL_H_ + +#define _NSIG 32 +#define NSIG _NSIG + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +#define SIGPWR 30 +#define SIGUNUSED 31 + +#endif diff --git a/sys/include/ubixos/sched.h b/sys/include/ubixos/sched.h index b6649d7..229fe6c 100644 --- a/sys/include/ubixos/sched.h +++ b/sys/include/ubixos/sched.h @@ -54,6 +54,7 @@ typedef struct taskStruct { pidType id; + char name[256]; struct taskStruct *prev; struct taskStruct *next; struct tssStruct tss; diff --git a/sys/sys/idt.c b/sys/sys/idt.c index cb27a24..1e1febd 100644 --- a/sys/sys/idt.c +++ b/sys/sys/idt.c @@ -68,7 +68,7 @@ /* Set up default vector table for all possible 256 interrupts */ for ( i = 0x0; i < 256; i++ ) { - setVector( intNull, i, dPresent + dInt + dDpl3 ); + setVector( intNull, i, dPresent + dTrap + dDpl3 ); } /* Load the IDT into the system */ @@ -90,7 +90,7 @@ setVector( _int3, 3, dPresent + dInt + dDpl0 ); setVector( _int4, 4, dPresent + dInt + dDpl0 ); setVector( _int5, 5, dPresent + dInt + dDpl0 ); - setVector( _int6, 6, dPresent + dInt + dDpl0 ); + setVector( _int6, 6, dPresent + dTrap + dDpl0 ); setVector( _int7, 7, dPresent + dInt + dDpl0 ); setTaskVector( 8, dPresent + dTask + dDpl0, 0x40 ); setVector( _int9, 9, dPresent + dInt + dDpl0 ); @@ -227,6 +227,8 @@ asm volatile( ".globl _int6 \n" "_int6: \n" + " pushl $0x0 \n" + " pushl $0x6 \n" " pushal \n" /* Save all registers */ " push %ds \n" " push %es \n" @@ -245,7 +247,11 @@ void __int6(struct trapframe *frame) { die_if_kernel("invalid_op",frame,6); +<<<<<<< HEAD //kprintf("tf_gs: 0x%X, tf_fs: 0x%X, tf_es: 0x%X, tf_ds: 0x%X\n", frame->tf_gs, frame->tf_fs, frame->tf_es, frame->tf_ds); +======= + kprintf("tf_gs: 0x%X, tf_fs: 0x%X, tf_es: 0x%X, tf_ds: 0x%X\n", frame->tf_gs, frame->tf_fs, frame->tf_es, frame->tf_ds); +>>>>>>> branch 'master' of http://Git.BrainChurts.com:8080/git/MrOlsen/UbixOS.git //kpanic( "int6: Invalid opcode! [%i:0x%X:0x%X]\n", _current->id, _current->tss.eip, frame->tf_eip ); endTask( _current->id ); sched_yield();