Avoiding LD_LIBRARY_PATH: The Options
With the introduction of the elfedit utility into Solaris, we
have a new answer to the age old question of how to avoid
everyones favorite way to get into trouble, the LD_LIBRARY_PATH
environment variable. This seems like an appropriate time to revisit this topic.
LD_LIBRARY_PATH Seems Useful. What's the Problem?
The problem is that LD_LIBRARY_PATH is a crude tool, and cannot be
easily targeted at a problem program without also hitting other
innocent programs. Sometimes this overspray is harmless (it costs
some time, but doesn't break anything). Other times, it causes a program
to link to the wrong version of something, and that program dies in
mysterious ways.
Historically, inappropriate use of LD_LIBRARY_PATH might be the
#1 one way to get yourself into trouble in an ELF environment.
In particular, people
who redistribute binaries with instructions for their users to set
LD_LIBRARY_PATH in their shell startup scripts are unleashing forces
beyond their control. Experience tells us that such use is destined to end
badly.
This subject has been written about many times by many people.
My colleague Rod Evans wrote about this
(
LD_LIBRARY_PATH - just say no)
for one of his first blog entries.
If you need additional convincing on this point, here are some suggested
Google searches you might want to try:
LD_LIBRARY_PATH problem
LD_LIBRARY_PATH bad
LD_LIBRARY_PATH evil
LD_LIBRARY_PATH darkest hell
If LD_LIBRARY_PATH is so bad, why does its use persist?
Simply because it is the option of last resort, used when everything
else has failed. We probably can't eliminate it, but we should strive to
reduce its use to the bare minimum.
How to Use, and How To Avoid Using LD_LIBRARY_PATH
The best way to use LD_LIBRARY_PATH is interactively, as a short term aid
for testing or development.
A developer might use it to point his test program at an alternative
version of a library. Beyond that, the less you use it, the better
off you'll be. With that in mind, here is a list of ways to avoid
LD_LIBRARY_PATH. The items are ordered from best to worst, with
the best option right at the top:
- Explicitly set the correct runpath for the objects you
build. If you have the ability to relink the object, you
can always do this, and no other workaround is needed.
To set a runpath in an object, use the -R compiler/linker option.
One common problem that people run into with a built in runpath
is the use of an absolute path (e.g. /usr/local/lib). Absolute
paths are no problem for the well known system libraries, because
their location is fixed by convention as well as by standards.
However, they
can be trouble for libraries supplied by third parties and installed
onto the system. Usually the user has a choice of where such
applications are installed, their home directory, or /usr/local
being two of the more popular places. An application that hard
wires the location of user installed libraries cannot handle this.
The solution in this case is to use the $ORIGIN token
in those runpaths.
The $ORIGIN token, which refers to the directory in which the using
object resides, can be used to set a non-absolute runpath that will
work in any location, as long as the desired libraries reside
at a known location relative to the using program. Fortunately,
this is often the case.
For example, consider the case of a 32-bit
application named myapp, which relies on a sharable library named
mylib.so, as well as on the standard system libraries found in
/lib and /usr/lib. The -R option to put the runpath into myapp
that will look in these places would be:
-R '$ORIGIN/../lib:/lib:/usr/lib'
This allows myapp and mylib.so to be installed anywhere, as long
as they are kept in the same positions relative to each other.
Even for system libraries, the use of $ORIGIN can be useful.
We use it for all of the linker components in
the system. For instance:
% elfdump -d /usr/bin/ld | grep RUNPATH
[7] RUNPATH 0x2e6 $ORIGIN/../../lib
By setting the runpath using $ORIGIN instead of simply hardwiring
the well known location /lib, we make it easier to test a tree
of alternative linker components, such as results when we do
a full build of the Solaris ON consolidation. We know that when
we run a test copy of ld, that it will use the related libraries
that were built with it, instead of binding to the installed system
libraries.
There is one exception to the advice to make heavy use of
$ORIGIN. The runtime linker will not expand tokens like
$ORIGIN for secure (setuid) applications. This should not be a
problem in the vast majority of cases.
- Many times, the problem comes in the form of open source software
that explicitly sets the runpath to an incorrect value for Solaris.
Can you fix the configuration script and contribute the change
back to the package maintainer? You'll be doing lots of people
a favor if you do.
- If you have an object with a bad runpath (or no runpath) and the
object cannot be rebuilt, it may be possible to alter its runpath
using the elfedit command. Using the myapp example from the
previous item:
elfedit -e 'dyn:runpath $ORIGIN/../lib:/lib:/usr/lib' myapp
For this option to be possible, you need to be
running a recent version of Solaris that has elfedit,
and your object has to
have been linked by a
version of Solaris that has the necessary extra room.
Quoting from the elfedit manpage:
The desired string must already exist in the dynamic string table, or there must be enough reserved space within this section for the new string to be added. If your object has a string table reservation area, the value of the .dynamic DT_SUNW_STRPAD element indicates the size of the area. The following elfedit command can be used to check this:
% elfedit -r -e 'dyn:tag DT_SUNW_STRPAD' file
The dynamic section must already have a runpath element, or there must be an unused dynamic slot available where one can be inserted. To test for the presence of an existing runpath:
% elfedit -r -e 'dyn:runpath' file
A dynamic section uses an element of type DT_NULL to terminate the array found in that section. The final DT_NULL cannot be changed, but if there are more than one of these, elfedit can convert one of them into a runpath element. To test for extra dynamic slots:
% elfedit -r -e 'dyn:tag DT_NULL' file
- If your application was linked with the -c option to the linker,
then you can use the crle command to alter the configuration file
associated with the application and change the settings for
LD_LIBRARY_PATH that are applied for that application. This is a
pretty good solution, but is limited by its complexity, and by the
fact that the person who linked the object needs to have thought
ahead far enough to provide for this option. Odds are that they
didn't. If they had, they might just as well have set the
runpath correctly in the first place, eliminating the need for
anything else.
You can use crle with an application that was not linked with
-c, either by setting the LD_CONFIG environment variable, or
by modifying the global system configuration file. However, both
of these options suffer from the same issues as the LD_LIBRARY_PATH
environment variable: They are too coarse grained to be applied
to a single application in a targeted way.
- If none of the above are possible, then you are indeed stuck with
LD_LIBRARY_PATH. In this case, the goal should be to minimize the
number of applications that see this environment variable. You should
never set it in your interactive shell environment (via whatever
dot file your shell supports: .profile, .login, .cshrc, .basrc, etc...).
Instead, put it in a wrapper shell script that you use to run the
specific program.
The use of a wrapper script is a pretty safe way to use
LD_LIBRARY_PATH, but you should be aware of one limitation of
this approach: If the program being wrapped starts any other
programs, then those
programs will see the LD_LIBRARY_PATH environment variable.
Since programs starting other programs is a common Unix technique,
this form of leakage can be more common that you might realize.
Technorati Tag:
OpenSolaris
Technorati Tag:
Solaris
Posted at
05:10PM Nov 02, 2007
by ali in Sun |
"If LD_LIBRARY_PATH is so bad, why does its use persist?"
The problem persists because very few people know how to compile and link binaries and .so libraries properly (i.e. through the front end driver, `cc`, with "-R/opt/abcd/lib" resp. "-R/opt/abcd/lib/64" set).
Linkers and libraries require very good understanding of the link editor, of the Executable and Linking Format (ELF) and of how the whole process works; this is some highly specialized knowledge, and most "developers" today don't have a clue about it. (I don't have a very high opinion of contemporary "developers", can you tell?)
I'm just so happy to have to recompile tons of binaries just so I can clean up somebody's else mess, and link the binary to have the correct link search paths hardcoded in.
Thankfully, `elfedit` should help a lot fixing these broken binaries. It should prove a highly useful tool. Any timelines as to when it will be backported to Solaris 10?
Posted by UX-admin on November 04, 2007 at 12:33 AM MDT #
There's no plan to backport elfedit to Solaris 10 at this moment, but that could change if there was enough pull. There are 2 issues: (1) There were lots of supporting changes, so this is a big move, and (2) The other linker changes that leave the extra room for runpaths would also need to be present, and the entire system rebuilt with that room. I appreciate how useful that would be though. We'll see what happens... Thanks!
Posted by Ali Bahrami on November 04, 2007 at 07:37 AM MST #