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 /* Finds a suitable PHDR to insert a hole into and expands it
73 * Returns memory address the hole will be mapped to, or 0 if
74 * the operation failed. */
75 GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
76 GElf_Word align, int w, int x,
79 ElfuPhdr *first = NULL;
80 ElfuPhdr *last = NULL;
85 /* Treat read-only data as executable.
86 * That's what the GNU toolchain does on x86. */
91 /* Find first and last LOAD PHDRs.
92 * Don't compare p_memsz - segments don't overlap in memory. */
93 CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
94 if (mp->phdr.p_type != PT_LOAD) {
97 if (!first || mp->phdr.p_vaddr < first->phdr.p_vaddr) {
100 if (!last || mp->phdr.p_vaddr > last->phdr.p_vaddr) {
105 if ((w && (last->phdr.p_flags & PF_W))
106 || (x && (last->phdr.p_flags & PF_X))) {
107 /* Need to append. */
108 GElf_Off injOffset = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz);
109 GElf_Word injSpace = 0;
110 GElf_Word nobitsize = last->phdr.p_memsz - last->phdr.p_filesz;
112 /* Expand NOBITS if any */
114 GElf_Off endOff = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz);
115 GElf_Off endAddr = OFFS_END(last->phdr.p_vaddr, last->phdr.p_filesz);
118 ELFU_INFO("Expanding NOBITS at address 0x%x...\n", (unsigned)endAddr);
120 CIRCLEQ_FOREACH(ms, &last->childScnList, elemChildScn) {
121 if (ms->shdr.sh_offset == endOff) {
122 assert(ms->shdr.sh_type == SHT_NOBITS);
123 assert(ms->shdr.sh_size == nobitsize);
124 ms->databuf = malloc(ms->shdr.sh_size);
125 memset(ms->databuf, '\0', ms->shdr.sh_size);
127 ELFU_WARN("mExpandNobits: Could not allocate %u bytes for NOBITS expansion. Data may be inconsistent.\n",
128 (unsigned)ms->shdr.sh_size);
133 ms->shdr.sh_type = SHT_PROGBITS;
134 ms->shdr.sh_addr = endAddr;
138 injSpace += shiftStuffAtAfterOffset(me, endOff, nobitsize);
139 injSpace -= nobitsize;
140 injOffset += nobitsize;
141 last->phdr.p_filesz += nobitsize;
142 assert(last->phdr.p_filesz == last->phdr.p_memsz);
145 /* Calculate how much space we need, taking alignment into account */
146 size += ROUNDUP(injOffset, align) - injOffset;
148 /* If there is not enough space left, create even more. */
149 if (injSpace < size) {
150 injSpace += shiftStuffAtAfterOffset(me, injOffset, size - injSpace);
152 assert(injSpace >= size);
154 /* Remap ourselves */
155 last->phdr.p_filesz += size;
156 last->phdr.p_memsz += size;
158 injOffset = ROUNDUP(injOffset, align);
163 return last->phdr.p_vaddr + (injOffset - last->phdr.p_offset);
164 } else if (((w && (first->phdr.p_flags & PF_W))
165 || (x && (first->phdr.p_flags & PF_X)))
166 && /* Enough space to expand downwards? */
167 (first->phdr.p_vaddr > 3 * first->phdr.p_align)) {
168 /* Need to prepend or split up the PHDR. */
169 GElf_Off injOffset = OFFS_END(first->phdr.p_offset, first->phdr.p_filesz);
172 /* Round up size to take PHDR alignment into account.
173 * We assume that this is a multiple of the alignment asked for. */
174 assert(first->phdr.p_align >= align);
175 size = ROUNDUP(size, first->phdr.p_align);
177 /* Find first section. We assume there is at least one. */
178 assert(!CIRCLEQ_EMPTY(&first->childScnList));
179 injOffset = CIRCLEQ_FIRST(&first->childScnList)->shdr.sh_offset;
181 /* Move our sections */
182 CIRCLEQ_FOREACH(ms, &first->childScnList, elemChildScn) {
183 if (ms->shdr.sh_offset >= injOffset) {
184 ms->shdr.sh_offset += size;
189 CIRCLEQ_FOREACH(mp, &first->childPhdrList, elemChildPhdr) {
190 if (mp->phdr.p_offset >= injOffset) {
191 mp->phdr.p_offset += size;
193 mp->phdr.p_vaddr -= size;
194 mp->phdr.p_paddr -= size;
198 /* Move other PHDRs and sections */
199 assert(size <= shiftStuffAtAfterOffset(me, injOffset + 1, size));
201 /* Remap ourselves */
202 first->phdr.p_vaddr -= size;
203 first->phdr.p_paddr -= size;
204 first->phdr.p_filesz += size;
205 first->phdr.p_memsz += size;
207 injOffset = ROUNDUP(injOffset, align);
212 return first->phdr.p_vaddr + (injOffset - first->phdr.p_offset);
225 static int cmpPhdrOffs(const void *mp1, const void *mp2)
233 p1 = *(ElfuPhdr**)mp1;
234 p2 = *(ElfuPhdr**)mp2;
240 if (p1->phdr.p_offset < p2->phdr.p_offset) {
242 } else if (p1->phdr.p_offset == p2->phdr.p_offset) {
244 } else /* if (p1->phdr.p_offset > p2->phdr.p_offset) */ {
249 int elfu_mLayoutAuto(ElfuElf *me)
254 GElf_Off lastend = 0;
259 phdrArr = malloc(elfu_mPhdrCount(me) * sizeof(*phdrArr));
261 ELFU_WARN("elfu_mLayoutAuto: malloc failed for phdrArr.\n");
266 CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
267 if (mp->phdr.p_type != PT_LOAD) {
275 /* Assume we have at least one LOAD PHDR,
276 * and that it ends after EHDR and PHDRs */
279 /* Sort array by file offset */
280 qsort(phdrArr, i, sizeof(*phdrArr), cmpPhdrOffs);
282 lastend = OFFS_END(phdrArr[0]->phdr.p_offset, phdrArr[0]->phdr.p_filesz);
284 /* Wiggle offsets of 2nd, 3rd etc so take minimum space */
285 for (j = 1; j < i; j++) {
286 GElf_Off subalign = phdrArr[j]->phdr.p_offset % phdrArr[j]->phdr.p_align;
288 if ((lastend % phdrArr[j]->phdr.p_align) <= subalign) {
289 lastend += subalign - (lastend % phdrArr[j]->phdr.p_align);
291 lastend += phdrArr[j]->phdr.p_align - ((lastend % phdrArr[j]->phdr.p_align) - subalign);
294 phdrArr[j]->phdr.p_offset = lastend;
296 elfu_mPhdrUpdateChildOffsets(phdrArr[j]);
298 lastend = OFFS_END(phdrArr[j]->phdr.p_offset, phdrArr[j]->phdr.p_filesz);
304 /* Place orphaned sections afterwards, maintaining alignment */
305 CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
306 lastend = ROUNDUP(lastend, ms->shdr.sh_addralign);
308 ms->shdr.sh_offset = lastend;
310 lastend = OFFS_END(ms->shdr.sh_offset, SCNFILESIZE(&ms->shdr));
314 /* Move SHDRs to end */
315 lastend = ROUNDUP(lastend, 8);
316 me->ehdr.e_shoff = lastend;