c46dd6c38dd727af0aaeec5aa0c569e65c010a65
[centaur.git] / src / modelops / fromFile.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <libelfu/libelfu.h>
6
7
8 static char* symstr(ElfuScn *symtab, size_t off)
9 {
10   assert(symtab);
11   assert(symtab->linkptr);
12   assert(symtab->linkptr->data.d_buf);
13   assert(off < symtab->linkptr->data.d_size);
14
15   return &(((char*)symtab->linkptr->data.d_buf)[off]);
16 }
17
18
19 static void parseSymtab32(ElfuScn *ms, ElfuScn**origScnArr)
20 {
21   size_t i;
22
23   assert(ms);
24   assert(ms->data.d_buf);
25   assert(origScnArr);
26
27
28   for (i = 1; (i + 1) * sizeof(Elf32_Sym) <= ms->shdr.sh_size; i++) {
29     Elf32_Sym *cursym = &(((Elf32_Sym*)ms->data.d_buf)[i]);
30     ElfuSym *sym = malloc(sizeof(*sym));
31     assert(sym);
32
33     sym->name = cursym->st_name;
34     sym->nameptr = symstr(ms, cursym->st_name);
35     sym->value = cursym->st_value;
36     sym->size = cursym->st_size;
37     sym->bind = ELF32_ST_BIND(cursym->st_info);
38     sym->type = ELF32_ST_TYPE(cursym->st_info);
39     sym->other = cursym->st_other;
40
41     switch (cursym->st_shndx) {
42       case SHN_UNDEF:
43       case SHN_ABS:
44       case SHN_COMMON:
45         sym->scnptr = NULL;
46         sym->shndx = cursym->st_shndx;
47         break;
48       default:
49         sym->scnptr = origScnArr[cursym->st_shndx - 1];
50         break;
51     }
52
53
54     CIRCLEQ_INSERT_TAIL(&ms->symtab.syms, sym, elem);
55   }
56 }
57
58
59 static void parseReltab32(ElfuScn *ms)
60 {
61   size_t i;
62
63   assert(ms);
64   assert(ms->data.d_buf);
65
66
67   for (i = 0; (i + 1) * sizeof(Elf32_Rel) <= ms->shdr.sh_size; i++) {
68     Elf32_Rel *currel = &(((Elf32_Rel*)ms->data.d_buf)[i]);
69     ElfuRel *rel;
70
71     rel = malloc(sizeof(*rel));
72     assert(rel);
73
74     rel->offset = currel->r_offset;
75
76     rel->sym = ELF32_R_SYM(currel->r_info);
77     rel->type = ELF32_R_TYPE(currel->r_info);
78
79     rel->addendUsed = 0;
80     rel->addend = 0;
81
82     CIRCLEQ_INSERT_TAIL(&ms->reltab.rels, rel, elem);
83   }
84 }
85
86
87 static int cmpScnOffs(const void *ms1, const void *ms2)
88 {
89   assert(ms1);
90   assert(ms2);
91
92   ElfuScn *s1 = *(ElfuScn**)ms1;
93   ElfuScn *s2 = *(ElfuScn**)ms2;
94
95   assert(s1);
96   assert(s2);
97
98
99   if (s1->shdr.sh_offset < s2->shdr.sh_offset) {
100     return -1;
101   } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) {
102     return 0;
103   } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ {
104     return 1;
105   }
106 }
107
108
109
110 static ElfuPhdr* parentPhdr(ElfuElf *me, ElfuScn *ms)
111 {
112   ElfuPhdr *mp;
113
114   assert(me);
115   assert(ms);
116
117   CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
118     if (mp->phdr.p_type != PT_LOAD) {
119       continue;
120     }
121
122     if (PHDR_CONTAINS_SCN_IN_MEMORY(&mp->phdr, &ms->shdr)) {
123       return mp;
124     }
125
126     /* Give sections a second chance if they do not have any sh_addr
127      * at all. */
128     /* Actually we don't, because it's ambiguous.
129      * Re-enable for experiments with strangely-formatted files.
130     if (ms->shdr.sh_addr == 0
131         && PHDR_CONTAINS_SCN_IN_FILE(&mp->phdr, &ms->shdr)
132         && OFFS_END(ms->shdr.sh_offset, ms->shdr.sh_size)
133             <= OFFS_END(mp->phdr.p_offset, mp->phdr.p_memsz)) {
134       return mp;
135     }
136     */
137   }
138
139   return NULL;
140 }
141
142
143 static ElfuPhdr* modelFromPhdr(GElf_Phdr *phdr)
144 {
145   ElfuPhdr *mp;
146
147   assert(phdr);
148
149   mp = malloc(sizeof(ElfuPhdr));
150   if (!mp) {
151     ELFU_WARN("modelFromPhdr: malloc() failed for ElfuPhdr.\n");
152     return NULL;
153   }
154
155   mp->phdr = *phdr;
156
157   CIRCLEQ_INIT(&mp->childScnList);
158   CIRCLEQ_INIT(&mp->childPhdrList);
159
160   return mp;
161 }
162
163
164 static ElfuScn* modelFromSection(Elf_Scn *scn)
165 {
166   ElfuScn *ms;
167
168   assert(scn);
169
170   ms = malloc(sizeof(ElfuScn));
171   if (!ms) {
172     ELFU_WARN("modelFromSection: malloc() failed for ElfuScn.\n");
173     goto ERROR;
174   }
175
176
177   assert(gelf_getshdr(scn, &ms->shdr) == &ms->shdr);
178
179
180   /* Copy each data part in source segment */
181   ms->data.d_align = 1;
182   ms->data.d_buf  = NULL;
183   ms->data.d_off  = 0;
184   ms->data.d_type = ELF_T_BYTE;
185   ms->data.d_size = ms->shdr.sh_size;
186   ms->data.d_version = elf_version(EV_NONE);
187   if (ms->shdr.sh_type != SHT_NOBITS
188       && ms->shdr.sh_size > 0) {
189     Elf_Data *data;
190
191     ms->data.d_buf = malloc(ms->shdr.sh_size);
192     if (!ms->data.d_buf) {
193       ELFU_WARN("modelFromSection: malloc() failed for data buffer (%jx bytes).\n", ms->shdr.sh_size);
194       goto ERROR;
195     }
196
197     /* A non-empty section should contain at least one data block. */
198     data = elf_rawdata(scn, NULL);
199     assert(data);
200
201     ms->data.d_align = data->d_align;
202     ms->data.d_type = data->d_type;
203     ms->data.d_version = data->d_version;
204
205     while (data) {
206       if (data->d_off + data->d_size > ms->shdr.sh_size) {
207         ELFU_WARN("modelFromSection: libelf delivered a bogus data blob. Skipping\n");
208       } else {
209         memcpy(ms->data.d_buf + data->d_off, data->d_buf, data->d_size);
210       }
211
212       data = elf_rawdata(scn, data);
213     }
214   }
215
216   ms->linkptr = NULL;
217   ms->infoptr = NULL;
218
219   ms->oldptr = NULL;
220
221   CIRCLEQ_INIT(&ms->symtab.syms);
222   CIRCLEQ_INIT(&ms->reltab.rels);
223
224
225   return ms;
226
227   ERROR:
228   if (ms) {
229     free(ms);
230   }
231   return NULL;
232 }
233
234
235
236
237 ElfuElf* elfu_mFromElf(Elf *e)
238 {
239   ElfuElf *me;
240   size_t shstrndx;
241   size_t i, numPhdr, numShdr;
242   ElfuScn **secArray = NULL;
243
244   assert(e);
245   if (elfu_eCheck(e)) {
246     goto ERROR;
247   }
248
249   me = malloc(sizeof(ElfuElf));
250   if (!me) {
251     ELFU_WARN("elfu_mFromElf: malloc() failed for ElfuElf.\n");
252     goto ERROR;
253   }
254
255
256   /* General stuff */
257   CIRCLEQ_INIT(&me->phdrList);
258   CIRCLEQ_INIT(&me->orphanScnList);
259   me->shstrtab = NULL;
260   me->symtab = NULL;
261
262   me->elfclass = gelf_getclass(e);
263   assert(me->elfclass != ELFCLASSNONE);
264   assert(gelf_getehdr(e, &me->ehdr) == &me->ehdr);
265
266
267   /* Get the section string table index */
268   if (elf_getshdrstrndx(e, &shstrndx) != 0) {
269     shstrndx = 0;
270   }
271
272
273   /* Load segments */
274   assert(!elf_getphdrnum(e, &numPhdr));
275   for (i = 0; i < numPhdr; i++) {
276     GElf_Phdr phdr;
277     ElfuPhdr *mp;
278
279     assert(gelf_getphdr(e, i, &phdr) == &phdr);
280
281     mp = modelFromPhdr(&phdr);
282     if (!mp) {
283       goto ERROR;
284     }
285
286     CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem);
287   }
288
289   if (numPhdr > 0) {
290     ElfuPhdr *mp;
291
292     /* Find PHDR -> PHDR dependencies (needs sorted sections) */
293     CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
294       ElfuPhdr *mp2;
295
296       if (mp->phdr.p_type != PT_LOAD) {
297         continue;
298       }
299
300       CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
301         if (mp2 == mp) {
302           continue;
303         }
304
305         if (mp->phdr.p_vaddr <= mp2->phdr.p_vaddr
306             && OFFS_END(mp2->phdr.p_vaddr, mp2->phdr.p_memsz) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) {
307           CIRCLEQ_INSERT_TAIL(&mp->childPhdrList, mp2, elemChildPhdr);
308         }
309       }
310     }
311   }
312
313
314   /* Load sections */
315   assert(!elf_getshdrnum(e, &numShdr));
316   if (numShdr > 1) {
317     secArray = malloc((numShdr - 1) * sizeof(*secArray));
318     if (!secArray) {
319       ELFU_WARN("elfu_mFromElf: malloc() failed for secArray.\n");
320       goto ERROR;
321     }
322
323     for (i = 1; i < numShdr; i++) {
324       Elf_Scn *scn;
325       ElfuScn *ms;
326
327       scn = elf_getscn(e, i);
328       assert(scn);
329
330       ms = modelFromSection(scn);
331       if (!ms) {
332         goto ERROR;
333       }
334
335       secArray[i-1] =  ms;
336
337       if (i == shstrndx) {
338         me->shstrtab = ms;
339       }
340     }
341
342
343     /* Find sh_link and sh_info dependencies (needs sections in original order) */
344     for (i = 0; i < numShdr - 1; i++) {
345       ElfuScn *ms = secArray[i];
346
347       switch (ms->shdr.sh_type) {
348         case SHT_REL:
349         case SHT_RELA:
350           if (ms->shdr.sh_info > 0) {
351             ms->infoptr = secArray[ms->shdr.sh_info - 1];
352           }
353         case SHT_DYNAMIC:
354         case SHT_HASH:
355         case SHT_SYMTAB:
356         case SHT_DYNSYM:
357         case SHT_GNU_versym:
358         case SHT_GNU_verdef:
359         case SHT_GNU_verneed:
360           if (ms->shdr.sh_link > 0) {
361             ms->linkptr = secArray[ms->shdr.sh_link - 1];
362           }
363       }
364     }
365
366
367     /* Parse symtabs (needs sections in original order) */
368     for (i = 0; i < numShdr - 1; i++) {
369       ElfuScn *ms = secArray[i];
370
371       switch (ms->shdr.sh_type) {
372         case SHT_SYMTAB:
373           me->symtab = ms;
374         case SHT_DYNSYM:
375           if (me->elfclass == ELFCLASS32) {
376             parseSymtab32(ms, secArray);
377           } else if (me->elfclass == ELFCLASS64) {
378             // TODO
379           }
380           break;
381       }
382     }
383
384
385     /* Parse relocations */
386     for (i = 0; i < numShdr - 1; i++) {
387       ElfuScn *ms = secArray[i];
388
389       switch (ms->shdr.sh_type) {
390         case SHT_REL:
391           if (me->elfclass == ELFCLASS32) {
392             parseReltab32(ms);
393           } else if (me->elfclass == ELFCLASS64) {
394             // TODO
395           }
396           break;
397         case SHT_RELA:
398           if (me->elfclass == ELFCLASS32) {
399             // TODO
400           } else if (me->elfclass == ELFCLASS64) {
401             // TODO
402           }
403           break;
404       }
405     }
406
407
408     /* Sort sections by file offset */
409     qsort(secArray, numShdr - 1, sizeof(*secArray), cmpScnOffs);
410
411
412     /* Find PHDR -> Section dependencies (needs sorted sections) */
413     for (i = 0; i < numShdr - 1; i++) {
414       ElfuScn *ms = secArray[i];
415
416       ElfuPhdr *parent = parentPhdr(me, ms);
417
418       if (parent) {
419         GElf_Off shaddr = parent->phdr.p_vaddr +
420                          (ms->shdr.sh_offset - parent->phdr.p_offset);
421
422         if (ms->shdr.sh_addr == 0) {
423           ms->shdr.sh_addr = shaddr;
424         } else {
425           assert(ms->shdr.sh_addr == shaddr);
426         }
427
428         CIRCLEQ_INSERT_TAIL(&parent->childScnList, ms, elemChildScn);
429       } else {
430         CIRCLEQ_INSERT_TAIL(&me->orphanScnList, ms, elemChildScn);
431       }
432     }
433   }
434
435
436   return me;
437
438
439   ERROR:
440   if (secArray) {
441     free(secArray);
442   }
443   if (me) {
444     // TODO: Free data structures
445   }
446
447   ELFU_WARN("elfu_mFromElf: Failed to load file.\n");
448   return NULL;
449 }