549afcfe46c42013851016f7f46468cffda2dfc4
[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             ELFU_INFO("Inserting %s at offset 0x%jx...\n",
102                       elfu_mScnName(mrel, ms),
103                       injOffset);
104
105             /* Make space */
106             elfu_mInsertSpaceBefore(me, injOffset, ms->shdr.sh_size);
107
108             /* Update memory offset */
109             newscn->shdr.sh_addr = injAnchor->phdr.p_vaddr;
110
111             /* Insert into chain of sections */
112             elfu_mInsertScnInChainBefore(me, firstScn, newscn);
113           }
114         } else {
115           /* Find last section and inject after it */
116           ElfuScn *lastScn = elfu_mScnLastInSegment(me, injAnchor);
117           if (!lastScn) {
118             ELFU_WARN("insertSection: mScnLastInSegment failed.\n");
119
120             // TODO: Error handling
121           } else {
122             injOffset = lastScn->shdr.sh_offset + SCNFILESIZE(&lastScn->shdr);
123
124             ELFU_INFO("Expanding at offset 0x%jx...\n",
125                       injOffset);
126
127             /* Expand NOBITS sections at injection site, if any. */
128             elfu_mExpandNobits(me, injOffset);
129
130             /* Recalculate injOffset in case we expanded a NOBITS section */
131             lastScn = elfu_mScnLastInSegment(me, injAnchor);
132             injOffset = lastScn->shdr.sh_offset + SCNFILESIZE(&lastScn->shdr);
133
134             ELFU_INFO("Inserting %s at offset 0x%jx...\n",
135                       elfu_mScnName(mrel, ms),
136                       injOffset);
137
138             /* Make space */
139             elfu_mInsertSpaceAfter(me, injOffset, ms->shdr.sh_size);
140
141             /* Update memory offset */
142             newscn->shdr.sh_addr = injAnchor->phdr.p_vaddr + (injOffset - injAnchor->phdr.p_offset);
143
144             /* Insert into chain of sections */
145             elfu_mInsertScnInChainAfter(me, lastScn, newscn);
146           }
147         }
148
149         /* Update file offset in new section BEFORE we do anything else */
150         newscn->shdr.sh_offset = injOffset;
151
152         /* Inject name */
153         // TODO
154         newscn->shdr.sh_name = 0;
155
156         // TODO: Relocate
157
158         return newscn;
159       }
160       break;
161
162     case DS_UNKNOWN:
163       ELFU_WARN("insertSection: Don't know where to insert ' %s with flags %jd (type %d).\n",
164                 elfu_mScnName(mrel, ms),
165                 ms->shdr.sh_flags,
166                 ms->shdr.sh_type);
167     default:
168       ELFU_WARN("insertSection: Skipping section %s with flags %jd (type %d).\n",
169                 elfu_mScnName(mrel, ms),
170                 ms->shdr.sh_flags,
171                 ms->shdr.sh_type);
172       return NULL;
173   }
174
175   if (newscn) {
176     // TODO: Destroy newscn
177   }
178   return NULL;
179 }
180
181
182
183 void elfu_mReladd(ElfuElf *me, ElfuElf *mrel)
184 {
185   ElfuScn *ms;
186
187   assert(me);
188   assert(mrel);
189
190
191   /* For each section in object file, guess how to insert it */
192   CIRCLEQ_FOREACH(ms, &mrel->scnList, elem) {
193     ElfuScn *newscn;
194
195     switch(ms->shdr.sh_type) {
196       case SHT_NULL: /* 0 */
197         continue;
198
199       case SHT_PROGBITS: /* 1 */
200         /* Find a place where it belongs and shove it in. */
201         newscn = insertSection(me, mrel, ms);
202         if (!newscn) {
203           ELFU_WARN("mReladd: Could not insert section %s (type %d), skipping.\n",
204                     elfu_mScnName(mrel, ms),
205                     ms->shdr.sh_type);
206         }
207         break;
208
209       case SHT_SYMTAB: /* 2 */
210       case SHT_DYNSYM: /* 11 */
211         /* Merge with the existing table. Take care of string tables also. */
212
213       case SHT_STRTAB: /* 3 */
214         /* May have to be merged with the existing string table for
215          * the symbol table. */
216
217       case SHT_RELA: /* 4 */
218       case SHT_REL: /* 9 */
219         /* Possibly append this in memory to the section model
220          * that it describes. */
221
222       case SHT_NOBITS: /* 8 */
223         /* Expand this to SHT_PROGBITS, then insert as such. */
224
225       case SHT_HASH: /* 5 */
226       case SHT_DYNAMIC: /* 6 */
227       case SHT_SHLIB: /* 10 */
228       case SHT_SYMTAB_SHNDX: /* 18 */
229
230       /* Don't care about the next ones yet. I've never seen
231        * them and they can be implemented when necessary. */
232       case SHT_NOTE: /* 7 */
233       case SHT_INIT_ARRAY: /* 14 */
234       case SHT_FINI_ARRAY: /* 15 */
235       case SHT_PREINIT_ARRAY: /* 16 */
236       case SHT_GROUP: /* 17 */
237       case SHT_NUM: /* 19 */
238       default:
239         ELFU_WARN("mReladd: Skipping section %s (type %d).\n",
240                   elfu_mScnName(mrel, ms),
241                   ms->shdr.sh_type);
242     }
243   }
244 }