From ae5c6775e4d6e12ddc1c94e93fd4177f8f830f08 Mon Sep 17 00:00:00 2001 From: Jonas Kuemmerlin Date: Sun, 28 Apr 2013 16:42:01 +0200 Subject: Add ability to add and replace DT_NEEDED entries --- README | 10 +++++ src/patchelf.cc | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/README b/README index fbdbcf5..c56ca34 100644 --- a/README +++ b/README @@ -26,6 +26,16 @@ libraries. In particular, it can do the following: This option can be given multiple times. +* Add a declared dependency on a dynamic library (DT_NEEDED): + $ patchelf --add-needed libfoo.so.1 my-program + + This option can be give multiple times. + +* Replace a declared dependency on a dynamic library with another one (DT_NEEDED): + $ patchelf --replace-needed liboriginal.so.1 libreplacement.so.1 my-program + + This option can be give multiple times. + AUTHOR diff --git a/src/patchelf.cc b/src/patchelf.cc index 1b7e510..cb5fef3 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -150,7 +150,11 @@ public: void modifyRPath(RPathOp op, string newRPath); + void addNeeded(set libs); + void removeNeeded(set libs); + + void replaceNeeded(map& libs); private: @@ -1079,6 +1083,98 @@ void ElfFile::removeNeeded(set libs) memset(last, 0, sizeof(Elf_Dyn) * (dyn - last)); } +template +void ElfFile::replaceNeeded(map& libs) +{ + if (libs.empty()) return; + + Elf_Shdr & shdrDynamic = findSection(".dynamic"); + Elf_Shdr & shdrDynStr = findSection(".dynstr"); + char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset); + + Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); + + unsigned int dynStrAddedBytes = 0; + + for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) { + if (rdi(dyn->d_tag) == DT_NEEDED) { + char * name = strTab + rdi(dyn->d_un.d_val); + if (libs.find(name) != libs.end()) { + const string & replacement = libs[name]; + + debug("replacing DT_NEEDED entry `%s' with `%s'\n", name, replacement.c_str()); + + // technically, the string referred by d_val could be used otherwise, too (although unlikely) + // we'll therefore add a new string + debug("resizing .dynstr ..."); + + string & newDynStr = replaceSection(".dynstr", + rdi(shdrDynStr.sh_size) + replacement.size() + 1 + dynStrAddedBytes); + setSubstr(newDynStr, rdi(shdrDynStr.sh_size) + dynStrAddedBytes, replacement + '\0'); + + dyn->d_un.d_val = shdrDynStr.sh_size + dynStrAddedBytes; + + dynStrAddedBytes += replacement.size() + 1; + + changed = true; + } else { + debug("keeping DT_NEEDED entry `%s'\n", name); + } + } + } +} + +template +void ElfFile::addNeeded(set libs) +{ + if (libs.empty()) return; + + Elf_Shdr & shdrDynamic = findSection(".dynamic"); + Elf_Shdr & shdrDynStr = findSection(".dynstr"); + char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset); + + Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); + + /* add all new libs to the dynstr string table */ + unsigned int length = 0; + for (set::iterator it = libs.begin(); it != libs.end(); it++) { + length += it->size() + 1; + } + + string & newDynStr = replaceSection(".dynstr", + rdi(shdrDynStr.sh_size) + length + 1); + set libStrings; + unsigned int pos = 0; + for (set::iterator it = libs.begin(); it != libs.end(); it++) { + setSubstr(newDynStr, rdi(shdrDynStr.sh_size) + pos, *it + '\0'); + libStrings.insert(rdi(shdrDynStr.sh_size) + pos); + pos += it->size() + 1; + } + + /* add all new needed entries to the dynamic section */ + string & newDynamic = replaceSection(".dynamic", + rdi(shdrDynamic.sh_size) + sizeof(Elf_Dyn) * libs.size()); + + unsigned int idx = 0; + for ( ; rdi(((Elf_Dyn *) newDynamic.c_str())[idx].d_tag) != DT_NULL; idx++) ; + debug("DT_NULL index is %d\n", idx); + + /* Shift all entries down by the number of new entries. */ + setSubstr(newDynamic, sizeof(Elf_Dyn) * libs.size(), + string(newDynamic, 0, sizeof(Elf_Dyn) * (idx + 1))); + + /* Add the DT_NEEDED entries at the top. */ + unsigned int i = 0; + for (set::iterator it = libStrings.begin(); it != libStrings.end(); it++, i++) { + Elf_Dyn newDyn; + wri(newDyn.d_tag, DT_NEEDED); + wri(newDyn.d_un.d_val, *it); + setSubstr(newDynamic, i * sizeof(Elf_Dyn), string((char *) &newDyn, sizeof(Elf_Dyn))); + } + + changed = true; +} + static bool printInterpreter = false; static string newInterpreter; @@ -1088,7 +1184,8 @@ static bool setRPath = false; static bool printRPath = false; static string newRPath; static set neededLibsToRemove; - +static map neededLibsToReplace; +static set neededLibsToAdd; template static void patchElf2(ElfFile & elfFile, mode_t fileMode) @@ -1110,6 +1207,8 @@ static void patchElf2(ElfFile & elfFile, mode_t fileMode) elfFile.modifyRPath(elfFile.rpSet, newRPath); elfFile.removeNeeded(neededLibsToRemove); + elfFile.replaceNeeded(neededLibsToReplace); + elfFile.addNeeded(neededLibsToAdd); if (elfFile.isChanged()){ elfFile.rewriteSections(); @@ -1161,7 +1260,9 @@ void showHelp(const string & progName) [--shrink-rpath]\n\ [--print-rpath]\n\ [--force-rpath]\n\ + [--add-needed LIBRARY]\n\ [--remove-needed LIBRARY]\n\ + [--replace-needed LIBRARY NEW_LIBRARY]\n\ [--debug]\n\ [--version]\n\ FILENAME\n", progName.c_str()); @@ -1212,10 +1313,19 @@ int main(int argc, char * * argv) added. */ forceRPath = true; } + else if (arg == "--add-needed") { + if (++i == argc) error("missing argument"); + neededLibsToAdd.insert(argv[i]); + } else if (arg == "--remove-needed") { if (++i == argc) error("missing argument"); neededLibsToRemove.insert(argv[i]); } + else if (arg == "--replace-needed") { + if (i+2 >= argc) error("missing argument(s)"); + neededLibsToReplace[ argv[i+1] ] = argv[i+2]; + i += 2; + } else if (arg == "--debug") { debugMode = true; } -- cgit v0.12