diff options
Diffstat (limited to 'sources/patchelf/patchelf.cc')
-rw-r--r-- | sources/patchelf/patchelf.cc | 1239 |
1 files changed, 0 insertions, 1239 deletions
diff --git a/sources/patchelf/patchelf.cc b/sources/patchelf/patchelf.cc deleted file mode 100644 index c6f495e1c..000000000 --- a/sources/patchelf/patchelf.cc +++ /dev/null @@ -1,1239 +0,0 @@ -#include <string> -#include <vector> -#include <set> -#include <map> -#include <algorithm> - -#include <stdlib.h> -#include <stdio.h> -#include <stdarg.h> -#include <assert.h> -#include <string.h> -#include <errno.h> - -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <fcntl.h> -#include <limits.h> - -#include "elf.h" - -using namespace std; - - -#ifdef MIPSEL -/* The lemote fuloong 2f kernel defconfig sets a page size of 16KB */ -const unsigned int pageSize = 4096*4; -#else -const unsigned int pageSize = 4096; -#endif - - -static bool debugMode = false; - -static bool forceRPath = false; - -static string fileName; - - -off_t fileSize, maxSize; -unsigned char * contents = 0; - - -#define ElfFileParams class Elf_Ehdr, class Elf_Phdr, class Elf_Shdr, class Elf_Addr, class Elf_Off, class Elf_Dyn, class Elf_Sym -#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym - - -template<ElfFileParams> -class ElfFile -{ - Elf_Ehdr * hdr; - vector<Elf_Phdr> phdrs; - vector<Elf_Shdr> shdrs; - - bool littleEndian; - - bool changed; - - bool isExecutable; - - typedef string SectionName; - typedef map<SectionName, string> ReplacedSections; - - ReplacedSections replacedSections; - - string sectionNames; /* content of the .shstrtab section */ - - /* Align on 4 or 8 bytes boundaries on 32- or 64-bit platforms - respectively. */ - unsigned int sectionAlignment; - - vector<SectionName> sectionsByOldIndex; - -public: - - ElfFile() - { - changed = false; - sectionAlignment = sizeof(Elf_Off); - } - - bool isChanged() - { - return changed; - } - - void parse(); - -private: - - struct CompPhdr - { - ElfFile * elfFile; - bool operator ()(const Elf_Phdr & x, const Elf_Phdr & y) - { - if (x.p_type == PT_PHDR) return true; - if (y.p_type == PT_PHDR) return false; - 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; - - void sortShdrs(); - - void shiftFile(unsigned int extraPages, Elf_Addr startPage); - - string getSectionName(const Elf_Shdr & shdr); - - Elf_Shdr & findSection(const SectionName & sectionName); - - Elf_Shdr * findSection2(const SectionName & sectionName); - - unsigned int findSection3(const SectionName & sectionName); - - string & replaceSection(const SectionName & sectionName, - unsigned int size); - - void writeReplacedSections(Elf_Off & curOff, - Elf_Addr startAddr, Elf_Off startOffset); - - void rewriteHeaders(Elf_Addr phdrAddress); - - void rewriteSectionsLibrary(); - - void rewriteSectionsExecutable(); - -public: - - void rewriteSections(); - - string getInterpreter(); - - void setInterpreter(const string & newInterpreter); - - typedef enum { rpPrint, rpShrink, rpSet } RPathOp; - - void modifyRPath(RPathOp op, string newRPath); - - void removeNeeded(set<string> libs); - -private: - - /* Convert an integer in big or little endian representation (as - specified by the ELF header) to this platform's integer - representation. */ - template<class I> - I rdi(I i); - - /* Convert back to the ELF representation. */ - template<class I> - I wri(I & t, unsigned long long i) - { - t = rdi((I) i); - return i; - } -}; - - -/* !!! G++ creates broken code if this function is inlined, don't know - why... */ -template<ElfFileParams> -template<class I> -I ElfFile<ElfFileParamNames>::rdi(I i) -{ - I r = 0; - if (littleEndian) { - for (unsigned int n = 0; n < sizeof(I); ++n) { - r |= ((I) *(((unsigned char *) &i) + n)) << (n * 8); - } - } else { - for (unsigned int n = 0; n < sizeof(I); ++n) { - r |= ((I) *(((unsigned char *) &i) + n)) << ((sizeof(I) - n - 1) * 8); - } - } - return r; -} - - -/* Ugly: used to erase DT_RUNPATH when using --force-rpath. */ -#define DT_IGNORE 0x00726e67 - - -static void debug(const char * format, ...) -{ - if (debugMode) { - va_list ap; - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - } -} - - -static void error(string msg) -{ - if (errno) perror(msg.c_str()); else fprintf(stderr, "%s\n", msg.c_str()); - exit(1); -} - - -static void growFile(off_t newSize) -{ - if (newSize > maxSize) error("maximum file size exceeded"); - if (newSize <= fileSize) return; - if (newSize > fileSize) - memset(contents + fileSize, 0, newSize - fileSize); - fileSize = newSize; -} - - -static void readFile(string fileName, mode_t * fileMode) -{ - struct stat st; - if (stat(fileName.c_str(), &st) != 0) error("stat"); - fileSize = st.st_size; - *fileMode = st.st_mode; - maxSize = fileSize + 8 * 1024 * 1024; - - contents = (unsigned char *) malloc(fileSize + maxSize); - if (!contents) abort(); - - int fd = open(fileName.c_str(), O_RDONLY); - if (fd == -1) error("open"); - - if (read(fd, contents, fileSize) != fileSize) error("read"); - - close(fd); -} - - -static void checkPointer(void * p, unsigned int size) -{ - unsigned char * q = (unsigned char *) p; - assert(q >= contents && q + size <= contents + fileSize); -} - - -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::parse() -{ - isExecutable = false; - - /* Check the ELF header for basic validity. */ - if (fileSize < (off_t) sizeof(Elf_Ehdr)) error("missing ELF header"); - - hdr = (Elf_Ehdr *) contents; - - if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0) - error("not an ELF executable"); - - littleEndian = contents[EI_DATA] == ELFDATA2LSB; - - if (rdi(hdr->e_type) != ET_EXEC && rdi(hdr->e_type) != ET_DYN) - error("wrong ELF type"); - - if ((off_t) (rdi(hdr->e_phoff) + rdi(hdr->e_phnum) * rdi(hdr->e_phentsize)) > fileSize) - error("missing program headers"); - - if ((off_t) (rdi(hdr->e_shoff) + rdi(hdr->e_shnum) * rdi(hdr->e_shentsize)) > fileSize) - error("missing section headers"); - - if (rdi(hdr->e_phentsize) != sizeof(Elf_Phdr)) - error("program headers have wrong size"); - - /* Copy the program and section headers. */ - for (int i = 0; i < rdi(hdr->e_phnum); ++i) { - phdrs.push_back(* ((Elf_Phdr *) (contents + rdi(hdr->e_phoff)) + i)); - if (rdi(phdrs[i].p_type) == PT_INTERP) isExecutable = true; - } - - for (int i = 0; i < rdi(hdr->e_shnum); ++i) - shdrs.push_back(* ((Elf_Shdr *) (contents + rdi(hdr->e_shoff)) + i)); - - /* Get the section header string table section (".shstrtab"). Its - index in the section header table is given by e_shstrndx field - of the ELF header. */ - unsigned int shstrtabIndex = rdi(hdr->e_shstrndx); - assert(shstrtabIndex < shdrs.size()); - unsigned int shstrtabSize = rdi(shdrs[shstrtabIndex].sh_size); - char * shstrtab = (char * ) contents + rdi(shdrs[shstrtabIndex].sh_offset); - checkPointer(shstrtab, shstrtabSize); - - assert(shstrtabSize > 0); - assert(shstrtab[shstrtabSize - 1] == 0); - - sectionNames = string(shstrtab, shstrtabSize); - - sectionsByOldIndex.resize(hdr->e_shnum); - for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) - sectionsByOldIndex[i] = getSectionName(shdrs[i]); -} - - -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::sortPhdrs() -{ - /* Sort the segments by offset. */ - CompPhdr comp; - comp.elfFile = this; - sort(phdrs.begin(), phdrs.end(), comp); -} - - -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::sortShdrs() -{ - /* Translate sh_link mappings to section names, since sorting the - sections will invalidate the sh_link fields. */ - map<SectionName, SectionName> linkage; - for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) - if (rdi(shdrs[i].sh_link) != 0) - linkage[getSectionName(shdrs[i])] = getSectionName(shdrs[rdi(shdrs[i].sh_link)]); - - /* Idem for sh_info on certain sections. */ - map<SectionName, SectionName> info; - for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) - if (rdi(shdrs[i].sh_info) != 0 && - (rdi(shdrs[i].sh_type) == SHT_REL || rdi(shdrs[i].sh_type) == SHT_RELA)) - info[getSectionName(shdrs[i])] = getSectionName(shdrs[rdi(shdrs[i].sh_info)]); - - /* Idem for the index of the .shstrtab section in the ELF header. */ - SectionName shstrtabName = getSectionName(shdrs[rdi(hdr->e_shstrndx)]); - - /* Sort the sections by offset. */ - CompShdr comp; - comp.elfFile = this; - sort(shdrs.begin(), shdrs.end(), comp); - - /* Restore the sh_link mappings. */ - for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) - if (rdi(shdrs[i].sh_link) != 0) - wri(shdrs[i].sh_link, - findSection3(linkage[getSectionName(shdrs[i])])); - - /* And the st_info mappings. */ - for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) - if (rdi(shdrs[i].sh_info) != 0 && - (rdi(shdrs[i].sh_type) == SHT_REL || rdi(shdrs[i].sh_type) == SHT_RELA)) - wri(shdrs[i].sh_info, - findSection3(info[getSectionName(shdrs[i])])); - - /* And the .shstrtab index. */ - wri(hdr->e_shstrndx, findSection3(shstrtabName)); -} - - -static void writeFile(string fileName, mode_t fileMode) -{ - string fileName2 = fileName + "_patchelf_tmp"; - - int fd = open(fileName2.c_str(), - O_CREAT | O_TRUNC | O_WRONLY, 0700); - if (fd == -1) error("open"); - - if (write(fd, contents, fileSize) != fileSize) error("write"); - - if (close(fd) != 0) error("close"); - - if (chmod(fileName2.c_str(), fileMode) != 0) error("chmod"); - - if (rename(fileName2.c_str(), fileName.c_str()) != 0) error("rename"); -} - - -static unsigned int roundUp(unsigned int n, unsigned int m) -{ - return ((n - 1) / m + 1) * m; -} - - -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::shiftFile(unsigned int extraPages, Elf_Addr startPage) -{ - /* Move the entire contents of the file `extraPages' pages - further. */ - unsigned int oldSize = fileSize; - unsigned int shift = extraPages * pageSize; - growFile(fileSize + extraPages * pageSize); - memmove(contents + extraPages * pageSize, contents, oldSize); - memset(contents + sizeof(Elf_Ehdr), 0, shift - sizeof(Elf_Ehdr)); - - /* Adjust the ELF header. */ - wri(hdr->e_phoff, sizeof(Elf_Ehdr)); - wri(hdr->e_shoff, rdi(hdr->e_shoff) + shift); - - /* Update the offsets in the section headers. */ - for (int i = 1; i < rdi(hdr->e_shnum); ++i) - wri(shdrs[i].sh_offset, rdi(shdrs[i].sh_offset) + shift); - - /* Update the offsets in the program headers. */ - for (int i = 0; i < rdi(hdr->e_phnum); ++i) { - wri(phdrs[i].p_offset, rdi(phdrs[i].p_offset) + shift); - if (rdi(phdrs[i].p_align) != 0 && - (rdi(phdrs[i].p_vaddr) - rdi(phdrs[i].p_offset)) % rdi(phdrs[i].p_align) != 0) { - debug("changing alignment of program header %d from %d to %d\n", i, - rdi(phdrs[i].p_align), pageSize); - wri(phdrs[i].p_align, pageSize); - } - } - - /* Add a segment that maps the new program/section headers and - PT_INTERP segment into memory. Otherwise glibc will choke. */ - phdrs.resize(rdi(hdr->e_phnum) + 1); - wri(hdr->e_phnum, rdi(hdr->e_phnum) + 1); - Elf_Phdr & phdr = phdrs[rdi(hdr->e_phnum) - 1]; - wri(phdr.p_type, PT_LOAD); - wri(phdr.p_offset, 0); - wri(phdr.p_vaddr, wri(phdr.p_paddr, startPage)); - wri(phdr.p_filesz, wri(phdr.p_memsz, shift)); - wri(phdr.p_flags, PF_R | PF_W); - wri(phdr.p_align, pageSize); -} - - -template<ElfFileParams> -string ElfFile<ElfFileParamNames>::getSectionName(const Elf_Shdr & shdr) -{ - return string(sectionNames.c_str() + rdi(shdr.sh_name)); -} - - -template<ElfFileParams> -Elf_Shdr & ElfFile<ElfFileParamNames>::findSection(const SectionName & sectionName) -{ - Elf_Shdr * shdr = findSection2(sectionName); - if (!shdr) - error("cannot find section " + sectionName); - return *shdr; -} - - -template<ElfFileParams> -Elf_Shdr * ElfFile<ElfFileParamNames>::findSection2(const SectionName & sectionName) -{ - unsigned int i = findSection3(sectionName); - return i ? &shdrs[i] : 0; -} - - -template<ElfFileParams> -unsigned int ElfFile<ElfFileParamNames>::findSection3(const SectionName & sectionName) -{ - for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) - if (getSectionName(shdrs[i]) == sectionName) return i; - return 0; -} - - -template<ElfFileParams> -string & ElfFile<ElfFileParamNames>::replaceSection(const SectionName & sectionName, - unsigned int size) -{ - ReplacedSections::iterator i = replacedSections.find(sectionName); - string s; - - if (i != replacedSections.end()) { - s = string(i->second); - } else { - Elf_Shdr & shdr = findSection(sectionName); - s = string((char *) contents + rdi(shdr.sh_offset), rdi(shdr.sh_size)); - } - - s.resize(size); - replacedSections[sectionName] = s; - - return replacedSections[sectionName]; -} - - -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::writeReplacedSections(Elf_Off & curOff, - Elf_Addr startAddr, Elf_Off startOffset) -{ - /* Overwrite the old section contents with 'X's. Do this - *before* writing the new section contents (below) to prevent - clobbering previously written new section contents. */ - for (ReplacedSections::iterator i = replacedSections.begin(); - i != replacedSections.end(); ++i) - { - string sectionName = i->first; - Elf_Shdr & shdr = findSection(sectionName); - memset(contents + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size)); - } - - for (ReplacedSections::iterator i = replacedSections.begin(); - i != replacedSections.end(); ++i) - { - string sectionName = i->first; - Elf_Shdr & shdr = findSection(sectionName); - 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()); - - memcpy(contents + curOff, (unsigned char *) i->second.c_str(), - i->second.size()); - - /* Update the section header for this section. */ - wri(shdr.sh_offset, curOff); - wri(shdr.sh_addr, startAddr + (curOff - startOffset)); - wri(shdr.sh_size, i->second.size()); - wri(shdr.sh_addralign, sectionAlignment); - - /* If this is the .interp section, then the PT_INTERP segment - must be sync'ed with it. */ - if (sectionName == ".interp") { - for (unsigned int j = 0; j < phdrs.size(); ++j) - if (rdi(phdrs[j].p_type) == PT_INTERP) { - phdrs[j].p_offset = shdr.sh_offset; - phdrs[j].p_vaddr = phdrs[j].p_paddr = shdr.sh_addr; - phdrs[j].p_filesz = phdrs[j].p_memsz = shdr.sh_size; - } - } - - /* If this is the .dynamic section, then the PT_DYNAMIC segment - must be sync'ed with it. */ - if (sectionName == ".dynamic") { - for (unsigned int j = 0; j < phdrs.size(); ++j) - if (rdi(phdrs[j].p_type) == PT_DYNAMIC) { - phdrs[j].p_offset = shdr.sh_offset; - phdrs[j].p_vaddr = phdrs[j].p_paddr = shdr.sh_addr; - phdrs[j].p_filesz = phdrs[j].p_memsz = shdr.sh_size; - } - } - - curOff += roundUp(i->second.size(), sectionAlignment); - } - - replacedSections.clear(); -} - - -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary() -{ - /* For dynamic libraries, we just place the replacement sections - at the end of the file. They're mapped into memory by a - PT_LOAD segment located directly after the last virtual address - page of other segments. */ - Elf_Addr startPage = 0; - for (unsigned int i = 0; i < phdrs.size(); ++i) { - Elf_Addr thisPage = roundUp(rdi(phdrs[i].p_vaddr) + rdi(phdrs[i].p_memsz), pageSize); - if (thisPage > startPage) startPage = thisPage; - } - - debug("last page is 0x%llx\n", (unsigned long long) startPage); - - - /* Compute the total space needed for the replaced sections and - the program headers. */ - off_t neededSpace = (phdrs.size() + 1) * sizeof(Elf_Phdr); - for (ReplacedSections::iterator i = replacedSections.begin(); - i != replacedSections.end(); ++i) - neededSpace += roundUp(i->second.size(), sectionAlignment); - debug("needed space is %d\n", neededSpace); - - - size_t startOffset = roundUp(fileSize, pageSize); - - growFile(startOffset + neededSpace); - - - /* Even though this file is of type ET_DYN, it could actually be - an executable. For instance, Gold produces executables marked - ET_DYN. In that case we can still hit the kernel bug that - necessitated rewriteSectionsExecutable(). However, such - executables also tend to start at virtual address 0, so - rewriteSectionsExecutable() won't work because it doesn't have - any virtual address space to grow downwards into. As a - workaround, make sure that the virtual address of our new - PT_LOAD segment relative to the first PT_LOAD segment is equal - to its offset; otherwise we hit the kernel bug. This may - require creating a hole in the executable. The bigger the size - of the uninitialised data segment, the bigger the hole. */ - if (isExecutable) { - if (startOffset >= startPage) { - debug("shifting new PT_LOAD segment by %d bytes to work around a Linux kernel bug\n", startOffset - startPage); - } else { - size_t hole = startPage - startOffset; - /* Print a warning, because the hole could be very big. */ - fprintf(stderr, "warning: working around a Linux kernel bug by creating a hole of %zu bytes in ā%sā\n", hole, fileName.c_str()); - assert(hole % pageSize == 0); - /* !!! We could create an actual hole in the file here, - but it's probably not worth the effort. */ - growFile(fileSize + hole); - startOffset += hole; - } - startPage = startOffset; - } - - - /* Add a segment that maps the replaced sections and program - headers into memory. */ - phdrs.resize(rdi(hdr->e_phnum) + 1); - wri(hdr->e_phnum, rdi(hdr->e_phnum) + 1); - Elf_Phdr & phdr = phdrs[rdi(hdr->e_phnum) - 1]; - wri(phdr.p_type, PT_LOAD); - wri(phdr.p_offset, startOffset); - wri(phdr.p_vaddr, wri(phdr.p_paddr, startPage)); - wri(phdr.p_filesz, wri(phdr.p_memsz, neededSpace)); - wri(phdr.p_flags, PF_R | PF_W); - wri(phdr.p_align, pageSize); - - - /* Write out the replaced sections. */ - Elf_Off curOff = startOffset + phdrs.size() * sizeof(Elf_Phdr); - writeReplacedSections(curOff, startPage, startOffset); - assert((off_t) curOff == startOffset + neededSpace); - - - /* Move the program header to the start of the new area. */ - wri(hdr->e_phoff, startOffset); - - rewriteHeaders(startPage); -} - - -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::rewriteSectionsExecutable() -{ - /* Sort the sections by offset, otherwise we won't correctly find - all the sections before the last replaced section. */ - sortShdrs(); - - - /* What is the index of the last replaced section? */ - unsigned int lastReplaced = 0; - for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) { - string sectionName = getSectionName(shdrs[i]); - if (replacedSections.find(sectionName) != replacedSections.end()) { - debug("using replaced section `%s'\n", sectionName.c_str()); - lastReplaced = i; - } - } - - assert(lastReplaced != 0); - - 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 - 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. */ - size_t startOffset = rdi(shdrs[lastReplaced + 1].sh_offset); - Elf_Addr startAddr = rdi(shdrs[lastReplaced + 1].sh_addr); - string prevSection; - for (unsigned int i = 1; i <= lastReplaced; ++i) { - Elf_Shdr & shdr(shdrs[i]); - string sectionName = getSectionName(shdr); - debug("looking at section `%s'\n", sectionName.c_str()); - /* !!! Why do we stop after a .dynstr section? I can't - remember! */ - if ((rdi(shdr.sh_type) == SHT_PROGBITS && sectionName != ".interp") - || prevSection == ".dynstr") - { - startOffset = rdi(shdr.sh_offset); - startAddr = rdi(shdr.sh_addr); - lastReplaced = i - 1; - break; - } else { - if (replacedSections.find(sectionName) == replacedSections.end()) { - debug("replacing section `%s' which is in the way\n", sectionName.c_str()); - replaceSection(sectionName, rdi(shdr.sh_size)); - } - } - prevSection = sectionName; - } - - debug("first reserved offset/addr is 0x%x/0x%llx\n", - startOffset, (unsigned long long) startAddr); - - assert(startAddr % pageSize == startOffset % pageSize); - Elf_Addr firstPage = startAddr - startOffset; - debug("first page is 0x%llx\n", (unsigned long long) firstPage); - - /* Right now we assume that the section headers are somewhere near - the end, which appears to be the case most of the time. - Therefore they're not accidentally overwritten by the replaced - sections. !!! Fix this. */ - assert((off_t) rdi(hdr->e_shoff) >= startOffset); - - - /* 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); - for (ReplacedSections::iterator i = replacedSections.begin(); - i != replacedSections.end(); ++i) - neededSpace += roundUp(i->second.size(), sectionAlignment); - - debug("needed space is %d\n", neededSpace); - - /* If we need more space at the start of the file, then grow the - file by the minimum number of pages and adjust internal - offsets. */ - if (neededSpace > startOffset) { - - /* We also need an additional program header, so adjust for that. */ - neededSpace += sizeof(Elf_Phdr); - debug("needed space is %d\n", neededSpace); - - unsigned int neededPages = roundUp(neededSpace - startOffset, pageSize) / pageSize; - debug("needed pages is %d\n", neededPages); - if (neededPages * pageSize > firstPage) - error("virtual address space underrun!"); - - firstPage -= neededPages * pageSize; - startOffset += neededPages * pageSize; - - shiftFile(neededPages, firstPage); - } - - - /* 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(contents + curOff, 0, startOffset - curOff); - - - /* Write out the replaced sections. */ - writeReplacedSections(curOff, firstPage, 0); - assert((off_t) curOff == neededSpace); - - - rewriteHeaders(firstPage + rdi(hdr->e_phoff)); -} - - -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::rewriteSections() -{ - if (replacedSections.empty()) return; - - for (ReplacedSections::iterator i = replacedSections.begin(); - i != replacedSections.end(); ++i) - debug("replacing section `%s' with size %d\n", - i->first.c_str(), i->second.size()); - - if (rdi(hdr->e_type) == ET_DYN) { - debug("this is a dynamic library\n"); - rewriteSectionsLibrary(); - } else if (rdi(hdr->e_type) == ET_EXEC) { - debug("this is an executable\n"); - rewriteSectionsExecutable(); - } else error("unknown ELF type"); -} - - -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress) -{ - /* Rewrite the program header table. */ - - /* If there is a segment for the program header table, update it. - (According to the ELF spec, it must be the first entry.) */ - if (rdi(phdrs[0].p_type) == PT_PHDR) { - phdrs[0].p_offset = hdr->e_phoff; - wri(phdrs[0].p_vaddr, wri(phdrs[0].p_paddr, phdrAddress)); - wri(phdrs[0].p_filesz, wri(phdrs[0].p_memsz, phdrs.size() * sizeof(Elf_Phdr))); - } - - sortPhdrs(); - - for (unsigned int i = 0; i < phdrs.size(); ++i) - * ((Elf_Phdr *) (contents + rdi(hdr->e_phoff)) + i) = phdrs[i]; - - - /* Rewrite the section header table. For neatness, keep the - sections sorted. */ - assert(rdi(hdr->e_shnum) == shdrs.size()); - sortShdrs(); - for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) - * ((Elf_Shdr *) (contents + rdi(hdr->e_shoff)) + i) = shdrs[i]; - - - /* Update all those nasty virtual addresses in the .dynamic - section. Note that not all executables have .dynamic sections - (e.g., those produced by klibc's klcc). */ - Elf_Shdr * shdrDynamic = findSection2(".dynamic"); - if (shdrDynamic) { - Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic->sh_offset)); - unsigned int d_tag; - for ( ; (d_tag = rdi(dyn->d_tag)) != DT_NULL; dyn++) - if (d_tag == DT_STRTAB) - dyn->d_un.d_ptr = findSection(".dynstr").sh_addr; - else if (d_tag == DT_STRSZ) - dyn->d_un.d_val = findSection(".dynstr").sh_size; - else if (d_tag == DT_SYMTAB) - dyn->d_un.d_ptr = findSection(".dynsym").sh_addr; - else if (d_tag == DT_HASH) - dyn->d_un.d_ptr = findSection(".hash").sh_addr; - else if (d_tag == DT_GNU_HASH) - dyn->d_un.d_ptr = findSection(".gnu.hash").sh_addr; - else if (d_tag == DT_JMPREL) { - Elf_Shdr * shdr = findSection2(".rel.plt"); - if (!shdr) shdr = findSection2(".rela.plt"); /* 64-bit Linux, x86-64 */ - if (!shdr) shdr = findSection2(".rela.IA_64.pltoff"); /* 64-bit Linux, IA-64 */ - if (!shdr) error("cannot find section corresponding to DT_JMPREL"); - dyn->d_un.d_ptr = shdr->sh_addr; - } - else if (d_tag == DT_REL) { /* !!! hack! */ - Elf_Shdr * shdr = findSection2(".rel.dyn"); - /* no idea if this makes sense, but it was needed for some - program */ - if (!shdr) shdr = findSection2(".rel.got"); - if (!shdr) error("cannot find .rel.dyn or .rel.got"); - dyn->d_un.d_ptr = shdr->sh_addr; - } - else if (d_tag == DT_RELA) - dyn->d_un.d_ptr = findSection(".rela.dyn").sh_addr; /* PPC Linux */ - else if (d_tag == DT_VERNEED) - dyn->d_un.d_ptr = findSection(".gnu.version_r").sh_addr; - else if (d_tag == DT_VERSYM) - dyn->d_un.d_ptr = findSection(".gnu.version").sh_addr; - } - - - /* Rewrite the .dynsym section. It contains the indices of the - sections in which symbols appear, so these need to be - remapped. */ - for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) { - if (rdi(shdrs[i].sh_type) != SHT_SYMTAB && rdi(shdrs[i].sh_type) != SHT_DYNSYM) continue; - 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)]; - 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); - wri(sym->st_shndx, newIndex); - } - } - } -} - - - -static void setSubstr(string & s, unsigned int pos, const string & t) -{ - assert(pos + t.size() <= s.size()); - copy(t.begin(), t.end(), s.begin() + pos); -} - - -template<ElfFileParams> -string ElfFile<ElfFileParamNames>::getInterpreter() -{ - Elf_Shdr & shdr = findSection(".interp"); - return string((char *) contents + rdi(shdr.sh_offset), rdi(shdr.sh_size)); -} - - -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::setInterpreter(const string & newInterpreter) -{ - string & section = replaceSection(".interp", newInterpreter.size() + 1); - setSubstr(section, 0, newInterpreter + '\0'); - changed = true; -} - - -static void concatToRPath(string & rpath, const string & path) -{ - if (!rpath.empty()) rpath += ":"; - rpath += path; -} - - -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op, string newRPath) -{ - Elf_Shdr & shdrDynamic = findSection(".dynamic"); - - /* !!! We assume that the virtual address in the DT_STRTAB entry - of the dynamic section corresponds to the .dynstr section. */ - 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"); - - assert(strTabAddr == rdi(shdrDynStr.sh_addr)); - - - /* Walk through the dynamic section, look for the RPATH/RUNPATH - entry. - - According to the ld.so docs, DT_RPATH is obsolete, we should - use DT_RUNPATH. DT_RUNPATH has two advantages: it can be - overriden by LD_LIBRARY_PATH, and it's scoped (the DT_RUNPATH - for an executable or library doesn't affect the search path for - libraries used by it). DT_RPATH is ignored if DT_RUNPATH is - present. The binutils `ld' still generates only DT_RPATH, - unless you use its `--enable-new-dtag' option, in which case it - generates a DT_RPATH and DT_RUNPATH pointing at the same - string. */ - static vector<string> neededLibs; - dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset)); - Elf_Dyn * dynRPath = 0, * dynRunPath = 0; - char * rpath = 0; - for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) { - if (rdi(dyn->d_tag) == DT_RPATH) { - dynRPath = dyn; - /* Only use DT_RPATH if there is no DT_RUNPATH. */ - if (!dynRunPath) - rpath = strTab + rdi(dyn->d_un.d_val); - } - else if (rdi(dyn->d_tag) == DT_RUNPATH) { - dynRunPath = dyn; - rpath = strTab + rdi(dyn->d_un.d_val); - } - else if (rdi(dyn->d_tag) == DT_NEEDED) - neededLibs.push_back(string(strTab + rdi(dyn->d_un.d_val))); - } - - if (op == rpPrint) { - printf("%s\n", rpath ? rpath : ""); - return; - } - - if (op == rpShrink && !rpath) { - debug("no RPATH to shrink\n"); - return; - } - - - /* For each directory in the RPATH, check if it contains any - needed library. */ - if (op == rpShrink) { - static vector<bool> neededLibFound(neededLibs.size(), false); - - newRPath = ""; - - char * pos = rpath; - while (*pos) { - char * end = strchr(pos, ':'); - if (!end) end = strchr(pos, 0); - - /* Get the name of the directory. */ - string dirName(pos, end - pos); - if (*end == ':') ++end; - pos = end; - - /* Non-absolute entries are allowed (e.g., the special - "$ORIGIN" hack). */ - if (dirName[0] != '/') { - concatToRPath(newRPath, dirName); - continue; - } - - /* For each library that we haven't found yet, see if it - exists in this directory. */ - bool libFound = false; - for (unsigned int j = 0; j < neededLibs.size(); ++j) - if (!neededLibFound[j]) { - string libName = dirName + "/" + neededLibs[j]; - struct stat st; - if (stat(libName.c_str(), &st) == 0) { - neededLibFound[j] = true; - libFound = true; - } - } - - if (!libFound) - debug("removing directory `%s' from RPATH\n", dirName.c_str()); - else - concatToRPath(newRPath, dirName); - } - } - - - if (string(rpath ? rpath : "") == newRPath) return; - - changed = true; - - /* Zero out the previous rpath to prevent retained dependencies in - Nix. */ - unsigned int rpathSize = 0; - if (rpath) { - rpathSize = strlen(rpath); - memset(rpath, 'X', rpathSize); - } - - debug("new rpath is `%s'\n", newRPath.c_str()); - - if (!forceRPath && dynRPath && !dynRunPath) { /* convert DT_RPATH to DT_RUNPATH */ - dynRPath->d_tag = DT_RUNPATH; - dynRunPath = dynRPath; - dynRPath = 0; - } - - if (forceRPath && dynRPath && dynRunPath) { /* convert DT_RUNPATH to DT_RPATH */ - dynRunPath->d_tag = DT_IGNORE; - } - - if (newRPath.size() <= rpathSize) { - strcpy(rpath, newRPath.c_str()); - return; - } - - /* Grow the .dynstr section to make room for the new RPATH. */ - debug("rpath is too long, resizing...\n"); - - string & newDynStr = replaceSection(".dynstr", - rdi(shdrDynStr.sh_size) + newRPath.size() + 1); - setSubstr(newDynStr, rdi(shdrDynStr.sh_size), newRPath + '\0'); - - /* Update the DT_RUNPATH and DT_RPATH entries. */ - if (dynRunPath || dynRPath) { - if (dynRunPath) dynRunPath->d_un.d_val = shdrDynStr.sh_size; - if (dynRPath) dynRPath->d_un.d_val = shdrDynStr.sh_size; - } - - else { - /* There is no DT_RUNPATH entry in the .dynamic section, so we - have to grow the .dynamic section. */ - string & newDynamic = replaceSection(".dynamic", - rdi(shdrDynamic.sh_size) + sizeof(Elf_Dyn)); - - 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 one. */ - setSubstr(newDynamic, sizeof(Elf_Dyn), - string(newDynamic, 0, sizeof(Elf_Dyn) * (idx + 1))); - - /* Add the DT_RUNPATH entry at the top. */ - Elf_Dyn newDyn; - wri(newDyn.d_tag, forceRPath ? DT_RPATH : DT_RUNPATH); - newDyn.d_un.d_val = shdrDynStr.sh_size; - setSubstr(newDynamic, 0, string((char *) &newDyn, sizeof(Elf_Dyn))); - } -} - - -template<ElfFileParams> -void ElfFile<ElfFileParamNames>::removeNeeded(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)); - Elf_Dyn * last = dyn; - 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()) { - debug("removing DT_NEEDED entry `%s'\n", name); - changed = true; - } else { - debug("keeping DT_NEEDED entry `%s'\n", name); - *last++ = *dyn; - } - } else - *last++ = *dyn; - } - - memset(last, 0, sizeof(Elf_Dyn) * (dyn - last)); -} - - -static bool printInterpreter = false; -static string newInterpreter; - -static bool shrinkRPath = false; -static bool setRPath = false; -static bool printRPath = false; -static string newRPath; -static set<string> neededLibsToRemove; - - -template<class ElfFile> -static void patchElf2(ElfFile & elfFile, mode_t fileMode) -{ - elfFile.parse(); - - if (printInterpreter) - printf("%s\n", elfFile.getInterpreter().c_str()); - - if (newInterpreter != "") - elfFile.setInterpreter(newInterpreter); - - if (printRPath) - elfFile.modifyRPath(elfFile.rpPrint, ""); - - if (shrinkRPath) - elfFile.modifyRPath(elfFile.rpShrink, ""); - else if (setRPath) - elfFile.modifyRPath(elfFile.rpSet, newRPath); - - elfFile.removeNeeded(neededLibsToRemove); - - if (elfFile.isChanged()){ - elfFile.rewriteSections(); - writeFile(fileName, fileMode); - } -} - - -static void patchElf() -{ - if (!printInterpreter && !printRPath) - debug("patching ELF file `%s'\n", fileName.c_str()); - - mode_t fileMode; - - readFile(fileName, &fileMode); - - - /* Check the ELF header for basic validity. */ - if (fileSize < (off_t) sizeof(Elf32_Ehdr)) error("missing ELF header"); - - if (memcmp(contents, ELFMAG, SELFMAG) != 0) - error("not an ELF executable"); - - if (contents[EI_CLASS] == ELFCLASS32 && - contents[EI_VERSION] == EV_CURRENT) - { - ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym> elfFile; - patchElf2(elfFile, fileMode); - } - else if (contents[EI_CLASS] == ELFCLASS64 && - contents[EI_VERSION] == EV_CURRENT) - { - ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym> elfFile; - patchElf2(elfFile, fileMode); - } - else { - error("ELF executable is not 32/64-bit, little/big-endian, version 1"); - } -} - - -void showHelp(const string & progName) -{ - fprintf(stderr, "syntax: %s\n\ - [--set-interpreter FILENAME]\n\ - [--print-interpreter]\n\ - [--set-rpath RPATH]\n\ - [--shrink-rpath]\n\ - [--print-rpath]\n\ - [--force-rpath]\n\ - [--remove-needed LIBRARY]\n\ - [--debug]\n\ - [--version]\n\ - FILENAME\n", progName.c_str()); -} - - -int main(int argc, char * * argv) -{ - if (argc <= 1) { - showHelp(argv[0]); - return 1; - } - - if (getenv("PATCHELF_DEBUG") != 0) debugMode = true; - - int i; - for (i = 1; i < argc; ++i) { - string arg(argv[i]); - if (arg == "--set-interpreter" || arg == "--interpreter") { - if (++i == argc) error("missing argument"); - newInterpreter = argv[i]; - } - else if (arg == "--print-interpreter") { - printInterpreter = true; - } - else if (arg == "--shrink-rpath") { - shrinkRPath = true; - } - else if (arg == "--set-rpath") { - if (++i == argc) error("missing argument"); - setRPath = true; - newRPath = argv[i]; - } - else if (arg == "--print-rpath") { - printRPath = true; - } - else if (arg == "--force-rpath") { - /* Generally we prefer to emit DT_RUNPATH instead of - DT_RPATH, as the latter is obsolete. However, there is - a slight semantic difference: DT_RUNPATH is "scoped", - it only affects the executable or library in question, - not its recursive imports. So maybe you really want to - force the use of DT_RPATH. That's what this option - does. Without it, DT_RPATH (if encountered) is - converted to DT_RUNPATH, and if neither is present, a - DT_RUNPATH is added. With it, DT_RPATH isn't converted - to DT_RUNPATH, and if neither is present, a DT_RPATH is - added. */ - forceRPath = true; - } - else if (arg == "--remove-needed") { - if (++i == argc) error("missing argument"); - neededLibsToRemove.insert(argv[i]); - } - else if (arg == "--debug") { - debugMode = true; - } - else if (arg == "--help") { - showHelp(argv[0]); - return 0; - } - else if (arg == "--version") { - printf("1.0\n"); - return 0; - } - else break; - } - - if (i == argc) error("missing filename"); - fileName = argv[i]; - - patchElf(); - - return 0; -} |