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)
76 ELFU_DEBUG("Appending new PHDR\n");
78 /* See if we have enough space for more PHDRs. If not, expand
79 * the PHDR they are in. */
80 phdrmp = elfu_mPhdrByOffset(me, me->ehdr.e_phoff);
82 /* No LOAD maps PHDRs into memory. Let re-layouter move them. */
84 GElf_Off phdr_maxsz = OFFS_END(phdrmp->phdr.p_offset, phdrmp->phdr.p_filesz);
85 ElfuScn *firstms = CIRCLEQ_FIRST(&phdrmp->childScnList);
87 /* How much can the PHDR table expand within its LOAD segment? */
89 phdr_maxsz = MIN(firstms->shdr.sh_offset, phdr_maxsz);
91 phdr_maxsz -= me->ehdr.e_phoff;
93 /* If we don't have enough space, try to make some by expanding
94 * the LOAD segment we are in. There is no other way. */
95 if (phdr_maxsz < (me->ehdr.e_phnum + 1) * me->ehdr.e_phentsize) {
98 GElf_Word size = ROUNDUP(me->ehdr.e_phentsize, phdrmp->phdr.p_align);
100 /* Move our sections */
101 CIRCLEQ_FOREACH(ms, &phdrmp->childScnList, elemChildScn) {
102 if (ms->shdr.sh_offset >= me->ehdr.e_phoff) {
103 ms->shdr.sh_offset += size;
108 CIRCLEQ_FOREACH(mp, &phdrmp->childPhdrList, elemChildPhdr) {
109 if (mp->phdr.p_offset > me->ehdr.e_phoff) {
110 mp->phdr.p_offset += size;
112 mp->phdr.p_vaddr -= size;
113 mp->phdr.p_paddr -= size;
116 if (mp->phdr.p_type == PT_PHDR) {
117 mp->phdr.p_filesz += me->ehdr.e_phentsize;
118 mp->phdr.p_memsz += me->ehdr.e_phentsize;
122 /* Move other PHDRs and sections */
123 assert(size <= shiftStuffAtAfterOffset(me, me->ehdr.e_phoff + 1, size));
125 /* Remap ourselves */
126 phdrmp->phdr.p_vaddr -= size;
127 phdrmp->phdr.p_paddr -= size;
128 phdrmp->phdr.p_filesz += size;
129 phdrmp->phdr.p_memsz += size;
133 newmp = elfu_mPhdrAlloc();
135 CIRCLEQ_INSERT_TAIL(&me->phdrList, newmp, elem);
144 /* Finds a suitable PHDR to insert a hole into and expands it
146 * Returns memory address the hole will be mapped to, or 0 if
147 * the operation failed. */
148 GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
149 GElf_Word align, int w, int x,
152 ElfuPhdr *lowestAddr;
153 ElfuPhdr *highestAddr;
154 ElfuPhdr *lowestOffs;
155 ElfuPhdr *highestOffsEnd;
160 /* Treat read-only data as executable.
161 * That's what the GNU toolchain does on x86. */
166 /* Find first and last LOAD PHDRs. */
167 elfu_mPhdrLoadLowestHighest(me, &lowestAddr, &highestAddr,
168 &lowestOffs, &highestOffsEnd);
170 if (((w && (highestAddr->phdr.p_flags & PF_W))
171 || (x && (highestAddr->phdr.p_flags & PF_X)))
172 /* Merging only works if the LOAD is the last both in file and mem */
173 && highestAddr == highestOffsEnd) {
174 /* Need to append. */
175 GElf_Off injOffset = OFFS_END(highestAddr->phdr.p_offset, highestAddr->phdr.p_filesz);
176 GElf_Word injSpace = 0;
177 GElf_Word nobitsize = highestAddr->phdr.p_memsz - highestAddr->phdr.p_filesz;
179 /* Expand NOBITS if any */
181 GElf_Off endOff = OFFS_END(highestAddr->phdr.p_offset, highestAddr->phdr.p_filesz);
182 GElf_Off endAddr = OFFS_END(highestAddr->phdr.p_vaddr, highestAddr->phdr.p_filesz);
185 ELFU_INFO("Expanding NOBITS at address 0x%x...\n", (unsigned)endAddr);
187 CIRCLEQ_FOREACH(ms, &highestAddr->childScnList, elemChildScn) {
188 if (ms->shdr.sh_offset == endOff) {
189 assert(ms->shdr.sh_type == SHT_NOBITS);
190 assert(ms->shdr.sh_size == nobitsize);
191 ms->databuf = malloc(ms->shdr.sh_size);
192 memset(ms->databuf, '\0', ms->shdr.sh_size);
194 ELFU_WARN("mExpandNobits: Could not allocate %u bytes for NOBITS expansion. Data may be inconsistent.\n",
195 (unsigned)ms->shdr.sh_size);
200 ms->shdr.sh_type = SHT_PROGBITS;
201 ms->shdr.sh_addr = endAddr;
205 injSpace += shiftStuffAtAfterOffset(me, endOff, nobitsize);
206 injSpace -= nobitsize;
207 injOffset += nobitsize;
208 highestAddr->phdr.p_filesz += nobitsize;
209 assert(highestAddr->phdr.p_filesz == highestAddr->phdr.p_memsz);
212 /* Calculate how much space we need, taking alignment into account */
213 size += ROUNDUP(injOffset, align) - injOffset;
215 /* If there is not enough space left, create even more. */
216 if (injSpace < size) {
217 injSpace += shiftStuffAtAfterOffset(me, injOffset, size - injSpace);
219 assert(injSpace >= size);
221 /* Remap ourselves */
222 highestAddr->phdr.p_filesz += size;
223 highestAddr->phdr.p_memsz += size;
225 injOffset = ROUNDUP(injOffset, align);
228 *injPhdr = highestAddr;
230 return highestAddr->phdr.p_vaddr + (injOffset - highestAddr->phdr.p_offset);
231 } else if (((w && (lowestAddr->phdr.p_flags & PF_W))
232 || (x && (lowestAddr->phdr.p_flags & PF_X)))
233 && /* Enough space to expand downwards? */
234 (lowestAddr->phdr.p_vaddr > 3 * lowestAddr->phdr.p_align)
235 /* Merging only works if the LOAD is the first both in file and mem */
236 && lowestAddr == lowestOffs) {
237 /* Need to prepend or split up the PHDR. */
238 GElf_Off injOffset = OFFS_END(lowestAddr->phdr.p_offset,
239 lowestAddr->phdr.p_filesz);
242 /* Round up size to take PHDR alignment into account.
243 * We assume that this is a multiple of the alignment asked for. */
244 assert(lowestAddr->phdr.p_align >= align);
245 size = ROUNDUP(size, lowestAddr->phdr.p_align);
247 /* Find first section. We assume there is at least one. */
248 assert(!CIRCLEQ_EMPTY(&lowestAddr->childScnList));
249 injOffset = CIRCLEQ_FIRST(&lowestAddr->childScnList)->shdr.sh_offset;
251 /* Move our sections */
252 CIRCLEQ_FOREACH(ms, &lowestAddr->childScnList, elemChildScn) {
253 if (ms->shdr.sh_offset >= injOffset) {
254 ms->shdr.sh_offset += size;
259 CIRCLEQ_FOREACH(mp, &lowestAddr->childPhdrList, elemChildPhdr) {
260 if (mp->phdr.p_offset >= injOffset) {
261 mp->phdr.p_offset += size;
263 mp->phdr.p_vaddr -= size;
264 mp->phdr.p_paddr -= size;
268 /* Move other PHDRs and sections */
269 assert(size <= shiftStuffAtAfterOffset(me, injOffset + 1, size));
271 /* Remap ourselves */
272 lowestAddr->phdr.p_vaddr -= size;
273 lowestAddr->phdr.p_paddr -= size;
274 lowestAddr->phdr.p_filesz += size;
275 lowestAddr->phdr.p_memsz += size;
277 injOffset = ROUNDUP(injOffset, align);
280 *injPhdr = lowestAddr;
282 return lowestAddr->phdr.p_vaddr + (injOffset - lowestAddr->phdr.p_offset);
288 /* Add a new LOAD PHDR. */
289 newmp = appendPhdr(me);
291 /* ELF spec: We need (p_offset % p_align) == (p_vaddr % p_align) */
292 injOffset = OFFS_END(highestOffsEnd->phdr.p_offset, highestOffsEnd->phdr.p_filesz);
293 injOffset = ROUNDUP(injOffset, align);
294 injAddr = OFFS_END(highestAddr->phdr.p_vaddr, highestAddr->phdr.p_memsz);
295 injAddr = ROUNDUP(injAddr, highestAddr->phdr.p_align);
296 injAddr += injOffset % highestAddr->phdr.p_align;
298 newmp->phdr.p_align = highestAddr->phdr.p_align;
299 newmp->phdr.p_filesz = size;
300 newmp->phdr.p_memsz = size;
301 newmp->phdr.p_flags = PF_R | (x ? PF_X : 0) | (w ? PF_W : 0);
302 newmp->phdr.p_type = PT_LOAD;
303 newmp->phdr.p_offset = injOffset;
304 newmp->phdr.p_vaddr = injAddr;
305 newmp->phdr.p_paddr = newmp->phdr.p_vaddr;
326 int elfu_mLayoutAuto(ElfuElf *me)
328 ElfuPhdr *lowestAddr;
329 ElfuPhdr *highestAddr;
330 ElfuPhdr *lowestOffs;
331 ElfuPhdr *highestOffsEnd;
335 GElf_Off lastend = 0;
339 /* Find first and last LOAD PHDRs. */
340 elfu_mPhdrLoadLowestHighest(me, &lowestAddr, &highestAddr,
341 &lowestOffs, &highestOffsEnd);
343 phdrArr = malloc(elfu_mPhdrCount(me) * sizeof(*phdrArr));
345 ELFU_WARN("elfu_mLayoutAuto: malloc failed for phdrArr.\n");
350 lastend = OFFS_END(highestOffsEnd->phdr.p_offset, highestOffsEnd->phdr.p_filesz);
353 /* If PHDRs are not mapped into memory, place them after LOAD segments. */
354 mp = elfu_mPhdrByOffset(me, me->ehdr.e_phoff);
356 lastend = ROUNDUP(lastend, 8);
357 me->ehdr.e_phoff = lastend;
358 lastend += me->ehdr.e_phnum * me->ehdr.e_phentsize;
360 /* Update size of PHDR PHDR */
363 CIRCLEQ_FOREACH(phdrmp, &mp->childPhdrList, elemChildPhdr) {
364 if (phdrmp->phdr.p_type == PT_PHDR) {
365 phdrmp->phdr.p_filesz = elfu_mPhdrCount(me) * me->ehdr.e_phentsize;
366 phdrmp->phdr.p_memsz = elfu_mPhdrCount(me) * me->ehdr.e_phentsize;
372 /* Place orphaned sections afterwards, maintaining alignment */
373 CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
374 lastend = ROUNDUP(lastend, ms->shdr.sh_addralign);
376 ms->shdr.sh_offset = lastend;
378 lastend = OFFS_END(ms->shdr.sh_offset, SCNFILESIZE(&ms->shdr));
382 /* Move SHDRs to end */
383 lastend = ROUNDUP(lastend, 8);
384 me->ehdr.e_shoff = lastend;