diff --git a/src/Makefile.am b/src/Makefile.am index 65d0fa89..4e2a7f0a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -AM_CXXFLAGS = -Wall +AM_CXXFLAGS = -Wall -std=c++11 bin_PROGRAMS = patchelf diff --git a/src/patchelf.cc b/src/patchelf.cc index 136098fb..dbe5ea70 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -146,6 +146,8 @@ class ElfFile string & replaceSection(const SectionName & sectionName, unsigned int size); + bool haveReplacedSection(const SectionName & sectionName); + void writeReplacedSections(Elf_Off & curOff, Elf_Addr startAddr, Elf_Off startOffset); @@ -366,7 +368,7 @@ void ElfFile::sortShdrs() /* Sort the sections by offset. */ CompShdr comp; comp.elfFile = this; - sort(shdrs.begin(), shdrs.end(), comp); + sort(shdrs.begin() + 1, shdrs.end(), comp); /* Restore the sh_link mappings. */ for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i) @@ -483,6 +485,15 @@ unsigned int ElfFile::findSection3(const SectionName & sectio return 0; } +template +bool ElfFile::haveReplacedSection(const SectionName & sectionName) +{ + ReplacedSections::iterator i = replacedSections.find(sectionName); + + if (i != replacedSections.end()) + return true; + return false; +} template string & ElfFile::replaceSection(const SectionName & sectionName, @@ -517,7 +528,8 @@ void ElfFile::writeReplacedSections(Elf_Off & curOff, { string sectionName = i->first; Elf_Shdr & shdr = findSection(sectionName); - memset(contents + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size)); + if (shdr.sh_type != SHT_NOBITS) + memset(contents + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size)); } for (ReplacedSections::iterator i = replacedSections.begin(); @@ -581,52 +593,49 @@ void ElfFile::rewriteSectionsLibrary() debug("last page is 0x%llx\n", (unsigned long long) startPage); + /* Because we're adding a new section header, we're necessarily increasing + the size of the program header table. This can cause the first section + to overlap the program header table in memory; we need to shift the first + few segments to someplace else. */ + /* Some sections may already be replaced so account for that */ + unsigned int i = 1; + Elf_Addr pht_size = sizeof(Elf_Ehdr) + (phdrs.size() + 1)*sizeof(Elf_Phdr); + while( shdrs[i].sh_addr <= pht_size && i < rdi(hdr->e_shnum) ) { + if (not haveReplacedSection(getSectionName(shdrs[i]))) + replaceSection(getSectionName(shdrs[i]), shdrs[i].sh_size); + i++; + } - /* 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); + /* Compute the total space needed for the replaced sections */ + off_t neededSpace = 0; + for (auto & i : replacedSections) + neededSpace += roundUp(i.second.size(), sectionAlignment); debug("needed space is %d\n", neededSpace); - size_t startOffset = roundUp(fileSize, getPageSize()); 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 + ET_DYN as does LD when linking with pie. If we move PT_PHDR, it + has to stay in the first PT_LOAD segment or any subsequent ones + if they're continuous in memory due to linux kernel constraints + (see BUGS). Since the end of the file would be after bss, we can't + move PHDR there, we therefore choose to leave PT_PHDR where it is but + move enough following sections such that we can add the extra PT_LOAD + section to it. This PT_LOAD segment ensures the sections at the end of + the file are mapped into memory for ld.so to process. + We can't use the approach in rewriteSectionsExecutable() + since DYN executables 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 % getPageSize() == 0); - /* !!! We could create an actual hole in the file here, - but it's probably not worth the effort. */ - growFile(fileSize + hole); - startOffset += hole; - } + any virtual address space to grow downwards into. */ + if (isExecutable && startOffset > startPage) { + debug("shifting new PT_LOAD segment by %d bytes to work around a Linux kernel bug\n", startOffset - startPage); startPage = startOffset; } - - /* Add a segment that maps the replaced sections and program - headers into memory. */ + /* Add a segment that maps the replaced sections 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]; @@ -639,15 +648,12 @@ void ElfFile::rewriteSectionsLibrary() /* Write out the replaced sections. */ - Elf_Off curOff = startOffset + phdrs.size() * sizeof(Elf_Phdr); + Elf_Off curOff = startOffset; writeReplacedSections(curOff, startPage, startOffset); assert(curOff == startOffset + neededSpace); - - /* Move the program header to the start of the new area. */ - wri(hdr->e_phoff, startOffset); - - rewriteHeaders(startPage); + /* Write out the updated program and section headers */ + rewriteHeaders(hdr->e_phoff); } diff --git a/tests/no-rpath-prebuild.sh b/tests/no-rpath-prebuild.sh index d059ddac..aa27b7ad 100755 --- a/tests/no-rpath-prebuild.sh +++ b/tests/no-rpath-prebuild.sh @@ -1,6 +1,7 @@ #! /bin/sh -e set -x ARCH="$1" +PAGESIZE=4096 if [ -z "$ARCH" ]; then ARCH=$(basename $0 .sh | sed -e 's/.*-//') @@ -25,13 +26,13 @@ mkdir -p ${SCRATCH} cp $no_rpath_bin ${SCRATCH}/no-rpath -oldRPath=$(../src/patchelf --print-rpath ${SCRATCH}/no-rpath) +oldRPath=$(../src/patchelf --page-size ${PAGESIZE} --print-rpath ${SCRATCH}/no-rpath) if test -n "$oldRPath"; then exit 1; fi -../src/patchelf \ - --set-interpreter "$(../src/patchelf --print-interpreter ../src/patchelf)" \ +../src/patchelf --page-size ${PAGESIZE} \ + --set-interpreter "$(../src/patchelf --page-size ${PAGESIZE} --print-interpreter ../src/patchelf)" \ --set-rpath /foo:/bar:/xxxxxxxxxxxxxxx ${SCRATCH}/no-rpath -newRPath=$(../src/patchelf --print-rpath ${SCRATCH}/no-rpath) +newRPath=$(../src/patchelf --page-size ${PAGESIZE} --print-rpath ${SCRATCH}/no-rpath) if ! echo "$newRPath" | grep -q '/foo:/bar'; then echo "incomplete RPATH" exit 1