diff options
| -rw-r--r-- | meta/recipes-devtools/patchelf/patchelf/avoidholes.patch | 163 | ||||
| -rw-r--r-- | meta/recipes-devtools/patchelf/patchelf_0.9.bb | 1 |
2 files changed, 164 insertions, 0 deletions
diff --git a/meta/recipes-devtools/patchelf/patchelf/avoidholes.patch b/meta/recipes-devtools/patchelf/patchelf/avoidholes.patch new file mode 100644 index 0000000000..a273688b98 --- /dev/null +++ b/meta/recipes-devtools/patchelf/patchelf/avoidholes.patch | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | Different types of binaries create challenges for patchelf. In order to extend | ||
| 2 | sections they need to be moved within the binary. The current approach to | ||
| 3 | handling ET_DYN binaries is to move the INTERP section to the end of the file. | ||
| 4 | This means changing PT_PHDR to add an extra PT_LOAD section so that the new section | ||
| 5 | is mmaped into memory by the elf loader in the kernel. In order to extend PHDR, | ||
| 6 | this means moving it to the end of the file. | ||
| 7 | |||
| 8 | Its documented in patchelf there is a kernel 'bug' which means that if you have holes | ||
| 9 | in memory between the base load address and the PT_LOAD segment that contains PHDR, | ||
| 10 | it will pass an incorrect PHDR address to ld.so and fail to load the binary, segfaulting. | ||
| 11 | |||
| 12 | To avoid this, the code currently inserts space into the binary to ensure that when | ||
| 13 | loaded into memory there are no holes between the PT_LOAD sections. This inflates the | ||
| 14 | binaries by many MBs in some cases. Whilst we could make them sparse, there is a second | ||
| 15 | issue which is that strip can fail to process these binaries: | ||
| 16 | |||
| 17 | $ strip fixincl | ||
| 18 | Not enough room for program headers, try linking with -N | ||
| 19 | [.note.ABI-tag]: Bad value | ||
| 20 | |||
| 21 | This turns out to be due to libbfd not liking the relocated PHDR section either | ||
| 22 | (https://github.com/NixOS/patchelf/issues/10). | ||
| 23 | |||
| 24 | Instead this patch implements a different approach, leaving PHDR where it is but extending | ||
| 25 | it in place to allow addition of a new PT_LOAD section. This overwrites sections in the | ||
| 26 | binary but those get moved to the end of the file in the new PT_LOAD section. | ||
| 27 | |||
| 28 | This is based on patches linked from the above github issue, however whilst the idea | ||
| 29 | was good, the implementation wasn't correct and they've been rewritten here. | ||
| 30 | |||
| 31 | RP | ||
| 32 | 2017/3/7 | ||
| 33 | |||
| 34 | Upstream-Status: Pending | ||
| 35 | |||
| 36 | Index: patchelf-0.9/src/patchelf.cc | ||
| 37 | =================================================================== | ||
| 38 | --- patchelf-0.9.orig/src/patchelf.cc | ||
| 39 | +++ patchelf-0.9/src/patchelf.cc | ||
| 40 | @@ -146,6 +146,8 @@ private: | ||
| 41 | string & replaceSection(const SectionName & sectionName, | ||
| 42 | unsigned int size); | ||
| 43 | |||
| 44 | + bool haveReplacedSection(const SectionName & sectionName); | ||
| 45 | + | ||
| 46 | void writeReplacedSections(Elf_Off & curOff, | ||
| 47 | Elf_Addr startAddr, Elf_Off startOffset); | ||
| 48 | |||
| 49 | @@ -497,6 +499,16 @@ unsigned int ElfFile<ElfFileParamNames>: | ||
| 50 | return 0; | ||
| 51 | } | ||
| 52 | |||
| 53 | +template<ElfFileParams> | ||
| 54 | +bool ElfFile<ElfFileParamNames>::haveReplacedSection(const SectionName & sectionName) | ||
| 55 | +{ | ||
| 56 | + ReplacedSections::iterator i = replacedSections.find(sectionName); | ||
| 57 | + | ||
| 58 | + if (i != replacedSections.end()) | ||
| 59 | + return true; | ||
| 60 | + return false; | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | |||
| 64 | template<ElfFileParams> | ||
| 65 | string & ElfFile<ElfFileParamNames>::replaceSection(const SectionName & sectionName, | ||
| 66 | @@ -595,52 +607,52 @@ void ElfFile<ElfFileParamNames>::rewrite | ||
| 67 | |||
| 68 | debug("last page is 0x%llx\n", (unsigned long long) startPage); | ||
| 69 | |||
| 70 | + /* Because we're adding a new section header, we're necessarily increasing | ||
| 71 | + the size of the program header table. This can cause the first section | ||
| 72 | + to overlap the program header table in memory; we need to shift the first | ||
| 73 | + few segments to someplace else. */ | ||
| 74 | + /* Some sections may already be replaced so account for that */ | ||
| 75 | + unsigned int i = 1; | ||
| 76 | + Elf_Addr pht_size = sizeof(Elf_Ehdr) + (phdrs.size() + 1)*sizeof(Elf_Phdr); | ||
| 77 | + while( shdrs[i].sh_addr <= pht_size && i < rdi(hdr->e_shnum) ) { | ||
| 78 | + if (not haveReplacedSection(getSectionName(shdrs[i]))) | ||
| 79 | + replaceSection(getSectionName(shdrs[i]), shdrs[i].sh_size); | ||
| 80 | + i++; | ||
| 81 | + } | ||
| 82 | |||
| 83 | - /* Compute the total space needed for the replaced sections and | ||
| 84 | - the program headers. */ | ||
| 85 | - off_t neededSpace = (phdrs.size() + 1) * sizeof(Elf_Phdr); | ||
| 86 | + /* Compute the total space needed for the replaced sections */ | ||
| 87 | + off_t neededSpace = 0; | ||
| 88 | for (ReplacedSections::iterator i = replacedSections.begin(); | ||
| 89 | i != replacedSections.end(); ++i) | ||
| 90 | neededSpace += roundUp(i->second.size(), sectionAlignment); | ||
| 91 | debug("needed space is %d\n", neededSpace); | ||
| 92 | |||
| 93 | - | ||
| 94 | size_t startOffset = roundUp(fileSize, getPageSize()); | ||
| 95 | |||
| 96 | growFile(startOffset + neededSpace); | ||
| 97 | |||
| 98 | - | ||
| 99 | /* Even though this file is of type ET_DYN, it could actually be | ||
| 100 | an executable. For instance, Gold produces executables marked | ||
| 101 | - ET_DYN. In that case we can still hit the kernel bug that | ||
| 102 | - necessitated rewriteSectionsExecutable(). However, such | ||
| 103 | - executables also tend to start at virtual address 0, so | ||
| 104 | + ET_DYN as does LD when linking with pie. If we move PT_PHDR, it | ||
| 105 | + has to stay in the first PT_LOAD segment or any subsequent ones | ||
| 106 | + if they're continuous in memory due to linux kernel constraints | ||
| 107 | + (see BUGS). Since the end of the file would be after bss, we can't | ||
| 108 | + move PHDR there, we therefore choose to leave PT_PHDR where it is but | ||
| 109 | + move enough following sections such that we can add the extra PT_LOAD | ||
| 110 | + section to it. This PT_LOAD segment ensures the sections at the end of | ||
| 111 | + the file are mapped into memory for ld.so to process. | ||
| 112 | + We can't use the approach in rewriteSectionsExecutable() | ||
| 113 | + since DYN executables tend to start at virtual address 0, so | ||
| 114 | rewriteSectionsExecutable() won't work because it doesn't have | ||
| 115 | - any virtual address space to grow downwards into. As a | ||
| 116 | - workaround, make sure that the virtual address of our new | ||
| 117 | - PT_LOAD segment relative to the first PT_LOAD segment is equal | ||
| 118 | - to its offset; otherwise we hit the kernel bug. This may | ||
| 119 | - require creating a hole in the executable. The bigger the size | ||
| 120 | - of the uninitialised data segment, the bigger the hole. */ | ||
| 121 | + any virtual address space to grow downwards into. */ | ||
| 122 | if (isExecutable) { | ||
| 123 | if (startOffset >= startPage) { | ||
| 124 | debug("shifting new PT_LOAD segment by %d bytes to work around a Linux kernel bug\n", startOffset - startPage); | ||
| 125 | - } else { | ||
| 126 | - size_t hole = startPage - startOffset; | ||
| 127 | - /* Print a warning, because the hole could be very big. */ | ||
| 128 | - fprintf(stderr, "warning: working around a Linux kernel bug by creating a hole of %zu bytes in ā%sā\n", hole, fileName.c_str()); | ||
| 129 | - assert(hole % getPageSize() == 0); | ||
| 130 | - /* !!! We could create an actual hole in the file here, | ||
| 131 | - but it's probably not worth the effort. */ | ||
| 132 | - growFile(fileSize + hole); | ||
| 133 | - startOffset += hole; | ||
| 134 | } | ||
| 135 | startPage = startOffset; | ||
| 136 | } | ||
| 137 | |||
| 138 | - | ||
| 139 | - /* Add a segment that maps the replaced sections and program | ||
| 140 | - headers into memory. */ | ||
| 141 | + /* Add a segment that maps the replaced sections into memory. */ | ||
| 142 | phdrs.resize(rdi(hdr->e_phnum) + 1); | ||
| 143 | wri(hdr->e_phnum, rdi(hdr->e_phnum) + 1); | ||
| 144 | Elf_Phdr & phdr = phdrs[rdi(hdr->e_phnum) - 1]; | ||
| 145 | @@ -653,15 +665,12 @@ void ElfFile<ElfFileParamNames>::rewrite | ||
| 146 | |||
| 147 | |||
| 148 | /* Write out the replaced sections. */ | ||
| 149 | - Elf_Off curOff = startOffset + phdrs.size() * sizeof(Elf_Phdr); | ||
| 150 | + Elf_Off curOff = startOffset; | ||
| 151 | writeReplacedSections(curOff, startPage, startOffset); | ||
| 152 | assert(curOff == startOffset + neededSpace); | ||
| 153 | |||
| 154 | - | ||
| 155 | - /* Move the program header to the start of the new area. */ | ||
| 156 | - wri(hdr->e_phoff, startOffset); | ||
| 157 | - | ||
| 158 | - rewriteHeaders(startPage); | ||
| 159 | + /* Write out the updated program and section headers */ | ||
| 160 | + rewriteHeaders(hdr->e_phoff); | ||
| 161 | } | ||
| 162 | |||
| 163 | |||
diff --git a/meta/recipes-devtools/patchelf/patchelf_0.9.bb b/meta/recipes-devtools/patchelf/patchelf_0.9.bb index 54e654bdcb..01f0e62135 100644 --- a/meta/recipes-devtools/patchelf/patchelf_0.9.bb +++ b/meta/recipes-devtools/patchelf/patchelf_0.9.bb | |||
| @@ -2,6 +2,7 @@ SRC_URI = "http://nixos.org/releases/${BPN}/${BPN}-${PV}/${BPN}-${PV}.tar.bz2 \ | |||
| 2 | file://Skip-empty-section-fixes-66.patch \ | 2 | file://Skip-empty-section-fixes-66.patch \ |
| 3 | file://handle-read-only-files.patch \ | 3 | file://handle-read-only-files.patch \ |
| 4 | file://Increase-maxSize-to-64MB.patch \ | 4 | file://Increase-maxSize-to-64MB.patch \ |
| 5 | file://avoidholes.patch \ | ||
| 5 | " | 6 | " |
| 6 | 7 | ||
| 7 | LICENSE = "GPLv3" | 8 | LICENSE = "GPLv3" |
