How to Set Rpath and Runpath with Gcc/Ld

How to set RPATH and RUNPATH with GCC/LD?


Is there a way to force the old behavior?

Yes. You can use this option -Wl,--disable-new-dtags to tell the new linker to use the old behavior, i.e. RPATH.

Is it possible to tell the old version of linker to produce the new output (i.e. RUNPATH instead of RPATH)?

Yes. Use -Wl,--enable-new-dtags to tell the old linker to use the new behavior, i.e. RUNPATH.

I verified the executable with readelf and these two options seem to control what will be written in the ELF Dynamic section. I think the problem was caused by a change in the defaults for the new version, although, interestingly, the manual page for ld would suggest that it should still be the same:

--enable-new-dtags

--disable-new-dtags

This linker can create the new dynamic tags in ELF. But the older ELF systems may not understand them. If you specify
--enable-new-dtags, the new dynamic tags will be created as needed and
older dynamic tags will be omitted. If you specify --disable-new-dtags, no new dynamic tags will be created. By default, the new dynamic tags are not created. Note that those options are
only available for ELF systems.

How do I set DT_RPATH or DT_RUNPATH?

When you are compiling a program, you create object files and then link them together. You may use GNU ld(1) to link them, there are also other linkers, LLVM linker. A linker combines object files into executable. GNU ld(1) is part of binutils with documentation available here.

When you execute an already compiled ready to use executable then the dynamic linker ld.so(8) finds the libraries on the system that the executable depends on, loads them and executes the executable. ld.so(8) is a shared library usually distributed as part of C standard library, usually on linux that's glibc, but there are also other, like musl.

I think it's confusing that both these programs are named "linker". One is like "compiling linker" and the other is "executable linker".

How do I set DT_RPATH or DT_RUNPATH?

Edit the elf file to include specific section.

When creating the elf file with GNU ld, nowadays you set RUNPATH section with -rpath=something. You can set RPATH section with --disable-new-dtags -rpath=something. RPATH is deprecated, so normally -rpath sets RUNPATH. https://www.javaer101.com/en/article/1127068.html *This does not check out on my system and I have to compile with gcc -Wl,--enable-new-dtags -Wl,-rpath=/tmp to set RUNPATH...

You can also set sections in any ELF file after compilation. See Can I change 'rpath' in an already compiled binary?

whether they are the same as -rpath and -rpath-link?

From ld documentation:

The difference between -rpath and -rpath-link is that directories specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is only effective at link time.

And the documentation also explains how -rpath-link works. It's to specify directories for searching dependent shared libraries.

Finally there is an LD_RUN_PATH environment variable

When compiling an executable GNU ld(1) also searches libraries in directories specified in this variable.

use RPATH but not RUNPATH?

When you ship a binary, it's good to provide means for the users to accommodate the binary to the specifics of their own system, among other things, adjusting library search paths.

A user can generally tweak LD_LIBRARY_PATH and /etc/ld.so.conf, both of which are with lower precedence than DT_RPATH, i.e. you can't override what is hardcoded in the binary, whereas if you use DT_RUNPATH instead, a user can override it with LD_LIBRARY_PATH.

