Allow threading libraries to register their own locking

implementation in case default one provided by rtld is
not suitable.

Consolidate various identical MD lock implementation into
a single file using appropriate machine/atomic.h.

Approved by:	re (scottl)
This commit is contained in:
Alexander Kabaev 2003-05-29 22:58:26 +00:00
parent 7792fe5719
commit 6d5d786f80
20 changed files with 558 additions and 480 deletions

View File

@ -1,10 +1,11 @@
# $FreeBSD$
PROG= ld-elf.so.1
SRCS= rtld_start.S rtld.c lockdflt.c map_object.c malloc.c \
SRCS= rtld_start.S rtld.c rtld_lock.c map_object.c malloc.c \
xmalloc.c debug.c reloc.c
MAN= rtld.1
CFLAGS+= -Wall -DFREEBSD_ELF -I${.CURDIR}/${MACHINE_ARCH} -I${.CURDIR}
CFLAGS+= -Wall -DFREEBSD_ELF -DIN_RTLD
CFLAGS+= -I${.CURDIR}/${MACHINE_ARCH} -I${.CURDIR}
LDFLAGS+= -nostdlib -e .rtld_start
INSTALLFLAGS= -fschg -C -b
MLINKS= rtld.1 ld-elf.so.1.1 \

View File

@ -29,6 +29,18 @@
#ifndef RTLD_MACHDEP_H
#define RTLD_MACHDEP_H 1
#include <sys/types.h>
#include <machine/atomic.h>
/*
* This value of CACHE_LINE_SIZE is conservative. The actual size
* is 32 on the 21064, 21064A, 21066, 21066A, and 21164. It is 64
* on the 21264. Compaq recommends sequestering each lock in its own
* 128-byte block to allow for future implementations with larger
* cache lines.
*/
#define CACHE_LINE_SIZE 128
struct Struct_Obj_Entry;
/* Return the address of the .dynamic section in the dynamic linker. */
@ -48,10 +60,4 @@ Elf_Addr reloc_jmpslot(Elf_Addr *, Elf_Addr,
/* Lazy binding entry point, called via PLT. */
void _rtld_bind_start_old(void);
/* Atomic operations. */
int cmp0_and_store_int(volatile int *, int);
void atomic_add_int(volatile int *, int);
void atomic_incr_int(volatile int *);
void atomic_decr_int(volatile int *);
#endif

View File

@ -209,52 +209,3 @@ NESTED_NOPROFILE(_rtld_bind_start_old, 0, 168, ra, 0, 0)
END(_rtld_bind_start_old)
/*
* int cmp0_and_store_int(volatile int *p, int newval);
*
* If an int holds 0, store newval into it; else do nothing. Returns
* the previous value.
*/
LEAF(cmp0_and_store_int, 2)
1: mov a1, t0
ldl_l v0, 0(a0)
bne v0, 3f
stl_c t0, 0(a0)
beq t0, 2f
mb
RET
2: br 1b
3: RET
END(cmp0_and_store_int)
LEAF(atomic_add_int, 2)
0: ldl_l t0, 0(a0)
addq t0, a1, t0
stl_c t0, 0(a0)
beq t0, 1f
mb
RET
1: br 0b
END(atomic_add_int)
/* Atomically increment an int. */
LEAF(atomic_incr_int, 1)
0: ldl_l t0, 0(a0)
addq t0, 1, t0
stl_c t0, 0(a0)
beq t0, 1f
mb
RET
1: br 0b
END(atomic_incr_int)
/* Atomically decrement an int. */
LEAF(atomic_decr_int, 1)
0: ldl_l t0, 0(a0)
subq t0, 1, t0
stl_c t0, 0(a0)
beq t0, 1f
mb
RET
1: br 0b
END(atomic_decr_int)

View File

@ -29,6 +29,11 @@
#ifndef RTLD_MACHDEP_H
#define RTLD_MACHDEP_H 1
#include <sys/types.h>
#include <machine/atomic.h>
#define CACHE_LINE_SIZE 32
struct Struct_Obj_Entry;
/* Return the address of the .dynamic section in the dynamic linker. */
@ -53,25 +58,4 @@ reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
#define call_initfini_pointer(obj, target) \
(((InitFunc)(target))())
static inline void
atomic_decr_int(volatile int *p)
{
__asm __volatile ("lock; decl %0" : "+m"(*p) : : "cc");
}
static inline void
atomic_incr_int(volatile int *p)
{
__asm __volatile ("lock; incl %0" : "+m"(*p) : : "cc");
}
static inline void
atomic_add_int(volatile int *p, int val)
{
__asm __volatile ("lock; addl %1, %0"
: "+m"(*p)
: "ri"(val)
: "cc");
}
#endif

View File

@ -50,23 +50,6 @@
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include "debug.h"
#include "rtld.h"
#define CACHE_LINE_SIZE 32
#define WAFLAG 0x1 /* A writer holds the lock */
#define RC_INCR 0x2 /* Adjusts count of readers desiring lock */
typedef struct Struct_Lock {
volatile int lock;
void *base;
} Lock;
static sigset_t fullsigmask, oldsigmask;
static inline int
cmpxchgl(int old, int new, volatile int *m)
@ -93,44 +76,6 @@ xchgl(int v, volatile int *m)
return result;
}
static void *
lock_create(void *context)
{
void *base;
char *p;
uintptr_t r;
Lock *l;
/*
* Arrange for the lock to occupy its own cache line. First, we
* optimistically allocate just a cache line, hoping that malloc
* will give us a well-aligned block of memory. If that doesn't
* work, we allocate a larger block and take a well-aligned cache
* line from it.
*/
base = xmalloc(CACHE_LINE_SIZE);
p = (char *)base;
if ((uintptr_t)p % CACHE_LINE_SIZE != 0) {
free(base);
base = xmalloc(2 * CACHE_LINE_SIZE);
p = (char *)base;
if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0)
p += CACHE_LINE_SIZE - r;
}
l = (Lock *)p;
l->base = base;
l->lock = 0;
return l;
}
static void
lock_destroy(void *lock)
{
Lock *l = (Lock *)lock;
free(l->base);
}
/*
* Crude exclusive locks for the 80386, which does not support the
* cmpxchg instruction.
@ -161,51 +106,6 @@ lock80386_release(void *lock)
sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
}
/*
* Better reader/writer locks for the 80486 and later CPUs.
*/
static void
rlock_acquire(void *lock)
{
Lock *l = (Lock *)lock;
atomic_add_int(&l->lock, RC_INCR);
while (l->lock & WAFLAG)
; /* Spin */
}
static void
wlock_acquire(void *lock)
{
Lock *l = (Lock *)lock;
sigset_t tmp_oldsigmask;
for ( ; ; ) {
sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
if (cmpxchgl(0, WAFLAG, &l->lock) == 0)
break;
sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
}
oldsigmask = tmp_oldsigmask;
}
static void
rlock_release(void *lock)
{
Lock *l = (Lock *)lock;
atomic_add_int(&l->lock, -RC_INCR);
}
static void
wlock_release(void *lock)
{
Lock *l = (Lock *)lock;
atomic_add_int(&l->lock, -WAFLAG);
sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
}
/*
* Code to determine at runtime whether the CPU supports the cmpxchg
* instruction. This instruction allows us to use locks that are more
@ -242,35 +142,3 @@ cpu_supports_cmpxchg(void)
return result;
}
void
lockdflt_init(LockInfo *li)
{
li->context = NULL;
li->context_destroy = NULL;
li->lock_create = lock_create;
li->lock_destroy = lock_destroy;
if (cpu_supports_cmpxchg()) {
/* Use fast locks that require an 80486 or later. */
li->rlock_acquire = rlock_acquire;
li->wlock_acquire = wlock_acquire;
li->rlock_release = rlock_release;
li->wlock_release = wlock_release;
} else {
/* It's a cruddy old 80386. */
li->rlock_acquire = li->wlock_acquire = lock80386_acquire;
li->rlock_release = li->wlock_release = lock80386_release;
}
/*
* Construct a mask to block all signals except traps which might
* conceivably be generated within the dynamic linker itself.
*/
sigfillset(&fullsigmask);
sigdelset(&fullsigmask, SIGILL);
sigdelset(&fullsigmask, SIGTRAP);
sigdelset(&fullsigmask, SIGABRT);
sigdelset(&fullsigmask, SIGEMT);
sigdelset(&fullsigmask, SIGFPE);
sigdelset(&fullsigmask, SIGBUS);
sigdelset(&fullsigmask, SIGSEGV);
sigdelset(&fullsigmask, SIGSYS);
}

