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