From f7d2b496b9a3f613aff50fef1e56d9f3aab99c7b Mon Sep 17 00:00:00 2001 From: Mark H Weaver Date: Tue, 6 Jan 2015 21:41:55 +0000 Subject: gnu: patchelf: On ARM, apply experimental patch to get it working. * gnu/packages/patches/patchelf-rework-for-arm.patch: New file. * gnu-system.am: Add it. * gnu/packages/elf.scm: Apply patch when the target is ARM. --- gnu/packages/elf.scm | 21 + gnu/packages/patches/patchelf-rework-for-arm.patch | 473 +++++++++++++++++++++ 2 files changed, 494 insertions(+) create mode 100644 gnu/packages/patches/patchelf-rework-for-arm.patch (limited to 'gnu') diff --git a/gnu/packages/elf.scm b/gnu/packages/elf.scm index 7fc689cb11..46c59c3a3b 100644 --- a/gnu/packages/elf.scm +++ b/gnu/packages/elf.scm @@ -100,6 +100,27 @@ addr2line, and more.") "1rqpg84wrd3fa16wa9vqdvasnc05yz49w207cz1l0wrl4k8q97y9")) (patches (list (search-patch "patchelf-page-size.patch"))))) (build-system gnu-build-system) + + ;; XXX: The upstream 'patchelf' doesn't support ARM. The only available + ;; patch makes significant changes to the algorithm, possibly + ;; introducing bugs. So, we apply the patch only on ARM systems. + (inputs + (if (string-prefix? "arm" (or (%current-target-system) (%current-system))) + `(("patch/rework-for-arm" ,(search-patch + "patchelf-rework-for-arm.patch"))) + '())) + (arguments + (if (string-prefix? "arm" (or (%current-target-system) (%current-system))) + `(#:phases (alist-cons-after + 'unpack 'patch/rework-for-arm + (lambda* (#:key inputs #:allow-other-keys) + (let ((patch-file + (assoc-ref inputs "patch/rework-for-arm"))) + (zero? (system* "patch" "--force" "-p1" + "--input" patch-file)))) + %standard-phases)) + '())) + (home-page "http://nixos.org/patchelf.html") (synopsis "Modify the dynamic linker and RPATH of ELF executables") (description diff --git a/gnu/packages/patches/patchelf-rework-for-arm.patch b/gnu/packages/patches/patchelf-rework-for-arm.patch new file mode 100644 index 0000000000..6f4eb8f72b --- /dev/null +++ b/gnu/packages/patches/patchelf-rework-for-arm.patch @@ -0,0 +1,473 @@ +Rework the growing algorithm in patchelf to support ARM systems. +See . +This patch copied from: + + +From 0a96239cea6b97b9a0fff80da576e58ca2dfb2a2 Mon Sep 17 00:00:00 2001 +From: Sebastian Parschauer +Date: Sat, 28 Jun 2014 01:24:57 +0200 +Subject: [PATCH] Rework the growing algorithm + +On ARM systems there is no space in virtual memory for another LOAD +area in front of the code LOAD area. So insert data to its end +instead. At this location there should be enough space in virtual +memory due to alignment. We can extend it until the end of the +alignment but the file shift may be greater as it must be aligned +to the page size. Do the same for the data LOAD area. +--- + src/patchelf.cc | 357 ++++++++++++++++++++++---------------------------------- + 1 file changed, 142 insertions(+), 215 deletions(-) + +diff --git a/src/patchelf.cc b/src/patchelf.cc +index dcbfd38..4fce9e6 100644 +--- a/src/patchelf.cc ++++ b/src/patchelf.cc +@@ -116,7 +116,11 @@ private: + + void sortShdrs(); + +- void shiftFile(unsigned int extraPages, Elf_Addr startPage); ++ void shiftFileSingle(size_t fileShift, Elf_Off insertOff); ++ ++ void shiftFile(size_t neededCode, size_t neededData, ++ Elf_Off codeOff[], Elf_Off dataOff[], ++ Elf_Addr *codePage, Elf_Addr *dataPage); + + string getSectionName(const Elf_Shdr & shdr); + +@@ -130,13 +134,11 @@ private: + unsigned int size); + + void writeReplacedSections(Elf_Off & curOff, +- Elf_Addr startAddr, Elf_Off startOffset); ++ Elf_Addr startAddr, Elf_Off startOffset, bool isData); + + void rewriteHeaders(Elf_Addr phdrAddress); + +- void rewriteSectionsLibrary(); +- +- void rewriteSectionsExecutable(); ++ void rewriteSectionsBinary(); + + public: + +@@ -391,46 +393,119 @@ static unsigned int roundUp(unsigned int n, unsigned int m) + + + template +-void ElfFile::shiftFile(unsigned int extraPages, Elf_Addr startPage) ++void ElfFile::shiftFileSingle(size_t fileShift, ++ Elf_Off insertOff) + { +- /* 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)); ++ ++ /* Grow at the end */ ++ growFile(fileSize + fileShift); ++ ++ /* move the data from the insertion point ++ to the end and zero inserted space */ ++ memmove(contents + insertOff + fileShift, ++ contents + insertOff, oldSize - insertOff); ++ memset(contents + insertOff, 0, fileShift); + + /* Adjust the ELF header. */ + wri(hdr->e_phoff, sizeof(Elf_Ehdr)); +- wri(hdr->e_shoff, rdi(hdr->e_shoff) + shift); ++ if (rdi(hdr->e_shoff) >= insertOff) ++ wri(hdr->e_shoff, rdi(hdr->e_shoff) + fileShift); + + /* 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); ++ for (int i = 1; i < rdi(hdr->e_shnum); ++i) { ++ if (rdi(shdrs[i].sh_offset) >= insertOff) ++ wri(shdrs[i].sh_offset, rdi(shdrs[i].sh_offset) + fileShift); ++ } + + /* 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); ++ if (rdi(phdrs[i].p_offset) >= insertOff) ++ wri(phdrs[i].p_offset, rdi(phdrs[i].p_offset) + fileShift); ++ /* Check for ELF load command alignment issue the same ++ way as glibc/elf/dl-load.c does. This gives us the ++ chance to run an interpreter explicitly. */ ++ if (rdi(phdrs[i].p_type) == PT_LOAD && ((rdi(phdrs[i].p_vaddr) - ++ rdi(phdrs[i].p_offset)) & (rdi(phdrs[i].p_align) - 1)) != 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); + } + } ++} ++ ++template ++void ElfFile::shiftFile(size_t neededCode, ++ size_t neededData, Elf_Off codeOff[], Elf_Off dataOff[], ++ Elf_Addr *codePage, Elf_Addr *dataPage) ++{ ++ /* Move some contents of the file further. The binary has one LOAD area ++ * for code and one for data. There is virtual memory space between ++ * these which we can use due to alignment. ++ */ ++ unsigned int memShift = neededCode; ++ unsigned int fileShift = roundUp(neededCode, pageSize); ++ unsigned int maxMemShift = 0; ++ ++ if (neededCode > 0) { ++ /* find the LOAD program header for code and extend it */ ++ for (int i = 0; i < rdi(hdr->e_phnum); ++i) { ++ if (rdi(phdrs[i].p_type) == PT_LOAD && ++ rdi(phdrs[i].p_flags) & PF_X) { ++ codeOff[1] = rdi(phdrs[i].p_filesz); ++ codeOff[0] = codeOff[1] + rdi(phdrs[i].p_offset); ++ maxMemShift = rdi(phdrs[i].p_memsz) % rdi(phdrs[i].p_align); ++ if (maxMemShift == 0) ++ continue; ++ maxMemShift = rdi(phdrs[i].p_align) - maxMemShift; ++ if (maxMemShift == 0 || memShift > maxMemShift) ++ continue; ++ *codePage = rdi(phdrs[i].p_vaddr); ++ wri(phdrs[i].p_filesz, rdi(phdrs[i].p_filesz) + memShift); ++ wri(phdrs[i].p_memsz, rdi(phdrs[i].p_memsz) + memShift); ++ break; ++ } ++ } ++ debug("codeOff: %#lx, memShift: %d, maxMemShift: %d, fileShift: %d\n", ++ codeOff[1], memShift, maxMemShift, fileShift); ++ if (codeOff[1] == 0 || maxMemShift == 0) ++ goto out; ++ ++ shiftFileSingle(fileShift, codeOff[0]); ++ } ++ ++ /* +++ Do the same for the data LOAD area +++ */ ++ memShift = neededData; ++ fileShift = roundUp(neededData, pageSize); ++ maxMemShift = 0; ++ if (neededData > 0) { ++ /* find the LOAD program header for data and extend it */ ++ for (int i = 0; i < rdi(hdr->e_phnum); ++i) { ++ if (rdi(phdrs[i].p_type) == PT_LOAD && ++ rdi(phdrs[i].p_flags) & PF_W) { ++ dataOff[1] = rdi(phdrs[i].p_filesz); ++ dataOff[0] = dataOff[1] + rdi(phdrs[i].p_offset); ++ maxMemShift = rdi(phdrs[i].p_memsz) % rdi(phdrs[i].p_align); ++ if (maxMemShift == 0) ++ continue; ++ maxMemShift = rdi(phdrs[i].p_align) - maxMemShift; ++ if (maxMemShift == 0 || memShift > maxMemShift) ++ continue; ++ *dataPage = rdi(phdrs[i].p_vaddr); ++ wri(phdrs[i].p_filesz, rdi(phdrs[i].p_filesz) + memShift); ++ wri(phdrs[i].p_memsz, rdi(phdrs[i].p_memsz) + memShift); ++ break; ++ } ++ } ++ debug("dataOff: %#lx, memShift: %d, maxMemShift: %d, fileShift: %d\n", ++ dataOff[1], memShift, maxMemShift, fileShift); ++ if (dataOff[1] == 0 || maxMemShift == 0) ++ goto out; + +- /* 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); ++ shiftFileSingle(fileShift, dataOff[0]); ++ } ++out: ++ return; + } + + +@@ -491,7 +566,7 @@ string & ElfFile::replaceSection(const SectionName & sectionN + + template + void ElfFile::writeReplacedSections(Elf_Off & curOff, +- Elf_Addr startAddr, Elf_Off startOffset) ++ Elf_Addr startAddr, Elf_Off startOffset, bool isData = false) + { + /* Overwrite the old section contents with 'X's. Do this + *before* writing the new section contents (below) to prevent +@@ -501,6 +576,9 @@ void ElfFile::writeReplacedSections(Elf_Off & curOff, + { + string sectionName = i->first; + Elf_Shdr & shdr = findSection(sectionName); ++ if ((!isData && rdi(shdr.sh_flags) & SHF_WRITE) || ++ (isData && ~(rdi(shdr.sh_flags)) & SHF_WRITE)) ++ continue; + memset(contents + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size)); + } + +@@ -509,6 +587,9 @@ void ElfFile::writeReplacedSections(Elf_Off & curOff, + { + string sectionName = i->first; + Elf_Shdr & shdr = findSection(sectionName); ++ if ((!isData && rdi(shdr.sh_flags) & SHF_WRITE) || ++ (isData && ~(rdi(shdr.sh_flags)) & SHF_WRITE)) ++ continue; + 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()); + +@@ -546,201 +627,47 @@ void ElfFile::writeReplacedSections(Elf_Off & curOff, + curOff += roundUp(i->second.size(), sectionAlignment); + } + +- replacedSections.clear(); ++ if (isData) ++ replacedSections.clear(); + } + + + template +-void ElfFile::rewriteSectionsLibrary() ++void ElfFile::rewriteSectionsBinary() + { +- /* 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); ++ Elf_Off codeOff[2] = {0}, dataOff[2] = {0}; ++ Elf_Addr codePage = 0, dataPage = 0; ++ size_t neededCode = 0, neededData = 0, oldCode = 0, oldData = 0; ++ Elf_Shdr shdr = findSection(".text"); ++ Elf_Addr firstPage = rdi(shdr.sh_addr) - rdi(shdr.sh_offset); + ++ debug("first page is 0x%llx\n", (unsigned long long) firstPage); + +- /* Compute the total space needed for the replaced sections and +- the program headers. */ +- off_t neededSpace = (phdrs.size() + 1) * sizeof(Elf_Phdr); ++ /* Compute the total space needed for the replaced sections */ + 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 +-void ElfFile::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; ++ i != replacedSections.end(); ++i) { ++ shdr = findSection(i->first); ++ if (rdi(shdr.sh_flags) & SHF_WRITE) { ++ oldData += rdi(shdr.sh_size); ++ neededData += roundUp(i->second.size(), sectionAlignment); + } 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)); +- } ++ oldCode += rdi(shdr.sh_size); ++ neededCode += roundUp(i->second.size(), sectionAlignment); + } +- 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); ++ debug("needed space is C: %d, D: %d\n", neededCode, neededData); + ++ /* If we need more space within the file, then grow the ++ file and adjust internal offsets. */ ++ shiftFile(neededCode, neededData, codeOff, dataOff, &codePage, ++ &dataPage); ++ assert(codeOff[0] > 0); + + /* Write out the replaced sections. */ +- writeReplacedSections(curOff, firstPage, 0); +- assert((off_t) curOff == neededSpace); +- ++ debug("codePage: %#lx, dataPage: %#lx\n", codePage, dataPage); ++ writeReplacedSections(codeOff[0], codePage + codeOff[1], codeOff[0]); ++ writeReplacedSections(dataOff[0], dataPage + dataOff[1], dataOff[0], true); + + rewriteHeaders(firstPage + rdi(hdr->e_phoff)); + } +@@ -758,10 +685,10 @@ void ElfFile::rewriteSections() + + if (rdi(hdr->e_type) == ET_DYN) { + debug("this is a dynamic library\n"); +- rewriteSectionsLibrary(); ++ rewriteSectionsBinary(); + } else if (rdi(hdr->e_type) == ET_EXEC) { + debug("this is an executable\n"); +- rewriteSectionsExecutable(); ++ rewriteSectionsBinary(); + } else error("unknown ELF type"); + } + +-- +2.1.2 + -- cgit v1.2.3