|
Path To EXE For DSOs
Submitted by |
I just read the "path to executable" article in the code of
the day, and I thought I'd explain how I solved a similar
problem with dynamic libraries.
The "path to executable" is important because it lets
you build software distributions that are relocatable in
the file system: you can move it around and it keeps working
because supporting files can be found relative to the binary.
A similar problem occurs when the binary is not monolithic,
but needs several private dynamic libraries before it loads
(not plugins, which are loaded after the binary is running,
and can be located using the path-to-exe method, but DLL/DSO's
that are actually needed by the app to simply load).
This problem also has a solution, different for each platforms.
On Win32:
On Win32, the problem isn't a problem: by default Win32
searches for needed DLL's in the directory where the binary
was found: the equivalent of having a permanent ${EXE_DIR}
prepended to LD_LIBRARY_PATH on Unix.
On Linux (and I guess other ELF-based systems)
On Linux, the problem is annoying and many software
distributions end up solving the problem in one of
three ways:
1. Install your DSO's in a standard place. This solution
albeit a standard Unix practice is IMHO a lousy one:
the filesystem pollution that ensues is horrible, everything
piles up in the same place and un-installing an app. is a
nightmare. And of course, you software can't be relocated.
2. Install your software in your own private directory, and
add the path to the DSO's in LD_LIBRARY_PATH. This solution
works slightly better than 1., but is still a lousy one. If
Linux is ever going to become a mainstream OS, you can't expect
non-technical users to go and fiddle with that kind of stuff.
3. The shell wrapper solution, where you basically launch a shell
script instead of your app. The shell figures out where it is
in the file system, sets the various PATH's and finally launches the
real program. This works, but it is more convoluted than necessary,
especially if you software is made of lots of small binaries: you'd
need a wrapper shell for each.
After struggling with the issue, I contacted Ulrich Drepper at redhat (the
guy that maintains glibc), and after a little grumbling about people who
don't read the ELF specs at breakfast, he gave me a very neat solution.
There is an apparently little-known paragraph in the ELF specification
manual about path substitutions: it lets you build a DSO whose soname
contains variables that will be interpreted at load time.
One of these variables is $ORIGIN: it contains at load time the "path to
the calling module". Practically: the path to the binary that needs the
dynamic library.
So, in other words, you can get the Linux dynamic loader to actually implement
the Win32 behaviour. Here's how it's done in practice (the example here is with
C++ code):
# Build DSO needed.so
g++
-shared
-Wl,-soname
-Wl,'${ORIGIN}/needed.so' # Make sure to double the $ sign
-o needed.so # if you call this from a Makefile
obj1.o
obj2.o
obj3.o
# Build application binary.exe
g++
-o tst.exe
tstMain.o
needed.so
|
And voilą! : you have two files tst.exe and needed.so
that can be freely moved about in the file system, and
as long as they are in the same directoy, tst.exe will
work like a charm.
On OSX:
OSX is not an ELF based system (ther binary format is called
Mach-O), so the Linux solution won't work, but the problem exists
nonetheless.
First, on OSX, there is a rather stern distinction between
two types of dynamically loadable objects: Dynamic libraries
and bundles (not to be confused with OSX "filesystem bundles"
that are just directories that are somehow tagged and treated
as a single file by the finder).
These two type of loadable objects are built and loaded
differently. Bundles are designed to be "plug-ins", and
loaded via a dlopen-like call. They can't be linked against
and are thus irrelevant to this discussion.
OTOH, the dynamic libraries (.dylib) are designed to be linked
against (although you can also do a dlopen-like thing on them
later, but that is not recommended by design) suffer from the
exact same problem: when the binary loads, it needs to locate
them, and it is done via one of the three methods very much like
those described in the Linux case.
But, never fear, just as in the ELF case, there is a way to tell
the dynamic linker to look for dylibs in a path relative to your
binary. Here's how it's done:
export MACOSX_DEPLOYMENT_TARGET=10.2
g++
-dynamic
-prebind
-dynamiclib
-flat_namespace
-Wl,-single_module
-install_name @executable_path/needed.dylib
obj1.o
obj2.o
obj3.o
|
That's it for today.
Enjoy,
- Mgix
|
The zip file viewer built into the Developer Toolbox made use
of the zlib library, as well as the zlibdll source additions.
|