1*4882a593SmuzhiyunFrom 79c093226e609b99fa889f6e37480b92b399610d Mon Sep 17 00:00:00 2001 2*4882a593SmuzhiyunFrom: Richard Purdie <richard.purdie@linuxfoundation.org> 3*4882a593SmuzhiyunDate: Tue, 7 Mar 2017 21:08:34 +0000 4*4882a593SmuzhiyunSubject: [PATCH] Avoid inflating file sizes needlessly and allow binaries to 5*4882a593Smuzhiyun be stripped 6*4882a593Smuzhiyun 7*4882a593SmuzhiyunThe current approach to changing sections in ET_DYN executables is to move 8*4882a593Smuzhiyunthe INTERP section to the end of the file. +This means changing PT_PHDR to 9*4882a593Smuzhiyunadd an extra PT_LOAD section so that the new section is mmaped into memory 10*4882a593Smuzhiyunby the elf loader in the kernel. In order to extend PHDR, this means moving 11*4882a593Smuzhiyunit to the end of the file. 12*4882a593Smuzhiyun 13*4882a593SmuzhiyunIts documented in BUGS there is a kernel 'bug' which means that if you have holes 14*4882a593Smuzhiyunin memory between the base load address and the PT_LOAD segment that contains PHDR, 15*4882a593Smuzhiyunit will pass an incorrect PHDR address to ld.so and fail to load the binary, segfaulting. 16*4882a593Smuzhiyun 17*4882a593SmuzhiyunTo avoid this, the code currently inserts space into the binary to ensure that when 18*4882a593Smuzhiyunloaded into memory there are no holes between the PT_LOAD sections. This inflates the 19*4882a593Smuzhiyunbinaries by many MBs in some cases. Whilst we could make them sparse, there is a second 20*4882a593Smuzhiyunissue which is that strip can fail to process these binaries: 21*4882a593Smuzhiyun 22*4882a593Smuzhiyun$ strip fixincl 23*4882a593SmuzhiyunNot enough room for program headers, try linking with -N 24*4882a593Smuzhiyun[.note.ABI-tag]: Bad value 25*4882a593Smuzhiyun 26*4882a593SmuzhiyunThis turns out to be due to libbfd not liking the relocated PHDR section either 27*4882a593Smuzhiyun(https://github.com/NixOS/patchelf/issues/10). 28*4882a593Smuzhiyun 29*4882a593SmuzhiyunInstead this patch implements a different approach, leaving PHDR where it is but extending 30*4882a593Smuzhiyunit in place to allow addition of a new PT_LOAD section. This overwrites sections in the 31*4882a593Smuzhiyunbinary but those get moved to the end of the file in the new PT_LOAD section. 32*4882a593Smuzhiyun 33*4882a593SmuzhiyunThis is based on patches linked from the above github issue, however whilst the idea 34*4882a593Smuzhiyunwas good, the implementation wasn't correct and they've been rewritten here. 35*4882a593Smuzhiyun 36*4882a593SmuzhiyunSigned-off-by: Richard Purdie <richard.purdie@linuxfoundation.org> 37*4882a593Smuzhiyun 38*4882a593SmuzhiyunFetch from: https://github.com/NixOS/patchelf/commit/c4deb5e9e1ce9c98a48e0d5bb37d87739b8cfee4 39*4882a593Smuzhiyun 40*4882a593SmuzhiyunBackported to v0.9 41*4882a593Smuzhiyun 42*4882a593SmuzhiyunSigned-off-by: Conrad Ratschan <conrad.ratschan@rockwellcollins.com> 43*4882a593Smuzhiyun--- 44*4882a593Smuzhiyun src/patchelf.cc | 71 ++++++++++++++++++++++++++++--------------------- 45*4882a593Smuzhiyun 1 file changed, 40 insertions(+), 31 deletions(-) 46*4882a593Smuzhiyun 47*4882a593Smuzhiyundiff --git a/src/patchelf.cc b/src/patchelf.cc 48*4882a593Smuzhiyunindex 1d58061..c2147af 100644 49*4882a593Smuzhiyun--- a/src/patchelf.cc 50*4882a593Smuzhiyun+++ b/src/patchelf.cc 51*4882a593Smuzhiyun@@ -209,6 +209,8 @@ private: 52*4882a593Smuzhiyun string & replaceSection(const SectionName & sectionName, 53*4882a593Smuzhiyun unsigned int size); 54*4882a593Smuzhiyun 55*4882a593Smuzhiyun+ bool haveReplacedSection(const SectionName & sectionName); 56*4882a593Smuzhiyun+ 57*4882a593Smuzhiyun void writeReplacedSections(Elf_Off & curOff, 58*4882a593Smuzhiyun Elf_Addr startAddr, Elf_Off startOffset); 59*4882a593Smuzhiyun 60*4882a593Smuzhiyun@@ -632,6 +634,15 @@ void ElfFile<ElfFileParamNames>::writeReplacedSections(Elf_Off & curOff, 61*4882a593Smuzhiyun replacedSections.clear(); 62*4882a593Smuzhiyun } 63*4882a593Smuzhiyun 64*4882a593Smuzhiyun+template<ElfFileParams> 65*4882a593Smuzhiyun+bool ElfFile<ElfFileParamNames>::haveReplacedSection(const SectionName & sectionName) 66*4882a593Smuzhiyun+{ 67*4882a593Smuzhiyun+ ReplacedSections::iterator i = replacedSections.find(sectionName); 68*4882a593Smuzhiyun+ 69*4882a593Smuzhiyun+ if (i != replacedSections.end()) 70*4882a593Smuzhiyun+ return true; 71*4882a593Smuzhiyun+ return false; 72*4882a593Smuzhiyun+} 73*4882a593Smuzhiyun 74*4882a593Smuzhiyun template<ElfFileParams> 75*4882a593Smuzhiyun void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary() 76*4882a593Smuzhiyun@@ -648,52 +659,53 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary() 77*4882a593Smuzhiyun 78*4882a593Smuzhiyun debug("last page is 0x%llx\n", (unsigned long long) startPage); 79*4882a593Smuzhiyun 80*4882a593Smuzhiyun+ /* Because we're adding a new section header, we're necessarily increasing 81*4882a593Smuzhiyun+ the size of the program header table. This can cause the first section 82*4882a593Smuzhiyun+ to overlap the program header table in memory; we need to shift the first 83*4882a593Smuzhiyun+ few segments to someplace else. */ 84*4882a593Smuzhiyun+ /* Some sections may already be replaced so account for that */ 85*4882a593Smuzhiyun+ unsigned int i = 1; 86*4882a593Smuzhiyun+ Elf_Addr pht_size = sizeof(Elf_Ehdr) + (phdrs.size() + 1)*sizeof(Elf_Phdr); 87*4882a593Smuzhiyun+ while( shdrs[i].sh_addr <= pht_size && i < rdi(hdr->e_shnum) ) { 88*4882a593Smuzhiyun+ if (not haveReplacedSection(getSectionName(shdrs[i]))) 89*4882a593Smuzhiyun+ replaceSection(getSectionName(shdrs[i]), shdrs[i].sh_size); 90*4882a593Smuzhiyun+ i++; 91*4882a593Smuzhiyun+ } 92*4882a593Smuzhiyun 93*4882a593Smuzhiyun- /* Compute the total space needed for the replaced sections and 94*4882a593Smuzhiyun- the program headers. */ 95*4882a593Smuzhiyun- off_t neededSpace = (phdrs.size() + 1) * sizeof(Elf_Phdr); 96*4882a593Smuzhiyun+ /* Compute the total space needed for the replaced sections */ 97*4882a593Smuzhiyun+ off_t neededSpace = 0; 98*4882a593Smuzhiyun for (ReplacedSections::iterator i = replacedSections.begin(); 99*4882a593Smuzhiyun i != replacedSections.end(); ++i) 100*4882a593Smuzhiyun neededSpace += roundUp(i->second.size(), sectionAlignment); 101*4882a593Smuzhiyun debug("needed space is %d\n", neededSpace); 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun- 104*4882a593Smuzhiyun size_t startOffset = roundUp(fileSize, getPageSize()); 105*4882a593Smuzhiyun 106*4882a593Smuzhiyun growFile(startOffset + neededSpace); 107*4882a593Smuzhiyun 108*4882a593Smuzhiyun- 109*4882a593Smuzhiyun /* Even though this file is of type ET_DYN, it could actually be 110*4882a593Smuzhiyun an executable. For instance, Gold produces executables marked 111*4882a593Smuzhiyun- ET_DYN. In that case we can still hit the kernel bug that 112*4882a593Smuzhiyun- necessitated rewriteSectionsExecutable(). However, such 113*4882a593Smuzhiyun- executables also tend to start at virtual address 0, so 114*4882a593Smuzhiyun+ ET_DYN as does LD when linking with pie. If we move PT_PHDR, it 115*4882a593Smuzhiyun+ has to stay in the first PT_LOAD segment or any subsequent ones 116*4882a593Smuzhiyun+ if they're continuous in memory due to linux kernel constraints 117*4882a593Smuzhiyun+ (see BUGS). Since the end of the file would be after bss, we can't 118*4882a593Smuzhiyun+ move PHDR there, we therefore choose to leave PT_PHDR where it is but 119*4882a593Smuzhiyun+ move enough following sections such that we can add the extra PT_LOAD 120*4882a593Smuzhiyun+ section to it. This PT_LOAD segment ensures the sections at the end of 121*4882a593Smuzhiyun+ the file are mapped into memory for ld.so to process. 122*4882a593Smuzhiyun+ We can't use the approach in rewriteSectionsExecutable() 123*4882a593Smuzhiyun+ since DYN executables tend to start at virtual address 0, so 124*4882a593Smuzhiyun rewriteSectionsExecutable() won't work because it doesn't have 125*4882a593Smuzhiyun- any virtual address space to grow downwards into. As a 126*4882a593Smuzhiyun- workaround, make sure that the virtual address of our new 127*4882a593Smuzhiyun- PT_LOAD segment relative to the first PT_LOAD segment is equal 128*4882a593Smuzhiyun- to its offset; otherwise we hit the kernel bug. This may 129*4882a593Smuzhiyun- require creating a hole in the executable. The bigger the size 130*4882a593Smuzhiyun- of the uninitialised data segment, the bigger the hole. */ 131*4882a593Smuzhiyun+ any virtual address space to grow downwards into. */ 132*4882a593Smuzhiyun if (isExecutable) { 133*4882a593Smuzhiyun if (startOffset >= startPage) { 134*4882a593Smuzhiyun debug("shifting new PT_LOAD segment by %d bytes to work around a Linux kernel bug\n", startOffset - startPage); 135*4882a593Smuzhiyun- } else { 136*4882a593Smuzhiyun- size_t hole = startPage - startOffset; 137*4882a593Smuzhiyun- /* Print a warning, because the hole could be very big. */ 138*4882a593Smuzhiyun- fprintf(stderr, "warning: working around a Linux kernel bug by creating a hole of %zu bytes in ‘%s’\n", hole, fileName.c_str()); 139*4882a593Smuzhiyun- assert(hole % getPageSize() == 0); 140*4882a593Smuzhiyun- /* !!! We could create an actual hole in the file here, 141*4882a593Smuzhiyun- but it's probably not worth the effort. */ 142*4882a593Smuzhiyun- growFile(fileSize + hole); 143*4882a593Smuzhiyun- startOffset += hole; 144*4882a593Smuzhiyun } 145*4882a593Smuzhiyun startPage = startOffset; 146*4882a593Smuzhiyun } 147*4882a593Smuzhiyun 148*4882a593Smuzhiyun 149*4882a593Smuzhiyun- /* Add a segment that maps the replaced sections and program 150*4882a593Smuzhiyun- headers into memory. */ 151*4882a593Smuzhiyun+ /* Add a segment that maps the replaced sections into memory. */ 152*4882a593Smuzhiyun phdrs.resize(rdi(hdr->e_phnum) + 1); 153*4882a593Smuzhiyun wri(hdr->e_phnum, rdi(hdr->e_phnum) + 1); 154*4882a593Smuzhiyun Elf_Phdr & phdr = phdrs[rdi(hdr->e_phnum) - 1]; 155*4882a593Smuzhiyun@@ -706,15 +718,12 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary() 156*4882a593Smuzhiyun 157*4882a593Smuzhiyun 158*4882a593Smuzhiyun /* Write out the replaced sections. */ 159*4882a593Smuzhiyun- Elf_Off curOff = startOffset + phdrs.size() * sizeof(Elf_Phdr); 160*4882a593Smuzhiyun+ Elf_Off curOff = startOffset; 161*4882a593Smuzhiyun writeReplacedSections(curOff, startPage, startOffset); 162*4882a593Smuzhiyun assert(curOff == startOffset + neededSpace); 163*4882a593Smuzhiyun 164*4882a593Smuzhiyun- 165*4882a593Smuzhiyun- /* Move the program header to the start of the new area. */ 166*4882a593Smuzhiyun- wri(hdr->e_phoff, startOffset); 167*4882a593Smuzhiyun- 168*4882a593Smuzhiyun- rewriteHeaders(startPage); 169*4882a593Smuzhiyun+ /* Write out the updated program and section headers */ 170*4882a593Smuzhiyun+ rewriteHeaders(hdr->e_phoff); 171*4882a593Smuzhiyun } 172*4882a593Smuzhiyun 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun-- 175*4882a593Smuzhiyun2.17.1 176*4882a593Smuzhiyun 177