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) {
107 GElf_Word size = ROUNDUP(me->ehdr.e_phentsize, phdrmp->phdr.p_align);
109 /* Move our sections */
110 CIRCLEQ_FOREACH(ms, &phdrmp->childScnList, elemChildScn) {
111 if (ms->shdr.sh_offset >= me->ehdr.e_phoff) {
112 ms->shdr.sh_offset += size;
117 CIRCLEQ_FOREACH(mp, &phdrmp->childPhdrList, elemChildPhdr) {
118 if (mp->phdr.p_offset > me->ehdr.e_phoff) {
119 mp->phdr.p_offset += size;
121 mp->phdr.p_vaddr -= size;
122 mp->phdr.p_paddr -= size;
125 if (mp->phdr.p_type == PT_PHDR) {
126 mp->phdr.p_filesz += me->ehdr.e_phentsize;
127 mp->phdr.p_memsz += me->ehdr.e_phentsize;
131 /* Move other PHDRs and sections */
132 assert(size <= shiftStuffAtAfterOffset(me, me->ehdr.e_phoff + 1, size));
134 /* Remap ourselves */
135 phdrmp->phdr.p_vaddr -= size;
136 phdrmp->phdr.p_paddr -= size;
137 phdrmp->phdr.p_filesz += size;
138 phdrmp->phdr.p_memsz += size;
142 newmp = elfu_mPhdrAlloc();
144 CIRCLEQ_INSERT_TAIL(&me->phdrList, newmp, elem);
153 /* Finds a suitable PHDR to insert a hole into and expands it
155 * Returns memory address the hole will be mapped to, or 0 if
156 * the operation failed. */
157 GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
158 GElf_Word align, int w, int x,
161 ElfuPhdr *lowestAddr;
162 ElfuPhdr *highestAddr;
163 ElfuPhdr *lowestOffs;
164 ElfuPhdr *highestOffsEnd;
169 /* Treat read-only data as executable.
170 * That's what the GNU toolchain does on x86. */
175 /* Find first and last LOAD PHDRs. */
176 elfu_mPhdrLoadLowestHighest(me, &lowestAddr, &highestAddr,
177 &lowestOffs, &highestOffsEnd);
179 if (((w && (highestAddr->phdr.p_flags & PF_W))
180 || (x && (highestAddr->phdr.p_flags & PF_X)))
181 /* Merging only works if the LOAD is the last both in file and mem */
182 && highestAddr == highestOffsEnd) {
183 /* Need to append. */
184 GElf_Off injOffset = OFFS_END(highestAddr->phdr.p_offset, highestAddr->phdr.p_filesz);
185 GElf_Word injSpace = 0;
186 GElf_Word nobitsize = highestAddr->phdr.p_memsz - highestAddr->phdr.p_filesz;
188 /* Expand NOBITS if any */
190 GElf_Off endOff = OFFS_END(highestAddr->phdr.p_offset, highestAddr->phdr.p_filesz);
191 GElf_Off endAddr = OFFS_END(highestAddr->phdr.p_vaddr, highestAddr->phdr.p_filesz);
194 ELFU_INFO("Expanding NOBITS at address 0x%x...\n", (unsigned)endAddr);
196 CIRCLEQ_FOREACH(ms, &highestAddr->childScnList, elemChildScn) {
197 if (ms->shdr.sh_offset == endOff) {
198 assert(ms->shdr.sh_type == SHT_NOBITS);
199 assert(ms->shdr.sh_size == nobitsize);
200 ms->databuf = malloc(ms->shdr.sh_size);
201 memset(ms->databuf, '\0', ms->shdr.sh_size);
203 ELFU_WARN("mExpandNobits: Could not allocate %u bytes for NOBITS expansion. Data may be inconsistent.\n",
204 (unsigned)ms->shdr.sh_size);
209 ms->shdr.sh_type = SHT_PROGBITS;
210 ms->shdr.sh_addr = endAddr;
214 injSpace += shiftStuffAtAfterOffset(me, endOff, nobitsize);
215 injSpace -= nobitsize;
216 injOffset += nobitsize;
217 highestAddr->phdr.p_filesz += nobitsize;
218 assert(highestAddr->phdr.p_filesz == highestAddr->phdr.p_memsz);
221 /* Calculate how much space we need, taking alignment into account */
222 size += ROUNDUP(injOffset, align) - injOffset;
224 /* If there is not enough space left, create even more. */
225 if (injSpace < size) {
226 injSpace += shiftStuffAtAfterOffset(me, injOffset, size - injSpace);
228 assert(injSpace >= size);
230 /* Remap ourselves */
231 highestAddr->phdr.p_filesz += size;
232 highestAddr->phdr.p_memsz += size;
234 injOffset = ROUNDUP(injOffset, align);
237 *injPhdr = highestAddr;
239 return highestAddr->phdr.p_vaddr + (injOffset - highestAddr->phdr.p_offset);
240 } else if (((w && (lowestAddr->phdr.p_flags & PF_W))
241 || (x && (lowestAddr->phdr.p_flags & PF_X)))
242 && /* Enough space to expand downwards? */
243 (lowestAddr->phdr.p_vaddr >= ((2 * lowestAddr->phdr.p_align)
244 + ROUNDUP(size, lowestAddr->phdr.p_align)))
245 /* Merging only works if the LOAD is the first both in file and mem */
246 && lowestAddr == lowestOffs) {
247 /* Need to prepend or split up the PHDR. */
248 GElf_Off injOffset = OFFS_END(lowestAddr->phdr.p_offset,
249 lowestAddr->phdr.p_filesz);
252 /* Round up size to take PHDR alignment into account.
253 * We assume that this is a multiple of the alignment asked for. */
254 assert(lowestAddr->phdr.p_align >= align);
255 size = ROUNDUP(size, lowestAddr->phdr.p_align);
257 /* Find first section. We assume there is at least one. */
258 assert(!CIRCLEQ_EMPTY(&lowestAddr->childScnList));
259 injOffset = CIRCLEQ_FIRST(&lowestAddr->childScnList)->shdr.sh_offset;
261 /* Move our sections */
262 CIRCLEQ_FOREACH(ms, &lowestAddr->childScnList, elemChildScn) {
263 if (ms->shdr.sh_offset >= injOffset) {
264 ms->shdr.sh_offset += size;
269 CIRCLEQ_FOREACH(mp, &lowestAddr->childPhdrList, elemChildPhdr) {
270 if (mp->phdr.p_offset >= injOffset) {
271 mp->phdr.p_offset += size;
273 mp->phdr.p_vaddr -= size;
274 mp->phdr.p_paddr -= size;
278 /* Move other PHDRs and sections */
279 assert(size <= shiftStuffAtAfterOffset(me, injOffset + 1, size));
281 /* Remap ourselves */
282 lowestAddr->phdr.p_vaddr -= size;
283 lowestAddr->phdr.p_paddr -= size;
284 lowestAddr->phdr.p_filesz += size;
285 lowestAddr->phdr.p_memsz += size;
287 injOffset = ROUNDUP(injOffset, align);
290 *injPhdr = lowestAddr;
292 return lowestAddr->phdr.p_vaddr + (injOffset - lowestAddr->phdr.p_offset);
298 /* Add a new LOAD PHDR. */
299 newmp = appendPhdr(me);
304 /* ELF spec: We need (p_offset % p_align) == (p_vaddr % p_align) */
305 injOffset = OFFS_END(highestOffsEnd->phdr.p_offset, highestOffsEnd->phdr.p_filesz);
306 injOffset = ROUNDUP(injOffset, align);
307 injAddr = OFFS_END(highestAddr->phdr.p_vaddr, highestAddr->phdr.p_memsz);
308 injAddr = ROUNDUP(injAddr, highestAddr->phdr.p_align);
309 injAddr += injOffset % highestAddr->phdr.p_align;
311 newmp->phdr.p_align = highestAddr->phdr.p_align;
312 newmp->phdr.p_filesz = size;
313 newmp->phdr.p_memsz = size;
314 newmp->phdr.p_flags = PF_R | (x ? PF_X : 0) | (w ? PF_W : 0);
315 newmp->phdr.p_type = PT_LOAD;
316 newmp->phdr.p_offset = injOffset;
317 newmp->phdr.p_vaddr = injAddr;
318 newmp->phdr.p_paddr = newmp->phdr.p_vaddr;
339 int elfu_mLayoutAuto(ElfuElf *me)
341 ElfuPhdr *lowestAddr;
342 ElfuPhdr *highestAddr;
343 ElfuPhdr *lowestOffs;
344 ElfuPhdr *highestOffsEnd;
348 GElf_Off lastend = 0;
352 /* Find first and last LOAD PHDRs. */
353 elfu_mPhdrLoadLowestHighest(me, &lowestAddr, &highestAddr,
354 &lowestOffs, &highestOffsEnd);
356 phdrArr = malloc(elfu_mPhdrCount(me) * sizeof(*phdrArr));
358 ELFU_WARN("elfu_mLayoutAuto: malloc failed for phdrArr.\n");
363 lastend = OFFS_END(highestOffsEnd->phdr.p_offset, highestOffsEnd->phdr.p_filesz);
366 /* If PHDRs are not mapped into memory, place them after LOAD segments. */
367 mp = elfu_mPhdrByOffset(me, me->ehdr.e_phoff);
369 lastend = ROUNDUP(lastend, 8);
370 me->ehdr.e_phoff = lastend;
371 lastend += me->ehdr.e_phnum * me->ehdr.e_phentsize;
373 /* Update size of PHDR PHDR */
376 CIRCLEQ_FOREACH(phdrmp, &mp->childPhdrList, elemChildPhdr) {
377 if (phdrmp->phdr.p_type == PT_PHDR) {
378 phdrmp->phdr.p_filesz = elfu_mPhdrCount(me) * me->ehdr.e_phentsize;
379 phdrmp->phdr.p_memsz = elfu_mPhdrCount(me) * me->ehdr.e_phentsize;
385 /* Place orphaned sections afterwards, maintaining alignment */
386 CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
387 lastend = ROUNDUP(lastend, ms->shdr.sh_addralign);
389 ms->shdr.sh_offset = lastend;
391 lastend = OFFS_END(ms->shdr.sh_offset, SCNFILESIZE(&ms->shdr));
395 /* Move SHDRs to end */
396 lastend = ROUNDUP(lastend, 8);
397 me->ehdr.e_shoff = lastend;