From 8ba25a51a89462888761d753518045067a5e4b58 Mon Sep 17 00:00:00 2001 From: Ronald Date: Tue, 28 Feb 2023 08:48:24 +0100 Subject: [PATCH 1/6] add cleaning of dynstr --- src/patchelf.cc | 341 ++++++++++++++++++++++++++++++++++++++---------- src/patchelf.h | 27 +++- 2 files changed, 297 insertions(+), 71 deletions(-) diff --git a/src/patchelf.cc b/src/patchelf.cc index 0c8e2607..65fde331 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -39,6 +41,7 @@ #include #include #include +#include #include "elf.h" #include "patchelf.h" @@ -56,9 +59,12 @@ static bool debugMode = false; static bool forceRPath = false; -static std::vector fileNames; -static std::string outputFileName; +using namespace std; + +static vector fileNames; +static string outputFileName; static bool alwaysWrite = false; +static bool cleanStrtab = false; #ifdef DEFAULT_PAGESIZE static int forcedPageSize = DEFAULT_PAGESIZE; #else @@ -104,7 +110,7 @@ static std::string downcase(std::string s) why... */ template template -I ElfFile::rdi(I i) const +I ElfFile::rdi(I i) const { I r = 0; if (littleEndian) { @@ -119,7 +125,6 @@ I ElfFile::rdi(I i) const return r; } - static void debug(const char * format, ...) { if (debugMode) { @@ -240,7 +245,7 @@ static void checkPointer(const FileContents & contents, void * p, unsigned int s template -ElfFile::ElfFile(FileContents fContents) +ElfFile::ElfFile(FileContents fContents) : fileContents(fContents) { /* Check the ELF header for basic validity. */ @@ -309,7 +314,7 @@ ElfFile::ElfFile(FileContents fContents) template -unsigned int ElfFile::getPageSize() const +unsigned int ElfFile::getPageSize() const { if (forcedPageSize > 0) return forcedPageSize; @@ -337,7 +342,7 @@ unsigned int ElfFile::getPageSize() const template -void ElfFile::sortPhdrs() +void ElfFile::sortPhdrs() { /* Sort the segments by offset. */ CompPhdr comp; @@ -347,7 +352,7 @@ void ElfFile::sortPhdrs() template -void ElfFile::sortShdrs() +void ElfFile::sortShdrs() { /* Translate sh_link mappings to section names, since sorting the sections will invalidate the sh_link fields. */ @@ -429,14 +434,8 @@ static void writeFile(const std::string & fileName, const FileContents & content } -static uint64_t roundUp(uint64_t n, uint64_t m) -{ - return ((n - 1) / m + 1) * m; -} - - template -void ElfFile::shiftFile(unsigned int extraPages, size_t startOffset) +void ElfFile::shiftFile(unsigned int extraPages, size_t startOffset) { assert(startOffset >= sizeof(Elf_Ehdr)); @@ -519,7 +518,7 @@ void ElfFile::shiftFile(unsigned int extraPages, size_t start template -std::string ElfFile::getSectionName(const Elf_Shdr & shdr) const +std::string ElfFile::getSectionName(const Elf_Shdr & shdr) const { const size_t name_off = rdi(shdr.sh_name); @@ -531,7 +530,7 @@ std::string ElfFile::getSectionName(const Elf_Shdr & shdr) co template -Elf_Shdr & ElfFile::findSectionHeader(const SectionName & sectionName) +Elf_Shdr & ElfFile::findSectionHeader(const SectionName & sectionName) { auto shdr = tryFindSectionHeader(sectionName); if (!shdr) { @@ -545,7 +544,7 @@ Elf_Shdr & ElfFile::findSectionHeader(const SectionName & sec template -std::optional> ElfFile::tryFindSectionHeader(const SectionName & sectionName) +std::optional> ElfFile::tryFindSectionHeader(const SectionName & sectionName) { auto i = getSectionIndex(sectionName); if (i) @@ -555,7 +554,7 @@ std::optional> ElfFile::tryF template -unsigned int ElfFile::getSectionIndex(const SectionName & sectionName) +unsigned int ElfFile::getSectionIndex(const SectionName & sectionName) { for (unsigned int i = 1; i < rdi(hdr()->e_shnum); ++i) if (getSectionName(shdrs.at(i)) == sectionName) return i; @@ -563,13 +562,13 @@ unsigned int ElfFile::getSectionIndex(const SectionName & sec } template -bool ElfFile::haveReplacedSection(const SectionName & sectionName) const +bool ElfFile::haveReplacedSection(const SectionName & sectionName) const { return replacedSections.count(sectionName); } template -std::string & ElfFile::replaceSection(const SectionName & sectionName, +std::string & ElfFile::replaceSection(const SectionName & sectionName, unsigned int size) { auto i = replacedSections.find(sectionName); @@ -588,9 +587,24 @@ std::string & ElfFile::replaceSection(const SectionName & sec return replacedSections[sectionName]; } +template +std::string & ElfFile::replaceSection(const SectionName & sectionName, + std::string & replacement) +{ + auto i = replacedSections.find(sectionName); + + if (i != replacedSections.end()) { + i->second = replacement; + } else { + replacedSections[sectionName] = replacement; + } + + return replacedSections[sectionName]; +} + template -void ElfFile::writeReplacedSections(Elf_Off & curOff, +void ElfFile::writeReplacedSections(Elf_Off & curOff, Elf_Addr startAddr, Elf_Off startOffset) { /* Overwrite the old section contents with 'X's. Do this @@ -600,7 +614,7 @@ void ElfFile::writeReplacedSections(Elf_Off & curOff, const std::string & sectionName = i.first; Elf_Shdr & shdr = findSectionHeader(sectionName); if (rdi(shdr.sh_type) != SHT_NOBITS) - memset(fileContents->data() + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size)); + memset(fileContents->data() + rdi(shdr.sh_offset), 'Y', rdi(shdr.sh_size)); } std::set noted_phdrs = {}; @@ -712,8 +726,9 @@ void ElfFile::writeReplacedSections(Elf_Off & curOff, } } } - - curOff += roundUp(i->second.size(), sectionAlignment); + debug("curoff %d, size %d, align %d\n", curOff, i->second.size(), sectionAlignment); + curOff += roundUp((Elf_Off)i->second.size(), sectionAlignment); + debug("-> next curoff %d\n", curOff); } replacedSections.clear(); @@ -721,7 +736,7 @@ void ElfFile::writeReplacedSections(Elf_Off & curOff, template -void ElfFile::rewriteSectionsLibrary() +void ElfFile::rewriteSectionsLibrary() { /* For dynamic libraries, we just place the replacement sections at the end of the file. They're mapped into memory by a @@ -730,7 +745,7 @@ void ElfFile::rewriteSectionsLibrary() Elf_Addr startPage = 0; Elf_Addr firstPage = 0; for (auto & phdr : phdrs) { - Elf_Addr thisPage = roundUp(rdi(phdr.p_vaddr) + rdi(phdr.p_memsz), getPageSize()); + Elf_Addr thisPage = roundUp((Elf_Off)(rdi(phdr.p_vaddr) + rdi(phdr.p_memsz)), getPageSize()); if (thisPage > startPage) startPage = thisPage; if (rdi(phdr.p_type) == PT_PHDR) firstPage = rdi(phdr.p_vaddr) - rdi(phdr.p_offset); } @@ -757,12 +772,12 @@ void ElfFile::rewriteSectionsLibrary() } /* Compute the total space needed for the replaced sections */ - off_t neededSpace = 0; + Elf_Off neededSpace = 0; for (auto & s : replacedSections) - neededSpace += roundUp(s.second.size(), sectionAlignment); + neededSpace += roundUp((Elf_Off)s.second.size(), sectionAlignment); debug("needed space is %d\n", neededSpace); - Elf_Off startOffset = roundUp(fileContents->size(), getPageSize()); + Elf_Off startOffset = roundUp((Elf_Off)fileContents->size(), getPageSize()); // In older version of binutils (2.30), readelf would check if the dynamic // section segment is strictly smaller than the file (and not same size). @@ -817,7 +832,7 @@ void ElfFile::rewriteSectionsLibrary() static bool noSort = false; template -void ElfFile::rewriteSectionsExecutable() +void ElfFile::rewriteSectionsExecutable() { if (!noSort) { /* Sort the sections by offset, otherwise we won't correctly find @@ -840,7 +855,7 @@ void ElfFile::rewriteSectionsExecutable() debug("last replaced is %d\n", lastReplaced); /* Try to replace all sections before that, as far as possible. - Stop when we reach an irreplacable section (such as one of type + Stop when we reach an irreplaceable section (such as one of type SHT_PROGBITS). These cannot be moved in virtual address space since that would invalidate absolute references to them. */ assert(lastReplaced + 1 < shdrs.size()); /* !!! I'm lazy. */ @@ -898,9 +913,9 @@ void ElfFile::rewriteSectionsExecutable() /* Compute the total space needed for the replaced sections, the ELF header, and the program headers. */ - size_t neededSpace = sizeof(Elf_Ehdr) + phdrs.size() * sizeof(Elf_Phdr); + Elf_Off neededSpace = sizeof(Elf_Ehdr) + phdrs.size() * sizeof(Elf_Phdr); for (auto & i : replacedSections) - neededSpace += roundUp(i.second.size(), sectionAlignment); + neededSpace += roundUp((Elf_Off)i.second.size(), sectionAlignment); debug("needed space is %d\n", neededSpace); @@ -912,7 +927,7 @@ void ElfFile::rewriteSectionsExecutable() neededSpace += sizeof(Elf_Phdr); debug("needed space is %d\n", neededSpace); - unsigned int neededPages = roundUp(neededSpace - startOffset, getPageSize()) / getPageSize(); + unsigned int neededPages = roundUp((Elf_Off)(neededSpace - startOffset), getPageSize()) / getPageSize(); debug("needed pages is %d\n", neededPages); if (neededPages * getPageSize() > firstPage) error("virtual address space underrun!"); @@ -927,7 +942,7 @@ void ElfFile::rewriteSectionsExecutable() /* Clear out the free space. */ Elf_Off curOff = sizeof(Elf_Ehdr) + phdrs.size() * sizeof(Elf_Phdr); debug("clearing first %d bytes\n", startOffset - curOff); - memset(fileContents->data() + curOff, 0, startOffset - curOff); + memset(fileContents->data() + curOff, 'F', startOffset - curOff); /* Write out the replaced sections. */ @@ -940,7 +955,7 @@ void ElfFile::rewriteSectionsExecutable() template -void ElfFile::normalizeNoteSegments() +void ElfFile::normalizeNoteSegments() { /* Break up PT_NOTE segments containing multiple SHT_NOTE sections. This is to avoid having to deal with moving multiple sections together if @@ -1008,7 +1023,7 @@ void ElfFile::normalizeNoteSegments() template -void ElfFile::rewriteSections() +void ElfFile::rewriteSections() { if (replacedSections.empty()) return; @@ -1028,7 +1043,7 @@ void ElfFile::rewriteSections() template -void ElfFile::rewriteHeaders(Elf_Addr phdrAddress) +void ElfFile::rewriteHeaders(Elf_Addr phdrAddress) { /* Rewrite the program header table. */ @@ -1184,14 +1199,14 @@ static void setSubstr(std::string & s, unsigned int pos, const std::string & t) template -std::string ElfFile::getInterpreter() +std::string ElfFile::getInterpreter() { auto shdr = findSectionHeader(".interp"); return std::string((char *) fileContents->data() + rdi(shdr.sh_offset), rdi(shdr.sh_size) - 1); } template -void ElfFile::modifyOsAbi(osAbiMode op, const std::string & newOsAbi) +void ElfFile::modifyOsAbi(osAbiMode op, const std::string & newOsAbi) { unsigned char abi = hdr()->e_ident[EI_OSABI]; @@ -1253,7 +1268,7 @@ void ElfFile::modifyOsAbi(osAbiMode op, const std::string & n } template -void ElfFile::modifySoname(sonameMode op, const std::string & newSoname) +void ElfFile::modifySoname(sonameMode op, const std::string & newSoname) { if (rdi(hdr()->e_type) != ET_DYN) { debug("this is not a dynamic library\n"); @@ -1324,16 +1339,16 @@ void ElfFile::modifySoname(sonameMode op, const std::string & } changed = true; - this->rewriteSections(); + rewriteSections(); } template -void ElfFile::setInterpreter(const std::string & newInterpreter) +void ElfFile::setInterpreter(const std::string & newInterpreter) { std::string & section = replaceSection(".interp", newInterpreter.size() + 1); setSubstr(section, 0, newInterpreter + '\0'); changed = true; - this->rewriteSections(); + rewriteSections(); } @@ -1346,7 +1361,7 @@ static void appendRPath(std::string & rpath, const std::string & path) /* For each directory in the RPATH, check if it contains any needed library. */ template -std::string ElfFile::shrinkRPath(char* rpath, std::vector &neededLibs, const std::vector & allowedRpathPrefixes) { +std::string ElfFile::shrinkRPath(char* rpath, std::vector &neededLibs, const std::vector & allowedRpathPrefixes) { std::vector neededLibFound(neededLibs.size(), false); std::string newRPath = ""; @@ -1395,7 +1410,7 @@ std::string ElfFile::shrinkRPath(char* rpath, std::vector -void ElfFile::removeRPath(Elf_Shdr & shdrDynamic) { +void ElfFile::removeRPath(Elf_Shdr & shdrDynamic) { auto dyn = (Elf_Dyn *)(fileContents->data() + rdi(shdrDynamic.sh_offset)); Elf_Dyn * last = dyn; for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) { @@ -1410,11 +1425,11 @@ void ElfFile::removeRPath(Elf_Shdr & shdrDynamic) { } } memset(last, 0, sizeof(Elf_Dyn) * (dyn - last)); - this->rewriteSections(); + rewriteSections(); } template -void ElfFile::modifyRPath(RPathOp op, +void ElfFile::modifyRPath(RPathOp op, const std::vector & allowedRpathPrefixes, std::string newRPath) { auto shdrDynamic = findSectionHeader(".dynamic"); @@ -1514,7 +1529,7 @@ void ElfFile::modifyRPath(RPathOp op, size_t rpathSize = 0; if (rpath) { rpathSize = strlen(rpath); - memset(rpath, 'X', rpathSize); + memset(rpath, 0, rpathSize); } debug("new rpath is '%s'\n", newRPath.c_str()); @@ -1558,12 +1573,12 @@ void ElfFile::modifyRPath(RPathOp op, newDyn.d_un.d_val = shdrDynStr.sh_size; setSubstr(newDynamic, 0, std::string((char *) &newDyn, sizeof(Elf_Dyn))); } - this->rewriteSections(); + rewriteSections(); } template -void ElfFile::removeNeeded(const std::set & libs) +void ElfFile::removeNeeded(const std::set & libs) { if (libs.empty()) return; @@ -1571,7 +1586,8 @@ void ElfFile::removeNeeded(const std::set & libs auto shdrDynStr = findSectionHeader(".dynstr"); char * strTab = (char *) fileContents->data() + rdi(shdrDynStr.sh_offset); - auto dyn = (Elf_Dyn *)(fileContents->data() + rdi(shdrDynamic.sh_offset)); + auto dynamic = std::string((char*)fileContents->data() + rdi(shdrDynamic.sh_offset), rdi(shdrDynamic.sh_size)); + auto dyn = (Elf_Dyn *)dynamic.c_str(); Elf_Dyn * last = dyn; for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) { if (rdi(dyn->d_tag) == DT_NEEDED) { @@ -1587,13 +1603,18 @@ void ElfFile::removeNeeded(const std::set & libs *last++ = *dyn; } - memset(last, 0, sizeof(Elf_Dyn) * (dyn - last)); + if (!changed) return; + auto newsize = rdi(shdrDynamic.sh_size) - sizeof(Elf_Dyn) * (dyn - last); + wri(shdrDynamic.sh_size, newsize); + dynamic.resize(newsize); + debug("new .dynamic size %d\n", rdi(shdrDynamic.sh_size)); + replaceSection(".dynamic", dynamic); - this->rewriteSections(); + rewriteSections(); } template -void ElfFile::replaceNeeded(const std::map & libs) +void ElfFile::replaceNeeded(const std::map & libs) { if (libs.empty()) return; @@ -1714,11 +1735,11 @@ void ElfFile::replaceNeeded(const std::maprewriteSections(); + rewriteSections(); } template -void ElfFile::addNeeded(const std::set & libs) +void ElfFile::addNeeded(const std::set & libs) { if (libs.empty()) return; @@ -1766,11 +1787,11 @@ void ElfFile::addNeeded(const std::set & libs) changed = true; - this->rewriteSections(); + rewriteSections(); } template -void ElfFile::printNeededLibs() // const +void ElfFile::printNeededLibs() // const { const auto shdrDynamic = findSectionHeader(".dynamic"); const auto shdrDynStr = findSectionHeader(".dynstr"); @@ -1788,7 +1809,7 @@ void ElfFile::printNeededLibs() // const template -void ElfFile::noDefaultLib() +void ElfFile::noDefaultLib() { auto shdrDynamic = findSectionHeader(".dynamic"); @@ -1823,12 +1844,12 @@ void ElfFile::noDefaultLib() setSubstr(newDynamic, 0, std::string((char *) &newDyn, sizeof(Elf_Dyn))); } - this->rewriteSections(); + rewriteSections(); changed = true; } template -void ElfFile::addDebugTag() +void ElfFile::addDebugTag() { auto shdrDynamic = findSectionHeader(".dynamic"); @@ -1855,12 +1876,12 @@ void ElfFile::addDebugTag() newDyn.d_un.d_val = 0; setSubstr(newDynamic, 0, std::string((char *) &newDyn, sizeof(Elf_Dyn))); - this->rewriteSections(); + rewriteSections(); changed = true; } template -void ElfFile::clearSymbolVersions(const std::set & syms) +void ElfFile::clearSymbolVersions(const std::set & syms) { if (syms.empty()) return; @@ -1885,9 +1906,186 @@ void ElfFile::clearSymbolVersions(const std::set } } changed = true; - this->rewriteSections(); + rewriteSections(); } +struct symstr { + const char *str; + size_t len; +}; + +// Elf32_Off, Elf32_Word, Elf54_Word = unsigned int +// Elf64_Off, Elf64_Xword = unsigned long (or long long on 32 bit) +//typedef variant symref; +//typedef variant symref; +typedef variant symref; + +struct Comparator { + using is_transparent = void; + bool operator()(const symstr& a, const symstr& b) const + { + return (a.str+a.len < b.str+b.len); + } + + bool operator()(const char* a, const symstr& b) const + { + return (a < b.str); + } + + bool operator()(const symstr& a, const char* b) const + { + return (a.str+a.len < b); + } +}; + +#include + +class mymap : public map, Comparator> { + + public: + size_t totalsize; + mymap(const char *s, size_t s_length) : totalsize(s_length) + { + const char *c = NULL; + while ((c=argz_next(s, s_length, c))) { + insert({(const struct symstr){c, strlen(c)},forward_list()}); + } + } + + forward_list& operator[](const char *i) + { + return lower_bound(i)->second; + } + + template + void incref(const char *idx, T *sym) + { + (*this)[idx].push_front(sym); + } + +}; + +template +void ElfFile::cleanstrtab() +{ + auto shdrDynamic = findSectionHeader(".dynamic"); + auto shdrDynStr = findSectionHeader(".dynstr"); + auto shdrDynSym = findSectionHeader(".dynsym"); + + auto strTab = (char *)fileContents->data() + rdi(shdrDynStr.sh_offset); + int verneednum = 0; + int verdefnum = 0; + //mkref(strTab, rdi(shdrDynStr.sh_size)); // new strInfo() + mymap m(strTab, rdi(shdrDynStr.sh_size)); + + Elf_Dyn* dyn = (Elf_Dyn *) (fileContents->data() + rdi(shdrDynamic.sh_offset)); + + /* NAMEs in .dynamic */ + for (; rdi(dyn->d_tag) != DT_NULL; dyn++) { + switch (rdi(dyn->d_tag)) { + case DT_NEEDED: case DT_SONAME: + m.incref(strTab + rdi(dyn->d_un.d_val), &dyn->d_un.d_val); + + //incref(strTab + rdi(dyn->d_un.d_val), &dyn->d_un.d_val, sizeof dyn->d_un.d_val); + //m[strTab + rdi(dyn->d_un.d_val)].push_front(lsym);} + break; + case DT_VERNEEDNUM: + verneednum = rdi(dyn->d_un.d_val); + break; + case DT_VERDEFNUM: + verdefnum = rdi(dyn->d_un.d_val); + break; + } + } + /* NAMEs in dynsym */ + Elf_Sym* sym = (Elf_Sym *) (fileContents->data() + rdi(shdrDynSym.sh_offset)); + int count = rdi(shdrDynSym.sh_size) / sizeof(Elf_Sym); + for (int i = 0; i < count; i++) { + auto dynsym = sym[i]; + //incref(strTab + rdi(dynsym.st_name), &dynsym.st_name, sizeof dynsym.st_name); + m.incref(strTab + rdi(dynsym.st_name), &dynsym.st_name); + } + + if (verneednum) { + auto shdrVerSymr = findSectionHeader(".gnu.version_r"); + Elf_Shdr & shdrVersionRStrings = shdrs.at(rdi(shdrVerSymr.sh_link)); + // this is where we find the actual version strings; is it dynstr? + if (shdrVersionRStrings.sh_offset == shdrDynStr.sh_offset) { + auto verr = fileContents->data() + rdi(shdrVerSymr.sh_offset); + auto versym = (Elf_Verneed *)verr; + while (verneednum>0) { + // NAMEs in verneed referring to dynstr + //incref(strTab + rdi(versym->vn_file), &versym->vn_file, sizeof versym->vn_file); + m.incref(strTab + rdi(versym->vn_file), &versym->vn_file); + int auxnum = rdi(versym->vn_cnt); + auto auxsym = (Elf_Vernaux *)((char*)versym + rdi(versym->vn_aux)); + while (auxnum>0) { + //incref(strTab + rdi(auxsym->vna_name), &auxsym->vna_name, sizeof auxsym->vna_name); + m.incref(strTab + rdi(auxsym->vna_name), &auxsym->vna_name); + auxsym = (Elf_Vernaux *)((char*)auxsym + rdi(auxsym->vna_next)); + auxnum--; + } + versym = (Elf_Verneed *)((char*)versym + rdi(versym->vn_next)); + verneednum--; + } + } + } + + if (verdefnum) { + auto shdrVerSymd = findSectionHeader(".gnu.version_d"); + Elf_Shdr & shdrVersionDStrings = shdrs.at(rdi(shdrVerSymd.sh_link)); + // this is where we find the actual version strings; is it dynstr? + if (shdrVersionDStrings.sh_offset == shdrDynStr.sh_offset) { + // NAMEs in verdef referring to dynstr + auto verd = fileContents->data() + rdi(shdrVerSymd.sh_offset); + auto versym = (Elf_Verdef *)verd; + while (verneednum>0) { + // NAMEs in verneed referring to dynstr + int auxnum = rdi(versym->vd_cnt); + auto auxsym = (Elf_Verdaux *)((char*)versym + rdi(versym->vd_aux)); + while (auxnum>0) { + //incref(strTab + rdi(auxsym->vda_name), &auxsym->vda_name, sizeof auxsym->vda_name); + m.incref(strTab + rdi(auxsym->vda_name), &auxsym->vda_name); + auxsym = (Elf_Verdaux *)(verd + rdi(auxsym->vda_next)); + auxnum--; + } + versym = (Elf_Verdef *)((char*)versym + rdi(versym->vd_next)); + verneednum--; + } + } + } + + int shift=0; + int newsize=m.totalsize; + for (auto& [s, l] : m) { + if (shift) { + for (auto& p : l) { + visit([this,shift](auto &x){ + wri(*x, rdi(*x)-shift); + },p); + } + debug("shifted sym %s by %d bytes, %s refs\n", s.str-shift, shift, l.empty()?"0":">=1"); + } + if (l.empty()) { + char *sym = (char *)s.str - shift; + debug("sym %s is unreferenced\n", sym); + shift+= s.len+1; + debug("shifting next symbols by %d bytes\n", shift); + newsize-=s.len+1; + memmove((void*)sym, sym+s.len+1, newsize-(sym-strTab)); + } + } + memset((void*)(strTab+newsize), 0, shift); + + if (shift) { + debug("new .dynstr size %d\n", newsize); + changed=true; + replaceSection(".dynstr", newsize); + rewriteSections(); + } +} + + static bool printInterpreter = false; static bool printOsAbi = false; static bool setOsAbi = false; @@ -1957,6 +2155,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con if (addDebugTag) elfFile.addDebugTag(); + if (cleanStrtab) + elfFile.cleanstrtab(); + if (elfFile.isChanged()){ writeFile(fileName, elfFile.fileContents); } else if (alwaysWrite) { @@ -1976,9 +2177,9 @@ static void patchElf() const std::string & outputFileName2 = outputFileName.empty() ? fileName : outputFileName; if (getElfType(fileContents).is32Bit) - patchElf2(ElfFile(fileContents), fileContents, outputFileName2); + patchElf2(ElfFile(fileContents), fileContents, outputFileName2); else - patchElf2(ElfFile(fileContents), fileContents, outputFileName2); + patchElf2(ElfFile(fileContents), fileContents, outputFileName2); } } @@ -2016,6 +2217,7 @@ void showHelp(const std::string & progName) [--no-default-lib]\n\ [--no-sort]\t\tDo not sort program+section headers; useful for debugging patchelf.\n\ [--clear-symbol-version SYMBOL]\n\ + [--clean-strtab]\n\ [--add-debug-tag]\n\ [--output FILE]\n\ [--debug]\n\ @@ -2125,6 +2327,9 @@ int mainWrapped(int argc, char * * argv) if (++i == argc) error("missing argument"); symbolsToClearVersion.insert(resolveArgument(argv[i])); } + else if (arg == "--clean-strtab") { + cleanStrtab=true; + } else if (arg == "--output") { if (++i == argc) error("missing argument"); outputFileName = resolveArgument(argv[i]); diff --git a/src/patchelf.h b/src/patchelf.h index c336c511..a2aedf33 100644 --- a/src/patchelf.h +++ b/src/patchelf.h @@ -1,7 +1,7 @@ using FileContents = std::shared_ptr>; -#define ElfFileParams class Elf_Ehdr, class Elf_Phdr, class Elf_Shdr, class Elf_Addr, class Elf_Off, class Elf_Dyn, class Elf_Sym, class Elf_Verneed, class Elf_Versym -#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed, Elf_Versym +#define ElfFileParams class Elf_Ehdr, class Elf_Phdr, class Elf_Shdr, class Elf_Addr, class Elf_Off, class Elf_Dyn, class Elf_Sym, class Elf_Verneed, class Elf_Vernaux, class Elf_Verdef, class Elf_Verdaux, class Elf_Versym +#define ElfFileParamNames(x) Elf##x##_Ehdr, Elf##x##_Phdr, Elf##x##_Shdr, Elf##x##_Addr, Elf##x##_Off, Elf##x##_Dyn, Elf##x##_Sym, Elf##x##_Verneed, Elf##x##_Vernaux, Elf##x##_Verdef, Elf##x##_Verdaux, Elf##x##_Versym template class ElfFile @@ -90,6 +90,9 @@ class ElfFile std::string & replaceSection(const SectionName & sectionName, unsigned int size); + std::string & replaceSection(const SectionName & sectionName, + std::string & replacement); + bool haveReplacedSection(const SectionName & sectionName) const; void writeReplacedSections(Elf_Off & curOff, @@ -139,6 +142,8 @@ class ElfFile void clearSymbolVersions(const std::set & syms); + void cleanstrtab(); + private: /* Convert an integer in big or little endian representation (as @@ -149,12 +154,28 @@ class ElfFile /* Convert back to the ELF representation. */ template - I wri(I & t, unsigned long long i) const + I wri(I & t, int64_t i) const { t = rdi((I) i); + return (I) i; + } + template + I wri(I & t, I i) const + { + t = rdi(i); return i; } + static Elf_Off roundUp(Elf_Off n, unsigned int m) + { + if (m==(m&-m)) { /* typical case: m is power of 2 */ + m--; + return (n+m)&~m; + } + /* fallback: not power of 2 */ + return ((n - 1) / m + 1) * m; + } + Elf_Ehdr *hdr() { return (Elf_Ehdr *)fileContents->data(); } From 9ab525eba455f651c7e02179d74d6f488afd439e Mon Sep 17 00:00:00 2001 From: Ronald Date: Tue, 28 Feb 2023 21:38:02 +0100 Subject: [PATCH 2/6] remove some leftover comment-out lines --- src/patchelf.cc | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/patchelf.cc b/src/patchelf.cc index 65fde331..b1113037 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -1975,7 +1975,7 @@ void ElfFile::cleanstrtab() auto strTab = (char *)fileContents->data() + rdi(shdrDynStr.sh_offset); int verneednum = 0; int verdefnum = 0; - //mkref(strTab, rdi(shdrDynStr.sh_size)); // new strInfo() + mymap m(strTab, rdi(shdrDynStr.sh_size)); Elf_Dyn* dyn = (Elf_Dyn *) (fileContents->data() + rdi(shdrDynamic.sh_offset)); @@ -1985,9 +1985,6 @@ void ElfFile::cleanstrtab() switch (rdi(dyn->d_tag)) { case DT_NEEDED: case DT_SONAME: m.incref(strTab + rdi(dyn->d_un.d_val), &dyn->d_un.d_val); - - //incref(strTab + rdi(dyn->d_un.d_val), &dyn->d_un.d_val, sizeof dyn->d_un.d_val); - //m[strTab + rdi(dyn->d_un.d_val)].push_front(lsym);} break; case DT_VERNEEDNUM: verneednum = rdi(dyn->d_un.d_val); @@ -2002,7 +1999,6 @@ void ElfFile::cleanstrtab() int count = rdi(shdrDynSym.sh_size) / sizeof(Elf_Sym); for (int i = 0; i < count; i++) { auto dynsym = sym[i]; - //incref(strTab + rdi(dynsym.st_name), &dynsym.st_name, sizeof dynsym.st_name); m.incref(strTab + rdi(dynsym.st_name), &dynsym.st_name); } @@ -2015,12 +2011,10 @@ void ElfFile::cleanstrtab() auto versym = (Elf_Verneed *)verr; while (verneednum>0) { // NAMEs in verneed referring to dynstr - //incref(strTab + rdi(versym->vn_file), &versym->vn_file, sizeof versym->vn_file); m.incref(strTab + rdi(versym->vn_file), &versym->vn_file); int auxnum = rdi(versym->vn_cnt); auto auxsym = (Elf_Vernaux *)((char*)versym + rdi(versym->vn_aux)); while (auxnum>0) { - //incref(strTab + rdi(auxsym->vna_name), &auxsym->vna_name, sizeof auxsym->vna_name); m.incref(strTab + rdi(auxsym->vna_name), &auxsym->vna_name); auxsym = (Elf_Vernaux *)((char*)auxsym + rdi(auxsym->vna_next)); auxnum--; @@ -2044,7 +2038,6 @@ void ElfFile::cleanstrtab() int auxnum = rdi(versym->vd_cnt); auto auxsym = (Elf_Verdaux *)((char*)versym + rdi(versym->vd_aux)); while (auxnum>0) { - //incref(strTab + rdi(auxsym->vda_name), &auxsym->vda_name, sizeof auxsym->vda_name); m.incref(strTab + rdi(auxsym->vda_name), &auxsym->vda_name); auxsym = (Elf_Verdaux *)(verd + rdi(auxsym->vda_next)); auxnum--; From 92640688b9e9e011bde5c167aa8f9750ad82f7c8 Mon Sep 17 00:00:00 2001 From: Ronald Date: Mon, 6 Mar 2023 21:04:21 +0100 Subject: [PATCH 3/6] address some cleanup suggestions --- src/patchelf.cc | 155 ++++++++++++++++++++++++------------------------ 1 file changed, 76 insertions(+), 79 deletions(-) diff --git a/src/patchelf.cc b/src/patchelf.cc index dc8620bf..b8e374e4 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -44,7 +44,6 @@ #include #include #include -#include #include "elf.h" #include "patchelf.h" @@ -683,7 +682,7 @@ void ElfFile::writeReplacedSections(Elf_Off & curOff, const std::string & sectionName = i.first; const Elf_Shdr & shdr = findSectionHeader(sectionName); if (rdi(shdr.sh_type) != SHT_NOBITS) - memset(fileContents->data() + rdi(shdr.sh_offset), 'Y', rdi(shdr.sh_size)); + memset(fileContents->data() + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size)); } std::set noted_phdrs = {}; @@ -795,9 +794,7 @@ void ElfFile::writeReplacedSections(Elf_Off & curOff, } } } - debug("curoff %d, size %d, align %d\n", curOff, i->second.size(), sectionAlignment); curOff += roundUp((Elf_Off)i->second.size(), sectionAlignment); - debug("-> next curoff %d\n", curOff); } replacedSections.clear(); @@ -1632,7 +1629,7 @@ void ElfFile::modifyRPath(RPathOp op, size_t rpathSize = 0; if (rpath) { rpathSize = strlen(rpath); - memset(rpath, 0, rpathSize); + memset(rpath, 'X', rpathSize); } debug("new rpath is '%s'\n", newRPath.c_str()); @@ -2259,8 +2256,8 @@ struct Comparator { class mymap : public map, Comparator> { - public: - size_t totalsize; +public: + size_t totalsize; mymap(const char *s, size_t s_length) : totalsize(s_length) { const char *c = NULL; @@ -2277,7 +2274,7 @@ class mymap : public map, Comparator> { template void incref(const char *idx, T *sym) { - (*this)[idx].push_front(sym); + (*this)[idx].push_front(sym); } }; @@ -2301,14 +2298,14 @@ void ElfFile::cleanstrtab() for (; rdi(dyn->d_tag) != DT_NULL; dyn++) { switch (rdi(dyn->d_tag)) { case DT_NEEDED: case DT_SONAME: - m.incref(strTab + rdi(dyn->d_un.d_val), &dyn->d_un.d_val); - break; + m.incref(strTab + rdi(dyn->d_un.d_val), &dyn->d_un.d_val); + break; case DT_VERNEEDNUM: - verneednum = rdi(dyn->d_un.d_val); - break; + verneednum = rdi(dyn->d_un.d_val); + break; case DT_VERDEFNUM: - verdefnum = rdi(dyn->d_un.d_val); - break; + verdefnum = rdi(dyn->d_un.d_val); + break; } } /* NAMEs in dynsym */ @@ -2321,77 +2318,77 @@ void ElfFile::cleanstrtab() if (verneednum) { auto shdrVerSymr = findSectionHeader(".gnu.version_r"); - Elf_Shdr & shdrVersionRStrings = shdrs.at(rdi(shdrVerSymr.sh_link)); - // this is where we find the actual version strings; is it dynstr? - if (shdrVersionRStrings.sh_offset == shdrDynStr.sh_offset) { - auto verr = fileContents->data() + rdi(shdrVerSymr.sh_offset); - auto versym = (Elf_Verneed *)verr; - while (verneednum>0) { - // NAMEs in verneed referring to dynstr - m.incref(strTab + rdi(versym->vn_file), &versym->vn_file); - int auxnum = rdi(versym->vn_cnt); - auto auxsym = (Elf_Vernaux *)((char*)versym + rdi(versym->vn_aux)); - while (auxnum>0) { - m.incref(strTab + rdi(auxsym->vna_name), &auxsym->vna_name); - auxsym = (Elf_Vernaux *)((char*)auxsym + rdi(auxsym->vna_next)); - auxnum--; - } - versym = (Elf_Verneed *)((char*)versym + rdi(versym->vn_next)); - verneednum--; - } - } + Elf_Shdr & shdrVersionRStrings = shdrs.at(rdi(shdrVerSymr.sh_link)); + // this is where we find the actual version strings; is it dynstr? + if (shdrVersionRStrings.sh_offset == shdrDynStr.sh_offset) { + auto verr = fileContents->data() + rdi(shdrVerSymr.sh_offset); + auto versym = (Elf_Verneed *)verr; + while (verneednum>0) { + // NAMEs in verneed referring to dynstr + m.incref(strTab + rdi(versym->vn_file), &versym->vn_file); + int auxnum = rdi(versym->vn_cnt); + auto auxsym = (Elf_Vernaux *)((char*)versym + rdi(versym->vn_aux)); + while (auxnum>0) { + m.incref(strTab + rdi(auxsym->vna_name), &auxsym->vna_name); + auxsym = (Elf_Vernaux *)((char*)auxsym + rdi(auxsym->vna_next)); + auxnum--; + } + versym = (Elf_Verneed *)((char*)versym + rdi(versym->vn_next)); + verneednum--; + } + } } if (verdefnum) { auto shdrVerSymd = findSectionHeader(".gnu.version_d"); - Elf_Shdr & shdrVersionDStrings = shdrs.at(rdi(shdrVerSymd.sh_link)); - // this is where we find the actual version strings; is it dynstr? - if (shdrVersionDStrings.sh_offset == shdrDynStr.sh_offset) { - // NAMEs in verdef referring to dynstr - auto verd = fileContents->data() + rdi(shdrVerSymd.sh_offset); - auto versym = (Elf_Verdef *)verd; - while (verneednum>0) { - // NAMEs in verneed referring to dynstr - int auxnum = rdi(versym->vd_cnt); - auto auxsym = (Elf_Verdaux *)((char*)versym + rdi(versym->vd_aux)); - while (auxnum>0) { - m.incref(strTab + rdi(auxsym->vda_name), &auxsym->vda_name); - auxsym = (Elf_Verdaux *)(verd + rdi(auxsym->vda_next)); - auxnum--; - } - versym = (Elf_Verdef *)((char*)versym + rdi(versym->vd_next)); - verneednum--; - } - } - } - - int shift=0; - int newsize=m.totalsize; - for (auto& [s, l] : m) { - if (shift) { - for (auto& p : l) { - visit([this,shift](auto &x){ - wri(*x, rdi(*x)-shift); - },p); - } - debug("shifted sym %s by %d bytes, %s refs\n", s.str-shift, shift, l.empty()?"0":">=1"); - } - if (l.empty()) { - const char *sym = s.str - shift; - debug("sym %s is unreferenced\n", sym); - shift+= s.len+1; - debug("shifting next symbols by %d bytes\n", shift); - newsize-=s.len+1; - memmove((void*)sym, sym+s.len+1, newsize-(sym-strTab)); - } - } - memset((void*)(strTab+newsize), 0, shift); + Elf_Shdr & shdrVersionDStrings = shdrs.at(rdi(shdrVerSymd.sh_link)); + // this is where we find the actual version strings; is it dynstr? + if (shdrVersionDStrings.sh_offset == shdrDynStr.sh_offset) { + // NAMEs in verdef referring to dynstr + auto verd = fileContents->data() + rdi(shdrVerSymd.sh_offset); + auto versym = (Elf_Verdef *)verd; + while (verneednum>0) { + // NAMEs in verneed referring to dynstr + int auxnum = rdi(versym->vd_cnt); + auto auxsym = (Elf_Verdaux *)((char*)versym + rdi(versym->vd_aux)); + while (auxnum>0) { + m.incref(strTab + rdi(auxsym->vda_name), &auxsym->vda_name); + auxsym = (Elf_Verdaux *)(verd + rdi(auxsym->vda_next)); + auxnum--; + } + versym = (Elf_Verdef *)((char*)versym + rdi(versym->vd_next)); + verneednum--; + } + } + } + + int shift=0; + int newsize=m.totalsize; + for (auto& [s, l] : m) { + if (shift) { + for (auto& p : l) { + visit([this,shift](auto &x){ + wri(*x, rdi(*x)-shift); + },p); + } + debug("shifted sym %s by %d bytes, %s refs\n", s.str-shift, shift, l.empty()?"0":">=1"); + } + if (l.empty()) { + const char *sym = s.str - shift; + debug("sym %s is unreferenced\n", sym); + shift+= s.len+1; + debug("shifting next symbols by %d bytes\n", shift); + newsize-=s.len+1; + memmove((void*)sym, sym+s.len+1, newsize-(sym-strTab)); + } + } + memset((void*)(strTab+newsize), 0, shift); if (shift) { - debug("new .dynstr size %d\n", newsize); - changed=true; - replaceSection(".dynstr", newsize); - rewriteSections(); + debug("new .dynstr size %d\n", newsize); + changed=true; + replaceSection(".dynstr", newsize); + rewriteSections(); } } From da45be2a6f64b285b3dd69c3489edf8ced609162 Mon Sep 17 00:00:00 2001 From: Ronald Date: Mon, 6 Mar 2023 21:47:33 +0100 Subject: [PATCH 4/6] get rid of a warning about casting away const --- src/patchelf.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/patchelf.cc b/src/patchelf.cc index b8e374e4..4f41c229 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -2374,12 +2374,13 @@ void ElfFile::cleanstrtab() debug("shifted sym %s by %d bytes, %s refs\n", s.str-shift, shift, l.empty()?"0":">=1"); } if (l.empty()) { - const char *sym = s.str - shift; + Elf_Off offset = s.str - strTab; + char *sym = strTab + offset - shift; debug("sym %s is unreferenced\n", sym); shift+= s.len+1; debug("shifting next symbols by %d bytes\n", shift); newsize-=s.len+1; - memmove((void*)sym, sym+s.len+1, newsize-(sym-strTab)); + memmove(sym, sym+s.len+1, newsize-(sym-strTab)); } } memset((void*)(strTab+newsize), 0, shift); From a8ffdc61774ff069dfbb699bab9e6a859e9cb391 Mon Sep 17 00:00:00 2001 From: Ronald Date: Mon, 6 Mar 2023 21:50:07 +0100 Subject: [PATCH 5/6] rename mymap to symmap --- src/patchelf.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/patchelf.cc b/src/patchelf.cc index 4f41c229..15ed0c19 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -2254,11 +2254,11 @@ struct Comparator { #include -class mymap : public map, Comparator> { +class symmap : public map, Comparator> { public: size_t totalsize; - mymap(const char *s, size_t s_length) : totalsize(s_length) + symmap(const char *s, size_t s_length) : totalsize(s_length) { const char *c = NULL; while ((c=argz_next(s, s_length, c))) { @@ -2290,7 +2290,7 @@ void ElfFile::cleanstrtab() int verneednum = 0; int verdefnum = 0; - mymap m(strTab, rdi(shdrDynStr.sh_size)); + symmap m(strTab, rdi(shdrDynStr.sh_size)); Elf_Dyn* dyn = (Elf_Dyn *) (fileContents->data() + rdi(shdrDynamic.sh_offset)); From 2b8a29e3b48bb7e55dca6790199ed10f5b8501dc Mon Sep 17 00:00:00 2001 From: Ronald Date: Mon, 6 Mar 2023 21:54:24 +0100 Subject: [PATCH 6/6] use string_view instead of my own struct and get rid of argz dependency --- src/patchelf.cc | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/patchelf.cc b/src/patchelf.cc index 15ed0c19..95228f39 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -2223,10 +2223,7 @@ void ElfFile::clearSymbolVersions(const std::set, Comparator> { size_t totalsize; symmap(const char *s, size_t s_length) : totalsize(s_length) { - const char *c = NULL; - while ((c=argz_next(s, s_length, c))) { - insert({(const struct symstr){c, strlen(c)},forward_list()}); + const char *e = s + s_length; + while (s()}); + s+=sym.length()+1; } } @@ -2371,18 +2370,19 @@ void ElfFile::cleanstrtab() wri(*x, rdi(*x)-shift); },p); } - debug("shifted sym %s by %d bytes, %s refs\n", s.str-shift, shift, l.empty()?"0":">=1"); + debug("shifted sym %s by %d bytes, %s refs\n", s.data()-shift, shift, l.empty()?"0":">=1"); } if (l.empty()) { - Elf_Off offset = s.str - strTab; + Elf_Off offset=s.data()-strTab; char *sym = strTab + offset - shift; debug("sym %s is unreferenced\n", sym); - shift+= s.len+1; + shift+= s.length()+1; debug("shifting next symbols by %d bytes\n", shift); - newsize-=s.len+1; - memmove(sym, sym+s.len+1, newsize-(sym-strTab)); + newsize-=s.length()+1; + memmove(sym, sym+s.length()+1, newsize-(sym-strTab)); } } + // not really necessary, as .dynstr is going to be shrunk by shift memset((void*)(strTab+newsize), 0, shift); if (shift) {