LOAD PHDRs at top level, others as children. mPhdrForall().
[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     /* elf_rawdata() always returns ELF_T_BYTE */
256     assert(data->d_type == ELF_T_BYTE);
257
258     while (data) {
259       if (data->d_off + data->d_size > ms->shdr.sh_size) {
260         ELFU_WARN("modelFromSection: libelf delivered a bogus data blob. Skipping\n");
261       } else {
262         memcpy((char*)ms->databuf + data->d_off, data->d_buf, data->d_size);
263       }
264
265       data = elf_rawdata(scn, data);
266     }
267   }
268
269   return ms;
270
271   ERROR:
272   if (ms) {
273     free(ms);
274   }
275   return NULL;
276 }
277
278
279
280
281 ElfuElf* elfu_mFromElf(Elf *e)
282 {
283   ElfuElf *me;
284   size_t shstrndx;
285   size_t i, numPhdr, numShdr;
286   ElfuScn **secArray = NULL;
287
288   assert(e);
289   if (elfu_eCheck(e)) {
290     goto ERROR;
291   }
292
293   me = elfu_mElfAlloc();
294   if (!me) {
295     goto ERROR;
296   }
297
298   me->elfclass = gelf_getclass(e);
299   assert(me->elfclass != ELFCLASSNONE);
300   assert(gelf_getehdr(e, &me->ehdr) == &me->ehdr);
301
302
303   /* Get the section string table index */
304   if (elf_getshdrstrndx(e, &shstrndx) != 0) {
305     shstrndx = 0;
306   }
307
308
309   /* Load segments */
310   assert(!elf_getphdrnum(e, &numPhdr));
311   for (i = 0; i < numPhdr; i++) {
312     GElf_Phdr phdr;
313     ElfuPhdr *mp;
314
315     assert(gelf_getphdr(e, i, &phdr) == &phdr);
316
317     mp = modelFromPhdr(&phdr);
318     if (!mp) {
319       goto ERROR;
320     }
321
322     CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem);
323   }
324
325   if (numPhdr > 0) {
326     ElfuPhdr *mp;
327
328     /* Find PHDR -> PHDR dependencies (needs sorted sections) */
329     CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
330       ElfuPhdr *mp2;
331
332       if (mp->phdr.p_type != PT_LOAD) {
333         continue;
334       }
335
336       CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
337         if (mp2 == mp) {
338           continue;
339         }
340
341         if (mp->phdr.p_vaddr <= mp2->phdr.p_vaddr
342             && OFFS_END(mp2->phdr.p_vaddr, mp2->phdr.p_memsz) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) {
343           /* Remove from the main list so only LOADs remain there */
344           CIRCLEQ_REMOVE(&me->phdrList, mp2, elem);
345           CIRCLEQ_INSERT_TAIL(&mp->childPhdrList, mp2, elemChildPhdr);
346         }
347       }
348     }
349   }
350
351
352   /* Load sections */
353   assert(!elf_getshdrnum(e, &numShdr));
354   if (numShdr > 1) {
355     secArray = malloc((numShdr - 1) * sizeof(*secArray));
356     if (!secArray) {
357       ELFU_WARN("elfu_mFromElf: malloc() failed for secArray.\n");
358       goto ERROR;
359     }
360
361     for (i = 1; i < numShdr; i++) {
362       Elf_Scn *scn;
363       ElfuScn *ms;
364
365       scn = elf_getscn(e, i);
366       assert(scn);
367
368       ms = modelFromSection(scn);
369       if (!ms) {
370         goto ERROR;
371       }
372
373       secArray[i-1] =  ms;
374
375       if (i == shstrndx) {
376         me->shstrtab = ms;
377       }
378     }
379
380
381     /* Find sh_link and sh_info dependencies (needs sections in original order) */
382     for (i = 0; i < numShdr - 1; i++) {
383       ElfuScn *ms = secArray[i];
384
385       switch (ms->shdr.sh_type) {
386         case SHT_REL:
387         case SHT_RELA:
388           if (ms->shdr.sh_info > 0) {
389             ms->infoptr = secArray[ms->shdr.sh_info - 1];
390           }
391         case SHT_DYNAMIC:
392         case SHT_HASH:
393         case SHT_SYMTAB:
394         case SHT_DYNSYM:
395         case SHT_GNU_versym:
396         case SHT_GNU_verdef:
397         case SHT_GNU_verneed:
398           if (ms->shdr.sh_link > 0) {
399             ms->linkptr = secArray[ms->shdr.sh_link - 1];
400           }
401       }
402     }
403
404
405     /* Parse symtabs (needs sections in original order) */
406     for (i = 0; i < numShdr - 1; i++) {
407       ElfuScn *ms = secArray[i];
408
409       switch (ms->shdr.sh_type) {
410         case SHT_SYMTAB:
411           me->symtab = ms;
412         case SHT_DYNSYM:
413           parseSymtab(me, ms, secArray);
414           break;
415       }
416     }
417
418
419     /* Parse relocations (needs sections in original order) */
420     for (i = 0; i < numShdr - 1; i++) {
421       ElfuScn *ms = secArray[i];
422
423       switch (ms->shdr.sh_type) {
424         case SHT_REL:
425           parseReltab(me, ms);
426           break;
427         case SHT_RELA:
428           parseRelatab(me, ms);
429           break;
430       }
431     }
432
433
434     /* Sort sections by file offset */
435     qsort(secArray, numShdr - 1, sizeof(*secArray), cmpScnOffs);
436
437
438     /* Find PHDR -> Section dependencies (needs sorted sections) */
439     for (i = 0; i < numShdr - 1; i++) {
440       ElfuScn *ms = secArray[i];
441
442       ElfuPhdr *parent = parentPhdr(me, ms);
443
444       if (parent) {
445         GElf_Off shaddr = parent->phdr.p_vaddr +
446                          (ms->shdr.sh_offset - parent->phdr.p_offset);
447
448         if (ms->shdr.sh_addr == 0) {
449           ms->shdr.sh_addr = shaddr;
450         } else {
451           assert(ms->shdr.sh_addr == shaddr);
452         }
453
454         CIRCLEQ_INSERT_TAIL(&parent->childScnList, ms, elemChildScn);
455       } else {
456         CIRCLEQ_INSERT_TAIL(&me->orphanScnList, ms, elemChildScn);
457       }
458     }
459   }
460
461
462   return me;
463
464
465   ERROR:
466   if (secArray) {
467     free(secArray);
468   }
469   if (me) {
470     elfu_mElfDestroy(me);
471   }
472
473   ELFU_WARN("elfu_mFromElf: Failed to load file.\n");
474   return NULL;
475 }