diff --git a/include/dlfcn.h b/include/dlfcn.h index 9990e6efbdf2..09edf2dde31b 100644 --- a/include/dlfcn.h +++ b/include/dlfcn.h @@ -66,6 +66,13 @@ __BEGIN_DECLS int dladdr __P((const void *, Dl_info *)); int dlclose __P((void *)); const char *dlerror __P((void)); +void dllockinit __P((void *_context, + void *(*_lock_create)(void *_context), + void (*_rlock_acquire)(void *_lock), + void (*_wlock_acquire)(void *_lock), + void (*_lock_release)(void *_lock), + void (*_lock_destroy)(void *_lock), + void (*_context_destroy)(void *_context))); void *dlopen __P((const char *, int)); void *dlsym __P((void *, const char *)); __END_DECLS diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index 9a23677c8873..a193cc2636c5 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -34,7 +34,7 @@ SRCS+= _rand48.c _spinlock_stub.c alarm.c arc4random.c assert.c \ .if ${LIB} == "c" MAN3+= alarm.3 arc4random.3 clock.3 \ confstr.3 crypt.3 ctermid.3 daemon.3 \ - devname.3 directory.3 dladdr.3 dlopen.3 \ + devname.3 directory.3 dladdr.3 dllockinit.3 dlopen.3 \ err.3 exec.3 fnmatch.3 frexp.3 ftok.3 fts.3 \ getbootfile.3 getbsize.3 getcap.3 getcwd.3 \ getdiskbyname.3 getdomainname.3 getfsent.3 \ diff --git a/lib/libc/gen/dlfcn.c b/lib/libc/gen/dlfcn.c index ff77b79d83f3..fd0be349c112 100644 --- a/lib/libc/gen/dlfcn.c +++ b/lib/libc/gen/dlfcn.c @@ -78,6 +78,20 @@ dlerror(void) return sorry; } +#pragma weak dllockinit +void +dllockinit(void *context, + void *(*lock_create)(void *context), + void (*rlock_acquire)(void *lock), + void (*wlock_acquire)(void *lock), + void (*lock_release)(void *lock), + void (*lock_destroy)(void *lock), + void (*context_destroy)(void *context)) +{ + if (context_destroy != NULL) + context_destroy(context); +} + #pragma weak dlopen void * dlopen(const char *name, int mode) diff --git a/lib/libc/gen/dllockinit.3 b/lib/libc/gen/dllockinit.3 new file mode 100644 index 000000000000..facb18ba0353 --- /dev/null +++ b/lib/libc/gen/dllockinit.3 @@ -0,0 +1,109 @@ +.\" +.\" Copyright (c) 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 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$ +.\" +.Dd December 26, 1999 +.Os FreeBSD +.Dt DLLOCKINIT 3 +.Sh NAME +.Nm dllockinit +.Nd register thread locking methods with the dynamic linker +.Sh SYNOPSIS +.Fd #include +.Ft void +.Fn dllockinit "const void *context" "void *(*lock_create)(void *context)" \ +"void (*rlock_acquire)(void *lock)" "void (*wlock_acquire)(void *lock)" \ +"void (*lock_release)(void *lock)" "void (*lock_destroy)(void *lock)" \ +"void (*context_destroy)(void *context)" +.Sh DESCRIPTION +Threads packages can call +.Nm +at initialization time to register locking functions for the dynamic +linker to use. This enables the dynamic linker to prevent multiple +threads from entering its critical sections simultaneously. +.Pp +The +.Fa context +parameter specifies an opaque context for creating locks. The +dynamic linker will pass it to the +.Fa lock_create +function when creating the locks it needs. When the dynamic linker +is permanently finished using the locking functions (e.g., if the +program makes a subsequent call to +.Nm +to register new locking functions) it will call +.Fa context_destroy +to destroy the context. +.Pp +The +.Fa lock_create +parameter specifies a function for creating a read/write lock. It +must return a pointer to the new lock. +.Pp +The +.Fa rlock_acquire +and +.Fa wlock_acquire +parameters specify functions which lock a lock for reading or +writing, respectively. The +.Fa lock_release +parameter specifies a function which unlocks a lock. Each of these +functions is passed a pointer to the lock. +.Pp +The +.Fa lock_destroy +parameter specifies a function to destroy a lock. It may be +.Dv NULL +if locks do not need to be destroyed. The +.Fa context_destroy +specifies a function to destroy the context. It may be +.Dv NULL +if the context does not need to be destroyed. +.Pp +Before +.Nm +is called, the dynamic linker protects its critical sections by +blocking the +.Dv SIGVTALRM , +.Dv SIGPROF , +and +.Dv SIGALRM +signals. This is sufficient for many application level threads +packages, which typically use one of these signals to implement +preemption. An application which has registered its own locking +methods with +.Nm +can restore the default locking by calling +.Nm +with all arguments +.Dv NULL . +.Sh SEE ALSO +.Xr rtld 1 , +.Xr signal 3 +.Sh HISTORY +The +.Nm +function first appeared in FreeBSD 4.0. diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile index 4b02d4b6aecd..29935143ace1 100644 --- a/libexec/rtld-elf/Makefile +++ b/libexec/rtld-elf/Makefile @@ -3,8 +3,8 @@ # MAINTAINER= jdp PROG= ld-elf.so.1 -SRCS= rtld_start.S rtld.c map_object.c malloc.c xmalloc.c debug.c \ - reloc.c +SRCS= rtld_start.S rtld.c lockdflt.c map_object.c malloc.c \ + xmalloc.c debug.c reloc.c NOMAN= true CFLAGS+= -Wall -DFREEBSD_ELF -I${.CURDIR}/${MACHINE_ARCH} -I${.CURDIR} LDFLAGS+= -nostdlib -e .rtld_start diff --git a/libexec/rtld-elf/alpha/lockdflt.c b/libexec/rtld-elf/alpha/lockdflt.c new file mode 100644 index 000000000000..6f278c3ab284 --- /dev/null +++ b/libexec/rtld-elf/alpha/lockdflt.c @@ -0,0 +1,94 @@ +/*- + * Copyright 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$ + */ + +/* + * Default thread locking implementation for the dynamic linker. It + * is used until the client registers a different implementation with + * dllockinit(). The default implementation does mutual exclusion + * by blocking the SIGVTALRM, SIGPROF, and SIGALRM signals. This is + * based on the observation that most userland thread packages use one + * of these signals to support preemption. + */ + +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +typedef struct Struct_LockDflt { + sigset_t lock_mask; + sigset_t old_mask; + int depth; +} LockDflt; + +void +lockdflt_acquire(void *lock) +{ + LockDflt *l = (LockDflt *)lock; + sigprocmask(SIG_BLOCK, &l->lock_mask, &l->old_mask); + assert(l->depth == 0); + l->depth++; +} + +void * +lockdflt_create(void *context) +{ + LockDflt *l; + + l = NEW(LockDflt); + l->depth = 0; + sigemptyset(&l->lock_mask); + sigaddset(&l->lock_mask, SIGVTALRM); + sigaddset(&l->lock_mask, SIGPROF); + sigaddset(&l->lock_mask, SIGALRM); + return l; +} + +void +lockdflt_destroy(void *lock) +{ + LockDflt *l = (LockDflt *)lock; + free(l); +} + +void +lockdflt_release(void *lock) +{ + LockDflt *l = (LockDflt *)lock; + assert(l->depth == 1); + l->depth--; + sigprocmask(SIG_SETMASK, &l->old_mask, NULL); +} + +void +lockdflt_init(void) +{ + dllockinit(NULL, lockdflt_create, lockdflt_acquire, lockdflt_acquire, + lockdflt_release, lockdflt_destroy, NULL); +} diff --git a/libexec/rtld-elf/alpha/reloc.c b/libexec/rtld-elf/alpha/reloc.c index 1b55d06e767c..114f3ae438ed 100644 --- a/libexec/rtld-elf/alpha/reloc.c +++ b/libexec/rtld-elf/alpha/reloc.c @@ -47,15 +47,6 @@ #include "debug.h" #include "rtld.h" -/* - * Debugging support. - */ - -#define assert(cond) ((cond) ? (void) 0 :\ - (msg("oops: " __XSTRING(__LINE__) "\n"), abort())) -#define msg(s) (write(1, s, strlen(s))) -#define trace() msg("trace: " __XSTRING(__LINE__) "\n"); - extern Elf_Dyn _DYNAMIC; /* Relocate a non-PLT object with addend. */ diff --git a/libexec/rtld-elf/amd64/lockdflt.c b/libexec/rtld-elf/amd64/lockdflt.c new file mode 100644 index 000000000000..6f278c3ab284 --- /dev/null +++ b/libexec/rtld-elf/amd64/lockdflt.c @@ -0,0 +1,94 @@ +/*- + * Copyright 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$ + */ + +/* + * Default thread locking implementation for the dynamic linker. It + * is used until the client registers a different implementation with + * dllockinit(). The default implementation does mutual exclusion + * by blocking the SIGVTALRM, SIGPROF, and SIGALRM signals. This is + * based on the observation that most userland thread packages use one + * of these signals to support preemption. + */ + +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +typedef struct Struct_LockDflt { + sigset_t lock_mask; + sigset_t old_mask; + int depth; +} LockDflt; + +void +lockdflt_acquire(void *lock) +{ + LockDflt *l = (LockDflt *)lock; + sigprocmask(SIG_BLOCK, &l->lock_mask, &l->old_mask); + assert(l->depth == 0); + l->depth++; +} + +void * +lockdflt_create(void *context) +{ + LockDflt *l; + + l = NEW(LockDflt); + l->depth = 0; + sigemptyset(&l->lock_mask); + sigaddset(&l->lock_mask, SIGVTALRM); + sigaddset(&l->lock_mask, SIGPROF); + sigaddset(&l->lock_mask, SIGALRM); + return l; +} + +void +lockdflt_destroy(void *lock) +{ + LockDflt *l = (LockDflt *)lock; + free(l); +} + +void +lockdflt_release(void *lock) +{ + LockDflt *l = (LockDflt *)lock; + assert(l->depth == 1); + l->depth--; + sigprocmask(SIG_SETMASK, &l->old_mask, NULL); +} + +void +lockdflt_init(void) +{ + dllockinit(NULL, lockdflt_create, lockdflt_acquire, lockdflt_acquire, + lockdflt_release, lockdflt_destroy, NULL); +} diff --git a/libexec/rtld-elf/amd64/reloc.c b/libexec/rtld-elf/amd64/reloc.c index b4a2a75c63f2..217d88e707de 100644 --- a/libexec/rtld-elf/amd64/reloc.c +++ b/libexec/rtld-elf/amd64/reloc.c @@ -47,15 +47,6 @@ #include "debug.h" #include "rtld.h" -/* - * Debugging support. - */ - -#define assert(cond) ((cond) ? (void) 0 :\ - (msg("oops: " __XSTRING(__LINE__) "\n"), abort())) -#define msg(s) (write(1, s, strlen(s))) -#define trace() msg("trace: " __XSTRING(__LINE__) "\n"); - /* * 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 diff --git a/libexec/rtld-elf/debug.h b/libexec/rtld-elf/debug.h index b3563386eaff..2600651997e8 100644 --- a/libexec/rtld-elf/debug.h +++ b/libexec/rtld-elf/debug.h @@ -38,6 +38,9 @@ #include +#include +#include + extern void debug_printf(const char *, ...) __printflike(1, 2); extern int debug; @@ -47,4 +50,10 @@ extern int debug; #define dbg(format, args...) ((void) 0) #endif +#define assert(cond) ((cond) ? (void) 0 : \ + (msg("ld-elf.so.1: assert failed: " __FILE__ ":" \ + __XSTRING(__LINE__) "\n"), abort())) +#define msg(s) write(1, s, strlen(s)) +#define trace() msg("ld-elf.so.1: " __XSTRING(__LINE__) "\n") + #endif /* DEBUG_H */ diff --git a/libexec/rtld-elf/i386/lockdflt.c b/libexec/rtld-elf/i386/lockdflt.c new file mode 100644 index 000000000000..6f278c3ab284 --- /dev/null +++ b/libexec/rtld-elf/i386/lockdflt.c @@ -0,0 +1,94 @@ +/*- + * Copyright 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$ + */ + +/* + * Default thread locking implementation for the dynamic linker. It + * is used until the client registers a different implementation with + * dllockinit(). The default implementation does mutual exclusion + * by blocking the SIGVTALRM, SIGPROF, and SIGALRM signals. This is + * based on the observation that most userland thread packages use one + * of these signals to support preemption. + */ + +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +typedef struct Struct_LockDflt { + sigset_t lock_mask; + sigset_t old_mask; + int depth; +} LockDflt; + +void +lockdflt_acquire(void *lock) +{ + LockDflt *l = (LockDflt *)lock; + sigprocmask(SIG_BLOCK, &l->lock_mask, &l->old_mask); + assert(l->depth == 0); + l->depth++; +} + +void * +lockdflt_create(void *context) +{ + LockDflt *l; + + l = NEW(LockDflt); + l->depth = 0; + sigemptyset(&l->lock_mask); + sigaddset(&l->lock_mask, SIGVTALRM); + sigaddset(&l->lock_mask, SIGPROF); + sigaddset(&l->lock_mask, SIGALRM); + return l; +} + +void +lockdflt_destroy(void *lock) +{ + LockDflt *l = (LockDflt *)lock; + free(l); +} + +void +lockdflt_release(void *lock) +{ + LockDflt *l = (LockDflt *)lock; + assert(l->depth == 1); + l->depth--; + sigprocmask(SIG_SETMASK, &l->old_mask, NULL); +} + +void +lockdflt_init(void) +{ + dllockinit(NULL, lockdflt_create, lockdflt_acquire, lockdflt_acquire, + lockdflt_release, lockdflt_destroy, NULL); +} diff --git a/libexec/rtld-elf/i386/reloc.c b/libexec/rtld-elf/i386/reloc.c index b4a2a75c63f2..217d88e707de 100644 --- a/libexec/rtld-elf/i386/reloc.c +++ b/libexec/rtld-elf/i386/reloc.c @@ -47,15 +47,6 @@ #include "debug.h" #include "rtld.h" -/* - * Debugging support. - */ - -#define assert(cond) ((cond) ? (void) 0 :\ - (msg("oops: " __XSTRING(__LINE__) "\n"), abort())) -#define msg(s) (write(1, s, strlen(s))) -#define trace() msg("trace: " __XSTRING(__LINE__) "\n"); - /* * 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 diff --git a/libexec/rtld-elf/lockdflt.c b/libexec/rtld-elf/lockdflt.c new file mode 100644 index 000000000000..6f278c3ab284 --- /dev/null +++ b/libexec/rtld-elf/lockdflt.c @@ -0,0 +1,94 @@ +/*- + * Copyright 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$ + */ + +/* + * Default thread locking implementation for the dynamic linker. It + * is used until the client registers a different implementation with + * dllockinit(). The default implementation does mutual exclusion + * by blocking the SIGVTALRM, SIGPROF, and SIGALRM signals. This is + * based on the observation that most userland thread packages use one + * of these signals to support preemption. + */ + +#include +#include +#include + +#include "debug.h" +#include "rtld.h" + +typedef struct Struct_LockDflt { + sigset_t lock_mask; + sigset_t old_mask; + int depth; +} LockDflt; + +void +lockdflt_acquire(void *lock) +{ + LockDflt *l = (LockDflt *)lock; + sigprocmask(SIG_BLOCK, &l->lock_mask, &l->old_mask); + assert(l->depth == 0); + l->depth++; +} + +void * +lockdflt_create(void *context) +{ + LockDflt *l; + + l = NEW(LockDflt); + l->depth = 0; + sigemptyset(&l->lock_mask); + sigaddset(&l->lock_mask, SIGVTALRM); + sigaddset(&l->lock_mask, SIGPROF); + sigaddset(&l->lock_mask, SIGALRM); + return l; +} + +void +lockdflt_destroy(void *lock) +{ + LockDflt *l = (LockDflt *)lock; + free(l); +} + +void +lockdflt_release(void *lock) +{ + LockDflt *l = (LockDflt *)lock; + assert(l->depth == 1); + l->depth--; + sigprocmask(SIG_SETMASK, &l->old_mask, NULL); +} + +void +lockdflt_init(void) +{ + dllockinit(NULL, lockdflt_create, lockdflt_acquire, lockdflt_acquire, + lockdflt_release, lockdflt_destroy, NULL); +} diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 8ba21b4f26c7..5bcd390a6232 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -52,21 +52,23 @@ #include "debug.h" #include "rtld.h" -/* - * Debugging support. - */ - -#define assert(cond) ((cond) ? (void) 0 :\ - (msg("oops: " __XSTRING(__LINE__) "\n"), abort())) -#define msg(s) (write(1, s, strlen(s))) -#define trace() msg("trace: " __XSTRING(__LINE__) "\n"); - #define END_SYM "_end" #define PATH_RTLD "/usr/libexec/ld-elf.so.1" /* Types. */ typedef void (*func_ptr_type)(); +typedef struct Struct_LockInfo { + void *context; /* Client context for creating locks */ + void *thelock; /* The one big lock */ + /* Methods */ + void (*rlock_acquire)(void *lock); + void (*wlock_acquire)(void *lock); + void (*lock_release)(void *lock); + void (*lock_destroy)(void *lock); + void (*context_destroy)(void *context); +} LockInfo; + /* * Function declarations. */ @@ -88,6 +90,7 @@ static void linkmap_delete(Obj_Entry *); static int load_needed_objects(Obj_Entry *); static int load_preload_objects(void); static Obj_Entry *load_object(char *); +static void lock_nop(void *); static Obj_Entry *obj_from_addr(const void *); static void objlist_add(Objlist *, Obj_Entry *); static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *); @@ -128,6 +131,8 @@ static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */ static Objlist list_main = /* Objects loaded at program startup */ STAILQ_HEAD_INITIALIZER(list_main); +static LockInfo lockinfo; + static Elf_Sym sym_zero; /* For resolving undefined weak refs. */ #define GDB_STATE(s) r_debug.r_state = s; r_debug_state(); @@ -147,6 +152,7 @@ static func_ptr_type exports[] = { (func_ptr_type) &dlopen, (func_ptr_type) &dlsym, (func_ptr_type) &dladdr, + (func_ptr_type) &dllockinit, NULL }; @@ -157,6 +163,24 @@ static func_ptr_type exports[] = { char *__progname; char **environ; +static __inline void +rlock_acquire(void) +{ + lockinfo.rlock_acquire(lockinfo.thelock); +} + +static __inline void +wlock_acquire(void) +{ + lockinfo.wlock_acquire(lockinfo.thelock); +} + +static __inline void +lock_release(void) +{ + lockinfo.lock_release(lockinfo.thelock); +} + /* * Main entry point for dynamic linking. The first argument is the * stack pointer. The stack is expected to be laid out as described @@ -322,6 +346,9 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) set_program_var("__progname", argv[0] != NULL ? basename(argv[0]) : ""); set_program_var("environ", env); + dbg("initializing default locks"); + dllockinit(NULL, NULL, NULL, NULL, NULL, NULL, NULL); + r_debug_state(); /* say hello to gdb! */ dbg("calling _init functions"); @@ -344,6 +371,7 @@ _rtld_bind(Obj_Entry *obj, Elf_Word reloff) Elf_Addr *where; Elf_Addr target; + wlock_acquire(); if (obj->pltrel) rel = (const Elf_Rel *) ((caddr_t) obj->pltrel + reloff); else @@ -361,6 +389,7 @@ _rtld_bind(Obj_Entry *obj, Elf_Word reloff) (void *)target, basename(defobj->path)); reloc_jmpslot(where, target); + lock_release(); return target; } @@ -1058,6 +1087,11 @@ load_object(char *path) return obj; } +static void +lock_nop(void *lock) +{ +} + static Obj_Entry * obj_from_addr(const void *addr) { @@ -1223,16 +1257,21 @@ search_library_path(const char *name, const char *path) int dlclose(void *handle) { - Obj_Entry *root = dlcheck(handle); + Obj_Entry *root; - if (root == NULL) + wlock_acquire(); + root = dlcheck(handle); + if (root == NULL) { + lock_release(); return -1; + } GDB_STATE(RT_DELETE); unload_object(root, true); root->dl_refcount--; GDB_STATE(RT_CONSISTENT); + lock_release(); return 0; } @@ -1244,14 +1283,67 @@ dlerror(void) return msg; } +void +dllockinit(void *context, + void *(*lock_create)(void *context), + void (*rlock_acquire)(void *lock), + void (*wlock_acquire)(void *lock), + void (*lock_release)(void *lock), + void (*lock_destroy)(void *lock), + void (*context_destroy)(void *context)) +{ + /* NULL arguments mean reset to the built-in locks. */ + if (lock_create == NULL) { + context = NULL; + lock_create = lockdflt_create; + rlock_acquire = wlock_acquire = lockdflt_acquire; + lock_release = lockdflt_release; + lock_destroy = lockdflt_destroy; + context_destroy = NULL; + } + + /* Temporarily set locking methods to no-ops. */ + lockinfo.rlock_acquire = lock_nop; + lockinfo.wlock_acquire = lock_nop; + lockinfo.lock_release = lock_nop; + + /* Release any existing locks and context. */ + if (lockinfo.lock_destroy != NULL) + lockinfo.lock_destroy(lockinfo.thelock); + if (lockinfo.context_destroy != NULL) + lockinfo.context_destroy(lockinfo.context); + + /* + * Allocate the locks we will need and call all the new locking + * methods, to accomplish any needed lazy binding for the methods + * themselves. + */ + lockinfo.thelock = lock_create(lockinfo.context); + rlock_acquire(lockinfo.thelock); + lock_release(lockinfo.thelock); + wlock_acquire(lockinfo.thelock); + lock_release(lockinfo.thelock); + + /* Record the new method information. */ + lockinfo.context = context; + lockinfo.rlock_acquire = rlock_acquire; + lockinfo.wlock_acquire = wlock_acquire; + lockinfo.lock_release = lock_release; + lockinfo.lock_destroy = lock_destroy; + lockinfo.context_destroy = context_destroy; +} + void * dlopen(const char *name, int mode) { - Obj_Entry **old_obj_tail = obj_tail; - Obj_Entry *obj = NULL; + Obj_Entry **old_obj_tail; + Obj_Entry *obj; + wlock_acquire(); GDB_STATE(RT_ADD); + old_obj_tail = obj_tail; + obj = NULL; if (name == NULL) { obj = obj_main; obj->refcount++; @@ -1280,7 +1372,7 @@ dlopen(const char *name, int mode) } GDB_STATE(RT_CONSISTENT); - + lock_release(); return obj; } @@ -1296,12 +1388,14 @@ dlsym(void *handle, const char *name) def = NULL; defobj = NULL; + wlock_acquire(); if (handle == NULL || handle == RTLD_NEXT) { void *retaddr; retaddr = __builtin_return_address(0); /* __GNUC__ only */ if ((obj = obj_from_addr(retaddr)) == NULL) { _rtld_error("Cannot determine caller's shared object"); + lock_release(); return NULL; } if (handle == NULL) { /* Just the caller's shared object. */ @@ -1316,8 +1410,10 @@ dlsym(void *handle, const char *name) } } } else { - if ((obj = dlcheck(handle)) == NULL) + if ((obj = dlcheck(handle)) == NULL) { + lock_release(); return NULL; + } if (obj->mainprog) { /* Search main program and all libraries loaded by it. */ @@ -1333,10 +1429,13 @@ dlsym(void *handle, const char *name) } } - if (def != NULL) + if (def != NULL) { + lock_release(); return defobj->relocbase + def->st_value; + } _rtld_error("Undefined symbol \"%s\"", name); + lock_release(); return NULL; } @@ -1348,9 +1447,11 @@ dladdr(const void *addr, Dl_info *info) void *symbol_addr; unsigned long symoffset; + wlock_acquire(); obj = obj_from_addr(addr); if (obj == NULL) { _rtld_error("No shared object contains address"); + lock_release(); return 0; } info->dli_fname = obj->path; @@ -1389,6 +1490,7 @@ dladdr(const void *addr, Dl_info *info) if (info->dli_saddr == addr) break; } + lock_release(); return 1; } diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index c63e7d08836b..b8fcf2c78fb2 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -157,6 +157,10 @@ unsigned long elf_hash(const char *); const Elf_Sym *find_symdef(unsigned long, Obj_Entry *, const Obj_Entry **, bool); void init_pltgot(Obj_Entry *); +void lockdflt_acquire(void *); +void *lockdflt_create(void *); +void lockdflt_destroy(void *); +void lockdflt_release(void *); void obj_free(Obj_Entry *); Obj_Entry *obj_new(void); int reloc_non_plt(Obj_Entry *, Obj_Entry *);