View File

@ -29,6 +29,11 @@
#ifndef RTLD_MACHDEP_H
#define RTLD_MACHDEP_H 1
#include <sys/types.h>
#include <machine/atomic.h>
#define CACHE_LINE_SIZE 32
struct Struct_Obj_Entry;
/* Return the address of the .dynamic section in the dynamic linker. */
@ -53,25 +58,4 @@ reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
#define call_initfini_pointer(obj, target) \
(((InitFunc)(target))())
static inline void
atomic_decr_int(volatile int *p)
{
__asm __volatile ("lock; decl %0" : "+m"(*p) : : "cc");
}
static inline void
atomic_incr_int(volatile int *p)
{
__asm __volatile ("lock; incl %0" : "+m"(*p) : : "cc");
}
static inline void
atomic_add_int(volatile int *p, int val)
{
__asm __volatile ("lock; addl %1, %0"
: "+m"(*p)
: "ri"(val)
: "cc");
}
#endif

View File

@ -29,6 +29,11 @@
#ifndef RTLD_MACHDEP_H
#define RTLD_MACHDEP_H 1
#include <sys/types.h>
#include <machine/atomic.h>
#define CACHE_LINE_SIZE 128
/*
* Macros for cracking ia64 function pointers.
*/
@ -50,10 +55,4 @@ Elf_Addr reloc_jmpslot(Elf_Addr *, Elf_Addr, const struct Struct_Obj_Entry *,
void *make_function_pointer(const Elf_Sym *, const struct Struct_Obj_Entry *);
void call_initfini_pointer(const struct Struct_Obj_Entry *, Elf_Addr);
/* Atomic operations. */
int cmp0_and_store_int(volatile int *, int);
void atomic_add_int(volatile int *, int);
void atomic_incr_int(volatile int *);
void atomic_decr_int(volatile int *);
#endif

View File

@ -157,60 +157,6 @@ ENTRY(_rtld_bind_start, 0)
}
END(_rtld_bind_start)
/*
* int cmp0_and_store_int(volatile int *p, int newval);
*
* If an int holds 0, store newval into it; else do nothing. Returns
* the previous value.
*/
ENTRY(cmp0_and_store_int, 2)
mov ar.ccv=0
;;
cmpxchg4.acq r8=[in0],in1,ar.ccv
br.ret.sptk.many rp
END(cmp0_and_store_int)
ENTRY(atomic_add_int, 2)
1: ld4 r14=[in0]
;;
mov ar.ccv=r14
add r15=in1,r14
;;
cmpxchg4.acq r16=[in0],r15,ar.ccv
;;
cmp.ne p6,p0=r14,r16
(p6) br.cond.spnt.few 1b
br.ret.sptk.many rp
END(atomic_add_int)
/* Atomically increment an int. */
ENTRY(atomic_incr_int, 1)
1: ld4 r14=[in0]
;;
mov ar.ccv=r14
add r15=1,r14
;;
cmpxchg4.acq r16=[in0],r15,ar.ccv
;;
cmp.ne p6,p0=r14,r16
(p6) br.cond.spnt.few 1b
br.ret.sptk.many rp
END(atomic_incr_int)
/* Atomically decrement an int. */
ENTRY(atomic_decr_int, 1)
1: ld4 r14=[in0]
;;
mov ar.ccv=r14
add r15=-1,r14
;;
cmpxchg4.acq r16=[in0],r15,ar.ccv
;;
cmp.ne p6,p0=r14,r16
(p6) br.cond.spnt.few 1b
br.ret.sptk.many rp
END(atomic_decr_int)
#define DT_NULL 0 /* Terminating entry. */
#define DT_RELA 7 /* Address of ElfNN_Rela relocations. */
#define DT_RELASZ 8 /* Total size of ElfNN_Rela relocations. */

