5 #include <libelf/gelf.h>
6 #include <libelfu/libelfu.h>
10 static GElf_Word makeSpaceAtOffset(ElfuElf *me, GElf_Off offset, GElf_Word size)
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) {
47 mp->phdr.p_offset += size;
48 CIRCLEQ_FOREACH(mpc, &mp->childPhdrList, elemChildPhdr) {
49 mpc->phdr.p_offset += size;
51 CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) {
52 ms->shdr.sh_offset += size;
57 CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
58 if (ms->shdr.sh_offset >= offset) {
59 ms->shdr.sh_offset += size;
63 if (me->ehdr.e_phoff >= offset) {
64 me->ehdr.e_phoff += size;
67 if (me->ehdr.e_shoff >= offset) {
68 me->ehdr.e_shoff += size;
76 /* Finds a suitable PHDR to insert a hole into and expands it
78 * Returns memory address the hole will be mapped to, or 0 if
79 * the operation failed. */
80 static GElf_Addr getSpaceInPhdr(ElfuElf *me, GElf_Word size,
81 GElf_Word align, int w, int x,
84 ElfuPhdr *first = NULL;
85 ElfuPhdr *last = NULL;
90 /* Find first and last LOAD PHDRs.
91 * Don't compare p_memsz - segments don't overlap in memory. */
92 CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
93 if (mp->phdr.p_type != PT_LOAD) {
96 if (!first || mp->phdr.p_vaddr < first->phdr.p_vaddr) {
99 if (!last || mp->phdr.p_vaddr > last->phdr.p_vaddr) {
104 if ((w && (last->phdr.p_flags & PF_W))
105 || (x && (last->phdr.p_flags & PF_X))) {
106 /* Need to append. */
107 GElf_Off injOffset = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz);
108 GElf_Word injSpace = 0;
109 GElf_Word nobitsize = last->phdr.p_memsz - last->phdr.p_filesz;
111 /* Expand NOBITS if any */
113 GElf_Off endOff = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz);
114 GElf_Off endAddr = OFFS_END(last->phdr.p_vaddr, last->phdr.p_filesz);
117 ELFU_INFO("Expanding NOBITS at address 0x%jx...\n", endAddr);
119 CIRCLEQ_FOREACH(ms, &last->childScnList, elemChildScn) {
120 if (ms->shdr.sh_offset == endOff) {
121 assert(ms->shdr.sh_type == SHT_NOBITS);
122 assert(ms->shdr.sh_size == nobitsize);
123 ms->data.d_buf = malloc(ms->shdr.sh_size);
124 memset(ms->data.d_buf, '\0', ms->shdr.sh_size);
125 if (!ms->data.d_buf) {
126 ELFU_WARN("mExpandNobits: Could not allocate %jd bytes for NOBITS expansion. Data may be inconsistent.\n", ms->shdr.sh_size);
131 ms->data.d_align = 1;
133 ms->data.d_type = ELF_T_BYTE;
134 ms->data.d_size = ms->shdr.sh_size;
135 ms->data.d_version = elf_version(EV_NONE);
137 ms->shdr.sh_type = SHT_PROGBITS;
138 ms->shdr.sh_addr = endAddr;
142 injSpace += makeSpaceAtOffset(me, endOff, nobitsize);
143 injSpace -= nobitsize;
144 injOffset += nobitsize;
145 last->phdr.p_filesz += nobitsize;
146 assert(last->phdr.p_filesz == last->phdr.p_memsz);
149 /* Calculate how much space we need, taking alignment into account */
150 size += ROUNDUP(injOffset, align) - injOffset;
152 /* If there is not enough space left, create even more. */
153 if (injSpace < size) {
154 injSpace += makeSpaceAtOffset(me, injOffset, size - injSpace);
156 assert(injSpace >= size);
158 /* Remap ourselves */
159 last->phdr.p_filesz += size;
160 last->phdr.p_memsz += size;
162 injOffset = ROUNDUP(injOffset, align);
167 return last->phdr.p_vaddr + (injOffset - last->phdr.p_offset);
168 } else if ((w && (first->phdr.p_flags & PF_W))
169 || (x && (first->phdr.p_flags & PF_X))) {
170 /* Need to prepend or split up the PHDR. */
171 GElf_Off injOffset = OFFS_END(first->phdr.p_offset, first->phdr.p_filesz);
174 /* Round up size to take PHDR alignment into account.
175 * We assume that this is a multiple of the alignment asked for. */
176 assert(first->phdr.p_align >= align);
177 size = ROUNDUP(size, first->phdr.p_align);
179 /* Find first section. We assume there is at least one. */
180 assert(!CIRCLEQ_EMPTY(&first->childScnList));
181 injOffset = CIRCLEQ_FIRST(&first->childScnList)->shdr.sh_offset;
183 /* Move our sections */
184 CIRCLEQ_FOREACH(ms, &first->childScnList, elemChildScn) {
185 if (ms->shdr.sh_offset >= injOffset) {
186 ms->shdr.sh_offset += size;
191 CIRCLEQ_FOREACH(mp, &first->childPhdrList, elemChildPhdr) {
192 if (mp->phdr.p_offset >= injOffset) {
193 mp->phdr.p_offset += size;
195 mp->phdr.p_vaddr -= size;
196 mp->phdr.p_paddr -= size;
200 /* Move other PHDRs and sections */
201 assert(size <= makeSpaceAtOffset(me, injOffset, size));
203 /* Remap ourselves */
204 first->phdr.p_vaddr -= size;
205 first->phdr.p_paddr -= size;
206 first->phdr.p_filesz += size;
207 first->phdr.p_memsz += size;
209 injOffset = ROUNDUP(injOffset, align);
214 return first->phdr.p_vaddr + (injOffset - first->phdr.p_offset);
227 static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn)
229 ElfuScn *newscn = NULL;
234 if (oldscn->shdr.sh_flags & SHF_ALLOC) {
235 newscn = elfu_mCloneScn(oldscn);
241 injAddr = getSpaceInPhdr(me, newscn->shdr.sh_size,
242 newscn->shdr.sh_addralign,
243 newscn->shdr.sh_flags & SHF_WRITE,
244 newscn->shdr.sh_flags & SHF_EXECINSTR,
248 ELFU_WARN("insertSection: Could not find a place to insert section.\n");
252 ELFU_INFO("Inserting %s at address 0x%jx...\n",
253 elfu_mScnName(mrel, oldscn),
256 injOffset = injAddr - injPhdr->phdr.p_vaddr + injPhdr->phdr.p_offset;
258 newscn->shdr.sh_addr = injAddr;
259 newscn->shdr.sh_offset = injOffset;
261 if (CIRCLEQ_EMPTY(&injPhdr->childScnList)
262 || CIRCLEQ_LAST(&injPhdr->childScnList)->shdr.sh_offset < injOffset) {
263 CIRCLEQ_INSERT_TAIL(&injPhdr->childScnList, newscn, elemChildScn);
266 CIRCLEQ_FOREACH(ms, &injPhdr->childScnList, elemChildScn) {
267 if (injOffset < ms->shdr.sh_offset) {
268 CIRCLEQ_INSERT_BEFORE(&injPhdr->childScnList, ms, newscn, elemChildScn);
276 newscn->shdr.sh_name = 0;
282 ELFU_WARN("insertSection: Skipping section %s with flags %jd (type %d).\n",
283 elfu_mScnName(mrel, oldscn),
284 oldscn->shdr.sh_flags,
285 oldscn->shdr.sh_type);
291 // TODO: Destroy newscn
297 int subScnAdd1(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2)
300 ElfuElf *me = (ElfuElf*)aux1;
304 switch(ms->shdr.sh_type) {
305 case SHT_PROGBITS: /* 1 */
306 /* Find a place where it belongs and shove it in. */
307 newscn = insertSection(me, mrel, ms);
309 ELFU_WARN("mReladd: Could not insert section %s (type %d), skipping.\n",
310 elfu_mScnName(mrel, ms),
320 int subScnAdd2(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2)
323 ElfuElf *me = (ElfuElf*)aux1;
326 switch(ms->shdr.sh_type) {
327 case SHT_NULL: /* 0 */
328 case SHT_PROGBITS: /* 1 */
331 case SHT_SYMTAB: /* 2 */
332 case SHT_DYNSYM: /* 11 */
333 /* Merge with the existing table. Take care of string tables also. */
335 case SHT_STRTAB: /* 3 */
336 /* May have to be merged with the existing string table for
337 * the symbol table. */
339 case SHT_RELA: /* 4 */
340 case SHT_REL: /* 9 */
341 /* Possibly append this in memory to the section model
342 * that it describes. */
344 case SHT_NOBITS: /* 8 */
345 /* Expand this to SHT_PROGBITS, then insert as such. */
347 case SHT_HASH: /* 5 */
348 case SHT_DYNAMIC: /* 6 */
349 case SHT_SHLIB: /* 10 */
350 case SHT_SYMTAB_SHNDX: /* 18 */
352 /* Don't care about the next ones yet. I've never seen
353 * them and they can be implemented when necessary. */
354 case SHT_NOTE: /* 7 */
355 case SHT_INIT_ARRAY: /* 14 */
356 case SHT_FINI_ARRAY: /* 15 */
357 case SHT_PREINIT_ARRAY: /* 16 */
358 case SHT_GROUP: /* 17 */
359 case SHT_NUM: /* 19 */
361 ELFU_WARN("mReladd: Skipping section %s (type %d).\n",
362 elfu_mScnName(mrel, ms),
370 void elfu_mReladd(ElfuElf *me, ElfuElf *mrel)
375 /* For each section in object file, guess how to insert it */
376 elfu_mScnForall(mrel, subScnAdd1, me, NULL);
378 /* Do relocations and other stuff */
379 elfu_mScnForall(mrel, subScnAdd2, me, NULL);