summaryrefslogtreecommitdiffstats
path: root/libexec/rtld-elf
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/rtld-elf')
-rw-r--r--libexec/rtld-elf/alpha/lockdflt.c151
-rw-r--r--libexec/rtld-elf/alpha/rtld_machdep.h8
-rw-r--r--libexec/rtld-elf/alpha/rtld_start.S53
-rw-r--r--libexec/rtld-elf/amd64/lockdflt.c242
-rw-r--r--libexec/rtld-elf/amd64/rtld_machdep.h23
-rw-r--r--libexec/rtld-elf/i386/lockdflt.c242
-rw-r--r--libexec/rtld-elf/i386/rtld_machdep.h23
-rw-r--r--libexec/rtld-elf/lockdflt.c89
-rw-r--r--libexec/rtld-elf/rtld.c250
-rw-r--r--libexec/rtld-elf/rtld.h23
10 files changed, 770 insertions, 334 deletions
diff --git a/libexec/rtld-elf/alpha/lockdflt.c b/libexec/rtld-elf/alpha/lockdflt.c
index 4233b36..65900a6 100644
--- a/libexec/rtld-elf/alpha/lockdflt.c
+++ b/libexec/rtld-elf/alpha/lockdflt.c
@@ -26,64 +26,133 @@
*/
/*
- * 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 almost all signals. This is based on the observation that
- * most userland thread packages use signals to support preemption.
+ * 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".
+ *
+ * These are spinlocks. When spinning we call nanosleep() for 1
+ * microsecond each time around the loop. This will most likely yield
+ * the CPU to other threads (including, we hope, the lockholder) allowing
+ * them to make some progress.
*/
-#include <dlfcn.h>
-#include <signal.h>
#include <stdlib.h>
+#include <time.h>
#include "debug.h"
#include "rtld.h"
-typedef struct Struct_LockDflt {
- sigset_t lock_mask;
- sigset_t old_mask;
- int depth;
-} LockDflt;
+/*
+ * 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
-void
-lockdflt_acquire(void *lock)
+#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 const struct timespec usec = { 0, 1000 }; /* 1 usec. */
+
+static void *
+lock_create(void *context)
{
- LockDflt *l = (LockDflt *)lock;
- sigprocmask(SIG_BLOCK, &l->lock_mask, &l->old_mask);
- assert(l->depth == 0);
- l->depth++;
+ 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;
}
-void *
-lockdflt_create(void *context)
+static void
+lock_destroy(void *lock)
{
- LockDflt *l;
-
- l = NEW(LockDflt);
- l->depth = 0;
- sigfillset(&l->lock_mask);
- sigdelset(&l->lock_mask, SIGTRAP);
- sigdelset(&l->lock_mask, SIGABRT);
- sigdelset(&l->lock_mask, SIGBUS);
- sigdelset(&l->lock_mask, SIGSEGV);
- sigdelset(&l->lock_mask, SIGKILL);
- sigdelset(&l->lock_mask, SIGSTOP);
- return l;
+ Lock *l = (Lock *)lock;
+
+ free(l->base);
}
-void
-lockdflt_destroy(void *lock)
+static void
+rlock_acquire(void *lock)
+{
+ Lock *l = (Lock *)lock;
+
+ atomic_add_int(&l->lock, RC_INCR);
+ while (l->lock & WAFLAG)
+ nanosleep(&usec, NULL);
+}
+
+static void
+wlock_acquire(void *lock)
+{
+ Lock *l = (Lock *)lock;
+
+ while (cmp0_and_store_int(&l->lock, WAFLAG) != 0)
+ nanosleep(&usec, NULL);
+}
+
+static void
+rlock_release(void *lock)
{
- LockDflt *l = (LockDflt *)lock;
- free(l);
+ 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);
}
void
-lockdflt_release(void *lock)
+lockdflt_init(LockInfo *li)
{
- LockDflt *l = (LockDflt *)lock;
- assert(l->depth == 1);
- l->depth--;
- sigprocmask(SIG_SETMASK, &l->old_mask, NULL);
+ li->context = NULL;
+ li->lock_create = lock_create;
+ li->rlock_acquire = rlock_acquire;
+ li->wlock_acquire = wlock_acquire;
+ li->rlock_release = rlock_release;
+ li->wlock_release = wlock_release;
+ li->lock_destroy = lock_destroy;
+ li->context_destroy = NULL;
}
diff --git a/libexec/rtld-elf/alpha/rtld_machdep.h b/libexec/rtld-elf/alpha/rtld_machdep.h
index 11927d6..13921c4 100644
--- a/libexec/rtld-elf/alpha/rtld_machdep.h
+++ b/libexec/rtld-elf/alpha/rtld_machdep.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 1999 John D. Polstra.
+ * Copyright (c) 1999, 2000 John D. Polstra.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,4 +34,10 @@
void reloc_jmpslot(Elf_Addr *, 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
diff --git a/libexec/rtld-elf/alpha/rtld_start.S b/libexec/rtld-elf/alpha/rtld_start.S
index d7ec0d3..29d6178 100644
--- a/libexec/rtld-elf/alpha/rtld_start.S
+++ b/libexec/rtld-elf/alpha/rtld_start.S
@@ -3,6 +3,7 @@
/*
* Copyright 1996 Matt Thomas <matt@3am-software.com>
+ * Copyright 2000 John D. Polstra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -165,7 +166,53 @@ $100: ldgp gp, 0(gp)
lda sp, 168(sp)
jmp $31, ($27)
.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.
+ */
+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)
diff --git a/libexec/rtld-elf/amd64/lockdflt.c b/libexec/rtld-elf/amd64/lockdflt.c
index 4233b36..b2ca9a5 100644
--- a/libexec/rtld-elf/amd64/lockdflt.c
+++ b/libexec/rtld-elf/amd64/lockdflt.c
@@ -26,64 +26,228 @@
*/
/*
- * 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 almost all signals. This is based on the observation that
- * most userland thread packages use signals to support preemption.
+ * Thread locking implementation for the dynamic linker.
+ *
+ * On 80486 and later CPUs 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.
+ *
+ * The "compare_and_store" operation requires the "cmpxchg" instruction
+ * on the x86. Unfortunately, the 80386 CPU does not support that
+ * instruction -- only the 80486 and later models support it. So on the
+ * 80386 we must use simple test-and-set exclusive locks instead. We
+ * determine which kind of lock to use by trying to execute a "cmpxchg"
+ * instruction and catching the SIGILL which results on the 80386.
+ *
+ * These are spinlocks. When spinning we call nanosleep() for 1
+ * microsecond each time around the loop. This will most likely yield
+ * the CPU to other threads (including, we hope, the lockholder) allowing
+ * them to make some progress.
*/
-#include <dlfcn.h>
+#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
+#include <time.h>
#include "debug.h"
#include "rtld.h"
-typedef struct Struct_LockDflt {
- sigset_t lock_mask;
- sigset_t old_mask;
- int depth;
-} LockDflt;
+#define CACHE_LINE_SIZE 32
-void
-lockdflt_acquire(void *lock)
+#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 const struct timespec usec = { 0, 1000 }; /* 1 usec. */
+
+static inline int
+cmpxchgl(int old, int new, volatile int *m)
{
- LockDflt *l = (LockDflt *)lock;
- sigprocmask(SIG_BLOCK, &l->lock_mask, &l->old_mask);
- assert(l->depth == 0);
- l->depth++;
+ int result;
+
+ __asm __volatile ("lock; cmpxchgl %2, %0"
+ : "=m"(*m), "=a"(result)
+ : "r"(new), "0"(*m), "1"(old)
+ : "cc");
+
+ return result;
}
-void *
-lockdflt_create(void *context)
+static inline int
+xchgl(int v, volatile int *m)
{
- LockDflt *l;
-
- l = NEW(LockDflt);
- l->depth = 0;
- sigfillset(&l->lock_mask);
- sigdelset(&l->lock_mask, SIGTRAP);
- sigdelset(&l->lock_mask, SIGABRT);
- sigdelset(&l->lock_mask, SIGBUS);
- sigdelset(&l->lock_mask, SIGSEGV);
- sigdelset(&l->lock_mask, SIGKILL);
- sigdelset(&l->lock_mask, SIGSTOP);
+ int result;
+
+ __asm __volatile ("xchgl %0, %1"
+ : "=r"(result), "=m"(*m)
+ : "0"(v), "1"(*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;
}
-void
-lockdflt_destroy(void *lock)
+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.
+ */
+static void
+lock80386_acquire(void *lock)
+{
+ Lock *l = (Lock *)lock;
+
+ while (xchgl(1, &l->lock) != 0)
+ while (l->lock != 0)
+ nanosleep(&usec, NULL);
+}
+
+static void
+lock80386_release(void *lock)
+{
+ Lock *l = (Lock *)lock;
+
+ l->lock = 0;
+}
+
+/*
+ * Better reader/writer locks for the 80486 and later CPUs.
+ */
+static void
+rlock_acquire(void *lock)
{
- LockDflt *l = (LockDflt *)lock;
- free(l);
+ Lock *l = (Lock *)lock;
+
+ atomic_add_int(&l->lock, RC_INCR);
+ while (l->lock & WAFLAG)
+ nanosleep(&usec, NULL);
+}
+
+static void
+wlock_acquire(void *lock)
+{
+ Lock *l = (Lock *)lock;
+
+ while (cmpxchgl(0, WAFLAG, &l->lock) != 0)
+ nanosleep(&usec, NULL);
+}
+
+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);
+}
+
+/*
+ * Code to determine at runtime whether the CPU supports the cmpxchg
+ * instruction. This instruction allows us to use locks that are more
+ * efficient, but it didn't exist on the 80386.
+ */
+static jmp_buf sigill_env;
+
+static void
+sigill(int sig)
+{
+ longjmp(sigill_env, 1);
+}
+
+static int
+cpu_supports_cmpxchg(void)
+{
+ struct sigaction act, oact;
+ int result;
+ volatile int lock;
+
+ memset(&act, 0, sizeof act);
+ act.sa_handler = sigill;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ sigaction(SIGILL, &act, &oact);
+ if (setjmp(sigill_env) == 0) {
+ lock = 0;
+ cmpxchgl(0, 1, &lock);
+ result = 1;
+ } else
+ result = 0;
+ sigaction(SIGILL, &oact, NULL);
+ return result;
}
void
-lockdflt_release(void *lock)
+lockdflt_init(LockInfo *li)
{
- LockDflt *l = (LockDflt *)lock;
- assert(l->depth == 1);
- l->depth--;
- sigprocmask(SIG_SETMASK, &l->old_mask, NULL);
+ 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;
+ }
}
diff --git a/libexec/rtld-elf/amd64/rtld_machdep.h b/libexec/rtld-elf/amd64/rtld_machdep.h
index b44129a..37a81d3 100644
--- a/libexec/rtld-elf/amd64/rtld_machdep.h
+++ b/libexec/rtld-elf/amd64/rtld_machdep.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 1999 John D. Polstra.
+ * Copyright (c) 1999, 2000 John D. Polstra.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,4 +41,25 @@
(*(Elf_Addr *)(where) = (Elf_Addr)(target)); \
} while (0)
+static inline void
+atomic_decr_int(volatile int *p)
+{
+ __asm __volatile ("lock; decl %0" : "=m"(*p) : "0"(*p) : "cc");
+}
+
+static inline void
+atomic_incr_int(volatile int *p)
+{
+ __asm __volatile ("lock; incl %0" : "=m"(*p) : "0"(*p) : "cc");
+}
+
+static inline void
+atomic_add_int(volatile int *p, int val)
+{
+ __asm __volatile ("lock; addl %1, %0"
+ : "=m"(*p)
+ : "ri"(val), "0"(*p)
+ : "cc");
+}
+
#endif
diff --git a/libexec/rtld-elf/i386/lockdflt.c b/libexec/rtld-elf/i386/lockdflt.c
index 4233b36..b2ca9a5 100644
--- a/libexec/rtld-elf/i386/lockdflt.c
+++ b/libexec/rtld-elf/i386/lockdflt.c
@@ -26,64 +26,228 @@
*/
/*
- * 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 almost all signals. This is based on the observation that
- * most userland thread packages use signals to support preemption.
+ * Thread locking implementation for the dynamic linker.
+ *
+ * On 80486 and later CPUs 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.
+ *
+ * The "compare_and_store" operation requires the "cmpxchg" instruction
+ * on the x86. Unfortunately, the 80386 CPU does not support that
+ * instruction -- only the 80486 and later models support it. So on the
+ * 80386 we must use simple test-and-set exclusive locks instead. We
+ * determine which kind of lock to use by trying to execute a "cmpxchg"
+ * instruction and catching the SIGILL which results on the 80386.
+ *
+ * These are spinlocks. When spinning we call nanosleep() for 1
+ * microsecond each time around the loop. This will most likely yield
+ * the CPU to other threads (including, we hope, the lockholder) allowing
+ * them to make some progress.
*/
-#include <dlfcn.h>
+#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
+#include <time.h>
#include "debug.h"
#include "rtld.h"
-typedef struct Struct_LockDflt {
- sigset_t lock_mask;
- sigset_t old_mask;
- int depth;
-} LockDflt;
+#define CACHE_LINE_SIZE 32
-void
-lockdflt_acquire(void *lock)
+#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 const struct timespec usec = { 0, 1000 }; /* 1 usec. */
+
+static inline int
+cmpxchgl(int old, int new, volatile int *m)
{
- LockDflt *l = (LockDflt *)lock;
- sigprocmask(SIG_BLOCK, &l->lock_mask, &l->old_mask);
- assert(l->depth == 0);
- l->depth++;
+ int result;
+
+ __asm __volatile ("lock; cmpxchgl %2, %0"
+ : "=m"(*m), "=a"(result)
+ : "r"(new), "0"(*m), "1"(old)
+ : "cc");
+
+ return result;
}
-void *
-lockdflt_create(void *context)
+static inline int
+xchgl(int v, volatile int *m)
{
- LockDflt *l;
-
- l = NEW(LockDflt);
- l->depth = 0;
- sigfillset(&l->lock_mask);
- sigdelset(&l->lock_mask, SIGTRAP);
- sigdelset(&l->lock_mask, SIGABRT);
- sigdelset(&l->lock_mask, SIGBUS);
- sigdelset(&l->lock_mask, SIGSEGV);
- sigdelset(&l->lock_mask, SIGKILL);
- sigdelset(&l->lock_mask, SIGSTOP);
+ int result;
+
+ __asm __volatile ("xchgl %0, %1"
+ : "=r"(result), "=m"(*m)
+ : "0"(v), "1"(*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;
}
-void
-lockdflt_destroy(void *lock)
+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.
+ */
+static void
+lock80386_acquire(void *lock)
+{
+ Lock *l = (Lock *)lock;
+
+ while (xchgl(1, &l->lock) != 0)
+ while (l->lock != 0)
+ nanosleep(&usec, NULL);
+}
+
+static void
+lock80386_release(void *lock)
+{
+ Lock *l = (Lock *)lock;
+
+ l->lock = 0;
+}
+
+/*
+ * Better reader/writer locks for the 80486 and later CPUs.
+ */
+static void
+rlock_acquire(void *lock)
{
- LockDflt *l = (LockDflt *)lock;
- free(l);
+ Lock *l = (Lock *)lock;
+
+ atomic_add_int(&l->lock, RC_INCR);
+ while (l->lock & WAFLAG)
+ nanosleep(&usec, NULL);
+}
+
+static void
+wlock_acquire(void *lock)
+{
+ Lock *l = (Lock *)lock;
+
+ while (cmpxchgl(0, WAFLAG, &l->lock) != 0)
+ nanosleep(&usec, NULL);
+}
+
+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);
+}
+
+/*
+ * Code to determine at runtime whether the CPU supports the cmpxchg
+ * instruction. This instruction allows us to use locks that are more
+ * efficient, but it didn't exist on the 80386.
+ */
+static jmp_buf sigill_env;
+
+static void
+sigill(int sig)
+{
+ longjmp(sigill_env, 1);
+}
+
+static int
+cpu_supports_cmpxchg(void)
+{
+ struct sigaction act, oact;
+ int result;
+ volatile int lock;
+
+ memset(&act, 0, sizeof act);
+ act.sa_handler = sigill;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ sigaction(SIGILL, &act, &oact);
+ if (setjmp(sigill_env) == 0) {
+ lock = 0;
+ cmpxchgl(0, 1, &lock);
+ result = 1;
+ } else
+ result = 0;
+ sigaction(SIGILL, &oact, NULL);
+ return result;
}
void
-lockdflt_release(void *lock)
+lockdflt_init(LockInfo *li)
{
- LockDflt *l = (LockDflt *)lock;
- assert(l->depth == 1);
- l->depth--;
- sigprocmask(SIG_SETMASK, &l->old_mask, NULL);
+ 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;
+ }
}
diff --git a/libexec/rtld-elf/i386/rtld_machdep.h b/libexec/rtld-elf/i386/rtld_machdep.h
index b44129a..37a81d3 100644
--- a/libexec/rtld-elf/i386/rtld_machdep.h
+++ b/libexec/rtld-elf/i386/rtld_machdep.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 1999 John D. Polstra.
+ * Copyright (c) 1999, 2000 John D. Polstra.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,4 +41,25 @@
(*(Elf_Addr *)(where) = (Elf_Addr)(target)); \
} while (0)
+static inline void
+atomic_decr_int(volatile int *p)
+{
+ __asm __volatile ("lock; decl %0" : "=m"(*p) : "0"(*p) : "cc");
+}
+
+static inline void
+atomic_incr_int(volatile int *p)
+{
+ __asm __volatile ("lock; incl %0" : "=m"(*p) : "0"(*p) : "cc");
+}
+
+static inline void
+atomic_add_int(volatile int *p, int val)
+{
+ __asm __volatile ("lock; addl %1, %0"
+ : "=m"(*p)
+ : "ri"(val), "0"(*p)
+ : "cc");
+}
+
#endif
diff --git a/libexec/rtld-elf/lockdflt.c b/libexec/rtld-elf/lockdflt.c
deleted file mode 100644
index 4233b36..0000000
--- a/libexec/rtld-elf/lockdflt.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/*-
- * 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.
- *
- * $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 almost all signals. This is based on the observation that
- * most userland thread packages use signals to support preemption.
- */
-
-#include <dlfcn.h>
-#include <signal.h>
-#include <stdlib.h>
-
-#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;
- sigfillset(&l->lock_mask);
- sigdelset(&l->lock_mask, SIGTRAP);
- sigdelset(&l->lock_mask, SIGABRT);
- sigdelset(&l->lock_mask, SIGBUS);
- sigdelset(&l->lock_mask, SIGSEGV);
- sigdelset(&l->lock_mask, SIGKILL);
- sigdelset(&l->lock_mask, SIGSTOP);
- 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);
-}
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 146b9b2..6a1ccf3 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -58,16 +58,15 @@
/* 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;
+/*
+ * 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 {
+ 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.
@@ -77,6 +76,7 @@ static void die(void);
static void digest_dynamic(Obj_Entry *);
static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *);
static Obj_Entry *dlcheck(void *);
+static bool donelist_check(DoneList *, Obj_Entry *);
static char *find_library(const char *, const Obj_Entry *);
static void funclist_call(Funclist *);
static void funclist_clear(Funclist *);
@@ -85,7 +85,7 @@ static void funclist_push_head(Funclist *, InitFunc);
static void funclist_push_tail(Funclist *, InitFunc);
static const char *gethints(void);
static void init_dag(Obj_Entry *);
-static void init_dag1(Obj_Entry *root, Obj_Entry *obj);
+static void init_dag1(Obj_Entry *root, Obj_Entry *obj, DoneList *);
static void init_rtld(caddr_t);
static bool is_exported(const Elf_Sym *);
static void linkmap_add(Obj_Entry *);
@@ -93,18 +93,17 @@ 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 void lock_check(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 *);
static void objlist_remove(Objlist *, Obj_Entry *);
-static void prebind(void *);
static int relocate_objects(Obj_Entry *, bool);
static void rtld_exit(void);
static char *search_library_path(const char *, const char *);
static void set_program_var(const char *, const void *);
static const Elf_Sym *symlook_list(const char *, unsigned long,
- Objlist *, const Obj_Entry **, bool in_plt);
+ Objlist *, const Obj_Entry **, bool in_plt, DoneList *);
static void trace_loaded_objects(Obj_Entry *obj);
static void unload_object(Obj_Entry *);
static void unref_dag(Obj_Entry *);
@@ -128,7 +127,7 @@ 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 Obj_Entry *obj_main; /* The main program shared object */
static Obj_Entry obj_rtld; /* The dynamic linker shared object */
-static unsigned long curmark; /* Current mark value */
+static unsigned int obj_count; /* Number of objects in obj_list */
static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */
STAILQ_HEAD_INITIALIZER(list_global);
@@ -167,22 +166,45 @@ static func_ptr_type exports[] = {
char *__progname;
char **environ;
+/*
+ * 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
+ * alloca and we want that to occur within the scope of the caller.
+ */
+#define donelist_init(dlp) \
+ ((dlp)->objs = alloca(obj_count * sizeof (dlp)->objs[0]), \
+ assert((dlp)->objs != NULL), \
+ (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
-lock_release(void)
+rlock_release(void)
{
- lockinfo.lock_release(lockinfo.thelock);
+ 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);
}
/*
@@ -316,6 +338,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
/* Link the main program into the list of objects. */
*obj_tail = obj_main;
obj_tail = &obj_main->next;
+ obj_count++;
obj_main->refcount++;
/* Initialize a fake symbol for resolving undefined weak references. */
@@ -358,15 +381,16 @@ _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);
+ dbg("initializing thread locks");
+ lockdflt_init(&lockinfo);
+ lockinfo.thelock = lockinfo.lock_create(lockinfo.context);
r_debug_state(); /* say hello to gdb! */
funclist_call(&initlist);
wlock_acquire();
funclist_clear(&initlist);
- lock_release();
+ wlock_release();
dbg("transferring control to program entry point = %p", obj_main->entry);
@@ -385,7 +409,7 @@ _rtld_bind(Obj_Entry *obj, Elf_Word reloff)
Elf_Addr *where;
Elf_Addr target;
- wlock_acquire();
+ rlock_acquire();
if (obj->pltrel)
rel = (const Elf_Rel *) ((caddr_t) obj->pltrel + reloff);
else
@@ -403,7 +427,7 @@ _rtld_bind(Obj_Entry *obj, Elf_Word reloff)
(void *)target, basename(defobj->path));
reloc_jmpslot(where, target);
- lock_release();
+ rlock_release();
return target;
}
@@ -671,6 +695,29 @@ dlcheck(void *handle)
}
/*
+ * If the given object is already in the donelist, return true. Otherwise
+ * add the object to the list and return false.
+ */
+static bool
+donelist_check(DoneList *dlp, Obj_Entry *obj)
+{
+ unsigned int i;
+
+ for (i = 0; i < dlp->num_used; i++)
+ if (dlp->objs[i] == obj)
+ return true;
+ /*
+ * Our donelist allocation should always be sufficient. But if
+ * our threads locking isn't working properly, more shared objects
+ * could have been loaded since we allocated the list. That should
+ * never happen, but we'll handle it properly just in case it does.
+ */
+ if (dlp->num_used < dlp->num_alloc)
+ dlp->objs[dlp->num_used++] = obj;
+ return false;
+}
+
+/*
* Hash function for symbol table lookup. Don't even think about changing
* this. It is specified by the System V ABI.
*/
@@ -741,6 +788,7 @@ const Elf_Sym *
find_symdef(unsigned long symnum, Obj_Entry *refobj,
const Obj_Entry **defobj_out, bool in_plt)
{
+ DoneList donelist;
const Elf_Sym *ref;
const Elf_Sym *def;
const Elf_Sym *symp;
@@ -755,11 +803,11 @@ find_symdef(unsigned long symnum, Obj_Entry *refobj,
hash = elf_hash(name);
def = NULL;
defobj = NULL;
- curmark++;
+ donelist_init(&donelist);
- if (refobj->symbolic) { /* Look first in the referencing object */
+ /* Look first in the referencing object if linked symbolically. */
+ if (refobj->symbolic && !donelist_check(&donelist, refobj)) {
symp = symlook_obj(name, hash, refobj, in_plt);
- refobj->mark = curmark;
if (symp != NULL) {
def = symp;
defobj = refobj;
@@ -768,7 +816,7 @@ find_symdef(unsigned long symnum, Obj_Entry *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);
+ 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;
@@ -780,7 +828,8 @@ find_symdef(unsigned long symnum, Obj_Entry *refobj,
STAILQ_FOREACH(elm, &refobj->dldags, link) {
if (def != NULL && ELF_ST_BIND(def->st_info) != STB_WEAK)
break;
- symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, in_plt);
+ 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;
@@ -790,7 +839,7 @@ find_symdef(unsigned long symnum, Obj_Entry *refobj,
/* Search all RTLD_GLOBAL objects. */
if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
- symp = symlook_list(name, hash, &list_global, &obj, in_plt);
+ symp = symlook_list(name, hash, &list_global, &obj, in_plt, &donelist);
if (symp != NULL &&
(def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
def = symp;
@@ -919,23 +968,24 @@ gethints(void)
static void
init_dag(Obj_Entry *root)
{
- curmark++;
- init_dag1(root, root);
+ DoneList donelist;
+
+ donelist_init(&donelist);
+ init_dag1(root, root, &donelist);
}
static void
-init_dag1(Obj_Entry *root, Obj_Entry *obj)
+init_dag1(Obj_Entry *root, Obj_Entry *obj, DoneList *dlp)
{
const Needed_Entry *needed;
- if (obj->mark == curmark)
+ if (donelist_check(dlp, obj))
return;
- obj->mark = curmark;
objlist_add(&obj->dldags, root);
objlist_add(&root->dagmembers, obj);
for (needed = obj->needed; needed != NULL; needed = needed->next)
if (needed->obj != NULL)
- init_dag1(root, needed->obj);
+ init_dag1(root, needed->obj, dlp);
}
/*
@@ -971,6 +1021,7 @@ init_rtld(caddr_t mapbase)
*/
obj_list = &obj_rtld;
obj_tail = &obj_rtld.next;
+ obj_count = 1;
relocate_objects(&obj_rtld, true);
}
@@ -978,6 +1029,7 @@ init_rtld(caddr_t mapbase)
/* Make the object list empty again. */
obj_list = NULL;
obj_tail = &obj_list;
+ obj_count = 0;
/* Replace the path with a dynamically allocated copy. */
obj_rtld.path = xstrdup(obj_rtld.path);
@@ -1118,6 +1170,7 @@ load_object(char *path)
*obj_tail = obj;
obj_tail = &obj->next;
+ obj_count++;
linkmap_add(obj); /* for GDB */
dbg(" %p .. %p: %s", obj->mapbase,
@@ -1131,9 +1184,24 @@ load_object(char *path)
return obj;
}
+/*
+ * Check for locking violations and die if one is found.
+ */
static void
-lock_nop(void *lock)
+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 *
@@ -1317,7 +1385,7 @@ dlclose(void *handle)
wlock_acquire();
root = dlcheck(handle);
if (root == NULL) {
- lock_release();
+ wlock_release();
return -1;
}
@@ -1336,7 +1404,7 @@ dlclose(void *handle)
if (obj->refcount == 0 && obj->fini != NULL)
funclist_push_tail(&finilist, obj->fini);
- lock_release();
+ wlock_release();
funclist_call(&finilist);
wlock_acquire();
funclist_clear(&finilist);
@@ -1346,7 +1414,7 @@ dlclose(void *handle)
unload_object(root);
GDB_STATE(RT_CONSISTENT);
}
- lock_release();
+ wlock_release();
return 0;
}
@@ -1358,6 +1426,9 @@ dlerror(void)
return msg;
}
+/*
+ * This function is deprecated and has no effect.
+ */
void
dllockinit(void *context,
void *(*lock_create)(void *context),
@@ -1367,68 +1438,14 @@ dllockinit(void *context,
void (*lock_destroy)(void *lock),
void (*context_destroy)(void *context))
{
- bool is_dflt = false;
-
- /* NULL arguments mean reset to the built-in locks. */
- if (lock_create == NULL) {
- is_dflt = true;
- 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);
-
- /*
- * Make sure the shared objects containing the locking methods are
- * fully bound, to avoid infinite recursion when they are called
- * from the lazy binding code.
- */
- if (!is_dflt) {
- prebind((void *)rlock_acquire);
- prebind((void *)wlock_acquire);
- prebind((void *)lock_release);
- }
-
- /* Allocate our lock. */
- lockinfo.thelock = lock_create(lockinfo.context);
-
- /* 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;
-}
-
-static void
-prebind(void *addr)
-{
- Obj_Entry *obj;
-
- if ((obj = obj_from_addr(addr)) == NULL) {
- _rtld_error("Cannot determine shared object of locking method at %p",
- addr);
- die();
- }
- if (!obj->rtld && !obj->jmpslots_done) {
- dbg("Pre-binding %s for locking", obj->path);
- if (reloc_jmpslots(obj) == -1)
- die();
- }
+ static void *cur_context;
+ static void (*cur_context_destroy)(void *);
+
+ /* Just destroy the context from the previous call, if necessary. */
+ if (cur_context_destroy != NULL)
+ cur_context_destroy(cur_context);
+ cur_context = context;
+ cur_context_destroy = context_destroy;
}
void *
@@ -1482,11 +1499,11 @@ dlopen(const char *name, int mode)
GDB_STATE(RT_CONSISTENT);
/* Call the init functions with no locks held. */
- lock_release();
+ wlock_release();
funclist_call(&initlist);
wlock_acquire();
funclist_clear(&initlist);
- lock_release();
+ wlock_release();
return obj;
}
@@ -1502,14 +1519,14 @@ dlsym(void *handle, const char *name)
def = NULL;
defobj = NULL;
- wlock_acquire();
+ rlock_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();
+ rlock_release();
return NULL;
}
if (handle == NULL) { /* Just the caller's shared object. */
@@ -1525,14 +1542,17 @@ dlsym(void *handle, const char *name)
}
} else {
if ((obj = dlcheck(handle)) == NULL) {
- lock_release();
+ rlock_release();
return NULL;
}
if (obj->mainprog) {
+ DoneList donelist;
+
/* Search main program and all libraries loaded by it. */
- curmark++;
- def = symlook_list(name, hash, &list_main, &defobj, true);
+ 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
@@ -1544,12 +1564,12 @@ dlsym(void *handle, const char *name)
}
if (def != NULL) {
- lock_release();
+ rlock_release();
return defobj->relocbase + def->st_value;
}
_rtld_error("Undefined symbol \"%s\"", name);
- lock_release();
+ rlock_release();
return NULL;
}
@@ -1561,11 +1581,11 @@ dladdr(const void *addr, Dl_info *info)
void *symbol_addr;
unsigned long symoffset;
- wlock_acquire();
+ rlock_acquire();
obj = obj_from_addr(addr);
if (obj == NULL) {
_rtld_error("No shared object contains address");
- lock_release();
+ rlock_release();
return 0;
}
info->dli_fname = obj->path;
@@ -1604,7 +1624,7 @@ dladdr(const void *addr, Dl_info *info)
if (info->dli_saddr == addr)
break;
}
- lock_release();
+ rlock_release();
return 1;
}
@@ -1695,7 +1715,7 @@ set_program_var(const char *name, const void *value)
static const Elf_Sym *
symlook_list(const char *name, unsigned long hash, Objlist *objlist,
- const Obj_Entry **defobj_out, bool in_plt)
+ const Obj_Entry **defobj_out, bool in_plt, DoneList *dlp)
{
const Elf_Sym *symp;
const Elf_Sym *def;
@@ -1705,9 +1725,8 @@ symlook_list(const char *name, unsigned long hash, Objlist *objlist,
def = NULL;
defobj = NULL;
STAILQ_FOREACH(elm, objlist, link) {
- if (elm->obj->mark == curmark)
+ if (donelist_check(dlp, elm->obj))
continue;
- elm->obj->mark = curmark;
if ((symp = symlook_obj(name, hash, elm->obj, in_plt)) != NULL) {
if (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK) {
def = symp;
@@ -1877,6 +1896,7 @@ unload_object(Obj_Entry *root)
munmap(obj->mapbase, obj->mapsize);
linkmap_delete(obj);
*linkp = obj->next;
+ obj_count--;
obj_free(obj);
} else
linkp = &obj->next;
diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h
index 6d1ebbf..ab00437 100644
--- a/libexec/rtld-elf/rtld.h
+++ b/libexec/rtld-elf/rtld.h
@@ -77,6 +77,23 @@ typedef struct Struct_Needed_Entry {
unsigned long name; /* Offset of name in string table */
} Needed_Entry;
+/* Lock object */
+typedef struct Struct_LockInfo {
+ void *context; /* Client context for creating locks */
+ void *thelock; /* The one big lock */
+ /* Debugging aids. */
+ volatile int rcount; /* Number of readers holding lock */
+ volatile int wcount; /* Number of writers holding lock */
+ /* Methods */
+ void *(*lock_create)(void *context);
+ void (*rlock_acquire)(void *lock);
+ void (*wlock_acquire)(void *lock);
+ void (*rlock_release)(void *lock);
+ void (*wlock_release)(void *lock);
+ void (*lock_destroy)(void *lock);
+ void (*context_destroy)(void *context);
+} LockInfo;
+
/*
* Shared object descriptor.
*
@@ -149,7 +166,6 @@ typedef struct Struct_Obj_Entry {
Objlist dagmembers; /* DAG has these members (%) */
dev_t dev; /* Object's filesystem's device */
ino_t ino; /* Object's inode number */
- unsigned long mark; /* Set to "curmark" to avoid repeat visits */
} Obj_Entry;
#define RTLD_MAGIC 0xd550b87a
@@ -170,10 +186,7 @@ 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 lockdflt_init(LockInfo *);
void obj_free(Obj_Entry *);
Obj_Entry *obj_new(void);
int reloc_non_plt(Obj_Entry *, Obj_Entry *);
OpenPOWER on IntegriCloud