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" |