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