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