Commit Graph

81 Commits

Author SHA1 Message Date
John Polstra
cf98e66403 Fix a bug which could cause programs with user threads packages to
lock against themselves, causing infinite spinning.  Brian Feldman
found this problem when testing with Mozilla and supplied the fix,
which I have revised slightly.

Here is the failure scenario.  A thread calls dlopen() and acquires
the writer lock.  While the thread still holds the lock, a signal
is delivered and caught.  The signal handler tries to call a function
which hasn't been bound yet.  It thus enters the dynamic linker
and tries to acquire the reader lock.  Since the writer lock is
already held, it will spin forever in the signal handler.  The
thread holding the lock won't be able to progress and release the
lock.

The solution is to block almost all signals while holding the
exclusive lock.

A similar problem could conceivably occur in the opposite order.
Namely, a thread is holding the reader lock and then a signal
handler calls dlopen() or dlclose() and spins waiting for the writer
lock.  We deal with this administratively by proclaiming that signal
handlers aren't allowed to call dlopen() or dlclose().  Actually
we don't have to proclaim a thing, since signal handlers aren't
allowed to call any system functions except those which are explicitly
permitted.

Submitted by:	Brian Fundakowski Feldman <green>
2000-07-17 17:18:13 +00:00
John Polstra
630df077ab Solve the dynamic linker's problems with multithreaded programs once
and for all (I hope).  Packages such as wine, JDK, and linuxthreads
should no longer have any problems with re-entering the dynamic
linker.

This commit replaces the locking used in the dynamic linker with a
new spinlock-based reader/writer lock implementation.  Brian
Fundakowski Feldman <green> argued for this from the very beginning,
but it took me a long time to come around to his point of view.
Spinlocks are the only kinds of locks that work with all thread
packages.  But on uniprocessor systems they can be inefficient,
because while a contender for the lock is spinning the holder of the
lock cannot make any progress toward releasing it.  To alleviate
this disadvantage I have borrowed a trick from Sleepycat's Berkeley
DB implementation.  When spinning for a lock, the requester does a
nanosleep() call for 1 usec. each time around the loop.  This will
generally yield the CPU to other threads, allowing the lock holder
to finish its business and release the lock.  I chose 1 usec. as the
minimum sleep which would with reasonable certainty not be rounded
down to 0.

The formerly machine-independent file "lockdflt.c" has been moved
into the architecture-specific subdirectories by repository copy.
It now contains the machine-dependent spinlocking code.  For the
spinlocks I used the very nifty "simple, non-scalable reader-preference
lock" which I found at

  <http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/rw.html>

on all CPUs except the 80386 (the specific CPU model, not the
architecture).  The 80386 CPU doesn't support the necessary "cmpxchg"
instruction, so on that CPU a simple exclusive test-and-set lock
is used instead.  80386 CPUs are detected at initialization time by
trying to execute "cmpxchg" and catching the resulting SIGILL
signal.

To reduce contention for the locks, I have revamped a couple of
key data structures, permitting all common operations to be done
under non-exclusive (reader) locking.  The only operations that
require exclusive locking now are the rare intrusive operations
such as dlopen() and dlclose().

The dllockinit() interface is now deprecated.  It still exists,
but only as a do-nothing stub.  I plan to remove it as soon as is
reasonably possible.  (From the very beginning it was clearly
labeled as experimental and subject to change.)  As far as I know,
only the linuxthreads port uses dllockinit().  This interface turned
out to have several problems.  As one example, when the dynamic
linker called a client-supplied locking function, that function
sometimes needed lazy binding, causing re-entry into the dynamic
linker and a big looping mess.  And in any case, it turned out to be
too burdensome to require threads packages to register themselves
with the dynamic linker.
2000-07-08 04:10:38 +00:00
John Polstra
517191eede When installing the dynamic linker, save the previous version in
"ld-elf.so.1.old".  The dynamic linker is a critical component of
the system, and it is difficult to recover if it is damaged and
there isn't a working backup available.  For instance, parts of
the toolchain such as the assembler are dynamically linked, making
it impossible to build a new dynamic linker if the installed one
doesn't work.
2000-07-08 03:27:54 +00:00
Sheldon Hearn
cbe10916b3 Only punctuation is an allowed argument type for open-close macros
such as Po/Pc, as explained by phantom.

Reported by:	billf
2000-06-30 06:30:53 +00:00
Jake Burkholder
e39756439c Back out the previous change to the queue(3) interface.
It was not discussed and should probably not happen.

