Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

change LD_RUN_PATH in an executable?

176 views
Skip to first unread message

Andreas Karrer

unread,
Feb 3, 1995, 7:16:03 AM2/3/95
to
I wonder if somebody has written a tool that allows one to change the
location of shared libraries that were specified at link time.

I have come across several occasions where this would be helpful:

- some commercial programs have shared libraries in weird places,
and I don't want to set up symbolic links,
- I would like to compile and test new versions of programs in a safe place
before I install them in their target location.
Re-linking all executables of a large package is often non-trivial
and can take quite some time.
- I can set LD_LIBRARY_PATH in a shell wrapper, but then each program started
by the wrapped program inherits it, and the grandchild program may have
set its library path correctly to begin with.

+-----------
Andi Karrer, Institute for Electronics, ETH Zuerich, Switzerland
kar...@ife.ee.ethz.ch
- CH stands for Calvin & Hobbs, the most influential Swiss religion
<2n04i5$4...@hebron.connected.com>

Sean O'Neill

unread,
Feb 3, 1995, 10:59:50 AM2/3/95
to
Andreas Karrer (kar...@ife.ee.ethz.ch) wrote:
: I wonder if somebody has written a tool that allows one to change the

Read the documentation (I can't remember where this is) on LD_RUN_PATH.
It may do what you are looking for.

Sean O'Neill
Rapid Systems Solutions, Inc.
swon...@rssi.com

Andreas Stolcke

unread,
Feb 3, 1995, 1:47:46 PM2/3/95
to
In article <3gt6q3$g...@elna.ethz.ch>,

Andreas Karrer <kar...@ife.ee.ethz.ch> wrote:
>I wonder if somebody has written a tool that allows one to change the
>location of shared libraries that were specified at link time.

Use an editor capable of editing binary files (e.g., nvi from Berkeley)
to replace the path embedded in the executable. Of course this will
only work if the replacement string is no longer than the original,
but that is seldom a problem. You'll have to make an educated
guess as what the original path is, then search for that string in
the binary file. Usually that's not hard since it's some list of
directories separated by colons. gcc-compiled binaries usually
have something like /usr/local/lib/gcc-lib/foo/bar in their
LD_RUN_PATH.

This should be obvious, but when replacing a string with something
shorter make sure the you don't actually make the file shorter,
simply terminate the new strings with a null byte (^@ on your keyboard).

I do this all the time for the exact reasons you mention. Even Sun
ships their own optional software with screwed-up run-time search paths.
I found this one in the sparcworks binary:

/usr/guide/lib:/set/pubs/ow3/lib:Sw/import/Derived-sun4-4_1-OWV3-O./lib:/set/ipe/swia/2.0/sun4-4_1-OWV3/./SC2.0.1/cg87:/set/ipe/swia/2.0/sun4-4_1-OWV3/./SC2.0.1

Notice how the canonical location for openwin libraries (/usr/openwin/lib)
is missing.

Happy hacking,

--
Andreas Stolcke sto...@icsi.berkeley.edu
International Computer Science Institute sto...@ucbicsi.bitnet
1947 Center St., Suite 600, Berkeley, CA 94704 (510) 642-4274 ext. 126

James Litchfield

unread,
Feb 3, 1995, 5:20:02 PM2/3/95
to
In article f...@agate.berkeley.edu, sto...@ICSI.Berkeley.EDU (Andreas Stolcke) writes:
>In article <3gt6q3$g...@elna.ethz.ch>,
>Andreas Karrer <kar...@ife.ee.ethz.ch> wrote:
>>I wonder if somebody has written a tool that allows one to change the
>>location of shared libraries that were specified at link time.
>
>Use an editor capable of editing binary files (e.g., nvi from Berkeley)
>to replace the path embedded in the executable. Of course this will
>only work if the replacement string is no longer than the original,
>but that is seldom a problem. You'll have to make an educated
>guess as what the original path is, then search for that string in
>the binary file.
>

dump -Lv executable | egrep RPATH will eliminate the guessing of what the
path is.

e.g.,

% dump -Lv shelltool | egrep RPATH
[12] RPATH /usr/openwin/lib/X11:/usr/openwin/lib

If someone is interested in writing a utility to do this, everything you need
should be in libelf...

ba...@ssm2.eng.sun.com

unread,
Feb 6, 1995, 8:41:22 PM2/6/95
to
In general, it is not possible to change the run path, or
create one if none was specified during linking.
The reason is that the storage for the string is allocated
as part of a large, fixed string table during linking, and
increasing the size of the string table will move other
sections of the file, such as bss and data, that are no longer
relocatable. However, it is possible to overwrite an
existing LD_RUN_PATH with one that is the same length or
smaller; one can use libelf to write a program to do so
(or just use a binary editor (gasp)). One can determine
the current value using dump to examine the dynamic
section information as follows:


barts@cyber:/usr/barts 50% dump -Lv /usr/openwin/bin/cmdtool

/usr/openwin/bin/cmdtool:

**** DYNAMIC SECTION INFORMATION ****
.dynamic:
[INDEX] Tag Value
[1] NEEDED libxview.so.3
[2] NEEDED libolgx.so.3
[3] NEEDED libw.so.1
[4] NEEDED libdl.so.1
[5] NEEDED libX11.so.4
[6] NEEDED libsocket.so.1
[7] NEEDED libnsl.so.1
[8] NEEDED libintl.so.1
[9] NEEDED libc.so.1
[10] INIT 0x11a00
[11] FINI 0x11a0c
[12] RPATH /usr/openwin/lib/X11:/usr/openwin/lib
[13] HASH 0x100e8
[14] STRTAB 0x107c8
[15] STRSZ 0x2be
[16] SYMTAB 0x102f8
[17] SYMENT 0x10
[18] DEBUG 0x0
[19] PLTGOT 0x21b84
[20] PLTSZ 0x174
[21] PLTREL 0x7
[22] JMPREL 0x10ad0
[23] RELA 0x10a88
[24] RELASZ 0x1bc
[25] RELAENT 0xc
barts@cyber:/usr/barts 51%


Hope this helps -

- Bart Smaalders OS Performance SunSoft

Andre Beck

unread,
Feb 8, 1995, 1:22:51 PM2/8/95
to
In article <3h6j42$e...@engnews2.Eng.Sun.COM>, ba...@ssm2.eng.sun.com writes:
|> In general, it is not possible to change the run path, or
|> create one if none was specified during linking.
|> The reason is that the storage for the string is allocated
|> as part of a large, fixed string table during linking, and
|> increasing the size of the string table will move other
|> sections of the file, such as bss and data, that are no longer
|> relocatable.

Indeed ? I assumed ELF was invented to be a more flexible object
file format then a.out or COFF, and they made such _basic_ mistake ?
Is ELF really that braindead ???

--
+-o-+--------------------------------------------------------+-o-+
| o | \\\- Brain Inside -/// | o |
| o | ^^^^^^^^^^^^^^ | o |
| o | Andre' Beck (ABPSoft) be...@ibh-dd.de XLink PoP Dresden | o |
+-o-+--------------------------------------------------------+-o-+

Casper H.S. Dik

unread,
Feb 10, 1995, 3:19:15 AM2/10/95
to
be...@dali2.zfe.siemens.de (Andre Beck) writes:

>In article <3h6j42$e...@engnews2.Eng.Sun.COM>, ba...@ssm2.eng.sun.com writes:
>|> In general, it is not possible to change the run path, or
>|> create one if none was specified during linking.
>|> The reason is that the storage for the string is allocated
>|> as part of a large, fixed string table during linking, and
>|> increasing the size of the string table will move other
>|> sections of the file, such as bss and data, that are no longer
>|> relocatable.

>Indeed ? I assumed ELF was invented to be a more flexible object
>file format then a.out or COFF, and they made such _basic_ mistake ?
>Is ELF really that braindead ???

The problem is that all of the ELF file is mapped into memory
before the runtime linker gets it. Tne runtime linker doesn't
read the file, it starts from the image in memory.

After the final link step it is no longer possible to move stuff around.
(The only way to get around that limitation is to postpone linking
until exec time, you don't want to do that).
To call something braindead because it fixes memory locations is a bit
unfair.

But, I wonder, it must be possible to put that string someplace, e.g,
after the executable code and in front of the data segment, w/o it affecting
the relocations done.

Casper

Davin Milun

unread,
Feb 21, 1995, 4:22:26 PM2/21/95
to
James Litchfield <ji...@Eng.Sun.COM> wrote:
»>Use an editor capable of editing binary files (e.g., nvi from Berkeley)

»>to replace the path embedded in the executable. Of course this will
»>only work if the replacement string is no longer than the original,
»>but that is seldom a problem. You'll have to make an educated
»>guess as what the original path is, then search for that string in
»>the binary file.
»>
»
»dump -Lv executable | egrep RPATH will eliminate the guessing of what the
»path is.
»
»e.g.,
»
»% dump -Lv shelltool | egrep RPATH
»[12] RPATH /usr/openwin/lib/X11:/usr/openwin/lib
»
»If someone is interested in writing a utility to do this, everything you need
»should be in libelf...

Yes, it indeed was. I played with elf a bit, and got the following program.
It works, but my coding is probably not the world's best for ELF, as this is
the first libelf program that I have written.

The program below works, and can change the RPATH of any ELF executable, as
long as the new RPATH is shorter than the one that was given to the program
at link time.

If you make the RPATH smaller, and then want to enlarge it (up to a maximum
of the link-time size) you can use the -f flag.

Note that it does not copy the file that you run it on - it modifies the
file. So, keep a copy if you're not completely sure what you're doing!

Also, with a -r, it will do the equivalent of the dump command above (and
only opens the file read-only).

The program follows below. Please send me any bug-reports or suggestions or
comments or patches etc.

Davin.
-------------------------------------------------------------------------------
Davin Milun Internet: mi...@cs.Buffalo.EDU
BITNET: milun%cs.buffalo.edu@ubvm
Fax: (716) 645-3464
WWW: http://www.cs.buffalo.edu/~milun/
-------------------------------------------------------------------------------

/************************************************************************/
/* setrpath */
/* */
/* By Davin Milun (mi...@cs.buffalo.edu) */
/* */
/* Program to set the RPATH in an ELF executable. */
/* However, it cannot set the RPATH longer than the RPATH set at */
/* compile time. */
/* */
/* Send any bug reports/fixes/suggestions to mi...@cs.buffalo.edu */
/************************************************************************/

/************************************************************************/
/* Copyright (C) 1995, Davin Milun */
/* Permission to use and modify this software for any purpose other */
/* than its incorporation into a commercial product is hereby granted */
/* without fee. */
/* */
/* Permission to copy and distribute this software only for */
/* non-commercial use is also granted without fee, provided, however, */
/* that the above copyright notice appear in all copies, that both that */
/* copyright notice and this permission notice appear in supporting */
/* documentation. The author makes no representations about the */
/* suitability of this software for any purpose. It is provided */
/* ``as is'' without express or implied warranty. */
/************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <libelf.h>
#include <link.h>

#define USAGE "USAGE:\t%s [-f] <ELF executable> <new RPATH>\n\
\t%s -r <ELF executable>\n"

#define ORNULL(s) (s?s:"(null)")

int
main(int argc, char *argv[])
{

int file;
Elf *elf;
Elf_Scn *scn, *strscn;
Elf32_Shdr *scn_shdr;
Elf32_Ehdr *scn_ehdr;
Elf_Data *data, *strdata;
Elf32_Dyn *dyn;
size_t strscnndx;
int oldlen, newlen=0, extra_space;
char *oldrpath, *newrpath=NULL;
unsigned char *strbuffer;
int strbuffersize;
int forceflag=0, readonly=0;

extern char *optarg;
extern int optind;
int c;

while ((c = getopt(argc, argv, "fr")) != EOF){
switch(c){
case 'f': forceflag=1;
break;
case 'r': readonly=1;
break;
case '?': fprintf(stderr, USAGE, argv[0], argv[0]);
break;
}
}

if (argc != (optind+2-readonly)) {
fprintf(stderr,"Wrong number of arguments\n");
fprintf(stderr, USAGE, argv[0], argv[0]);
exit(1);
}

if (!readonly){
newrpath = strdup(argv[optind+1]);
newlen = strlen(newrpath);
}

if (elf_version(EV_CURRENT) == EV_NONE) {
fprintf(stderr,"Old version of ELF.\n");
exit(2);
}

if ((file = open(argv[optind],(readonly?O_RDONLY:O_RDWR))) == -1) {
fprintf(stderr,"Cannot open %s for %s\n",(readonly?"reading":"writing"),
ORNULL(argv[optind]));
perror("open");
exit(3);
}

elf = elf_begin(file, (readonly?ELF_C_READ:ELF_C_RDWR), (Elf *)NULL);

if (elf_kind(elf) != ELF_K_ELF) {
fprintf(stderr,"%s is not an ELF file.\n",argv[1]);
exit(4);
}

if ((scn_ehdr = elf32_getehdr(elf)) == 0) {
fprintf(stderr,"elf32_getehdr failed.\n");
exit(5);
}

scn = NULL;

/* Process sections */
while ((scn = elf_nextscn(elf, scn)) != NULL) {
scn_shdr = elf32_getshdr(scn);

/* Only look at SHT_DYNAMIC section */
if (scn_shdr->sh_type == SHT_DYNAMIC) {
data = NULL;

/* Process data blocks in the section */
while ((data = elf_getdata(scn, data)) != NULL) {
dyn = (Elf32_Dyn *) data->d_buf;

/* Process entries in dynamic linking table */
while (dyn->d_tag != DT_NULL) {
/* Look at DT_RPATH entry */
if (dyn->d_tag == DT_RPATH) {
scn_shdr = elf32_getshdr(scn);
strscnndx = scn_shdr->sh_link;
oldrpath = elf_strptr(elf, strscnndx, dyn->d_un.d_ptr);
printf("%s RPATH: %s\n",(readonly?"Current":"Old"),ORNULL(oldrpath));
oldlen = strlen(oldrpath);

if (readonly) { /* Quit now if readonly*/
elf_end(elf);
exit(0);
}

/* Load the section that contains the strings */
strscn = elf_getscn(elf,strscnndx);
strdata = NULL;
while ((strdata = elf_getdata(strscn, strdata)) != NULL) {
strbuffersize = strdata->d_size;
strbuffer = strdata->d_buf;

/* Get next data block if needed */
if ((dyn->d_un.d_ptr > (strdata->d_off + strdata->d_size)) ||
(dyn->d_un.d_ptr < strdata->d_off)) {
fprintf(stderr,"The string table is not in one data block\n");
fprintf(stderr,"This is not handled by this program\n");
exit(6);
}

/* See if there is "slack" after end of RPATH */
extra_space = strdata->d_size - (dyn->d_un.d_ptr + oldlen + 1);

/* Mark the data block as dirty */
elf_flagdata(strdata,ELF_C_SET,ELF_F_DIRTY);

if (newlen > (oldlen + extra_space)) {
fprintf(stderr,"New RPATH would be longer than current RPATH \
plus any extra space.\n");
fprintf(stderr,"Aborting...\n");
exit(7);
}

if ((newlen > oldlen) && !forceflag) {
fprintf(stderr,"New RPATH would be longer than current RPATH.\n");
fprintf(stderr,"(Use -f to use any extra space in string table)\n");
exit(8);
}

/* Since it will fit in the old place, we can do it */
memmove(&strbuffer[dyn->d_un.d_ptr], newrpath, newlen);
strbuffer[dyn->d_un.d_ptr+newlen] = 0;

} /* while elf_getdata on the string table */

} /* if (dyn->d_tag == DT_RPATH) */
dyn++;
} /* while (dyn->d_tag != DT_NULL) */
} /* while elf_getdata */
} /* if SHT_DYNAMIC */
} /* while elf_nextscn */

if (elf_update(elf, ELF_C_WRITE) == -1 ) {
fprintf(stderr,"elf_update failed.\n");
exit(9);
}

printf("New RPATH set to: %s\n", ORNULL(newrpath));

elf_end(elf);

exit(0);
}

0 new messages