diff --git a/src/patchelf.cc b/src/patchelf.cc index eaf2a42..4064113 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -41,6 +41,7 @@ #include #include "elf.h" +#include "patchelf.h" #ifndef PACKAGE_STRING #define PACKAGE_STRING "patchelf" @@ -63,11 +64,6 @@ static int forcedPageSize = -1; #define EM_LOONGARCH 258 #endif -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 - static std::vector splitColonDelimitedString(const char * s) { @@ -85,163 +81,6 @@ static bool hasAllowedPrefix(const std::string & s, const std::vector -class ElfFile -{ -public: - - const FileContents fileContents; - -private: - - std::vector phdrs; - std::vector shdrs; - - bool littleEndian; - - bool changed = false; - - bool isExecutable = false; - - using SectionName = std::string; - using ReplacedSections = std::map; - - ReplacedSections replacedSections; - - std::string sectionNames; /* content of the .shstrtab section */ - - /* Align on 4 or 8 bytes boundaries on 32- or 64-bit platforms - respectively. */ - size_t sectionAlignment = sizeof(Elf_Off); - - std::vector sectionsByOldIndex; - -public: - explicit ElfFile(FileContents fileContents); - - bool isChanged() - { - return changed; - } - -private: - - struct CompPhdr - { - ElfFile * elfFile; - bool operator ()(const Elf_Phdr & x, const Elf_Phdr & y) - { - // A PHDR comes before everything else. - if (elfFile->rdi(y.p_type) == PT_PHDR) return false; - if (elfFile->rdi(x.p_type) == PT_PHDR) return true; - - // Sort non-PHDRs by address. - return elfFile->rdi(x.p_paddr) < elfFile->rdi(y.p_paddr); - } - }; - - friend struct CompPhdr; - - void sortPhdrs(); - - struct CompShdr - { - ElfFile * elfFile; - bool operator ()(const Elf_Shdr & x, const Elf_Shdr & y) - { - return elfFile->rdi(x.sh_offset) < elfFile->rdi(y.sh_offset); - } - }; - - friend struct CompShdr; - - unsigned int getPageSize() const; - - void sortShdrs(); - - void shiftFile(unsigned int extraPages, Elf_Addr startPage); - - std::string getSectionName(const Elf_Shdr & shdr) const; - - Elf_Shdr & findSection(const SectionName & sectionName); - - std::optional> findSection2(const SectionName & sectionName); - - unsigned int findSection3(const SectionName & sectionName); - - std::string & replaceSection(const SectionName & sectionName, - unsigned int size); - - bool haveReplacedSection(const SectionName & sectionName) const; - - void writeReplacedSections(Elf_Off & curOff, - Elf_Addr startAddr, Elf_Off startOffset); - - void rewriteHeaders(Elf_Addr phdrAddress); - - void rewriteSectionsLibrary(); - - void rewriteSectionsExecutable(); - - void normalizeNoteSegments(); - -public: - - void rewriteSections(); - - std::string getInterpreter(); - - typedef enum { printSoname, replaceSoname } sonameMode; - - void modifySoname(sonameMode op, const std::string & newSoname); - - void setInterpreter(const std::string & newInterpreter); - - typedef enum { rpPrint, rpShrink, rpSet, rpAdd, rpRemove } RPathOp; - - void modifyRPath(RPathOp op, const std::vector & allowedRpathPrefixes, std::string newRPath); - std::string shrinkRPath(char* rpath, std::vector &neededLibs, const std::vector & allowedRpathPrefixes); - void removeRPath(Elf_Shdr & shdrDynamic); - - void addNeeded(const std::set & libs); - - void removeNeeded(const std::set & libs); - - void replaceNeeded(const std::map & libs); - - void printNeededLibs() /* should be const */; - - void noDefaultLib(); - - void clearSymbolVersions(const std::set & syms); - -private: - - /* Convert an integer in big or little endian representation (as - specified by the ELF header) to this platform's integer - representation. */ - template - I rdi(I i) const; - - /* Convert back to the ELF representation. */ - template - I wri(I & t, unsigned long long i) const - { - t = rdi((I) i); - return i; - } - - Elf_Ehdr *hdr() { - return (Elf_Ehdr *)fileContents->data(); - } - - const Elf_Ehdr *hdr() const { - return (const Elf_Ehdr *)fileContents->data(); - } -}; - - /* !!! G++ creates broken code if this function is inlined, don't know why... */ template @@ -630,7 +469,7 @@ std::string ElfFile::getSectionName(const Elf_Shdr & shdr) co template -Elf_Shdr & ElfFile::findSection(const SectionName & sectionName) +Elf_Shdr & ElfFile::findSectionHeader(const SectionName & sectionName) { auto shdr = findSection2(sectionName); if (!shdr) { @@ -654,7 +493,7 @@ std::optional> ElfFile::find template -unsigned int ElfFile::findSection3(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; @@ -677,7 +516,7 @@ std::string & ElfFile::replaceSection(const SectionName & sec if (i != replacedSections.end()) { s = std::string(i->second); } else { - auto shdr = findSection(sectionName); + auto shdr = findSectionHeader(sectionName); s = std::string((char *) fileContents->data() + rdi(shdr.sh_offset), rdi(shdr.sh_size)); } @@ -697,7 +536,7 @@ void ElfFile::writeReplacedSections(Elf_Off & curOff, clobbering previously written new section contents. */ for (auto & i : replacedSections) { const std::string & sectionName = i.first; - Elf_Shdr & shdr = findSection(sectionName); + Elf_Shdr & shdr = findSectionHeader(sectionName); if (rdi(shdr.sh_type) != SHT_NOBITS) memset(fileContents->data() + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size)); } @@ -705,7 +544,7 @@ void ElfFile::writeReplacedSections(Elf_Off & curOff, std::set noted_phdrs = {}; for (auto & i : replacedSections) { const std::string & sectionName = i.first; - auto & shdr = findSection(sectionName); + auto & shdr = findSectionHeader(sectionName); Elf_Shdr orig_shdr = shdr; debug("rewriting section '%s' from offset 0x%x (size %d) to offset 0x%x (size %d)\n", sectionName.c_str(), rdi(shdr.sh_offset), rdi(shdr.sh_size), curOff, i.second.size()); @@ -1024,7 +863,7 @@ void ElfFile::normalizeNoteSegments() /* We don't need to do anything if no note segments were replaced. */ bool replaced_note = std::any_of(replacedSections.begin(), replacedSections.end(), - [this](std::pair & i) { return rdi(findSection(i.first).sh_type) == SHT_NOTE; }); + [this](std::pair & i) { return rdi(findSectionHeader(i.first).sh_type) == SHT_NOTE; }); if (!replaced_note) return; std::vector newPhdrs; @@ -1134,13 +973,13 @@ void ElfFile::rewriteHeaders(Elf_Addr phdrAddress) unsigned int d_tag; for (auto dyn = dyn_table; (d_tag = rdi(dyn->d_tag)) != DT_NULL; dyn++) if (d_tag == DT_STRTAB) - dyn->d_un.d_ptr = findSection(".dynstr").sh_addr; + dyn->d_un.d_ptr = findSectionHeader(".dynstr").sh_addr; else if (d_tag == DT_STRSZ) - dyn->d_un.d_val = findSection(".dynstr").sh_size; + dyn->d_un.d_val = findSectionHeader(".dynstr").sh_size; else if (d_tag == DT_SYMTAB) - dyn->d_un.d_ptr = findSection(".dynsym").sh_addr; + dyn->d_un.d_ptr = findSectionHeader(".dynsym").sh_addr; else if (d_tag == DT_HASH) - dyn->d_un.d_ptr = findSection(".hash").sh_addr; + dyn->d_un.d_ptr = findSectionHeader(".hash").sh_addr; else if (d_tag == DT_GNU_HASH) { auto shdr = findSection2(".gnu.hash"); // some binaries might this section stripped @@ -1172,15 +1011,15 @@ void ElfFile::rewriteHeaders(Elf_Addr phdrAddress) dyn->d_un.d_ptr = (*shdr).get().sh_addr; } else if (d_tag == DT_VERNEED) - dyn->d_un.d_ptr = findSection(".gnu.version_r").sh_addr; + dyn->d_un.d_ptr = findSectionHeader(".gnu.version_r").sh_addr; else if (d_tag == DT_VERSYM) - dyn->d_un.d_ptr = findSection(".gnu.version").sh_addr; + dyn->d_un.d_ptr = findSectionHeader(".gnu.version").sh_addr; else if (d_tag == DT_MIPS_RLD_MAP_REL) { /* the MIPS_RLD_MAP_REL tag stores the offset to the debug pointer, relative to the address of the tag */ auto shdr = findSection2(".rld_map"); if (shdr) { - auto rld_map_addr = findSection(".rld_map").sh_addr; + auto rld_map_addr = findSectionHeader(".rld_map").sh_addr; auto dyn_offset = ((char*)dyn) - ((char*)dyn_table); dyn->d_un.d_ptr = rld_map_addr + dyn_offset - (*shdrDynamic).get().sh_addr; } else { @@ -1236,7 +1075,7 @@ static void setSubstr(std::string & s, unsigned int pos, const std::string & t) template std::string ElfFile::getInterpreter() { - auto shdr = findSection(".interp"); + auto shdr = findSectionHeader(".interp"); return std::string((char *) fileContents->data() + rdi(shdr.sh_offset), rdi(shdr.sh_size) - 1); } @@ -1248,8 +1087,8 @@ void ElfFile::modifySoname(sonameMode op, const std::string & return; } - auto shdrDynamic = findSection(".dynamic"); - auto shdrDynStr = findSection(".dynstr"); + auto shdrDynamic = findSectionHeader(".dynamic"); + auto shdrDynStr = findSectionHeader(".dynstr"); char * strTab = (char *) fileContents->data() + rdi(shdrDynStr.sh_offset); /* Walk through the dynamic section, look for the DT_SONAME entry. */ @@ -1401,7 +1240,7 @@ template void ElfFile::modifyRPath(RPathOp op, const std::vector & allowedRpathPrefixes, std::string newRPath) { - auto shdrDynamic = findSection(".dynamic"); + auto shdrDynamic = findSectionHeader(".dynamic"); if (rdi(shdrDynamic.sh_type) == SHT_NOBITS) { debug("no dynamic section\n"); @@ -1410,7 +1249,7 @@ void ElfFile::modifyRPath(RPathOp op, /* !!! We assume that the virtual address in the DT_STRTAB entry of the dynamic section corresponds to the .dynstr section. */ - auto shdrDynStr = findSection(".dynstr"); + auto shdrDynStr = findSectionHeader(".dynstr"); char * strTab = (char *) fileContents->data() + rdi(shdrDynStr.sh_offset); @@ -1549,8 +1388,8 @@ void ElfFile::removeNeeded(const std::set & libs { if (libs.empty()) return; - auto shdrDynamic = findSection(".dynamic"); - auto shdrDynStr = findSection(".dynstr"); + auto shdrDynamic = findSectionHeader(".dynamic"); + auto shdrDynStr = findSectionHeader(".dynstr"); char * strTab = (char *) fileContents->data() + rdi(shdrDynStr.sh_offset); auto dyn = (Elf_Dyn *)(fileContents->data() + rdi(shdrDynamic.sh_offset)); @@ -1577,8 +1416,8 @@ void ElfFile::replaceNeeded(const std::mapdata() + rdi(shdrDynStr.sh_offset); auto dyn = (Elf_Dyn *)(fileContents->data() + rdi(shdrDynamic.sh_offset)); @@ -1634,7 +1473,7 @@ void ElfFile::replaceNeeded(const std::mapsh_link to figure out @@ -1700,8 +1539,8 @@ void ElfFile::addNeeded(const std::set & libs) { if (libs.empty()) return; - auto shdrDynamic = findSection(".dynamic"); - auto shdrDynStr = findSection(".dynstr"); + auto shdrDynamic = findSectionHeader(".dynamic"); + auto shdrDynStr = findSectionHeader(".dynstr"); unsigned int length = 0; @@ -1748,8 +1587,8 @@ void ElfFile::addNeeded(const std::set & libs) template void ElfFile::printNeededLibs() // const { - const auto shdrDynamic = findSection(".dynamic"); - const auto shdrDynStr = findSection(".dynstr"); + const auto shdrDynamic = findSectionHeader(".dynamic"); + const auto shdrDynStr = findSectionHeader(".dynstr"); const char *strTab = (char *)fileContents->data() + rdi(shdrDynStr.sh_offset); const Elf_Dyn *dyn = (Elf_Dyn *) (fileContents->data() + rdi(shdrDynamic.sh_offset)); @@ -1766,7 +1605,7 @@ void ElfFile::printNeededLibs() // const template void ElfFile::noDefaultLib() { - auto shdrDynamic = findSection(".dynamic"); + auto shdrDynamic = findSectionHeader(".dynamic"); auto dyn = (Elf_Dyn *)(fileContents->data() + rdi(shdrDynamic.sh_offset)); auto dynFlags1 = (Elf_Dyn *)nullptr; @@ -1807,9 +1646,9 @@ void ElfFile::clearSymbolVersions(const std::set { if (syms.empty()) return; - auto shdrDynStr = findSection(".dynstr"); - auto shdrDynsym = findSection(".dynsym"); - auto shdrVersym = findSection(".gnu.version"); + auto shdrDynStr = findSectionHeader(".dynstr"); + auto shdrDynsym = findSectionHeader(".dynsym"); + auto shdrVersym = findSectionHeader(".gnu.version"); auto strTab = (char *)fileContents->data() + rdi(shdrDynStr.sh_offset); auto dynsyms = (Elf_Sym *)(fileContents->data() + rdi(shdrDynsym.sh_offset)); diff --git a/src/patchelf.h b/src/patchelf.h new file mode 100644 index 0000000..d361084 --- /dev/null +++ b/src/patchelf.h @@ -0,0 +1,180 @@ +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 + +template +class ElfFile +{ + + /** + * @brief The ELF section header + * + */ + class Section { + public: + const std::string & getName() { + return name; + } + const Elf_Shdr * toStruct() { + return (Elf_Shdr *) this->data.data(); + } + void resize(unsigned int size) { + data.resize(size, '\0'); + } + private: + std::string name; + std::vector data; + }; + +public: + + const FileContents fileContents; + +private: + + std::vector phdrs; + std::vector shdrs; + + bool littleEndian; + + bool changed = false; + + bool isExecutable = false; + + using SectionName = std::string; + using ReplacedSections = std::map; + + ReplacedSections replacedSections; + + std::string sectionNames; /* content of the .shstrtab section */ + + /* Align on 4 or 8 bytes boundaries on 32- or 64-bit platforms + respectively. */ + size_t sectionAlignment = sizeof(Elf_Off); + + std::vector sectionsByOldIndex; + +public: + explicit ElfFile(FileContents fileContents); + + bool isChanged() + { + return changed; + } + +private: + + struct CompPhdr + { + ElfFile * elfFile; + bool operator ()(const Elf_Phdr & x, const Elf_Phdr & y) + { + // A PHDR comes before everything else. + if (elfFile->rdi(y.p_type) == PT_PHDR) return false; + if (elfFile->rdi(x.p_type) == PT_PHDR) return true; + + // Sort non-PHDRs by address. + return elfFile->rdi(x.p_paddr) < elfFile->rdi(y.p_paddr); + } + }; + + friend struct CompPhdr; + + void sortPhdrs(); + + struct CompShdr + { + ElfFile * elfFile; + bool operator ()(const Elf_Shdr & x, const Elf_Shdr & y) + { + return elfFile->rdi(x.sh_offset) < elfFile->rdi(y.sh_offset); + } + }; + + friend struct CompShdr; + + unsigned int getPageSize() const; + + void sortShdrs(); + + void shiftFile(unsigned int extraPages, Elf_Addr startPage); + + std::string getSectionName(const Elf_Shdr & shdr) const; + + Elf_Shdr & findSectionHeader(const SectionName & sectionName); + + std::optional> findSection2(const SectionName & sectionName); + + unsigned int getSectionIndex(const SectionName & sectionName); + + std::string & replaceSection(const SectionName & sectionName, + unsigned int size); + + bool haveReplacedSection(const SectionName & sectionName) const; + + void writeReplacedSections(Elf_Off & curOff, + Elf_Addr startAddr, Elf_Off startOffset); + + void rewriteHeaders(Elf_Addr phdrAddress); + + void rewriteSectionsLibrary(); + + void rewriteSectionsExecutable(); + + void normalizeNoteSegments(); + +public: + + void rewriteSections(); + + std::string getInterpreter(); + + typedef enum { printSoname, replaceSoname } sonameMode; + + void modifySoname(sonameMode op, const std::string & newSoname); + + void setInterpreter(const std::string & newInterpreter); + + typedef enum { rpPrint, rpShrink, rpSet, rpAdd, rpRemove } RPathOp; + + void modifyRPath(RPathOp op, const std::vector & allowedRpathPrefixes, std::string newRPath); + std::string shrinkRPath(char* rpath, std::vector &neededLibs, const std::vector & allowedRpathPrefixes); + void removeRPath(Elf_Shdr & shdrDynamic); + + void addNeeded(const std::set & libs); + + void removeNeeded(const std::set & libs); + + void replaceNeeded(const std::map & libs); + + void printNeededLibs() /* should be const */; + + void noDefaultLib(); + + void clearSymbolVersions(const std::set & syms); + +private: + + /* Convert an integer in big or little endian representation (as + specified by the ELF header) to this platform's integer + representation. */ + template + I rdi(I i) const; + + /* Convert back to the ELF representation. */ + template + I wri(I & t, unsigned long long i) const + { + t = rdi((I) i); + return i; + } + + Elf_Ehdr *hdr() { + return (Elf_Ehdr *)fileContents->data(); + } + + const Elf_Ehdr *hdr() const { + return (const Elf_Ehdr *)fileContents->data(); + } +}; \ No newline at end of file