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