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