Requested by:		msmith and others
2000-05-26 02:09:24 +00:00
Jake Burkholder
740a1973a6 Change the way that the queue(3) structures are declared; don't assume that
the type argument to *_HEAD and *_ENTRY is a struct.

Suggested by:	phk
Reviewed by:	phk
Approved by:	mdodd
2000-05-23 20:41:01 +00:00
John Polstra
a0f2601e13 Eliminate unaligned accesses that occurred when relocating the
DWARF2 exception tables emitted by the compiler for C++ sources.
These tables are tightly packed, and they contain some relocated
addresses which are not well-aligned.
2000-05-22 16:31:18 +00:00
Sheldon Hearn
35add0e9a7 Cross-reference ldd(1) in rtld(1) and vice versa. 2000-03-28 09:01:04 +00:00
Bruce Evans
9d08570309 Fixed missing DPADDs.
Fixed some style bugs (some usual ones for LDADD, and misformatting of
$FreeBSD$).
2000-03-27 16:11:27 +00:00
John Polstra
ea5cc7f114 Add a manual page for the ELF dynamic linker. I initially created
rtld.1 by means of a repository copy from "src/libexec/rtld-aout/rtld.1".
Then I edited it to make it (more) accurate for the ELF dynamic
linker.
2000-01-29 03:16:54 +00:00
John Polstra
7dbe16fbee When a threads package registers locking methods with dllockinit(),
figure out which shared object(s) contain the the locking methods
and fully bind those objects as if they had been loaded with
LD_BIND_NOW=1.  The goal is to keep the locking methods from
requiring any lazy binding.  Otherwise infinite recursion occurs
in _rtld_bind.

This fixes the infinite recursion problem in the linuxthreads port.
2000-01-29 01:27:04 +00:00
John Polstra
5bc2f0f789 Block almost all signals in the default locking method instead of
just a few of them.  This looks like it solves the recent

  ld-elf.so.1: assert failed: /usr/src/libexec/rtld-elf/lockdflt.c:55

failures seen by some applications such as JDK.
2000-01-25 01:32:56 +00:00
John Polstra
924d965ba0 Allow files in LD_PRELOAD to be separated by white space, like Solaris
and Linux.
2000-01-22 22:20:05 +00:00
John Polstra
9bfb1dfc29 Revamp the mechanism for enumerating and calling shared objects'
init and fini functions.  Now the code is very careful to hold no
locks when calling these functions.  Thus the dynamic linker cannot
be re-entered with a lock already held.

Remove the tolerance for recursive locking that I added in revision
1.2 of dllockinit.c.  Recursive locking shouldn't happen any more.

Mozilla and JDK users: I'd appreciate confirmation that things still
work right (or at least the same) with these changes.
2000-01-09 21:13:48 +00:00
John Polstra
3600eb76c6 Work around an assert failure in the dynamic linker's default thread
locking functions.  If an application loads a shared object with
dlopen() and the shared object has an init function which requires
lazy binding, then _rtld_bind is called when the thread is already
inside the dynamic linker.  This leads to a recursive acquisition
of the lock, which I was not expecting -- hence the assert failure.

This work-around makes the default locking functions handle recursive
locking.  It is NOT the correct fix -- that should be implemented
at the generic locking level rather than in the default locking
functions.  I will implement the correct fix in a future commit.

Since the dllockinit() interface will likely need to change, warn
about that in both the man page and the header file.
1999-12-28 04:38:17 +00:00
John Polstra
d3980376e8 Add a new function dllockinit() for registering thread locking
functions to be used by the dynamic linker.  This can be called by
threads packages at start-up time.  I will add the call to libc_r
soon.

Also add a default locking method that is used up until dllockinit()
is called.  The default method works by blocking SIGVTALRM, SIGPROF,
and SIGALRM in critical sections.  It is based on the observation
that most user-space threads packages implement thread preemption
with one of these signals (usually SIGVTALRM).

The dynamic linker has never been reentrant, but it became less
reentrant in revision 1.34 of "src/libexec/rtld-elf/rtld.c".
Starting with that revision, multiple threads each doing lazy
binding could interfere with each other.  The usual symptom was
that a symbol was falsely reported as undefined at start-up time.
It was rare but not unseen.  This commit fixes it.
1999-12-27 04:44:04 +00:00
John Polstra
df618d033c In revision 1.21 I changed the search order for shared libraries,
but I forgot to make the corresponding fix to the comment.  Rectify
that.