(FWIW, I think ld.so.conf should also take precedence over DT_RUNPATH, but, anyway, at least we've got LD_LIBRARY_PATH).

Also, I strongly disagree with the suggestion above to use DT_RPATH. IMO, its best to use nether DT_RPATH not DT_RUNPATH in shipped binaries.

unless

you ship all your dependent libraries with your executables and wish to ensure that things JustWork(tm) after installation, in this case use DT_RPATH.

Why would runpath be ignored?

I have been able to solve this issue on my side. For me it was because the not found library was an indirect one and runpath actually does not resolve indirect dependencies. I fixed it with using rpath instead of runpath by passing the additional -Wl,--disable-new-dtags linker option to the compiler.

There is a good and detailed explanation here: How to set RPATH and RUNPATH with GCC/LD?

c++ secondary dependency resolution with runpath

libprimary is responsible for loading libsecondary, not app. That means that the DT_RUNPATH entry for app does not apply.

Since you have control over all libraries, you could add a -Wl,-rpath=... to the compile statements for libprimary and libsecondary as well.

Gentoo ld generates RUNPATH in library when -Wl,-rpath is used

DT_RPATH tags are deprecated, DT_RUNPATH is the modern implementation with a couple different semantics. Gentoo link editors (both ld and gold) will not generate the deprecated tags by default. You can (but probably shouldn't) disable these by passing -Wl,--disable-new-dtags, but that is not recommended as I said.

There is an older pot from Qt that explains the difference of these two when using plugins: http://blog.qt.io/blog/2011/10/28/rpath-and-runpath/

How to build and install gcc with built-in rpath?

If you don't want to export paths there's an alternative solution:

with your toolchain in the PATH:

gcc -dumpspecs > specsfile

edit specsfile and in the section link add your -rpath example:

*link:
%{!static:--eh-frame-hdr} -m %(link_emulation) %{shared:-shared} %{!shared: %{!static: %{rdynamic:-export-dynamic} -dynamic-linker %(dynamic_linker)} %{static:-static}} -rpath /usr/local/lib64

at this point you can test if it work with:

g++ -specs=specsfile test.cpp
readelf -d a.out |grep RPATH

if it work you can make it permanent (no need to pass -specs everytime)

strace -fF -o /tmp/g++.log g++ test.cpp
grep specs /tmp/g++.log

the grep should show the paths where gcc look for the default specs file.

The specs files are flexible enough to allow conditional linking depending on variables, example:

{!static: %{mabi=n32:-rpath-link %R/lib32:%R/usr/lib32} %{mabi=64:-rpath-link %R/lib64:%R/usr/lib64} %{mabi=32:-rpath-link %R/lib:%R/usr/lib}}

should use different and multiple paths depending on mabi (untested, yet), %R should be the sysroot path, can be changed with needed full path.

There's also a --with-specs= option gcc configure eventually to be used at build time, not clear to me yet how to use with the link section (working on it).

--with-specs="%{shared:-Wl,-rpath -Wl,$(DESTDIR)/lib}%{!shared:-Wl,-rpath -Wl,$(DESTDIR)/lib}"

It work, I used both shared and not !shared just for test, probably some smarter condition should be used, note that it isn't reported with -dumpspecs.

Reading through some thread of the gcc mailing list I had the impression specs aren't liked by everyone (but if I'm not wrong 4.9 add another option --with-extra-specs) instead preferred way to do such customizations appears to be configure.host, but I'm done and not looking into it, have fun! :-)

see also: gcc faq rpath

update above

I don't know if you can set a pre-defined rpath, probably if you can would be in the linker ld of binutils not in gcc/g++, but why would you do that?

Just export LD_LIBRARY_PATH at runtime and LD_RUN_PATH at build time

export LD_LIBRARY_PATH=/usr/local/lib64:$LD_LIBRARY_PATH
ldd a.out

ldd should show the paths you exported.

To quote a message given when a shared library is built with libtool:

If you ever happen to want to link against installed libraries in a given directory, LIBDIR, you must either use libtool, and specify the full pathname of the library, or use the `-LLIBDIR' flag during linking and do at least one of the following:

  • add LIBDIR to the `LD_LIBRARY_PATH' environment variable during execution
  • add LIBDIR to the `LD_RUN_PATH' environment variable during linking
  • use the `-Wl,--rpath -Wl,LIBDIR' linker flag
  • have your system administrator add LIBDIR to `/etc/ld.so.conf'

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.

for completeness
the Makefile I used for testing the thing, all the configure options, environment variables (see boot ldflags) I tried didn't work, --enable-rpath included.

use with mkdir ~/gcc copy the Makefile below into ~/gcc then cd ~/gcc && make build-gcc

notice the options used are only for this test case, don't use as reference.

FETCH = aria2c --file-allocation=none -c -d dl
NICE = nice -n 19
PARALLEL = -j4
DESTDIR = $(HOME)/gcc/install
SRCDIR = $(HOME)/gcc/src

all:

# if more downloads are added just remove {dl,src}/*-my-stamp not the .bak
# the .bak should avoid to rebuild targets because of timestamp
touch_stamp = if [ -f $@.bak ]; then \
touch -r $@.bak $@; \
else \
touch $@ $@.bak; \
fi

dl/dl-my-stamp:
$(FETCH) https://ftp.gnu.org/gnu/gcc/gcc-4.7.2/gcc-4.7.2.tar.bz2
$(FETCH) http://ftp.gnu.org/gnu/gmp/gmp-4.3.2.tar.bz2
$(FETCH) ftp://gcc.gnu.org/pub/gcc/infrastructure/mpc-0.8.1.tar.gz
$(FETCH) https://ftp.gnu.org/gnu/mpfr/mpfr-2.4.2.tar.bz2
$(FETCH) --check-certificate=false http://www.mirrorservice.org/sites/sourceware.org/pub/binutils/snapshots/binutils-2.24.51.tar.bz2 \
ftp://sourceware.org/pub/binutils/snapshots/binutils-2.24.51.tar.bz2
$(touch_stamp)

untar_dep = src/untar-my-stamp
src/untar-my-stamp: dl/dl-my-stamp
mkdir -p src
tar -C src -xjf dl/gcc-4.7.2.tar.bz2
tar -C src -xjf dl/gmp-4.3.2.tar.bz2
tar -C src -xzf dl/mpc-0.8.1.tar.gz
tar -C src -xjf dl/mpfr-2.4.2.tar.bz2
tar -C src -xjf dl/binutils-2.24.51.tar.bz2
$(touch_stamp)

define configure-rule
$(1)_install = $(DESTDIR)/$(1)-install-my-stamp
$(1)_builddir = $$($(1)_dir)/build
$(DESTDIR)/$(1)-install-my-stamp: $$($(1)_deps)
mkdir -p $$($(1)_builddir)
cd $$($(1)_builddir) && \
$$($(1)_env) ../configure --cache-file=$(SRCDIR)/$(1)-config.cache \
$$($(1)_configure)
$(NICE) make -C $$($(1)_builddir) $$($(1)_make_target) $(PARALLEL)
ifneq ($$($(1)_post_make),)
$$($(1)_post_make)
endif
touch $$@
.PHONY: build-$(1) clean-$(1)
build-$(1): $$($(1)_install)
clean-$(1):
-rm -fr $$($(1)_builddir)
endef

gmp_dir = src/gmp-4.3.2
gmp_env = CONFIG_SITE=$(SRCDIR)/config.site
gmp_configure = --prefix=$(DESTDIR) \
--disable-shared --enable-static --enable-cxx
gmp_deps = $(untar_dep)
gmp_make_target = install
$(eval $(call configure-rule,gmp))

mpfr_dir = src/mpfr-2.4.2
mpfr_env = CONFIG_SITE=$(SRCDIR)/config.site
mpfr_configure = --prefix=$(DESTDIR) \
--disable-shared --enable-static \
--with-gmp=$(DESTDIR)
mpfr_deps = $(untar_dep) $(gmp_install)
mpfr_make_target = install
$(eval $(call configure-rule,mpfr))

mpc_dir = src/mpc-0.8.1
mpc_env = CONFIG_SITE=$(SRCDIR)/config.site
mpc_configure = --prefix=$(DESTDIR) \
--disable-shared --enable-static \
--with-gmp=$(DESTDIR) --with-mpfr=$(DESTDIR)
mpc_deps = $(untar_dep) $(gmp_install) $(mpfr_install)
mpc_make_target = install
$(eval $(call configure-rule,mpc))

gcc_dir = src/gcc-4.7.2
gcc_env = CONFIG_SITE=$(SRCDIR)/config.site \
CFLAGS="-I/usr/include/i386-linux-gnu" \
CXXFLAGS="-I/usr/include/i386-linux-gnu"
gcc_configure = --prefix=$(DESTDIR) \
--libdir=$(DESTDIR)/lib \
--with-local-prefix=$(DESTDIR) \
--with-gmp=$(DESTDIR) --with-mpfr=$(DESTDIR) \
--with-mpc=$(DESTDIR) \
--disable-bootstrap \
--enable-languages=c,c++ \
--disable-libgomp --disable-multilib \
--disable-libmudflap --disable-libssp \
--disable-libquadmath \
--enable-rpath \
MAKEINFO=missing
gcc_deps = $(untar_dep) $(gmp_install) $(mpfr_install) $(mpc_install)
gcc_make_target =
gcc_post_make = make -C $(gcc_builddir) install
$(eval $(call configure-rule,gcc))

binutils_dir = src/binutils-2.24.51
#binutils_env = LDFLAGS=-Wl,-rpath\ $(DESTDIR)/lib
binutils_env = CONFIG_SITE=$(SRCDIR)/config.site \
CFLAGS="-I/usr/include/i386-linux-gnu" \
BOOT_LDFLAGS="-rpath-link=$(DESTDIR)/lib -rpath=$(DESTDIR)/lib"
binutils_configure = --prefix=$(DESTDIR) \
--libdir=$(DESTDIR)/lib \
--with-gmp=$(DESTDIR) \
--enable-rpath
binutils_deps = $(untar_dep) $(gmp_install)
#binutils_make_target = install
binutils_post_make = make -C $(binutils_builddir) install
$(eval $(call configure-rule,binutils))


.PHONY: env
env:
@echo export PATH=$(DESTDIR)/bin:\$$PATH
@echo export LIBRARY_PATH=/usr/lib/i386-linux-gnu


Related Topics



Leave a reply



Submit