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