diff --git a/.gitignore b/.gitignore index 921b9f85..c3f60a5d 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ Makefile .direnv/ .vscode/ .idea/ +result diff --git a/src/patchelf.cc b/src/patchelf.cc index 668a5e49..6a57daf2 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -36,9 +36,12 @@ #include #include +#include #include #include #include +#include +#include #include "elf.h" #include "patchelf.h" @@ -221,8 +224,8 @@ static void checkPointer(const FileContents & contents, void * p, unsigned int s template -ElfFile::ElfFile(FileContents fContents) - : fileContents(fContents) +ElfFile::ElfFile(FileContents fContents, std::string fileName) + : fileContents(fContents), fileName(fileName) { /* Check the ELF header for basic validity. */ if (fileContents->size() < (off_t) sizeof(Elf_Ehdr)) error("missing ELF header"); @@ -1584,8 +1587,59 @@ void ElfFile::addNeeded(const std::set & libs) changed = true; } +// https://stackoverflow.com/a/54738358/143733 +// https://stackoverflow.com/a/478960/143733 +std::pair exec(const std::string & cmd) { + std::array buffer; + std::string result; + // overwrite the destructor to capture also the return code + int return_code = -1; + auto pclose_wrapper = [&return_code](FILE* cmd){ return_code = pclose(cmd); }; + { + std::unique_ptr pipe(popen(cmd.c_str(), "r"), pclose_wrapper); + if (pipe) { + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + } + } + } + return make_pair(result, return_code); +} + template -void ElfFile::printNeededLibs() // const +void ElfFile::shrinkWrap(std::map & neededLibsToReplace, std::set & neededLibsToAdd) +{ + const std::string interpreter = getInterpreter(); + const std::vector needed = getNeededLibs(); + const std::string cmd = fmt(interpreter, " --list ", this->fileName); + const std::pair result = exec(cmd); + if (result.second) { + error(fmt("ldd failed. ", result.second, "-", result.first)); + } + std::istringstream iss(result.first); + std::string line; + std::regex r("\\s*([^ ]+) => ([^ ]+)"); + while (std::getline(iss, line)) { + std::smatch matches; + if (!std::regex_search(line, matches, r)) { + continue; + } + + std::string soname = matches.str(1); + std::string location = matches.str(2); + debug("Found %s => %s\n", soname.c_str(), location.c_str()); + + // if the ELF file has this soname, then merely replace it + if (std::find(needed.begin(), needed.end(), soname) != needed.end()) { + neededLibsToReplace.insert({soname, location}); + } else { + neededLibsToAdd.insert(location); + } + } +} + +template +std::vector ElfFile::getNeededLibs() // const { const auto shdrDynamic = findSectionHeader(".dynamic"); const auto shdrDynStr = findSectionHeader(".dynstr"); @@ -1593,12 +1647,26 @@ void ElfFile::printNeededLibs() // const const Elf_Dyn *dyn = (Elf_Dyn *) (fileContents->data() + rdi(shdrDynamic.sh_offset)); + std::vector results; + for (; rdi(dyn->d_tag) != DT_NULL; dyn++) { if (rdi(dyn->d_tag) == DT_NEEDED) { const char *name = strTab + rdi(dyn->d_un.d_val); - printf("%s\n", name); + results.push_back(std::string(name)); } } + + return results; +} + + +template +void ElfFile::printNeededLibs() // const +{ + const std::vector needed = getNeededLibs(); + for (std::string soname : needed) { + printf("%s\n", soname.c_str()); + } } @@ -1671,6 +1739,7 @@ void ElfFile::clearSymbolVersions(const std::set static bool printInterpreter = false; static bool printSoname = false; +static bool shrinkWrap = false; static bool setSoname = false; static std::string newSoname; static std::string newInterpreter; @@ -1694,6 +1763,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con if (printInterpreter) printf("%s\n", elfFile.getInterpreter().c_str()); + if (shrinkWrap) + elfFile.shrinkWrap(neededLibsToReplace, neededLibsToAdd); + if (printSoname) elfFile.modifySoname(elfFile.printSoname, ""); @@ -1745,9 +1817,9 @@ static void patchElf() const std::string & outputFileName2 = outputFileName.empty() ? fileName : outputFileName; if (getElfType(fileContents).is32Bit) - patchElf2(ElfFile(fileContents), fileContents, outputFileName2); + patchElf2(ElfFile(fileContents, fileName), fileContents, outputFileName2); else - patchElf2(ElfFile(fileContents), fileContents, outputFileName2); + patchElf2(ElfFile(fileContents, fileName), fileContents, outputFileName2); } } @@ -1766,6 +1838,7 @@ void showHelp(const std::string & progName) fprintf(stderr, "syntax: %s\n\ [--set-interpreter FILENAME]\n\ [--page-size SIZE]\n\ + [--shrink-wrap]\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.\n\ @@ -1817,6 +1890,9 @@ int mainWrapped(int argc, char * * argv) else if (arg == "--print-soname") { printSoname = true; } + else if (arg == "--shrink-wrap") { + shrinkWrap = true; + } else if (arg == "--set-soname") { if (++i == argc) error("missing argument"); setSoname = true; diff --git a/src/patchelf.h b/src/patchelf.h index 93a0e5c2..57e84611 100644 --- a/src/patchelf.h +++ b/src/patchelf.h @@ -9,6 +9,7 @@ class ElfFile public: const FileContents fileContents; + const std::string fileName; private: @@ -35,7 +36,7 @@ class ElfFile std::vector sectionsByOldIndex; public: - explicit ElfFile(FileContents fileContents); + explicit ElfFile(FileContents fileContents, std::string filename); bool isChanged() { @@ -127,6 +128,10 @@ class ElfFile void replaceNeeded(const std::map & libs); + void shrinkWrap(std::map & neededLibsToReplace, std::set & neededLibsToAdd); + + std::vector getNeededLibs(); + void printNeededLibs() /* should be const */; void noDefaultLib();