[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
shared text, shared libs again
Sorry to divert the discussion, sometimes this list gets so busy it's hard
to track all the threads... But anyway, I've finally come up with a shared
text
scheme that I believe is workable, even though it will require a lot of work
to
implement. Since I haven't seen Magic's shared lib implementation, bear with
me.
Ever since I first had the notion of shared text support in MiNT, my goal
was to
come up with something that even a lowly 68000 can use. That first attempt
kind
of works, but it has limitations that are pretty annoying. I've been
browsing thru
my gcc 2.8 source code again, and stealing ideas from the MIPS R4000
support...
The basic shared text scheme remains as before - all data is referenced
relative to
a global pointer in address register a4. This allows up to 64Kbytes to be
directly
addressed in a very efficient, position-independent manner. To extend its
usable
limits, this data/bss space must be divided into "small data" and "large
data".
Large data consists of any arrays or structures that are larger than 4
bytes. Whenever these things are declared, they go into the "blockdata"
segment, with only a pointer to
the blockdata being stored in the "smalldata" segment. (Note that gcc is
already smart enough to handle arrays & structs of 4 bytes or smaller as
longwords, so they can still
be manipulated efficiently.)
I've talked about the above scheme before, but never got the implementation
finished.
Also, I wasn't sure how to extend this for shared libraries. Now, after
having spent a lot more time with SVR4 (solaris) and AIX shared libraries, I
have a better idea of how I'd want them to work. The main thing to note
about a shared library is that it is itself a fully linked object. It cannot
contain unresolved references, although it *can* contain references to other
shared libraries. As such, a shared library is not much different than a
regular executable file, and I intend to use plain ol' Pexec to load them.
Note in SVR4/Solaris that the dynamic linker is itself a shared library...
Anyway, my main point here is that when you load a shared library, you are
loading an executable file that happens to have a shared text segment and an
un-shared data/bss segment. All of the library code expects to find its data
using its own a4 global pointer. The only trick to making everything happy
is to make sure that you have the correct pointer in a4 for the currently
executing function. In most shared library implementations, to invoke a
function that resides in some other module, you go thru an intermediate
wrapper function that was statically linked into the main module. So, in
this case, we will have a wrapper function that looks up the correct pointer
for the target function, then jumps into
that target. The static linker will be modified to generate these wrappers
on the fly,
whenever it sees that a function reference resides in a shared library. Part
of my problem in figuring out this scheme was deciding how to save the
caller's a4 pointer. The only rational place to save it is on the stack, but
that means the caller must know to save it in advance. I want a scheme that
requires no source-code changes, which means you shouldn't have to know in
advance if your target function is locally available or resides in a
separate dynamic object. Currently my thinking is to make the compiler
always save a4 before a function call and restore it after, and then make
the linker delete those
instructions whenever they prove unnecessary (because the target function is
statically linked). Yes, the linker requires a lot more intelligence now. I
am also going to add
automatic PC-relative code optimization to the linker...
This global pointer handling works for all cases where you call a function
directly.
However, there's a complication if you have a shared library function that
takes a
function pointer as one of its arguments, to be used as a callback function.
If the
callback function resides in a different module than the library function,
then the callback's a4 somehow needs to be fixed, or the callback must only
use local variables.
My current thought here is to make the "pointer to function" type an 8-byte
type, so
that both the function address and its a4 pointer may be passed along. I
believe this will work transparently enough without breaking a lot of code.
(It occurs to me that using an address register as an explicit argument
pointer during
function calls would allow arbitrary additional stack pushes and pops in
intermediate
wrapper functions, without complicating anyone else's life. Hm.... That
might be a much
better way to go than overburdening the linker... Changing the calling
conventions in this manner will mean rewriting any assembly code functions,
but that's life...)
There are some other details to decide... In SVR4, the dynamic relocation
table
includes a list of library names, and a list of symbol names to resolve, and
possibly
a list of directory paths to search for the library names. In this scheme, a
particular
symbol can be satisfied by any module found in the search path, not just the
specific
module that was referenced when the program was linked. This allows you to
insert replacements for individual functions into a file, without having to
replace an entire shared library. This also allows for wonderful security
holes in dynamically linked programs, because all you have to do is change
the library path in your environment, to insert your own functions in front
of the system librarys'. In AIX, dynamic references include both the
filename and the symbol name, although again the file can appear anywhere in
a given list of directory paths. In this scheme, if you wanted to spoof a
single function, you would have to duplicate the entire shared library
first. I'm leaning toward the AIX scheme... Also in AIX, the static linker
can bind full executables along with relocatable .o files. As such, .a
archives are superfluous, and a shared library object can also be used for
static linking. I think this is much superior to SVR4/Solaris, which
requires two complete copies of a library if you want both static and
dynamic linking ability.
Anyone else have ideas on making this all work?