Submitted by:	Tony Finch <fanf@demon.net>
1999-11-19 04:45:07 +00:00
Alexey Zelkin
8bd2d9a0e6 .Nm += "rtld"
apropos(1) now knows about rtld(1) manpage.
1999-09-28 05:35:59 +00:00
John Polstra
825316056a Make jdk-1.1.8 work again. It turns out that some code inside
libjava peeks into the dynamic linker's private Obj_Entry structures.
My recent changes introduced some new members near the front of
the structures, causing libjava to get the wrong fields.  This commit
moves the new members toward the end of the structure so that the
layout of the portion that is relevant to JDK remains the same as
before.

I will work with the JDK porting team to see if we can come up with
a less fragile way for them to do what they need to do.  I understand
the current approach was necessary in order to work around some
limitations of the dynamic linker.  Maybe it's not necessary any
more.
1999-09-05 21:12:53 +00:00
John Polstra
0edd3ca778 Enable -Wformat checking for debug_printf(). 1999-09-04 20:36:27 +00:00
John Polstra
ed5e1b5537 Change the warning about unrecognized entries in the dynamic table
to a debug message which is disabled in production builds of the
dynamic linker.  The condition warned about is normally harmless.

PR:		bin/12849
1999-09-04 20:14:48 +00:00
John Polstra
476015a33b When looking up symbols, search the objects loaded at program start
up first -- before the dlopened DAGs containing the referencing
object.

This makes dynamically loaded perl modules work properly again.
1999-09-04 04:00:09 +00:00
John Polstra
a607e5d7f8 Get the actual pathname of the dynamic linker from the executable's
PT_INTERP program header entry, to ensure that gdb always finds
the right dynamic linker.

Use obj->relocbase to simplify a few calculations where appropriate.
1999-08-30 01:54:13 +00:00
John Polstra
7360ae0f2a When checking to see if a shared object is already loaded, look for
a device/inode match if no pathname match is found.
1999-08-30 01:50:41 +00:00
John Polstra
926ea445fe Revamp the symbol lookup algorithm to cope better with objects
loaded separately by dlopen that have global symbols with identical
names.  Viewing each dlopened object as a DAG which is linked by its
DT_NEEDED entries in the dynamic table, the search order is as
follows:

  * If the referencing object was linked with -Bsymbolic, search it
    internally.
  * Search all dlopened DAGs containing the referencing object.
  * Search all objects loaded at program start up.
  * Search all objects which were dlopened() using the RTLD_GLOBAL
    flag (which is now supported too).

The search terminates as soon as a strong definition is found.
Lacking that, the first weak definition is used.

These rules match those of Solaris, as best I could determine them
from its vague manual pages and the results of experiments I performed.

PR:		misc/12438
1999-08-30 01:48:19 +00:00
John Polstra
7326e0b620 When honoring -Bsymbolic, still keep searching if only a weak
definition was found in the referencing object.
1999-08-30 01:25:38 +00:00
John Polstra
6bd9374580 Simplify the logic in find_symdef(). 1999-08-30 01:24:08 +00:00
Peter Wemm
7f3dea244c $Id$ -> $FreeBSD$ 1999-08-28 00:22:10 +00:00
John Polstra
41f83b07a8 Add a NULL pointer check whose absence could cause segmentation
violations in certain obscure cases involving failed dlopens.  Many
thanks to Archie Cobbs for providing me with a good test case.

Eliminate a block that existed only to localize a declaration.
1999-08-20 22:33:44 +00:00
John Polstra
bfb1ef6058 Change many asserts into normal errors. They were all for conditions
caused by invalid shared objects rather than by internal errors.

Enable format string mismatch checking for _rtld_error().
1999-07-18 00:02:19 +00:00
John Polstra
cb435fa919 Change the symbol used to find the end of an object's address space
from "end" to "_end".  The former does not exist in most shared
libraries.  This fixes problems in dladdr() and dlsym(RTLD_NEXT, ...).
1999-07-14 04:09:11 +00:00
Doug Rabson
e85422ad1d Add code to 'handle' R_ALPHA_NONE relocations by ignoring them. 1999-07-12 07:54:45 +00:00
John Polstra
18cd0551a7 Add a MAINTAINER line naming myself. We control the vertical. We
control the horizontal.
1999-07-09 16:27:43 +00:00
John Polstra
8d05e8c453 Fix bug: if a dlopen() failed (e.g., because of undefined symbols),
the dynamic linker didn't clean up properly.  A subsequent dlopen()
of the same object would appear to succeed.

