From 704c22c6a8fc49a902b3b29636f7b78af97124a6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 27 May 2008 16:23:21 +0000 Subject: * Support DT_RUNPATH (in fact, prefer DT_RUNPATH over DT_RPATH unless --force-rpath is set). --- README | 25 ++++++++++++++++++++--- src/patchelf.cc | 58 ++++++++++++++++++++++++++++++++++++++++++++--------- tests/big-dynstr.sh | 2 +- tests/set-rpath.sh | 2 +- 4 files changed, 73 insertions(+), 14 deletions(-) diff --git a/README b/README index 73709a1..11dc16c 100644 --- a/README +++ b/README @@ -22,13 +22,13 @@ libraries. In particular, it can do the following: AUTHOR -Copyright 2004, 2005, 2006, 2007 Eelco Dolstra . See -COPYING for the license. +Copyright 2004, 2005, 2006, 2007, 2008 Eelco Dolstra . +See COPYING for the license. HOMEPAGE -http://nix.cs.uu.nl/patchelf.html +http://nixos.org/patchelf.html BUGS @@ -36,9 +36,28 @@ BUGS Currently setting the RPATH on libraries ("--set-rpath") will usually fail if the new RPATH is longer then the old RPATH. +The `strip' command from binutils generated broken executables when +applied to the output of patchelf (if `--set-rpath' or +`--set-interpreter' with a larger path than the original is used). +This appears to be a bug in binutils +(http://bugs.strategoxt.org/browse/NIXPKGS-85). + RELEASE HISTORY +0.4 (TBA): + +* IA-64 support (not tested) and related 64-bit fixes. + +* FreeBSD support. + +* `--set-rpath', `--shrink-rpath' and `--print-rpath' now prefer + DT_RUNPATH over DT_RPATH, which is obsolete. When updating, if both + are present, both are updated. If only DT_RPATH is present, it is + converted to DT_RUNPATH unless `--force-rpath' is specified. If + neither is present, a DT_RUNPATH is added unless `--force-rpath' is + specified, in which case a DT_RPATH is added. + 0.3 (May 24, 2007): * Support for 64-bit ELF binaries (such as on x86_64-linux). diff --git a/src/patchelf.cc b/src/patchelf.cc index 7088ca1..d8187bb 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -26,6 +26,8 @@ const unsigned int pageSize = 4096; static bool debugMode = false; +static bool forceRPath = false; + static string fileName; @@ -715,16 +717,31 @@ void ElfFile::modifyRPath(RPathOp op, string newRPath) assert(strTabAddr == rdi(shdrDynStr.sh_addr)); - /* Walk through the dynamic section, look for the RPATH entry. */ + /* Walk through the dynamic section, look for the RPATH/RUNPATH + entry. + + According to the ld.so docs, DT_RPATH is obsolete, we should + use DT_RUNPATH. DT_RUNPATH has two advantages: it can be + overriden by LD_LIBRARY_PATH, and it's scoped (the DT_RUNPATH + for an executable or library doesn't affect the search path for + libraries used by it). DT_RPATH is ignored if DT_RUNPATH is + present. The binutils `ld' still generates only DT_RPATH, + unless you use its `--enable-new-dtag' option, in which case it + generates a DT_RPATH and DT_RUNPATH pointing at the same + string. */ static vector neededLibs; dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); - Elf_Dyn * dynRPath = 0; - Elf_Dyn * rpathEntry = 0; + Elf_Dyn * dynRPath = 0, * dynRunPath = 0; char * rpath = 0; for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) { if (rdi(dyn->d_tag) == DT_RPATH) { dynRPath = dyn; - rpathEntry = dyn; + /* Only use DT_RPATH if there is no DT_RUNPATH. */ + if (!dynRunPath) + rpath = strTab + rdi(dyn->d_un.d_val); + } + else if (rdi(dyn->d_tag) == DT_RUNPATH) { + dynRunPath = dyn; rpath = strTab + rdi(dyn->d_un.d_val); } else if (rdi(dyn->d_tag) == DT_NEEDED) @@ -800,6 +817,12 @@ void ElfFile::modifyRPath(RPathOp op, string newRPath) } debug("new rpath is `%s'\n", newRPath.c_str()); + + if (!forceRPath && dynRPath && !dynRunPath) { /* convert DT_RPATH to DT_RUNPATH */ + dynRPath->d_tag = DT_RUNPATH; + dynRunPath = dynRPath; + dynRPath = 0; + } if (newRPath.size() <= rpathSize) { strcpy(rpath, newRPath.c_str()); @@ -813,12 +836,14 @@ void ElfFile::modifyRPath(RPathOp op, string newRPath) rdi(shdrDynStr.sh_size) + newRPath.size() + 1); setSubstr(newDynStr, rdi(shdrDynStr.sh_size), newRPath + '\0'); - /* Update the DT_RPATH entry. */ - if (dynRPath) - dynRPath->d_un.d_val = shdrDynStr.sh_size; + /* Update the DT_RUNPATH and DT_RPATH entries. */ + if (dynRunPath || dynRPath) { + if (dynRunPath) dynRunPath->d_un.d_val = shdrDynStr.sh_size; + if (dynRPath) dynRPath->d_un.d_val = shdrDynStr.sh_size; + } else { - /* There is no DT_RPATH entry in the .dynamic section, so we + /* There is no DT_RUNPATH entry in the .dynamic section, so we have to grow the .dynamic section. */ string & newDynamic = replaceSection(".dynamic", rdi(shdrDynamic.sh_size) + sizeof(Elf_Dyn)); @@ -830,7 +855,7 @@ void ElfFile::modifyRPath(RPathOp op, string newRPath) debug("DT_NULL index is %d\n", idx); Elf_Dyn newDyn; - wri(newDyn.d_tag, DT_RPATH); + wri(newDyn.d_tag, forceRPath ? DT_RPATH : DT_RUNPATH); newDyn.d_un.d_val = shdrDynStr.sh_size; setSubstr(newDynamic, idx * sizeof(Elf_Dyn), string((char *) &newDyn, sizeof(Elf_Dyn))); @@ -923,6 +948,7 @@ int main(int argc, char * * argv) [--set-rpath RPATH]\n\ [--shrink-rpath]\n\ [--print-rpath]\n\ + [--force-rpath]\n\ [--debug]\n\ FILENAME\n", argv[0]); return 1; @@ -951,6 +977,20 @@ int main(int argc, char * * argv) else if (arg == "--print-rpath") { printRPath = true; } + else if (arg == "--force-rpath") { + /* Generally we prefer to emit DT_RUNPATH instead of + DT_RPATH, as the latter is obsolete. However, there is + a slight semantic difference: DT_RUNPATH is "scoped", + it only affects the executable or library in question, + not its recursive imports. So maybe you really want to + force the use of DT_RPATH. That's what this option + does. Without it, DT_RPATH (if encountered) is + converted to DT_RUNPATH, and if neither is present, a + DT_RUNPATH is added. With it, DT_RPATH isn't converted + to DT_RUNPATH, and if neither is present, a DT_RPATH is + added. */ + forceRPath = true; + } else if (arg == "--debug") { debugMode = true; } diff --git a/tests/big-dynstr.sh b/tests/big-dynstr.sh index 9f76600..bb95366 100755 --- a/tests/big-dynstr.sh +++ b/tests/big-dynstr.sh @@ -11,7 +11,7 @@ cp libbar.so scratch/libsB/ oldRPath=$(../src/patchelf --print-rpath scratch/big-dynstr) if test -z "$oldRPath"; then oldRPath="/oops"; fi -../src/patchelf --set-rpath $oldRPath:$(pwd)/scratch/libsA:$(pwd)/scratch/libsB scratch/big-dynstr +../src/patchelf --force-rpath --set-rpath $oldRPath:$(pwd)/scratch/libsA:$(pwd)/scratch/libsB scratch/big-dynstr if test "$(uname)" = FreeBSD; then export LD_LIBRARY_PATH=$(pwd)/scratch/libsB diff --git a/tests/set-rpath.sh b/tests/set-rpath.sh index b71e0f4..999acb4 100755 --- a/tests/set-rpath.sh +++ b/tests/set-rpath.sh @@ -11,7 +11,7 @@ cp libbar.so scratch/libsB/ oldRPath=$(../src/patchelf --print-rpath scratch/main) if test -z "$oldRPath"; then oldRPath="/oops"; fi -../src/patchelf --set-rpath $oldRPath:$(pwd)/scratch/libsA:$(pwd)/scratch/libsB scratch/main +../src/patchelf --force-rpath --set-rpath $oldRPath:$(pwd)/scratch/libsA:$(pwd)/scratch/libsB scratch/main #oldRPath=$(../src/patchelf --print-rpath scratch/libsA/libfoo.so) #if test -z "$oldRPath"; then oldRPath="/oops"; fi -- cgit v0.12