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