Another excellent fix from Max Khon.

PR:		bin/12471
Submitted by:	Max Khon <fjoe@iclub.nsu.ru>
1999-07-09 16:22:55 +00:00
John Polstra
5bf3700dae Shake hands with GDB a little bit earlier so that it is possible to
debug the init functions.

Submitted by:	dfr
1999-07-03 23:54:02 +00:00
John Polstra
d16ad2d055 Fix a reference counting problem when using dlopen(NULL, ...).
PR:		bin/12129
1999-06-25 04:50:06 +00:00
John Polstra
962fdc466a Fix a serious performance bug for large programs on the Alpha,
discovered by Hidetoshi Shimokawa.  Large programs need multiple
GOTs.  The lazy binding stub in the PLT can be reached from any of
these GOTs, but the dynamic linker only has enough information to
fix up the first GOT entry.  Thus calls through the other GOTs went
through the time-consuming lazy binding process on every call.

This fix rewrites the PLT entries themselves to bypass the lazy
binding.

Tested by Hidetoshi Shimokawa and Steve Price.

Reviewed by:	Doug Rabson <dfr@freebsd.org>
1999-06-25 02:53:59 +00:00
John Polstra
6d30b16752 Back out my change from 6 April PDT that added a new dlversion()
function.  It was an ill-considered feature.  It didn't solve the
problem I wanted it to solve.   And it added Yet Another Version
Number that would have to be maintained at every release point.
I'm nuking it now before anybody grows too fond of it.
1999-04-22 01:54:38 +00:00
John Polstra
5353bfc3b4 After relocating the main program, but before calling any of the
_init() functions, initialize the global variables "__progname" and
"environ".  This makes it possible for the _init() functions to call
things like getenv() and err().
1999-04-21 04:06:57 +00:00
John Polstra
a18cde535d The ELF specification says that the RPATH in the executable or
shared object takes precedence over LD_LIBRARY_PATH.  Make the
dynamic linker do it that way.
1999-04-09 06:42:00 +00:00
John Polstra
d5b537d01a Eliminate all machine-dependent code from the main source body and
the Makefile, and move it down into the architecture-specific
subdirectories.

Eliminate an asm() statement for the i386.

Make the dynamic linker work if it is built as an executable instead
of as a shared library.  See i386/Makefile.inc to find out how to
do it.  Note, this change is not enabled and it might never be
enabled.  But it might be useful in the future.  Building the
dynamic linker as an executable should make it start up faster,
because it won't have any relocations.  But in practice I suspect
the difference is negligible.
1999-04-09 00:28:43 +00:00
John Polstra
a16ed197f2 Fix a couple of typos in comments. 1999-04-07 02:48:43 +00:00
John Polstra
14f5fa0596 Add a new function dlversion() which returns the version number of
the dynamic linker in the same form as __FreeBSD_version.  This is
mainly intended for checking the dynamic linker version during a make
world.
1999-04-07 02:43:11 +00:00
John Polstra
5e4636f2b0 Resolve undefined weak references to a value of 0. This solves the
"__deregister_frame_info" problem that was seen when combining a
program linked using the old gcc with shared libraries that were
built using egcs.
1999-04-05 02:36:40 +00:00
Peter Wemm
faba5e7488 If somebody does an execv("foo", NULL) (which theoretically is an error),
avoid crashing inside rtld (since it's easy) since everything else handles
it.  Of course, if the target program checks argv[], it'll fall over.

Reviewed by:	jdp
1999-04-04 06:01:09 +00:00
Nate Williams
38ccb4c214 - Commit the correct dladdr() implementation.
Reviewed by:	jdp@FreeBSD.org <This is the version he reviewed!>
1999-03-24 23:47:29 +00:00
Nate Williams
e818e307ee - Added dladdr(3) support.
Reviewed by:	jdp@FreeBSD.org
1999-03-24 23:37:35 +00:00
Nate Williams
0b8dcbe23c - Set the system immutable flag when installing ld.so to avoid people
accidentally clobbering it.

Submitted by:	numberous people on -current
1999-02-15 05:02:54 +00:00
Doug Rabson
eace1a8ad9 Use the runpath of the main program for locating libraries loaded by
dlopen().

Reviewed by: jdp
1998-11-27 21:19:52 +00:00
John Polstra
1280c211e2 Fix a bug in dlclose that broke the apache13 port. The list of
loaded objects wasn't being maintained properly.
1998-10-13 03:31:59 +00:00