Implement orphaned sections
[centaur.git] / src / model / fromFile.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <libelf/libelf.h>
6 #include <libelf/gelf.h>
7 #include <libelfu/libelfu.h>
8
9
10 static int cmpScnOffs(const void *ms1, const void *ms2)
11 {
12   assert(ms1);
13   assert(ms2);
14
15   ElfuScn *s1 = *(ElfuScn**)ms1;
16   ElfuScn *s2 = *(ElfuScn**)ms2;
17
18   assert(s1);
19   assert(s2);
20
21
22   if (s1->shdr.sh_offset < s2->shdr.sh_offset) {
23     return -1;
24   } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) {
25     return 0;
26   } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ {
27     return 1;
28   }
29 }
30
31
32
33 static ElfuPhdr* parentPhdr(ElfuElf *me, ElfuScn *ms)
34 {
35   ElfuPhdr *mp;
36
37   assert(me);
38   assert(ms);
39
40   CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
41     if (mp->phdr.p_type != PT_LOAD) {
42       continue;
43     }
44
45     if (ms->shdr.sh_addr >= mp->phdr.p_vaddr
46         && OFFS_END(ms->shdr.sh_addr, ms->shdr.sh_size) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) {
47       return mp;
48     } else if (ms->shdr.sh_offset >= mp->phdr.p_offset
49                && OFFS_END(ms->shdr.sh_offset, SCNFILESIZE(&ms->shdr)) <= OFFS_END(mp->phdr.p_offset, mp->phdr.p_filesz)
50                && OFFS_END(ms->shdr.sh_offset, ms->shdr.sh_size) <= OFFS_END(mp->phdr.p_offset, mp->phdr.p_memsz)) {
51       return mp;
52     }
53   }
54
55   return NULL;
56 }
57
58
59 static ElfuPhdr* modelFromPhdr(GElf_Phdr *phdr)
60 {
61   ElfuPhdr *mp;
62
63   assert(phdr);
64
65   mp = malloc(sizeof(ElfuPhdr));
66   if (!mp) {
67     ELFU_WARN("modelFromPhdr: malloc() failed for ElfuPhdr.\n");
68     return NULL;
69   }
70
71   mp->phdr = *phdr;
72
73   CIRCLEQ_INIT(&mp->childScnList);
74   CIRCLEQ_INIT(&mp->childPhdrList);
75
76   return mp;
77 }
78
79
80 static ElfuScn* modelFromSection(Elf_Scn *scn)
81 {
82   ElfuScn *ms;
83
84   assert(scn);
85
86   ms = malloc(sizeof(ElfuScn));
87   if (!ms) {
88     ELFU_WARN("modelFromSection: malloc() failed for ElfuScn.\n");
89     goto ERROR;
90   }
91
92
93   assert(gelf_getshdr(scn, &ms->shdr) == &ms->shdr);
94
95
96   /* Copy each data part in source segment */
97   ms->data.d_align = 1;
98   ms->data.d_buf  = NULL;
99   ms->data.d_off  = 0;
100   ms->data.d_type = ELF_T_BYTE;
101   ms->data.d_size = ms->shdr.sh_size;
102   ms->data.d_version = elf_version(EV_NONE);
103   if (ms->shdr.sh_type != SHT_NOBITS
104       && ms->shdr.sh_size > 0) {
105     Elf_Data *data;
106
107     ms->data.d_buf = malloc(ms->shdr.sh_size);
108     if (!ms->data.d_buf) {
109       ELFU_WARN("modelFromSection: malloc() failed for data buffer (%jx bytes).\n", ms->shdr.sh_size);
110       goto ERROR;
111     }
112
113     /* A non-empty section should contain at least one data block. */
114     data = elf_rawdata(scn, NULL);
115     assert(data);
116
117     ms->data.d_align = data->d_align;
118     ms->data.d_type = data->d_type;
119     ms->data.d_version = data->d_version;
120
121     while (data) {
122       if (data->d_off + data->d_size > ms->shdr.sh_size) {
123         ELFU_WARN("modelFromSection: libelf delivered a bogus data blob. Skipping\n");
124       } else {
125         memcpy(ms->data.d_buf + data->d_off, data->d_buf, data->d_size);
126       }
127
128       data = elf_rawdata(scn, data);
129     }
130   }
131
132   ms->linkptr = NULL;
133   ms->infoptr = NULL;
134
135
136   return ms;
137
138   ERROR:
139   if (ms) {
140     free(ms);
141   }
142   return NULL;
143 }
144
145
146
147
148 ElfuElf* elfu_mFromElf(Elf *e)
149 {
150   ElfuElf *me;
151   size_t shstrndx;
152   size_t i, numPhdr, numShdr;
153   ElfuScn **secArray = NULL;
154
155   assert(e);
156   if (elfu_eCheck(e)) {
157     goto ERROR;
158   }
159
160   me = malloc(sizeof(ElfuElf));
161   if (!me) {
162     ELFU_WARN("elfu_mFromElf: malloc() failed for ElfuElf.\n");
163     goto ERROR;
164   }
165
166
167   /* General stuff */
168   CIRCLEQ_INIT(&me->scnList);
169   CIRCLEQ_INIT(&me->phdrList);
170   CIRCLEQ_INIT(&me->orphanScnList);
171   me->shstrtab = NULL;
172
173   me->elfclass = gelf_getclass(e);
174   assert(me->elfclass != ELFCLASSNONE);
175   assert(gelf_getehdr(e, &me->ehdr) == &me->ehdr);
176
177
178   /* Get the section string table index */
179   if (elf_getshdrstrndx(e, &shstrndx) != 0) {
180     shstrndx = 0;
181   }
182
183
184   /* Load segments */
185   assert(!elf_getphdrnum(e, &numPhdr));
186   for (i = 0; i < numPhdr; i++) {
187     GElf_Phdr phdr;
188     ElfuPhdr *mp;
189
190     assert(gelf_getphdr(e, i, &phdr) == &phdr);
191
192     mp = modelFromPhdr(&phdr);
193     if (!mp) {
194       goto ERROR;
195     }
196
197     CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem);
198   }
199
200   if (numPhdr > 0) {
201     ElfuPhdr *mp;
202
203     /* Find PHDR -> PHDR dependencies (needs sorted sections) */
204     CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
205       ElfuPhdr *mp2;
206
207       if (mp->phdr.p_type != PT_LOAD) {
208         continue;
209       }
210
211       CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
212         if (mp2 == mp) {
213           continue;
214         }
215
216         if (mp->phdr.p_vaddr <= mp2->phdr.p_vaddr
217             && OFFS_END(mp2->phdr.p_vaddr, mp2->phdr.p_memsz) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) {
218           CIRCLEQ_INSERT_TAIL(&mp->childPhdrList, mp2, elemChildPhdr);
219         }
220       }
221     }
222   }
223
224
225   /* Load sections */
226   assert(!elf_getshdrnum(e, &numShdr));
227   if (numShdr > 1) {
228     secArray = malloc((numShdr - 1) * sizeof(*secArray));
229     if (!secArray) {
230       ELFU_WARN("elfu_mFromElf: malloc() failed for secArray.\n");
231       goto ERROR;
232     }
233
234     for (i = 1; i < numShdr; i++) {
235       Elf_Scn *scn;
236       ElfuScn *ms;
237
238       scn = elf_getscn(e, i);
239       assert(scn);
240
241       ms = modelFromSection(scn);
242       if (!ms) {
243         goto ERROR;
244       }
245
246       secArray[i-1] =  ms;
247
248       if (i == shstrndx) {
249         me->shstrtab = ms;
250       }
251     }
252
253
254     /* Find sh_link dependencies */
255     for (i = 0; i < numShdr - 1; i++) {
256       ElfuScn *ms = secArray[i];
257
258       switch (ms->shdr.sh_type) {
259         case SHT_REL:
260         case SHT_RELA:
261           if (ms->shdr.sh_info > 0) {
262             ms->infoptr = secArray[ms->shdr.sh_info - 1];
263           }
264         case SHT_DYNAMIC:
265         case SHT_HASH:
266         case SHT_SYMTAB:
267         case SHT_DYNSYM:
268         case SHT_GNU_versym:
269         case SHT_GNU_verdef:
270         case SHT_GNU_verneed:
271           if (ms->shdr.sh_link > 0) {
272             ms->linkptr = secArray[ms->shdr.sh_link - 1];
273           }
274       }
275     }
276
277
278     /* Sort sections by file offset */
279     qsort(secArray, numShdr - 1, sizeof(*secArray), cmpScnOffs);
280
281
282     /* Find PHDR -> Section dependencies (needs sorted sections) */
283     for (i = 0; i < numShdr - 1; i++) {
284       ElfuScn *ms = secArray[i];
285
286       ElfuPhdr *parent = parentPhdr(me, ms);
287
288       if (parent) {
289         CIRCLEQ_INSERT_TAIL(&parent->childScnList, ms, elemChildScn);
290       } else {
291         CIRCLEQ_INSERT_TAIL(&me->orphanScnList, ms, elemChildScn);
292       }
293     }
294
295
296     /* Put sections into list of all sections */
297     for (i = 0; i < numShdr - 1; i++) {
298       CIRCLEQ_INSERT_TAIL(&me->scnList, secArray[i], elem);
299     }
300   }
301
302
303   return me;
304
305
306   ERROR:
307   if (secArray) {
308     free(secArray);
309   }
310   if (me) {
311     // TODO: Free data structures
312   }
313
314   ELFU_WARN("elfu_mFromElf: Failed to load file.\n");
315   return NULL;
316 }