4 #include <libelfu/libelfu.h>
8 static GElf_Word shiftStuffAtAfterOffset(ElfuElf *me,
14 /* Force a minimum alignment, just to be sure. */
17 /* Find maximum alignment size by which we have to shift.
18 * Assumes alignment sizes are always 2^x. */
19 CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
20 if (mp->phdr.p_offset >= offset) {
21 if (mp->phdr.p_align > align) {
22 align = mp->phdr.p_align;
27 CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
28 if (ms->shdr.sh_offset >= offset) {
29 if (ms->shdr.sh_addralign > align) {
30 align = ms->shdr.sh_addralign;
35 size = ROUNDUP(size, align);
38 CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
39 if (mp->phdr.p_type != PT_LOAD) {
43 if (mp->phdr.p_offset >= offset) {
44 mp->phdr.p_offset += size;
46 elfu_mPhdrUpdateChildOffsets(mp);
50 CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
51 if (ms->shdr.sh_offset >= offset) {
52 ms->shdr.sh_offset += size;
56 if (me->ehdr.e_phoff >= offset) {
57 me->ehdr.e_phoff += size;
60 if (me->ehdr.e_shoff >= offset) {
61 me->ehdr.e_shoff += size;
71 static ElfuPhdr* appendPhdr(ElfuElf *me)
74 ElfuPhdr *highestAddr;
76 ElfuPhdr *highestOffsEnd;
80 ELFU_DEBUG("Appending new PHDR\n");
82 /* See if we have enough space for more PHDRs. If not, expand
83 * the PHDR they are in. */
84 phdrmp = elfu_mPhdrByOffset(me, me->ehdr.e_phoff);
86 /* No LOAD maps PHDRs into memory. Let re-layouter move them. */
88 GElf_Off phdr_maxsz = OFFS_END(phdrmp->phdr.p_offset, phdrmp->phdr.p_filesz);
89 ElfuScn *firstms = CIRCLEQ_FIRST(&phdrmp->childScnList);
91 /* How much can the PHDR table expand within its LOAD segment? */
93 phdr_maxsz = MIN(firstms->shdr.sh_offset, phdr_maxsz);
95 phdr_maxsz -= me->ehdr.e_phoff;
97 /* If we don't have enough space, try to make some by expanding
98 * the LOAD segment we are in. There is no other way.
99 * Also, we can only expand if it is the first LOAD PHDR. */
100 elfu_mPhdrLoadLowestHighest(me, &lowestAddr, &highestAddr,
101 &lowestOffs, &highestOffsEnd);
102 if (phdr_maxsz < (me->ehdr.e_phnum + 1) * me->ehdr.e_phentsize
103 && phdrmp == lowestAddr
104 && phdrmp == lowestOffs
105 && (lowestAddr->phdr.p_vaddr >= 2 * lowestAddr->phdr.p_align)) {
108 GElf_Word size = ROUNDUP(me->ehdr.e_phentsize, phdrmp->phdr.p_align);
110 /* Move our sections */
111 CIRCLEQ_FOREACH(ms, &phdrmp->childScnList, elemChildScn) {
112 if (ms->shdr.sh_offset >= me->ehdr.e_phoff) {
113 ms->shdr.sh_offset += size;
118 CIRCLEQ_FOREACH(mp, &phdrmp->childPhdrList, elemChildPhdr) {
119 if (mp->phdr.p_offset > me->ehdr.e_phoff) {
120 mp->phdr.p_offset += size;
122 mp->phdr.p_vaddr -= size;
123 mp->phdr.p_paddr -= size;
126 if (mp->phdr.p_type == PT_PHDR) {
127 mp->phdr.p_filesz += me->ehdr.e_phentsize;
128 mp->phdr.p_memsz += me->ehdr.e_phentsize;
132 /* Move other PHDRs and sections */
133 assert(size <= shiftStuffAtAfterOffset(me, me->ehdr.e_phoff + 1, size));
135 /* Remap ourselves */
136 phdrmp->phdr.p_vaddr -= size;
137 phdrmp->phdr.p_paddr -= size;
138 phdrmp->phdr.p_filesz += size;
139 phdrmp->phdr.p_memsz += size;
143 newmp = elfu_mPhdrAlloc();
145 CIRCLEQ_INSERT_TAIL(&me->phdrList, newmp, elem);
154 /* Finds a suitable PHDR to insert a hole into and expands it
156 * Returns memory address the hole will be mapped to, or 0 if
157 * the operation failed. */
158 GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
159 GElf_Word align, int w, int x,
162 ElfuPhdr *lowestAddr;
163 ElfuPhdr *highestAddr;
164 ElfuPhdr *lowestOffs;
165 ElfuPhdr *highestOffsEnd;
170 /* Treat read-only data as executable.
171 * That's what the GNU toolchain does on x86. */
176 /* Find first and last LOAD PHDRs. */
177 elfu_mPhdrLoadLowestHighest(me, &lowestAddr, &highestAddr,
178 &lowestOffs, &highestOffsEnd);
180 if (((w && (highestAddr->phdr.p_flags & PF_W))
181 || (x && (highestAddr->phdr.p_flags & PF_X)))
182 /* Merging only works if the LOAD is the last both in file and mem */
183 && highestAddr == highestOffsEnd) {
184 /* Need to append. */
185 GElf_Off injOffset = OFFS_END(highestAddr->phdr.p_offset, highestAddr->phdr.p_filesz);
186 GElf_Word injSpace = 0;
187 GElf_Word nobitsize = highestAddr->phdr.p_memsz - highestAddr->phdr.p_filesz;
189 /* Expand NOBITS if any */
191 GElf_Off endOff = OFFS_END(highestAddr->phdr.p_offset, highestAddr->phdr.p_filesz);
192 GElf_Off endAddr = OFFS_END(highestAddr->phdr.p_vaddr, highestAddr->phdr.p_filesz);
195 ELFU_INFO("Expanding NOBITS at address 0x%x...\n", (unsigned)endAddr);
197 CIRCLEQ_FOREACH(ms, &highestAddr->childScnList, elemChildScn) {
198 if (ms->shdr.sh_offset == endOff) {
199 assert(ms->shdr.sh_type == SHT_NOBITS);
200 assert(ms->shdr.sh_size == nobitsize);
201 ms->databuf = malloc(ms->shdr.sh_size);
202 memset(ms->databuf, '\0', ms->shdr.sh_size);
204 ELFU_WARN("mExpandNobits: Could not allocate %u bytes for NOBITS expansion. Data may be inconsistent.\n",
205 (unsigned)ms->shdr.sh_size);
210 ms->shdr.sh_type = SHT_PROGBITS;
211 ms->shdr.sh_addr = endAddr;
215 injSpace += shiftStuffAtAfterOffset(me, endOff, nobitsize);
216 injSpace -= nobitsize;
217 injOffset += nobitsize;
218 highestAddr->phdr.p_filesz += nobitsize;
219 assert(highestAddr->phdr.p_filesz == highestAddr->phdr.p_memsz);
222 /* Calculate how much space we need, taking alignment into account */
223 size += ROUNDUP(injOffset, align) - injOffset;
225 /* If there is not enough space left, create even more. */
226 if (injSpace < size) {
227 injSpace += shiftStuffAtAfterOffset(me, injOffset, size - injSpace);
229 assert(injSpace >= size);
231 /* Remap ourselves */
232 highestAddr->phdr.p_filesz += size;
233 highestAddr->phdr.p_memsz += size;
235 injOffset = ROUNDUP(injOffset, align);
238 *injPhdr = highestAddr;
240 return highestAddr->phdr.p_vaddr + (injOffset - highestAddr->phdr.p_offset);
241 } else if (((w && (lowestAddr->phdr.p_flags & PF_W))
242 || (x && (lowestAddr->phdr.p_flags & PF_X)))
243 && /* Enough space to expand downwards? */
244 (lowestAddr->phdr.p_vaddr >= ((2 * lowestAddr->phdr.p_align)
245 + ROUNDUP(size, lowestAddr->phdr.p_align)))
246 /* Merging only works if the LOAD is the first both in file and mem */
247 && lowestAddr == lowestOffs) {
248 /* Need to prepend or split up the PHDR. */
249 GElf_Off injOffset = OFFS_END(lowestAddr->phdr.p_offset,
250 lowestAddr->phdr.p_filesz);
253 /* Round up size to take PHDR alignment into account.
254 * We assume that this is a multiple of the alignment asked for. */
255 assert(lowestAddr->phdr.p_align >= align);
256 size = ROUNDUP(size, lowestAddr->phdr.p_align);
258 /* Find first section. We assume there is at least one. */
259 assert(!CIRCLEQ_EMPTY(&lowestAddr->childScnList));
260 injOffset = CIRCLEQ_FIRST(&lowestAddr->childScnList)->shdr.sh_offset;
262 /* Move our sections */
263 CIRCLEQ_FOREACH(ms, &lowestAddr->childScnList, elemChildScn) {
264 if (ms->shdr.sh_offset >= injOffset) {
265 ms->shdr.sh_offset += size;
270 CIRCLEQ_FOREACH(mp, &lowestAddr->childPhdrList, elemChildPhdr) {
271 if (mp->phdr.p_offset >= injOffset) {
272 mp->phdr.p_offset += size;
274 mp->phdr.p_vaddr -= size;
275 mp->phdr.p_paddr -= size;
279 /* Move other PHDRs and sections */
280 assert(size <= shiftStuffAtAfterOffset(me, injOffset + 1, size));
282 /* Remap ourselves */
283 lowestAddr->phdr.p_vaddr -= size;
284 lowestAddr->phdr.p_paddr -= size;
285 lowestAddr->phdr.p_filesz += size;
286 lowestAddr->phdr.p_memsz += size;
288 injOffset = ROUNDUP(injOffset, align);
291 *injPhdr = lowestAddr;
293 return lowestAddr->phdr.p_vaddr + (injOffset - lowestAddr->phdr.p_offset);
299 /* Add a new LOAD PHDR. */
300 newmp = appendPhdr(me);
305 /* ELF spec: We need (p_offset % p_align) == (p_vaddr % p_align) */
306 injOffset = OFFS_END(highestOffsEnd->phdr.p_offset, highestOffsEnd->phdr.p_filesz);
307 injOffset = ROUNDUP(injOffset, align);
308 injAddr = OFFS_END(highestAddr->phdr.p_vaddr, highestAddr->phdr.p_memsz);
309 injAddr = ROUNDUP(injAddr, highestAddr->phdr.p_align);
310 injAddr += injOffset % highestAddr->phdr.p_align;
312 newmp->phdr.p_align = highestAddr->phdr.p_align;
313 newmp->phdr.p_filesz = size;
314 newmp->phdr.p_memsz = size;
315 newmp->phdr.p_flags = PF_R | (x ? PF_X : 0) | (w ? PF_W : 0);
316 newmp->phdr.p_type = PT_LOAD;
317 newmp->phdr.p_offset = injOffset;
318 newmp->phdr.p_vaddr = injAddr;
319 newmp->phdr.p_paddr = newmp->phdr.p_vaddr;
340 int elfu_mLayoutAuto(ElfuElf *me)
342 ElfuPhdr *lowestAddr;
343 ElfuPhdr *highestAddr;
344 ElfuPhdr *lowestOffs;
345 ElfuPhdr *highestOffsEnd;
348 GElf_Off lastend = 0;
352 /* Find first and last LOAD PHDRs. */
353 elfu_mPhdrLoadLowestHighest(me, &lowestAddr, &highestAddr,
354 &lowestOffs, &highestOffsEnd);
357 lastend = OFFS_END(highestOffsEnd->phdr.p_offset, highestOffsEnd->phdr.p_filesz);
360 /* If PHDRs are not mapped into memory, place them after LOAD segments. */
361 mp = elfu_mPhdrByOffset(me, me->ehdr.e_phoff);
363 lastend = ROUNDUP(lastend, 8);
364 me->ehdr.e_phoff = lastend;
365 lastend += me->ehdr.e_phnum * me->ehdr.e_phentsize;
367 /* Update size of PHDR PHDR */
370 CIRCLEQ_FOREACH(phdrmp, &mp->childPhdrList, elemChildPhdr) {
371 if (phdrmp->phdr.p_type == PT_PHDR) {
372 phdrmp->phdr.p_filesz = elfu_mPhdrCount(me) * me->ehdr.e_phentsize;
373 phdrmp->phdr.p_memsz = elfu_mPhdrCount(me) * me->ehdr.e_phentsize;
379 /* Place orphaned sections afterwards, maintaining alignment */
380 CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
381 lastend = ROUNDUP(lastend, ms->shdr.sh_addralign);
383 ms->shdr.sh_offset = lastend;
385 lastend = OFFS_END(ms->shdr.sh_offset, SCNFILESIZE(&ms->shdr));
389 /* Move SHDRs to end */
390 lastend = ROUNDUP(lastend, 8);
391 me->ehdr.e_shoff = lastend;