View File

@ -91,7 +91,7 @@ lm_init (void)
/*
* There should be nothing except whitespace or comment
* from this point to the end of the line.
from this point to the end of the line.
*/
while(isspace(*cp++));
if (!iseol(*cp)) continue;
@ -167,10 +167,6 @@ lm_add (char *p, char *f, char *t)
if (p == NULL)
p = "$DEFAULT$";
#if 0
printf("%s(\"%s\", \"%s\", \"%s\")\n", __func__, p, f, t);
#endif
if ((lml = lmp_find(p)) == NULL)
lml = lmp_init(xstrdup(p));
@ -240,3 +236,4 @@ lmp_init (char *n)
return (&lmp->lml);
}

View File

@ -29,11 +29,9 @@
#ifndef RTLD_MACHDEP_H
#define RTLD_MACHDEP_H 1
#include <sys/types.h>
#include <machine/atomic.h>
#define atomic_incr_int(p) atomic_add_int((p), 1)
#define atomic_decr_int(p) atomic_subtract_int((p), 1)
#define CACHE_LINE_SIZE 32
struct Struct_Obj_Entry;

View File

@ -100,7 +100,6 @@ 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_check(void);
static Obj_Entry *obj_from_addr(const void *);
static void objlist_call_fini(Objlist *);
static void objlist_call_init(Objlist *);
@ -155,8 +154,6 @@ static Objlist list_main = /* Objects loaded at program startup */
static Objlist list_fini = /* Objects needing fini() calls */
STAILQ_HEAD_INITIALIZER(list_fini);
static LockInfo lockinfo;
static 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);
@ -178,6 +175,7 @@ static func_ptr_type exports[] = {
(func_ptr_type) &dladdr,
(func_ptr_type) &dllockinit,
(func_ptr_type) &dlinfo,
(func_ptr_type) &_rtld_thread_init,
NULL
};
@ -199,36 +197,6 @@ char **environ;
(dlp)->num_alloc = obj_count, \
(dlp)->num_used = 0)
static __inline void
rlock_acquire(void)
{
lockinfo.rlock_acquire(lockinfo.thelock);
atomic_incr_int(&lockinfo.rcount);
lock_check();
}
static __inline void
wlock_acquire(void)
{
lockinfo.wlock_acquire(lockinfo.thelock);
atomic_incr_int(&lockinfo.wcount);
lock_check();
}
static __inline void
rlock_release(void)
{
atomic_decr_int(&lockinfo.rcount);
lockinfo.rlock_release(lockinfo.thelock);
}
static __inline void
wlock_release(void)
{
atomic_decr_int(&lockinfo.wcount);
lockinfo.wlock_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
@ -259,6 +227,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
Obj_Entry *obj;
Obj_Entry **preload_tail;
Objlist initlist;
int lockstate;
/*
* On entry, the dynamic linker itself has not been relocated yet.
@ -406,8 +375,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
set_program_var("environ", env);
dbg("initializing thread locks");
lockdflt_init(&lockinfo);
lockinfo.thelock = lockinfo.lock_create(lockinfo.context);
lockdflt_init();
/* Make a list of init functions to call. */
objlist_init(&initlist);
@ -416,9 +384,9 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
r_debug_state(NULL, &obj_main->linkmap); /* say hello to gdb! */
objlist_call_init(&initlist);
wlock_acquire();
lockstate = wlock_acquire(rtld_bind_lock);
objlist_clear(&initlist);
wlock_release();
wlock_release(rtld_bind_lock, lockstate);
dbg("transferring control to program entry point = %p", obj_main->entry);
@ -436,8 +404,9 @@ _rtld_bind(Obj_Entry *obj, Elf_Word reloff)
const Obj_Entry *defobj;
Elf_Addr *where;
Elf_Addr target;
int lockstate;
rlock_acquire();
lockstate = rlock_acquire(rtld_bind_lock);
if (obj->pltrel)
rel = (const Elf_Rel *) ((caddr_t) obj->pltrel + reloff);
else
@ -462,7 +431,7 @@ _rtld_bind(Obj_Entry *obj, Elf_Word reloff)
* that the trampoline needs.
*/
target = reloc_jmpslot(where, target, defobj, obj, rel);
rlock_release();
rlock_release(rtld_bind_lock, lockstate);
return target;
}
@ -1230,26 +1199,6 @@ load_object(char *path)
return obj;
}
/*
* Check for locking violations and die if one is found.
*/
static void
lock_check(void)
{
int rcount, wcount;
rcount = lockinfo.rcount;
wcount = lockinfo.wcount;
assert(rcount >= 0);
assert(wcount >= 0);
if (wcount > 1 || (wcount != 0 && rcount != 0)) {
_rtld_error("Application locking error: %d readers and %d writers"
" in dynamic linker. See DLLOCKINIT(3) in manual pages.",
rcount, wcount);
die();
}
}
static Obj_Entry *
obj_from_addr(const void *addr)
{
@ -1572,11 +1521,12 @@ int
dlclose(void *handle)
{
Obj_Entry *root;
int lockstate;
wlock_acquire();
lockstate = wlock_acquire(rtld_bind_lock);
root = dlcheck(handle);
if (root == NULL) {
wlock_release();
wlock_release(rtld_bind_lock, lockstate);
return -1;
}
@ -1590,9 +1540,9 @@ dlclose(void *handle)
* The object is no longer referenced, so we must unload it.
* First, call the fini functions with no locks held.
*/
wlock_release();
wlock_release(rtld_bind_lock, lockstate);
objlist_call_fini(&list_fini);
wlock_acquire();
lockstate = wlock_acquire(rtld_bind_lock);
objlist_remove_unref(&list_fini);
/* Finish cleaning up the newly-unreferenced objects. */
@ -1600,7 +1550,7 @@ dlclose(void *handle)
unload_object(root);
GDB_STATE(RT_CONSISTENT,NULL);
}
wlock_release();
wlock_release(rtld_bind_lock, lockstate);
return 0;
}
@ -1640,7 +1590,7 @@ dlopen(const char *name, int mode)
Obj_Entry **old_obj_tail;
Obj_Entry *obj;
Objlist initlist;
int result;
int result, lockstate;
ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1";
if (ld_tracing != NULL)
@ -1648,7 +1598,7 @@ dlopen(const char *name, int mode)
objlist_init(&initlist);
wlock_acquire();
lockstate = wlock_acquire(rtld_bind_lock);
GDB_STATE(RT_ADD,NULL);
old_obj_tail = obj_tail;
@ -1699,15 +1649,15 @@ dlopen(const char *name, int mode)
GDB_STATE(RT_CONSISTENT,obj ? &obj->linkmap : NULL);
/* Call the init functions with no locks held. */
wlock_release();
wlock_release(rtld_bind_lock, lockstate);
objlist_call_init(&initlist);
wlock_acquire();
lockstate = wlock_acquire(rtld_bind_lock);
objlist_clear(&initlist);
wlock_release();
wlock_release(rtld_bind_lock, lockstate);
return obj;
trace:
trace_loaded_objects(obj);
wlock_release();
wlock_release(rtld_bind_lock, lockstate);
exit(0);
}
@ -1718,12 +1668,13 @@ dlsym(void *handle, const char *name)
unsigned long hash;
const Elf_Sym *def;
const Obj_Entry *defobj;
int lockstate;
hash = elf_hash(name);
def = NULL;
defobj = NULL;
rlock_acquire();
lockstate = rlock_acquire(rtld_bind_lock);
if (handle == NULL || handle == RTLD_NEXT ||
handle == RTLD_DEFAULT || handle == RTLD_SELF) {
void *retaddr;
@ -1731,7 +1682,7 @@ dlsym(void *handle, const char *name)
retaddr = __builtin_return_address(0); /* __GNUC__ only */
if ((obj = obj_from_addr(retaddr)) == NULL) {
_rtld_error("Cannot determine caller's shared object");
rlock_release();
rlock_release(rtld_bind_lock, lockstate);
return NULL;
}
if (handle == NULL) { /* Just the caller's shared object. */
@ -1753,7 +1704,7 @@ dlsym(void *handle, const char *name)
}
} else {
if ((obj = dlcheck(handle)) == NULL) {
rlock_release();
rlock_release(rtld_bind_lock, lockstate);
return NULL;
}
@ -1775,7 +1726,7 @@ dlsym(void *handle, const char *name)
}
if (def != NULL) {
rlock_release();
rlock_release(rtld_bind_lock, lockstate);
/*
* The value required by the caller is derived from the value
@ -1792,7 +1743,7 @@ dlsym(void *handle, const char *name)
}
_rtld_error("Undefined symbol \"%s\"", name);
rlock_release();
rlock_release(rtld_bind_lock, lockstate);
return NULL;
}
@ -1803,12 +1754,13 @@ dladdr(const void *addr, Dl_info *info)
const Elf_Sym *def;
void *symbol_addr;
unsigned long symoffset;
int lockstate;
rlock_acquire();
lockstate = rlock_acquire(rtld_bind_lock);
obj = obj_from_addr(addr);
if (obj == NULL) {
_rtld_error("No shared object contains address");
rlock_release();
rlock_release(rtld_bind_lock, lockstate);
return 0;
}
info->dli_fname = obj->path;
@ -1847,7 +1799,7 @@ dladdr(const void *addr, Dl_info *info)
if (info->dli_saddr == addr)
break;
}
rlock_release();
rlock_release(rtld_bind_lock, lockstate);
return 1;
}
@ -1855,9 +1807,9 @@ int
dlinfo(void *handle, int request, void *p)
{
const Obj_Entry *obj;
int error;
int error, lockstate;
rlock_acquire();
lockstate = rlock_acquire(rtld_bind_lock);
if (handle == NULL || handle == RTLD_SELF) {
void *retaddr;
@ -1869,7 +1821,7 @@ dlinfo(void *handle, int request, void *p)
obj = dlcheck(handle);
if (obj == NULL) {
rlock_release();
rlock_release(rtld_bind_lock, lockstate);
return (-1);
}
@ -1892,7 +1844,7 @@ dlinfo(void *handle, int request, void *p)
error = -1;
}
rlock_release();
rlock_release(rtld_bind_lock, lockstate);
return (error);
}
@ -2447,3 +2399,5 @@ unref_dag(Obj_Entry *root)
STAILQ_FOREACH(elm, &root->dagmembers , link)
elm->obj->refcount--;
}

View File

@ -36,6 +36,7 @@
#include <link.h>
#include <stddef.h>
#include "rtld_lock.h"
#include "rtld_machdep.h"
#ifndef STANDARD_LIBRARY_PATH
@ -191,7 +192,7 @@ unsigned long elf_hash(const char *);
const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *,
const Obj_Entry **, bool, SymCache *);
void init_pltgot(Obj_Entry *);
void lockdflt_init(LockInfo *);
void lockdflt_init();
void obj_free(Obj_Entry *);
Obj_Entry *obj_new(void);
int reloc_non_plt(Obj_Entry *, Obj_Entry *);

View File

@ -0,0 +1,336 @@
/*-
* Copyright 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 ``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.
*
* from: FreeBSD: src/libexec/rtld-elf/sparc64/lockdflt.c,v 1.3 2002/10/09
* $FreeBSD$
*/
/*
* Thread locking implementation for the dynamic linker.
*
* We use the "simple, non-scalable reader-preference lock" from:
*
* J. M. Mellor-Crummey and M. L. Scott. "Scalable Reader-Writer
* Synchronization for Shared-Memory Multiprocessors." 3rd ACM Symp. on
* Principles and Practice of Parallel Programming, April 1991.
*
* 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".
*/
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include "debug.h"
#include "rtld.h"
#include "rtld_machdep.h"
#define WAFLAG 0x1 /* A writer holds the lock */
#define RC_INCR 0x2 /* Adjusts count of readers desiring lock */
typedef struct Struct_Lock {
volatile int lock;
void *base;
} Lock;
static sigset_t fullsigmask, oldsigmask;
static int thread_flag;
static void *
def_lock_create()
{
void *base;
char *p;
uintptr_t r;
Lock *l;
/*
* Arrange for the lock to occupy its own cache line. First, we
* optimistically allocate just a cache line, hoping that malloc
* will give us a well-aligned block of memory. If that doesn't
* work, we allocate a larger block and take a well-aligned cache
* line from it.
*/
base = xmalloc(CACHE_LINE_SIZE);
p = (char *)base;
if ((uintptr_t)p % CACHE_LINE_SIZE != 0) {
free(base);
base = xmalloc(2 * CACHE_LINE_SIZE);
p = (char *)base;
if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0)
p += CACHE_LINE_SIZE - r;
}
l = (Lock *)p;
l->base = base;
l->lock = 0;
return l;
}
static void
def_lock_destroy(void *lock)
{
Lock *l = (Lock *)lock;
free(l->base);
}
static void
def_rlock_acquire(void *lock)
{
Lock *l = (Lock *)lock;
atomic_add_acq_int(&l->lock, RC_INCR);
while (l->lock & WAFLAG)
; /* Spin */
}
static void
def_wlock_acquire(void *lock)
{
Lock *l = (Lock *)lock;
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;
}
static void
def_lock_release(void *lock)
{
Lock *l = (Lock *)lock;
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);
}
}
#if __i386__
/*
* Import a crude exclusive lock implementation for i386 processors.
* This file will be removed once i386 support is deprecated in favor
* of i486+.
*/
#include "i386/lockdflt.c"
#endif
static int
def_thread_set_flag(int mask)
{
int old_val = thread_flag;
thread_flag |= mask;
return (old_val);
}
static int
def_thread_clr_flag(int mask)
{
int old_val = thread_flag;
thread_flag &= ~mask;
return (old_val);
}
/*
* Public interface exposed to the rest of the dynamic linker.
*/
static struct RtldLockInfo lockinfo;
static struct RtldLockInfo deflockinfo;
static __inline__ int
thread_mask_set(int mask)
{
return lockinfo.thread_set_flag(mask);
}
static __inline__ void
thread_mask_clear(int mask)
{
lockinfo.thread_clr_flag(mask);
}
#define RTLD_LOCK_CNT 2
struct rtld_lock {
void *handle;
int mask;
} rtld_locks[RTLD_LOCK_CNT];
rtld_lock_t rtld_bind_lock = &rtld_locks[0];
rtld_lock_t rtld_libc_lock = &rtld_locks[1];
int
rlock_acquire(rtld_lock_t lock)
{
if (thread_mask_set(lock->mask)) {
dbg("rlock_acquire: recursed");
return (0);
}
lockinfo.rlock_acquire(lock->handle);
return (1);
}
int
wlock_acquire(rtld_lock_t lock)
{
if (thread_mask_set(lock->mask)) {
dbg("wlock_acquire: recursed");
return (0);
}
lockinfo.wlock_acquire(lock->handle);
return (1);
}
void
rlock_release(rtld_lock_t lock, int locked)
{
if (locked == 0)
return;
thread_mask_clear(lock->mask);
lockinfo.lock_release(lock->handle);
}
void
wlock_release(rtld_lock_t lock, int locked)
{
if (locked == 0)
return;
thread_mask_clear(lock->mask);
lockinfo.lock_release(lock->handle);
}
void
lockdflt_init()
{
int i;
deflockinfo.rtli_version = RTLI_VERSION;
deflockinfo.lock_create = def_lock_create;
deflockinfo.lock_destroy = def_lock_destroy;
deflockinfo.rlock_acquire = def_rlock_acquire;
deflockinfo.wlock_acquire = def_wlock_acquire;
deflockinfo.lock_release = def_lock_release;
deflockinfo.thread_set_flag = def_thread_set_flag;
deflockinfo.thread_clr_flag = def_thread_clr_flag;
deflockinfo.at_fork = NULL;
for (i = 0; i < RTLD_LOCK_CNT; i++) {
rtld_locks[i].mask = (1 << i);
rtld_locks[i].handle = NULL;
}
#if __i386__
if (!cpu_supports_cmpxchg()) {
/* It's a cruddy old 80386. */
deflockinfo.rlock_acquire = lock80386_acquire;
deflockinfo.wlock_acquire = lock80386_acquire;
deflockinfo.lock_release = lock80386_release;
}
#endif
memcpy(&lockinfo, &deflockinfo, sizeof(lockinfo));
_rtld_thread_init(NULL);
/*
* Construct a mask to block all signals except traps which might
* conceivably be generated within the dynamic linker itself.
*/
sigfillset(&fullsigmask);
sigdelset(&fullsigmask, SIGILL);
sigdelset(&fullsigmask, SIGTRAP);
sigdelset(&fullsigmask, SIGABRT);
sigdelset(&fullsigmask, SIGEMT);
sigdelset(&fullsigmask, SIGFPE);
sigdelset(&fullsigmask, SIGBUS);
sigdelset(&fullsigmask, SIGSEGV);
sigdelset(&fullsigmask, SIGSYS);
}
/*
* Callback function to allow threads implementation to
* register their own locking primitives if the default
* one is not suitable.
* The current context should be the only context
* executing at the invocation time.
*/
void
_rtld_thread_init(struct RtldLockInfo *pli)
{
int flags, i;
void *locks[RTLD_LOCK_CNT];
/* disable all locking while this function is running */
flags = thread_mask_set(~0);
if (pli == NULL)
pli = &deflockinfo;
for (i = 0; i < RTLD_LOCK_CNT; i++)
if ((locks[i] = pli->lock_create()) == NULL)
break;
if (i < RTLD_LOCK_CNT) {
while (--i >= 0)
pli->lock_destroy(locks[i]);
abort();
}
for (i = 0; i < RTLD_LOCK_CNT; i++) {
if (rtld_locks[i].handle == NULL)
continue;
if (flags & rtld_locks[i].mask)
lockinfo.lock_release(rtld_locks[i].handle);
lockinfo.lock_destroy(rtld_locks[i].handle);
}
for (i = 0; i < RTLD_LOCK_CNT; i++) {
rtld_locks[i].handle = locks[i];
if (flags & rtld_locks[i].mask)
pli->wlock_acquire(rtld_locks[i].handle);
}
lockinfo.lock_create = pli->lock_create;
lockinfo.lock_destroy = pli->lock_destroy;
lockinfo.rlock_acquire = pli->rlock_acquire;
lockinfo.wlock_acquire = pli->wlock_acquire;
lockinfo.lock_release = pli->lock_release;
lockinfo.thread_set_flag = pli->thread_set_flag;
lockinfo.thread_clr_flag = pli->thread_clr_flag;
lockinfo.at_fork = pli->at_fork;
/* restore thread locking state, this time with new locks */
thread_mask_clear(~0);
thread_mask_set(flags);
dbg("_rtld_thread_init: done");
}

