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
How to Change Bash History Completion to Complete What's Already on the Line
Faster Forking of Large Processes on Linux
Running 32 Bit Assembly Code on a 64 Bit Linux & 64 Bit Processor:Explain the Anomaly
How to Find the Java Sdk in Linux After Installing It
Is /Usr/Local/Lib Searched for Shared Libraries
Best Way to Get MAChine Id on Linux
Responsibility of Stack Alignment in 32-Bit X86 Assembly
Linux/Unix Environment Variables
How Do Unix Domain Sockets Differentiate Between Multiple Clients
How to Sort a File, Based on Its Numerical Values for a Field
What Is the Linux Equivalent to Dos Pause
How to Increase Ble Advertisement Frequency in Bluez
Extract Lines Between Two Patterns from a File
How to Monitor the Thread Count of a Process on Linux
How to Perform a For-Each Loop Over All the Files Under a Specified Path
What Is Path on a MAC (Unix) System
How to Calculate CPU Utilization of a Process & All Its Child Processes in Linux
How to Modify the Source of Buildroot Packages for Package Development