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