Object file injection, first part
[centaur.git] / src / model / reladd.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <libelf/gelf.h>
5 #include <libelfu/libelfu.h>
6
7 typedef enum Destsegment {
8   DS_UNKNOWN,
9   DS_TEXT,
10   DS_DATA,
11 } Destsegment;
12
13
14 static Destsegment destsegment(ElfuScn *ms)
15 {
16   if (!(ms->shdr.sh_flags & SHF_ALLOC)) {
17     return DS_UNKNOWN;
18   }
19
20   if (!(ms->shdr.sh_flags & SHF_WRITE)
21       && (ms->shdr.sh_flags & SHF_EXECINSTR)) {
22     return DS_TEXT;
23   } else if ((ms->shdr.sh_flags & SHF_WRITE)
24              && !(ms->shdr.sh_flags & SHF_EXECINSTR)) {
25     return DS_DATA;
26   }
27
28   return DS_UNKNOWN;
29 }
30
31
32
33 static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *ms)
34 {
35   ElfuPhdr *mp;
36   ElfuPhdr *first = NULL;
37   ElfuPhdr *last = NULL;
38   ElfuScn *newscn = NULL;
39   ElfuPhdr *injAnchor;
40   int searchForCode = 0;
41
42   switch (destsegment(ms)) {
43     case DS_TEXT:
44       searchForCode = 1;
45     case DS_DATA:
46       newscn = elfu_mCloneScn(ms);
47       if (!newscn) {
48         return NULL;
49       }
50
51       /* Find first and last LOAD PHDRs. */
52       CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
53         if (mp->phdr.p_type != PT_LOAD) {
54           continue;
55         }
56
57         if (!first || mp->phdr.p_vaddr < first->phdr.p_vaddr) {
58           first = mp;
59         }
60         if (!last || mp->phdr.p_vaddr > last->phdr.p_vaddr) {
61           /* No need to check p_memsz as segments may not overlap in memory. */
62           last = mp;
63         }
64       }
65
66       if (searchForCode) {
67         if ((first->phdr.p_flags & PF_X) && !(first->phdr.p_flags & PF_W)) {
68           injAnchor = first;
69         } else if ((last->phdr.p_flags & PF_X) && !(last->phdr.p_flags & PF_W)) {
70           injAnchor = last;
71         } else {
72           injAnchor = NULL;
73         }
74       } else {
75         if ((first->phdr.p_flags & PF_W) && !(first->phdr.p_flags & PF_X)) {
76           injAnchor = first;
77         } else if ((last->phdr.p_flags & PF_W) && !(last->phdr.p_flags & PF_X)) {
78           injAnchor = last;
79         } else {
80           injAnchor = NULL;
81         }
82       }
83
84       if (!injAnchor) {
85         ELFU_WARN("insertSection: Could not find injection anchor.\n"
86                   "               It has to be the first or last segment in the memory image.\n");
87       } else {
88         GElf_Off injOffset;
89
90         /* If the anchor is first or last, insert before or after */
91         if (injAnchor == first) {
92           /* Find first section and inject before it */
93           ElfuScn *firstScn = elfu_mScnFirstInSegment(me, injAnchor);
94           if (!firstScn) {
95             ELFU_WARN("insertSection: mScnFirstInSegment failed.\n");
96
97             // TODO: Error handling
98           } else {
99             injOffset = firstScn->shdr.sh_offset;
100
101             /* Make space */
102             elfu_mInsertSpaceBefore(me, injOffset, ms->shdr.sh_size);
103
104             /* Update memory offset */
105             newscn->shdr.sh_addr = injAnchor->phdr.p_vaddr;
106
107             /* Insert into chain of sections */
108             elfu_mInsertScnInChainBefore(me, firstScn, newscn);
109           }
110         } else {
111           /* Find last section and inject after it */
112           ElfuScn *lastScn = elfu_mScnLastInSegment(me, injAnchor);
113           if (!lastScn) {
114             ELFU_WARN("insertSection: mScnLastInSegment failed.\n");
115
116             // TODO: Error handling
117           } else {
118             injOffset = lastScn->shdr.sh_offset + elfu_gScnSizeFile(&lastScn->shdr);
119
120             /* Expand NOBITS sections at injection site, if any. */
121             elfu_mExpandNobits(me, injOffset);
122
123             /* Recalculate injOffset in case we expanded a NOBITS section */
124             lastScn = elfu_mScnLastInSegment(me, injAnchor);
125             injOffset = lastScn->shdr.sh_offset + elfu_gScnSizeFile(&lastScn->shdr);
126
127             /* Make space */
128             elfu_mInsertSpaceAfter(me, injOffset, ms->shdr.sh_size);
129
130             /* Update memory offset */
131             newscn->shdr.sh_addr = injAnchor->phdr.p_vaddr + (injOffset - injAnchor->phdr.p_offset);
132
133             /* Insert into chain of sections */
134             elfu_mInsertScnInChainAfter(me, lastScn, newscn);
135           }
136         }
137
138         /* Update file offset in new section BEFORE we do anything else */
139         newscn->shdr.sh_offset = injOffset;
140
141         /* Inject name */
142         // TODO
143         newscn->shdr.sh_name = 0;
144
145         // TODO: Relocate
146
147         return newscn;
148       }
149       break;
150
151     case DS_UNKNOWN:
152       ELFU_WARN("insertSection: Don't know where to insert ' %s with flags %jd (type %d).\n",
153                 elfu_mScnName(mrel, ms),
154                 ms->shdr.sh_flags,
155                 ms->shdr.sh_type);
156     default:
157       ELFU_WARN("insertSection: Skipping section %s with flags %jd (type %d).\n",
158                 elfu_mScnName(mrel, ms),
159                 ms->shdr.sh_flags,
160                 ms->shdr.sh_type);
161       return NULL;
162   }
163
164   if (newscn) {
165     // TODO: Destroy newscn
166   }
167   return NULL;
168 }
169
170
171
172 void elfu_mReladd(ElfuElf *me, ElfuElf *mrel)
173 {
174   ElfuScn *ms;
175
176   assert(me);
177   assert(mrel);
178
179
180   /* For each section in object file, guess how to insert it */
181   CIRCLEQ_FOREACH(ms, &mrel->scnList, elem) {
182     ElfuScn *newscn;
183
184     switch(ms->shdr.sh_type) {
185       case SHT_NULL: /* 0 */
186         continue;
187
188       case SHT_PROGBITS: /* 1 */
189         /* Find a place where it belongs and shove it in. */
190         newscn = insertSection(me, mrel, ms);
191         if (!newscn) {
192           ELFU_WARN("mReladd: Could not insert section %s (type %d), skipping.\n",
193                     elfu_mScnName(mrel, ms),
194                     ms->shdr.sh_type);
195         }
196         break;
197
198       case SHT_SYMTAB: /* 2 */
199       case SHT_DYNSYM: /* 11 */
200         /* Merge with the existing table. Take care of string tables also. */
201
202       case SHT_STRTAB: /* 3 */
203         /* May have to be merged with the existing string table for
204          * the symbol table. */
205
206       case SHT_RELA: /* 4 */
207       case SHT_REL: /* 9 */
208         /* Possibly append this in memory to the section model
209          * that it describes. */
210
211       case SHT_NOBITS: /* 8 */
212         /* Expand this to SHT_PROGBITS, then insert as such. */
213
214       case SHT_HASH: /* 5 */
215       case SHT_DYNAMIC: /* 6 */
216       case SHT_SHLIB: /* 10 */
217       case SHT_SYMTAB_SHNDX: /* 18 */
218
219       /* Don't care about the next ones yet. I've never seen
220        * them and they can be implemented when necessary. */
221       case SHT_NOTE: /* 7 */
222       case SHT_INIT_ARRAY: /* 14 */
223       case SHT_FINI_ARRAY: /* 15 */
224       case SHT_PREINIT_ARRAY: /* 16 */
225       case SHT_GROUP: /* 17 */
226       case SHT_NUM: /* 19 */
227       default:
228         ELFU_WARN("mReladd: Skipping section %s (type %d).\n",
229                   elfu_mScnName(mrel, ms),
230                   ms->shdr.sh_type);
231     }
232   }
233 }