diff --git a/src/patchelf.cc b/src/patchelf.cc index d814d3fd..16a746cf 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -59,9 +61,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 @@ -111,7 +116,7 @@ static std::string downcase(std::string s) why... */ template template -constexpr I ElfFile::rdi(I i) const noexcept +constexpr I ElfFile::rdi(I i) const noexcept { I r = 0; if (littleEndian) { @@ -126,7 +131,6 @@ constexpr I ElfFile::rdi(I i) const noexcept return r; } - static void debug(const char * format, ...) { if (debugMode) { @@ -265,7 +269,7 @@ static std::string extractString(const FileContents & contents, size_t offset, s template -ElfFile::ElfFile(FileContents fContents) +ElfFile::ElfFile(FileContents fContents) : fileContents(fContents) { /* Check the ELF header for basic validity. */ @@ -357,7 +361,7 @@ ElfFile::ElfFile(FileContents fContents) template -unsigned int ElfFile::getPageSize() const noexcept +unsigned int ElfFile::getPageSize() const noexcept { if (forcedPageSize > 0) return forcedPageSize; @@ -385,7 +389,7 @@ unsigned int ElfFile::getPageSize() const noexcept template -void ElfFile::sortPhdrs() +void ElfFile::sortPhdrs() { /* Sort the segments by offset. */ CompPhdr comp; @@ -395,7 +399,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. */ @@ -477,16 +481,8 @@ static void writeFile(const std::string & fileName, const FileContents & content } -static uint64_t roundUp(uint64_t n, uint64_t m) -{ - if (n == 0) - return m; - return ((n - 1) / m + 1) * m; -} - - template -void ElfFile::shiftFile(unsigned int extraPages, size_t startOffset, size_t extraBytes) +void ElfFile::shiftFile(unsigned int extraPages, size_t startOffset, size_t extraBytes) { assert(startOffset >= sizeof(Elf_Ehdr)); @@ -569,7 +565,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); @@ -581,7 +577,7 @@ std::string ElfFile::getSectionName(const Elf_Shdr & shdr) co template -const Elf_Shdr & ElfFile::findSectionHeader(const SectionName & sectionName) const +const Elf_Shdr & ElfFile::findSectionHeader(const SectionName & sectionName) const { auto shdr = tryFindSectionHeader(sectionName); if (!shdr) { @@ -595,7 +591,7 @@ const Elf_Shdr & ElfFile::findSectionHeader(const SectionName template -std::optional> ElfFile::tryFindSectionHeader(const SectionName & sectionName) const +std::optional> ElfFile::tryFindSectionHeader(const SectionName & sectionName) const { auto i = getSectionIndex(sectionName); if (i) @@ -605,28 +601,28 @@ std::optional> ElfFile template template -span ElfFile::getSectionSpan(const Elf_Shdr & shdr) const +span ElfFile::getSectionSpan(const Elf_Shdr & shdr) const { return span((T*)(fileContents->data() + rdi(shdr.sh_offset)), rdi(shdr.sh_size)/sizeof(T)); } template template -span ElfFile::getSectionSpan(const SectionName & sectionName) +span ElfFile::getSectionSpan(const SectionName & sectionName) { return getSectionSpan(findSectionHeader(sectionName)); } template template -span ElfFile::tryGetSectionSpan(const SectionName & sectionName) +span ElfFile::tryGetSectionSpan(const SectionName & sectionName) { auto shdrOpt = tryFindSectionHeader(sectionName); return shdrOpt ? getSectionSpan(*shdrOpt) : span(); } template -unsigned int ElfFile::getSectionIndex(const SectionName & sectionName) const +unsigned int ElfFile::getSectionIndex(const SectionName & sectionName) const { for (unsigned int i = 1; i < rdi(hdr()->e_shnum); ++i) if (getSectionName(shdrs.at(i)) == sectionName) return i; @@ -634,13 +630,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); @@ -659,9 +655,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 @@ -783,8 +794,7 @@ void ElfFile::writeReplacedSections(Elf_Off & curOff, } } } - - curOff += roundUp(i->second.size(), sectionAlignment); + curOff += roundUp((Elf_Off)i->second.size(), sectionAlignment); } replacedSections.clear(); @@ -792,7 +802,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 @@ -801,7 +811,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); } @@ -829,16 +839,16 @@ void ElfFile::rewriteSectionsLibrary() bool moveHeaderTableToTheEnd = rdi(hdr()->e_shoff) < pht_size; /* 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); off_t headerTableSpace = roundUp(rdi(hdr()->e_shnum) * rdi(hdr()->e_shentsize), sectionAlignment); if (moveHeaderTableToTheEnd) neededSpace += headerTableSpace; 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). @@ -917,7 +927,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 @@ -940,7 +950,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. */ @@ -998,9 +1008,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); @@ -1014,7 +1024,7 @@ void ElfFile::rewriteSectionsExecutable() /* Calculate how many bytes are needed out of the additional pages. */ size_t extraSpace = neededSpace - startOffset; - unsigned int neededPages = roundUp(extraSpace, getPageSize()) / getPageSize(); + unsigned int neededPages = roundUp((Elf_Off)extraSpace, getPageSize()) / getPageSize(); debug("needed pages is %d\n", neededPages); if (neededPages * getPageSize() > firstPage) error("virtual address space underrun!"); @@ -1029,7 +1039,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. */ @@ -1042,7 +1052,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 @@ -1110,7 +1120,7 @@ void ElfFile::normalizeNoteSegments() template -void ElfFile::rewriteSections(bool force) +void ElfFile::rewriteSections(bool force) { if (!force && replacedSections.empty()) return; @@ -1130,7 +1140,7 @@ void ElfFile::rewriteSections(bool force) template -void ElfFile::rewriteHeaders(Elf_Addr phdrAddress) +void ElfFile::rewriteHeaders(Elf_Addr phdrAddress) { /* Rewrite the program header table. */ @@ -1286,7 +1296,7 @@ static void setSubstr(std::string & s, unsigned int pos, const std::string & t) template -std::string ElfFile::getInterpreter() const +std::string ElfFile::getInterpreter() const { auto shdr = findSectionHeader(".interp"); auto size = rdi(shdr.sh_size); @@ -1296,7 +1306,7 @@ std::string ElfFile::getInterpreter() const } 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]; @@ -1358,7 +1368,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"); @@ -1429,16 +1439,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(); } @@ -1451,7 +1461,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 = ""; @@ -1500,7 +1510,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++) { @@ -1515,11 +1525,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"); @@ -1663,12 +1673,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; @@ -1676,7 +1686,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[0]; Elf_Dyn * last = dyn; for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) { if (rdi(dyn->d_tag) == DT_NEEDED) { @@ -1692,13 +1703,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; @@ -1819,11 +1835,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; @@ -1871,11 +1887,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"); @@ -1893,7 +1909,7 @@ void ElfFile::printNeededLibs() const template -void ElfFile::noDefaultLib() +void ElfFile::noDefaultLib() { auto shdrDynamic = findSectionHeader(".dynamic"); @@ -1928,12 +1944,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"); @@ -1960,7 +1976,7 @@ void ElfFile::addDebugTag() newDyn.d_un.d_val = 0; setSubstr(newDynamic, 0, std::string((char *) &newDyn, sizeof(Elf_Dyn))); - this->rewriteSections(); + rewriteSections(); changed = true; } @@ -1972,7 +1988,7 @@ static uint32_t gnuHash(std::string_view name) { } template -auto ElfFile::parseGnuHashTable(span sectionData) -> GnuHashTable +auto ElfFile::parseGnuHashTable(span sectionData) -> GnuHashTable { auto hdr = (typename GnuHashTable::Header*)sectionData.begin(); auto bloomFilters = span((typename GnuHashTable::BloomWord*)(hdr+1), rdi(hdr->maskwords)); @@ -1982,7 +1998,7 @@ auto ElfFile::parseGnuHashTable(span sectionData) -> Gn } template -void ElfFile::rebuildGnuHashTable(span strTab, span dynsyms) +void ElfFile::rebuildGnuHashTable(span strTab, span dynsyms) { auto sectionData = tryGetSectionSpan(".gnu.hash"); if (!sectionData) @@ -2105,7 +2121,7 @@ static uint32_t sysvHash(std::string_view name) { } template -auto ElfFile::parseHashTable(span sectionData) -> HashTable +auto ElfFile::parseHashTable(span sectionData) -> HashTable { auto hdr = (typename HashTable::Header*)sectionData.begin(); auto buckets = span((uint32_t*)(hdr+1), rdi(hdr->numBuckets)); @@ -2114,7 +2130,7 @@ auto ElfFile::parseHashTable(span sectionData) -> HashT } template -void ElfFile::rebuildHashTable(span strTab, span dynsyms) +void ElfFile::rebuildHashTable(span strTab, span dynsyms) { auto sectionData = tryGetSectionSpan(".hash"); if (!sectionData) @@ -2140,7 +2156,7 @@ void ElfFile::rebuildHashTable(span strTab, span -void ElfFile::renameDynamicSymbols(const std::unordered_map& remap) +void ElfFile::renameDynamicSymbols(const std::unordered_map& remap) { auto dynsyms = getSectionSpan(".dynsym"); auto strTab = getSectionSpan(".dynstr"); @@ -2179,7 +2195,7 @@ void ElfFile::renameDynamicSymbols(const std::unordered_map -void ElfFile::clearSymbolVersions(const std::set & syms) +void ElfFile::clearSymbolVersions(const std::set & syms) { if (syms.empty()) return; @@ -2204,11 +2220,182 @@ void ElfFile::clearSymbolVersions(const std::set } } changed = true; - this->rewriteSections(); + rewriteSections(); +} + +typedef string_view symstr; + +// 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.data()+a.length() < b.data()+b.length()); + } + + bool operator()(const char* a, const symstr& b) const + { + return (a < b.data()); + } + + bool operator()(const symstr& a, const char* b) const + { + return (a.data()+a.length() < b); + } +}; + +#include + +class symmap : public map, Comparator> { + +public: + size_t totalsize; + symmap(const char *s, size_t s_length) : totalsize(s_length) + { + const char *e = s + s_length; + while (s()}); + s+=sym.length()+1; + } + } + + 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; + + symmap 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); + 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]; + 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 + 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.data()-shift, shift, l.empty()?"0":">=1"); + } + if (l.empty()) { + Elf_Off offset=s.data()-strTab; + char *sym = strTab + offset - shift; + debug("sym %s is unreferenced\n", sym); + shift+= s.length()+1; + debug("shifting next symbols by %d bytes\n", shift); + 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) { + debug("new .dynstr size %d\n", newsize); + changed=true; + replaceSection(".dynstr", newsize); + rewriteSections(); + } } + template -void ElfFile::modifyExecstack(ExecstackMode op) +void ElfFile::modifyExecstack(ExecstackMode op) { if (op == ExecstackMode::clear || op == ExecstackMode::set) { size_t nullhdr = (size_t)-1; @@ -2368,6 +2555,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con if (addDebugTag) elfFile.addDebugTag(); + if (cleanStrtab) + elfFile.cleanstrtab(); + if (renameDynamicSymbols) elfFile.renameDynamicSymbols(symbolsToRename); @@ -2390,9 +2580,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); } } @@ -2430,6 +2620,7 @@ static 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\ [--print-execstack]\t\tPrints whether the object requests an executable stack\n\ [--clear-execstack]\n\ @@ -2543,6 +2734,9 @@ static 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 == "--print-execstack") { printExecstack = true; } diff --git a/src/patchelf.h b/src/patchelf.h index 9fab18c0..af03003f 100644 --- a/src/patchelf.h +++ b/src/patchelf.h @@ -10,8 +10,11 @@ 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, class Elf_Rel, class Elf_Rela, unsigned ElfClass -#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed, Elf_Versym, Elf_Rel, Elf_Rela, ElfClass +#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, class Elf_Rel, class Elf_Rela, unsigned ElfClass +#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, Elf##x##_Rel, Elf##x##_Rela, ElfClass##x + +#define ElfClass32 32 +#define ElfClass64 64 template struct span @@ -120,6 +123,9 @@ class ElfFile std::string & replaceSection(const SectionName & sectionName, unsigned int size); + std::string & replaceSection(const SectionName & sectionName, + std::string & replacement); + [[nodiscard]] bool haveReplacedSection(const SectionName & sectionName) const; void writeReplacedSections(Elf_Off & curOff, @@ -171,6 +177,8 @@ class ElfFile void clearSymbolVersions(const std::set & syms); + void cleanstrtab(); + enum class ExecstackMode { print, set, clear }; void modifyExecstack(ExecstackMode op); @@ -248,12 +256,22 @@ class ElfFile constexpr inline I wri(I & t, U i) const { I val = static_cast(i); - if (static_cast(val) != i) + if (static_cast(val) != i) throw std::runtime_error { "value truncation" }; t = rdi(val); return val; } + static constexpr Elf_Off roundUp(Elf_Off n, unsigned int m) //const + { + 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; + } + [[nodiscard]] Elf_Ehdr *hdr() noexcept { return reinterpret_cast(fileContents->data()); }