Typo
[centaur.git] / src / model / insert.c
1 #include <assert.h>
2 #include <sys/types.h>
3 #include <gelf.h>
4 #include <libelfu/libelfu.h>
5
6
7 // TODO: Take p_align into account
8
9
10
11 /*
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.
15  *
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.
18  *
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.
22  */
23 GElf_Xword elfu_mInsertBefore(ElfuElf *me, GElf_Off off, GElf_Xword size)
24 {
25   ElfuScn *ms;
26   ElfuPhdr *mp;
27
28   assert(me);
29
30   /* Move SHDRs and PHDRs */
31   if (me->ehdr.e_shoff >= off) {
32     me->ehdr.e_shoff += size;
33   }
34
35   if (me->ehdr.e_phoff >= off) {
36     me->ehdr.e_phoff += size;
37   }
38
39   /* Patch PHDRs to include new data */
40   CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
41     GElf_Off end = mp->phdr.p_offset + mp->phdr.p_filesz;
42
43     if (mp->phdr.p_offset >= off) {
44       /* Insertion before PHDR's content, so it's just shifted */
45       mp->phdr.p_offset += size;
46     } else {
47       /* mp->phdr.p_offset < off */
48
49       if (off < end) {
50         /* Insertion in the middle of PHDR, so let it span the new data */
51         mp->phdr.p_filesz += size;
52         mp->phdr.p_memsz += size;
53         mp->phdr.p_vaddr -= size;
54         mp->phdr.p_paddr -= size;
55       } else {
56         /* Insertion after PHDR's content, so it may need to be
57            remapped. This will happen in a second pass.
58          */
59       }
60     }
61   }
62
63   /* For each LOAD header, find clashing headers that need to be
64      remapped to lower memory areas.
65    */
66   CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
67     if (mp->phdr.p_type == PT_LOAD) {
68       ElfuPhdr *mp2;
69
70       CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
71         if (mp2->phdr.p_type != PT_LOAD
72             && mp2->phdr.p_offset + mp2->phdr.p_filesz <= off) {
73           /* The PHDR ends in the file before the injection site */
74           GElf_Off vend1 = mp->phdr.p_vaddr + mp->phdr.p_memsz;
75           GElf_Off pend1 = mp->phdr.p_paddr + mp->phdr.p_memsz;
76           GElf_Off vend2 = mp2->phdr.p_vaddr + mp2->phdr.p_memsz;
77           GElf_Off pend2 = mp2->phdr.p_paddr + mp2->phdr.p_memsz;
78
79           /* If mp and mp2 now overlap in memory */
80           if ((mp2->phdr.p_vaddr < vend1 && vend2 > mp->phdr.p_vaddr)
81               || (mp2->phdr.p_paddr < pend1 && pend2 > mp->phdr.p_paddr)) {
82             /* Move mp2 down in memory, as mp has been resized.
83                Maintaining the relative offset between them is the best
84                guess at maintaining consistency.
85              */
86             mp2->phdr.p_vaddr -= size;
87             mp2->phdr.p_paddr -= size;
88           }
89         }
90       }
91     }
92   }
93
94   /* Move the sections themselves */
95   CIRCLEQ_FOREACH(ms, &me->scnList, elem) {
96     if (ms->shdr.sh_offset >= off) {
97       ms->shdr.sh_offset += size;
98     } else {
99       /* sh_offset < off */
100
101       /* If this was in a LOAD segment, it has been adjusted there
102          and this synchronises it.
103          If not, it doesn't matter anyway.
104        */
105       ms->shdr.sh_addr -= size;
106     }
107   }
108
109   return size;
110 }
111
112
113
114 /*
115  * Insert space at a given position in the file by moving everything
116  * after it towards the end of the file, and towards higher memory
117  * regions where it is mapped.
118  *
119  * off must not be in the middle of any data structure, such as
120  * PHDRs, SHDRs, or sections. Behaviour is undefined if it is.
121  *
122  * PHDRs will be patched such that everything AFTER off is shifted to
123  * higher addresses, making space for the new data in-between.
124  */
125 GElf_Xword elfu_mInsertAfter(ElfuElf *me, GElf_Off off, GElf_Xword size)
126 {
127   ElfuScn *ms;
128   ElfuPhdr *mp;
129
130   assert(me);
131
132 /* Move SHDRs and PHDRs */
133   if (me->ehdr.e_shoff >= off) {
134     me->ehdr.e_shoff += size;
135   }
136
137   if (me->ehdr.e_phoff >= off) {
138     me->ehdr.e_phoff += size;
139   }
140
141   /* Patch PHDRs to include new data */
142   CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
143     GElf_Off end = mp->phdr.p_offset + mp->phdr.p_filesz;
144
145     if (mp->phdr.p_offset >= off) {
146       /* Insertion before PHDR's content, so it's shifted. */
147       mp->phdr.p_offset += size;
148
149       /* It may also need to be remapped. See second pass below. */
150     } else {
151       /* mp->phdr.p_offset < off */
152
153       if (off < end) {
154         /* Insertion in the middle of PHDR, so let it span the new data */
155         mp->phdr.p_filesz += size;
156         mp->phdr.p_memsz += size;
157       } else {
158         /* Insertion after PHDR's content. Nothing to do. */
159       }
160     }
161   }
162
163   /* For each LOAD header, find clashing headers that need to be
164      remapped to higher memory areas.
165    */
166   CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
167     if (mp->phdr.p_type == PT_LOAD) {
168       ElfuPhdr *mp2;
169
170       CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
171         if (mp2->phdr.p_type != PT_LOAD
172             && mp2->phdr.p_offset + mp2->phdr.p_filesz > off) {
173           /* The PHDR now ends in the file after the injection site */
174           GElf_Off vend1 = mp->phdr.p_vaddr + mp->phdr.p_memsz;
175           GElf_Off pend1 = mp->phdr.p_paddr + mp->phdr.p_memsz;
176           GElf_Off vend2 = mp2->phdr.p_vaddr + mp2->phdr.p_memsz;
177           GElf_Off pend2 = mp2->phdr.p_paddr + mp2->phdr.p_memsz;
178
179           /* If mp and mp2 now overlap in memory */
180           if ((mp2->phdr.p_vaddr < vend1 && vend2 > mp->phdr.p_vaddr)
181               || (mp2->phdr.p_paddr < pend1 && pend2 > mp->phdr.p_paddr)) {
182             /* Move mp2 up in memory, as mp has been resized.
183                Maintaining the relative offset between them is the best
184                guess at maintaining consistency.
185              */
186
187             mp2->phdr.p_vaddr += size;
188             mp2->phdr.p_paddr += size;
189           }
190         }
191       }
192     }
193   }
194
195   /* Move the sections themselves */
196   CIRCLEQ_FOREACH(ms, &me->scnList, elem) {
197     if (ms->shdr.sh_offset >= off) {
198       ms->shdr.sh_offset += size;
199
200       /* If this was in a LOAD segment, it has been adjusted there
201          and this synchronises it.
202          If not, it doesn't matter anyway.
203        */
204       ms->shdr.sh_addr += size;
205     }
206   }
207
208   return size;
209 }