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,
80 ElfuPhdr *highestAddr;
82 ElfuPhdr *highestOffsEnd;
87 /* Treat read-only data as executable.
88 * That's what the GNU toolchain does on x86. */
93 /* Find first and last LOAD PHDRs. */
94 elfu_mPhdrLoadLowestHighest(me, &lowestAddr, &highestAddr,
95 &lowestOffs, &highestOffsEnd);
97 if (0 && ((w && (highestAddr->phdr.p_flags & PF_W))
98 || (x && (highestAddr->phdr.p_flags & PF_X)))
99 /* Merging only works if the LOAD is the last both in file and mem */
100 && highestAddr == highestOffsEnd) {
101 /* Need to append. */
102 GElf_Off injOffset = OFFS_END(highestAddr->phdr.p_offset, highestAddr->phdr.p_filesz);
103 GElf_Word injSpace = 0;
104 GElf_Word nobitsize = highestAddr->phdr.p_memsz - highestAddr->phdr.p_filesz;
106 /* Expand NOBITS if any */
108 GElf_Off endOff = OFFS_END(highestAddr->phdr.p_offset, highestAddr->phdr.p_filesz);
109 GElf_Off endAddr = OFFS_END(highestAddr->phdr.p_vaddr, highestAddr->phdr.p_filesz);
112 ELFU_INFO("Expanding NOBITS at address 0x%x...\n", (unsigned)endAddr);
114 CIRCLEQ_FOREACH(ms, &highestAddr->childScnList, elemChildScn) {
115 if (ms->shdr.sh_offset == endOff) {
116 assert(ms->shdr.sh_type == SHT_NOBITS);
117 assert(ms->shdr.sh_size == nobitsize);
118 ms->databuf = malloc(ms->shdr.sh_size);
119 memset(ms->databuf, '\0', ms->shdr.sh_size);
121 ELFU_WARN("mExpandNobits: Could not allocate %u bytes for NOBITS expansion. Data may be inconsistent.\n",
122 (unsigned)ms->shdr.sh_size);
127 ms->shdr.sh_type = SHT_PROGBITS;
128 ms->shdr.sh_addr = endAddr;
132 injSpace += shiftStuffAtAfterOffset(me, endOff, nobitsize);
133 injSpace -= nobitsize;
134 injOffset += nobitsize;
135 highestAddr->phdr.p_filesz += nobitsize;
136 assert(highestAddr->phdr.p_filesz == highestAddr->phdr.p_memsz);
139 /* Calculate how much space we need, taking alignment into account */
140 size += ROUNDUP(injOffset, align) - injOffset;
142 /* If there is not enough space left, create even more. */
143 if (injSpace < size) {
144 injSpace += shiftStuffAtAfterOffset(me, injOffset, size - injSpace);
146 assert(injSpace >= size);
148 /* Remap ourselves */
149 highestAddr->phdr.p_filesz += size;
150 highestAddr->phdr.p_memsz += size;
152 injOffset = ROUNDUP(injOffset, align);
155 *injPhdr = highestAddr;
157 return highestAddr->phdr.p_vaddr + (injOffset - highestAddr->phdr.p_offset);
158 } else if (0 && ((w && (lowestAddr->phdr.p_flags & PF_W))
159 || (x && (lowestAddr->phdr.p_flags & PF_X)))
160 && /* Enough space to expand downwards? */
161 (lowestAddr->phdr.p_vaddr > 3 * lowestAddr->phdr.p_align)
162 /* Merging only works if the LOAD is the first both in file and mem */
163 && lowestAddr == lowestOffs) {
164 /* Need to prepend or split up the PHDR. */
165 GElf_Off injOffset = OFFS_END(lowestAddr->phdr.p_offset,
166 lowestAddr->phdr.p_filesz);
169 /* Round up size to take PHDR alignment into account.
170 * We assume that this is a multiple of the alignment asked for. */
171 assert(lowestAddr->phdr.p_align >= align);
172 size = ROUNDUP(size, lowestAddr->phdr.p_align);
174 /* Find first section. We assume there is at least one. */
175 assert(!CIRCLEQ_EMPTY(&lowestAddr->childScnList));
176 injOffset = CIRCLEQ_FIRST(&lowestAddr->childScnList)->shdr.sh_offset;
178 /* Move our sections */
179 CIRCLEQ_FOREACH(ms, &lowestAddr->childScnList, elemChildScn) {
180 if (ms->shdr.sh_offset >= injOffset) {
181 ms->shdr.sh_offset += size;
186 CIRCLEQ_FOREACH(mp, &lowestAddr->childPhdrList, elemChildPhdr) {
187 if (mp->phdr.p_offset >= injOffset) {
188 mp->phdr.p_offset += size;
190 mp->phdr.p_vaddr -= size;
191 mp->phdr.p_paddr -= size;
195 /* Move other PHDRs and sections */
196 assert(size <= shiftStuffAtAfterOffset(me, injOffset + 1, size));
198 /* Remap ourselves */
199 lowestAddr->phdr.p_vaddr -= size;
200 lowestAddr->phdr.p_paddr -= size;
201 lowestAddr->phdr.p_filesz += size;
202 lowestAddr->phdr.p_memsz += size;
204 injOffset = ROUNDUP(injOffset, align);
207 *injPhdr = lowestAddr;
209 return lowestAddr->phdr.p_vaddr + (injOffset - lowestAddr->phdr.p_offset);
223 int elfu_mLayoutAuto(ElfuElf *me)
225 ElfuPhdr *lowestAddr;
226 ElfuPhdr *highestAddr;
227 ElfuPhdr *lowestOffs;
228 ElfuPhdr *highestOffsEnd;
232 GElf_Off lastend = 0;
236 /* Find first and last LOAD PHDRs. */
237 elfu_mPhdrLoadLowestHighest(me, &lowestAddr, &highestAddr,
238 &lowestOffs, &highestOffsEnd);
240 phdrArr = malloc(elfu_mPhdrCount(me) * sizeof(*phdrArr));
242 ELFU_WARN("elfu_mLayoutAuto: malloc failed for phdrArr.\n");
247 lastend = OFFS_END(highestOffsEnd->phdr.p_offset, highestOffsEnd->phdr.p_filesz);
250 /* If PHDRs are not mapped into memory, place them after LOAD segments. */
251 mp = elfu_mPhdrByOffset(me, me->ehdr.e_phoff);
253 lastend = ROUNDUP(lastend, 8);
254 me->ehdr.e_phoff = lastend;
255 lastend += me->ehdr.e_phnum * me->ehdr.e_phentsize;
257 /* Update size of PHDR PHDR */
260 CIRCLEQ_FOREACH(phdrmp, &mp->childPhdrList, elemChildPhdr) {
261 if (phdrmp->phdr.p_type == PT_PHDR) {
262 phdrmp->phdr.p_filesz = elfu_mPhdrCount(me) * me->ehdr.e_phentsize;
263 phdrmp->phdr.p_memsz = elfu_mPhdrCount(me) * me->ehdr.e_phentsize;
269 /* Place orphaned sections afterwards, maintaining alignment */
270 CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
271 lastend = ROUNDUP(lastend, ms->shdr.sh_addralign);
273 ms->shdr.sh_offset = lastend;
275 lastend = OFFS_END(ms->shdr.sh_offset, SCNFILESIZE(&ms->shdr));
279 /* Move SHDRs to end */
280 lastend = ROUNDUP(lastend, 8);
281 me->ehdr.e_shoff = lastend;