Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set RPATH and RUNPATH with GCC/LD?

I recently encountered this problem after an upgrade of the system: using the GCC -Wl,-rpath= option works differently than before.

I used it to set the search paths to some shared libraries that were built in a sub-module of my project. At the time I considered it better than setting LD_LIBRARY_PATH system-wide (I didn't want to set it every time I turned on the computer). Everything worked fine, and these two approaches seemed equivalent.

Now it seams that the behavior of -rpath has changed. It still works for directly dependent libraries, but not for those that link other libraries from the same directory set via -rpath=. Exporting LD_LIBRARY_PATH still works as before.

I inspected the output of the compilation using readelf and there is a difference. Before upgrade (Linux Mint 18.2 with GCC 5.4) the Dynamic section had this line:

0x000000000000000f (RPATH)            Library rpath: [submod/lib]

After the upgrade (Linux Mint 19 with GCC 7.3) the line changed to:

0x000000000000001d (RUNPATH)            Library runpath: [submod/lib]

In use RPATH but not RUNPATH? it is suggested that RPATH was replaced by RUNPATH (or that it at least servers a different purpose as it has a lower priority), but it doesn't give an answer as to why this effects indirect linking. The libraries themselves have neither RPATH, or RUNPATH in readelf output.

So my question is this: Why has the linker suddenly started to interpret the -rpath= option differently, and is there a way to force the old behavior? (Or do something different that will yield an equivalent result.)

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


EDIT

This is not a duplicate of How to set RunPath of a binary? -- my question is the opposite: I want the behavior of RPATH. I figured it out (thanks to the tip in the comment), and I will answer my questions here.

like image 419
Machta Avatar asked Aug 25 '18 14:08

Machta


2 Answers

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.

like image 176
Machta Avatar answered Sep 18 '22 13:09

Machta


The project GNU Binutils (containing GNU linker (ld)) is not at the origin of this change of behaviour, but Debian (2016)1, and Gentoo (2013!)2.

According to gentoo commit from Mike Frysinger on Jan 2013 :

"The "new" dtags options have been around for 14+ years now, so for Linux and GNU targets, enable them by default."

This change are not well received 3, 4, 5, as RUNPATH and RPATH have "undocumented behaviour difference"... Surprisingly this changes is now applied on Debian stable.

The problem is that using RUNPATH, leads to unpredictable problems... But mainly just works . From wikipedia :

The ld dynamic linker does not search DT_RUNPATH locations for transitive dependencies, unlike DT_RPATH.

like image 20
Javier Avatar answered Sep 20 '22 13:09

Javier