diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/patchelf.cc | 235 |
1 files changed, 230 insertions, 5 deletions
diff --git a/src/patchelf.cc b/src/patchelf.cc index 826f78f..dcbfd38 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -144,13 +144,21 @@ public: string getInterpreter(); + string getSoname(); + + void setSoname(const string & newSoname); + void setInterpreter(const string & newInterpreter); typedef enum { rpPrint, rpShrink, rpSet } RPathOp; void modifyRPath(RPathOp op, string newRPath); + void addNeeded(set<string> libs); + void removeNeeded(set<string> libs); + + void replaceNeeded(map<string, string>& libs); private: @@ -835,12 +843,21 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress) debug("rewriting symbol table section %d\n", i); for (size_t entry = 0; (entry + 1) * sizeof(Elf_Sym) <= rdi(shdrs[i].sh_size); entry++) { Elf_Sym * sym = (Elf_Sym *) (contents + rdi(shdrs[i].sh_offset) + entry * sizeof(Elf_Sym)); - if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE) { - string section = sectionsByOldIndex[rdi(sym->st_shndx)]; + unsigned int shndx = rdi(sym->st_shndx); + if (shndx != SHN_UNDEF && shndx < SHN_LORESERVE) { + if (shndx >= sectionsByOldIndex.size()) { + fprintf(stderr, "warning: entry %d in symbol table refers to a non-existent section, skipping\n", shndx); + continue; + } + string section = sectionsByOldIndex.at(shndx); assert(!section.empty()); unsigned int newIndex = findSection3(section); // inefficient - //debug("rewriting symbol %d: index = %d (%s) -> %d\n", entry, rdi(sym->st_shndx), section.c_str(), newIndex); + //debug("rewriting symbol %d: index = %d (%s) -> %d\n", entry, shndx, section.c_str(), newIndex); wri(sym->st_shndx, newIndex); + /* Rewrite st_value. FIXME: we should do this for all + types, but most don't actually change. */ + if (ELF32_ST_TYPE(rdi(sym->st_info)) == STT_SECTION) + wri(sym->st_value, rdi(shdrs[newIndex].sh_addr)); } } } @@ -862,6 +879,89 @@ string ElfFile<ElfFileParamNames>::getInterpreter() return string((char *) contents + rdi(shdr.sh_offset), rdi(shdr.sh_size)); } +template<ElfFileParams> +string ElfFile<ElfFileParamNames>::getSoname() +{ + Elf_Shdr & shdrDynamic = findSection(".dynamic"); + Elf_Shdr & shdrDynStr = findSection(".dynstr"); + char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset); + + /* Find the DT_STRTAB entry in the dynamic section. */ + Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); + Elf_Addr strTabAddr = 0; + for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) + if (rdi(dyn->d_tag) == DT_STRTAB) strTabAddr = rdi(dyn->d_un.d_ptr); + if (!strTabAddr) error("strange: no string table"); + + /* We assume that the virtual address in the DT_STRTAB entry + of the dynamic section corresponds to the .dynstr section. */ + assert(strTabAddr == rdi(shdrDynStr.sh_addr)); + + Elf_Dyn * dynSoname = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); + char * soname = 0; + for ( ; rdi(dynSoname->d_tag) != DT_NULL; dynSoname++) { + if (rdi(dynSoname->d_tag) == DT_SONAME) { + soname = strTab + rdi(dynSoname->d_un.d_val); + break; + } + } + if (rdi(dynSoname->d_tag) == DT_NULL) { + error("Specified ELF file does not contain any DT_SONAME entry in .dynamic section!"); + } + else { + return soname; + } +} + +template<ElfFileParams> +void ElfFile<ElfFileParamNames>::setSoname(const string & newSoname) +{ + Elf_Shdr & shdrDynamic = findSection(".dynamic"); + Elf_Shdr & shdrDynStr = findSection(".dynstr"); + char * strTab = (char *) contents + rdi(shdrDynStr.sh_offset); + + /* Find the DT_STRTAB entry in the dynamic section. */ + Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); + Elf_Addr strTabAddr = 0; + for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) + if (rdi(dyn->d_tag) == DT_STRTAB) strTabAddr = rdi(dyn->d_un.d_ptr); + if (!strTabAddr) error("strange: no string table"); + + /* We assume that the virtual address in the DT_STRTAB entry + of the dynamic section corresponds to the .dynstr section. */ + assert(strTabAddr == rdi(shdrDynStr.sh_addr)); + + Elf_Dyn * dynSoname = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); + char * soname = 0; + for ( ; rdi(dynSoname->d_tag) != DT_NULL; dynSoname++) { + if (rdi(dynSoname->d_tag) == DT_SONAME) { + soname = strTab + rdi(dynSoname->d_un.d_val); + break; + } + } + if (rdi(dynSoname->d_tag) == DT_NULL) + error("Specified ELF file does not contain any DT_SONAME entry in .dynamic section!"); + + if (newSoname.size() <= strlen(soname)) { + debug("old soname: `%s', new soname: `%s'\n", soname, newSoname.c_str()); + strcpy(soname, newSoname.c_str()); + changed = true; + } + else { + /* Grow the .dynstr section to make room for the new DT_SONAME */ + debug("new soname is too long, resizing .dynstr section...\n"); + + string & newDynStr = replaceSection(".dynstr", + rdi(shdrDynStr.sh_size) + newSoname.size() + 1); + setSubstr(newDynStr, rdi(shdrDynStr.sh_size), newSoname + '\0'); + /* Update the DT_SONAME entry, if any */ + if (dynSoname) { + debug("old soname: `%s', new soname: `%s'\n", soname, newSoname.c_str()); + dynSoname->d_un.d_val = shdrDynStr.sh_size; + changed = true; + } + } +} template<ElfFileParams> void ElfFile<ElfFileParamNames>::setInterpreter(const string & newInterpreter) @@ -1079,8 +1179,103 @@ void ElfFile<ElfFileParamNames>::removeNeeded(set<string> libs) memset(last, 0, sizeof(Elf_Dyn) * (dyn - last)); } +template<ElfFileParams> +void ElfFile<ElfFileParamNames>::replaceNeeded(map<string, string>& 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<ElfFileParams> +void ElfFile<ElfFileParamNames>::addNeeded(set<string> 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<string>::iterator it = libs.begin(); it != libs.end(); it++) { + length += it->size() + 1; + } + + string & newDynStr = replaceSection(".dynstr", + rdi(shdrDynStr.sh_size) + length + 1); + set<Elf64_Xword> libStrings; + unsigned int pos = 0; + for (set<string>::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<Elf64_Xword>::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 bool printSoname = false; +static bool setSoname = false; +static string newSoname; static string newInterpreter; static bool shrinkRPath = false; @@ -1088,7 +1283,8 @@ static bool setRPath = false; static bool printRPath = false; static string newRPath; static set<string> neededLibsToRemove; - +static map<string, string> neededLibsToReplace; +static set<string> neededLibsToAdd; template<class ElfFile> static void patchElf2(ElfFile & elfFile, mode_t fileMode) @@ -1098,6 +1294,12 @@ static void patchElf2(ElfFile & elfFile, mode_t fileMode) if (printInterpreter) printf("%s\n", elfFile.getInterpreter().c_str()); + if (printSoname) + printf("%s\n", elfFile.getSoname().c_str()); + + if (setSoname) + elfFile.setSoname(newSoname); + if (newInterpreter != "") elfFile.setInterpreter(newInterpreter); @@ -1110,6 +1312,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(); @@ -1120,7 +1324,7 @@ static void patchElf2(ElfFile & elfFile, mode_t fileMode) static void patchElf() { - if (!printInterpreter && !printRPath) + if (!printInterpreter && !printRPath && !printSoname) debug("patching ELF file `%s'\n", fileName.c_str()); mode_t fileMode; @@ -1157,11 +1361,15 @@ void showHelp(const string & progName) fprintf(stderr, "syntax: %s\n\ [--set-interpreter FILENAME]\n\ [--print-interpreter]\n\ + [--print-soname]\t\tPrints 'DT_SONAME' entry of .dynamic section. Raises an error if DT_SONAME doesn't exist\n\ + [--set-soname SONAME]\t\tSets 'DT_SONAME' entry to SONAME. Raises an error if DT_SONAME doesn't exist\n\ [--set-rpath RPATH]\n\ [--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()); @@ -1187,6 +1395,14 @@ int main(int argc, char * * argv) else if (arg == "--print-interpreter") { printInterpreter = true; } + else if (arg == "--print-soname") { + printSoname = true; + } + else if (arg == "--set-soname") { + if (++i == argc) error("missing argument"); + setSoname = true; + newSoname = argv[i]; + } else if (arg == "--shrink-rpath") { shrinkRPath = true; } @@ -1212,10 +1428,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; } |