3 #include <libelf/gelf.h>
4 #include <libelfu/libelfu.h>
7 // TODO: Take p_align into account
12 * Insert space at a given position in the file by moving everything
13 * after it towards the end of the file, and everything before it
14 * towards lower memory regions where it is mapped.
16 * off must not be in the middle of any data structure, such as
17 * PHDRs, SHDRs, or sections. Behaviour is undefined if it is.
19 * PHDRs will be patched such that everything AFTER off is mapped to
20 * the same address in memory, and everything BEFORE it is shifted to
21 * lower addresses, making space for the new data in-between.
23 GElf_Xword elfu_mInsertSpaceBefore(ElfuElf *me, GElf_Off off, GElf_Xword size)
30 /* Round up size to 4096 bytes to keep page alignment on x86 when
31 * remapping existing data to lower addresses. */
32 size += (4096 - (size % 4096)) % 4096;
33 // TODO: Find alignment size by checking p_align in PHDRs
35 /* Move SHDRs and PHDRs */
36 if (me->ehdr.e_shoff >= off) {
37 me->ehdr.e_shoff += size;
40 if (me->ehdr.e_phoff >= off) {
41 me->ehdr.e_phoff += size;
44 /* Patch PHDRs to include new data */
45 CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
46 GElf_Off end = mp->phdr.p_offset + mp->phdr.p_filesz;
48 if (mp->phdr.p_offset >= off) {
49 /* Insertion before PHDR's content, so it's just shifted */
50 mp->phdr.p_offset += size;
52 /* mp->phdr.p_offset < off */
55 /* Insertion in the middle of PHDR, so let it span the new data */
56 mp->phdr.p_filesz += size;
57 mp->phdr.p_memsz += size;
58 mp->phdr.p_vaddr -= size;
59 mp->phdr.p_paddr -= size;
61 /* Insertion after PHDR's content, so it may need to be
62 remapped. This will happen in a second pass.
68 /* For each LOAD header, find clashing headers that need to be
69 remapped to lower memory areas.
71 CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
72 if (mp->phdr.p_type == PT_LOAD) {
75 CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
76 if (mp2->phdr.p_type != PT_LOAD
77 && mp2->phdr.p_offset + mp2->phdr.p_filesz <= off) {
78 /* The PHDR ends in the file before the injection site */
79 GElf_Off vend1 = mp->phdr.p_vaddr + mp->phdr.p_memsz;
80 GElf_Off pend1 = mp->phdr.p_paddr + mp->phdr.p_memsz;
81 GElf_Off vend2 = mp2->phdr.p_vaddr + mp2->phdr.p_memsz;
82 GElf_Off pend2 = mp2->phdr.p_paddr + mp2->phdr.p_memsz;
84 /* If mp and mp2 now overlap in memory */
85 if ((mp2->phdr.p_vaddr < vend1 && vend2 > mp->phdr.p_vaddr)
86 || (mp2->phdr.p_paddr < pend1 && pend2 > mp->phdr.p_paddr)) {
87 /* Move mp2 down in memory, as mp has been resized.
88 Maintaining the relative offset between them is the best
89 guess at maintaining consistency.
91 mp2->phdr.p_vaddr -= size;
92 mp2->phdr.p_paddr -= size;
99 /* Move the sections themselves */
100 CIRCLEQ_FOREACH(ms, &me->scnList, elem) {
101 if (ms->shdr.sh_offset >= off) {
102 ms->shdr.sh_offset += size;
104 /* sh_offset < off */
106 /* If this was in a LOAD segment, it has been adjusted there
107 and this synchronises it.
108 If not, it doesn't matter anyway.
110 ms->shdr.sh_addr -= size;
120 * Insert space at a given position in the file by moving everything
121 * after it towards the end of the file, and towards higher memory
122 * regions where it is mapped.
124 * off must not be in the middle of any data structure, such as
125 * PHDRs, SHDRs, or sections. Behaviour is undefined if it is.
127 * PHDRs will be patched such that everything AFTER off is shifted to
128 * higher addresses, making space for the new data in-between.
130 * CAUTION: This also moves NOBITS sections. If such are present,
131 * use mExpandNobits() first and then inject at the end of
132 * the expansion site.
134 GElf_Xword elfu_mInsertSpaceAfter(ElfuElf *me, GElf_Off off, GElf_Xword size)
141 /* Move SHDRs and PHDRs */
142 if (me->ehdr.e_shoff >= off) {
143 me->ehdr.e_shoff += size;
146 if (me->ehdr.e_phoff >= off) {
147 me->ehdr.e_phoff += size;
150 /* Patch PHDRs to include new data */
151 CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
152 GElf_Off end = mp->phdr.p_offset + mp->phdr.p_filesz;
154 if (mp->phdr.p_offset >= off) {
155 /* Insertion before PHDR's content, so it's shifted. */
156 mp->phdr.p_offset += size;
158 /* It may also need to be remapped. See second pass below. */
160 /* mp->phdr.p_offset < off */
163 /* Insertion in the middle of PHDR, so let it span the new data */
164 mp->phdr.p_filesz += size;
165 mp->phdr.p_memsz += size;
167 /* Insertion after PHDR's content. Nothing to do. */
172 /* For each LOAD header, find clashing headers that need to be
173 remapped to higher memory areas.
175 CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
176 if (mp->phdr.p_type == PT_LOAD) {
179 CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
180 if (mp2->phdr.p_type != PT_LOAD
181 && mp2->phdr.p_offset + mp2->phdr.p_filesz > off) {
182 /* The PHDR now ends in the file after the injection site */
183 GElf_Off vend1 = mp->phdr.p_vaddr + mp->phdr.p_memsz;
184 GElf_Off pend1 = mp->phdr.p_paddr + mp->phdr.p_memsz;
185 GElf_Off vend2 = mp2->phdr.p_vaddr + mp2->phdr.p_memsz;
186 GElf_Off pend2 = mp2->phdr.p_paddr + mp2->phdr.p_memsz;
188 /* If mp and mp2 now overlap in memory */
189 if ((mp2->phdr.p_vaddr < vend1 && vend2 > mp->phdr.p_vaddr)
190 || (mp2->phdr.p_paddr < pend1 && pend2 > mp->phdr.p_paddr)) {
191 /* Move mp2 up in memory, as mp has been resized.
192 Maintaining the relative offset between them is the best
193 guess at maintaining consistency.
196 mp2->phdr.p_vaddr += size;
197 mp2->phdr.p_paddr += size;
204 /* Move the sections themselves */
205 CIRCLEQ_FOREACH(ms, &me->scnList, elem) {
206 if (ms->shdr.sh_offset >= off) {
207 ms->shdr.sh_offset += size;
209 /* If this was in a LOAD segment, it has been adjusted there
210 and this synchronises it.
211 If not, it doesn't matter anyway.
213 ms->shdr.sh_addr += size;
224 /* Update cross-references */
225 static void shiftSections(ElfuElf *me, ElfuScn *first)
228 size_t firstIndex = elfu_mScnIndex(me, first);
231 if (ms == me->shstrtab) {
232 me->ehdr.e_shstrndx++;
235 ms = CIRCLEQ_LOOP_NEXT(&me->scnList, ms, elem);
236 } while (ms != CIRCLEQ_FIRST(&me->scnList));
238 CIRCLEQ_FOREACH(ms, &me->scnList, elem) {
239 switch (ms->shdr.sh_type) {
242 if (ms->shdr.sh_info >= firstIndex) {
251 case SHT_GNU_verneed:
252 if (ms->shdr.sh_link >= firstIndex) {
261 * Insert a section into an ELF model, /before/ a given other section
263 void elfu_mInsertScnInChainBefore(ElfuElf *me, ElfuScn *oldscn, ElfuScn *newscn)
269 shiftSections(me, oldscn);
271 CIRCLEQ_INSERT_BEFORE(&me->scnList, oldscn, newscn, elem);
276 * Insert a section into an ELF model, /after/ a given other section
278 void elfu_mInsertScnInChainAfter(ElfuElf *me, ElfuScn *oldscn, ElfuScn *newscn)
284 if (oldscn != CIRCLEQ_LAST(&me->scnList)) {
285 shiftSections(me, CIRCLEQ_NEXT(oldscn, elem));
288 CIRCLEQ_INSERT_AFTER(&me->scnList, oldscn, newscn, elem);