View File

@ -0,0 +1,63 @@
/*-
* 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$
*/
#ifndef _RTLD_LOCK_H_
#define _RTLD_LOCK_H_
#define RTLI_VERSION 0x01
struct RtldLockInfo
{
unsigned int rtli_version;
void *(*lock_create)();
void (*lock_destroy)(void *);
void (*rlock_acquire)(void *);
void (*wlock_acquire)(void *);
void (*lock_release)(void *);
int (*thread_set_flag)(int);
int (*thread_clr_flag)(int);
void (*at_fork)();
};
extern void _rtld_thread_init(struct RtldLockInfo *);
#ifdef IN_RTLD
struct rtld_lock;
typedef struct rtld_lock *rtld_lock_t;
extern rtld_lock_t rtld_bind_lock;
extern rtld_lock_t rtld_libc_lock;
int rlock_acquire(rtld_lock_t);
int wlock_acquire(rtld_lock_t);
void rlock_release(rtld_lock_t, int);
void wlock_release(rtld_lock_t, int);
#endif /* IN_RTLD */
#endif

View File

@ -32,16 +32,6 @@
#include <sys/types.h>
#include <machine/atomic.h>
#define atomic_incr_int(p) atomic_add_int((p), 1)
#define atomic_decr_int(p) atomic_subtract_int((p), 1)
/*
* This value of CACHE_LINE_SIZE is conservative. The actual size
* is 32 on the 21064, 21064A, 21066, 21066A, and 21164. It is 64
* on the 21264. Compaq recommends sequestering each lock in its own
* 128-byte block to allow for future implementations with larger
* cache lines.
*/
#define CACHE_LINE_SIZE 128
struct Struct_Obj_Entry;