Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 20 Jan 1997 17:25:40 -0700 (MST)
From:      Terry Lambert <terry@lambert.org>
To:        mark@grondar.za (Mark Murray)
Cc:        chuckr@glue.umd.edu, terry@lambert.org, jdp@polstra.com, mark@grondar.za, peter@freebsd.org, current@freebsd.org
Subject:   Re: Static binaries and dlopen(3) with a new crypt(3) lib.
Message-ID:  <199701210025.RAA16750@phaeton.artisoft.com>
In-Reply-To: <199701202107.XAA07293@grackle.grondar.za> from "Mark Murray" at Jan 20, 97 11:07:28 pm

next in thread | previous in thread | raw e-mail | index | archive | help
> I woke this sleeping bear. What I need is the ability to link in a
> DES crypt only if it exists, and this must work for the /bin/* and
> /sbin/* applets too.

You need to map an object so that it is faulted by reference rather
than by value for the first fault.

To do this, you would establish a virtual mapping for a file, and
actualize it on reference.

You can do this by using a call table for your symbol references,
and pointing the call table to a common routine.

The job of the common routine is to resolve the symbol from the
shared image for the entry point the call table was entered through:

calling code:
	...
	call table[ n]
	...

table[ n]:
	jmp [long addr]		<- default this to "relocator"


relocator:
	get return address off stack
	look at call instruction at return address to determine target
	resolve table in which target is located
	get index by dividing table base by sizeof(jmp [long addr])
	map image in table header, if possible; otherwise ret error
	modify table[ n]->[long addr] to point to mapping location
	restore registers and stack
	jmp table[n]->[long addr]

On average this will add 6 clock cycles to the call (after the
initial call); alternately, you could back-patch the calling code and
save the 6 clocks, at the cost of having to copy-on-write the page
(unless you synced mappings with other callers to the same library).
Basically, the copy-on-write table gets rewritten in any case.

Note: this will fail in the case of tail optimization, where functions
jmp to other functions instead of calling them and ret'ing to their
own callers.  To deal with this requires determining the JMP
instrustion from the set of all allowable jmp instructions, and
looking at the stack pointers.

This actually becomes more difficult in the presence of library
constructors (for things like virtual base classes in C++ libraries,
and so on).  You must delay the constructor call until the mapping
phase takes place.  This allows you to run without the library
present, yet still have the virtual base classes constructued before
use.  For subclasses of constructed classes, there is an implicit
invocation of the constructor -- which is code in the library; the
mapping still occurs, and the construction of the base takes place
before the base constructure is actually called.

I did code similar to this for entry/exit block profiling in MSVC++,
which can generate calls on function entry, but not on function
exit.  To deal with the problem, I established a per thread return
address stack of my own, and replaced the return address for the
caller on the stack with my return handler function.  It then did
the profiling and then poped the entry off the thread stack onto the
user stack to rebuild the call frame, and returned.  Unfortuantely,
it's all MASM code, so it would need to be rewritten for GAS in any
case... probably from scratch, since it is dependent on the optimizations
which the compiler might do during assembly code generation.


					Terry Lambert
					terry@lambert.org
---
Any opinions in this posting are my own and not those of my present
or previous employers.



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199701210025.RAA16750>