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