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