Extend relocation parser to full 32/64 bits
[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 = malloc(sizeof(ElfuElf));
294   if (!me) {
295     ELFU_WARN("elfu_mFromElf: malloc() failed for ElfuElf.\n");
296     goto ERROR;
297   }
298
299
300   /* General stuff */
301   CIRCLEQ_INIT(&me->phdrList);
302   CIRCLEQ_INIT(&me->orphanScnList);
303   me->shstrtab = NULL;
304   me->symtab = NULL;
305
306   me->elfclass = gelf_getclass(e);
307   assert(me->elfclass != ELFCLASSNONE);
308   assert(gelf_getehdr(e, &me->ehdr) == &me->ehdr);
309
310
311   /* Get the section string table index */
312   if (elf_getshdrstrndx(e, &shstrndx) != 0) {
313     shstrndx = 0;
314   }
315
316
317   /* Load segments */
318   assert(!elf_getphdrnum(e, &numPhdr));
319   for (i = 0; i < numPhdr; i++) {
320     GElf_Phdr phdr;
321     ElfuPhdr *mp;
322
323     assert(gelf_getphdr(e, i, &phdr) == &phdr);
324
325     mp = modelFromPhdr(&phdr);
326     if (!mp) {
327       goto ERROR;
328     }
329
330     CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem);
331   }
332
333   if (numPhdr > 0) {
334     ElfuPhdr *mp;
335
336     /* Find PHDR -> PHDR dependencies (needs sorted sections) */
337     CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
338       ElfuPhdr *mp2;
339
340       if (mp->phdr.p_type != PT_LOAD) {
341         continue;
342       }
343
344       CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
345         if (mp2 == mp) {
346           continue;
347         }
348
349         if (mp->phdr.p_vaddr <= mp2->phdr.p_vaddr
350             && OFFS_END(mp2->phdr.p_vaddr, mp2->phdr.p_memsz) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) {
351           CIRCLEQ_INSERT_TAIL(&mp->childPhdrList, mp2, elemChildPhdr);
352         }
353       }
354     }
355   }
356
357
358   /* Load sections */
359   assert(!elf_getshdrnum(e, &numShdr));
360   if (numShdr > 1) {
361     secArray = malloc((numShdr - 1) * sizeof(*secArray));
362     if (!secArray) {
363       ELFU_WARN("elfu_mFromElf: malloc() failed for secArray.\n");
364       goto ERROR;
365     }
366
367     for (i = 1; i < numShdr; i++) {
368       Elf_Scn *scn;
369       ElfuScn *ms;
370
371       scn = elf_getscn(e, i);
372       assert(scn);
373
374       ms = modelFromSection(scn);
375       if (!ms) {
376         goto ERROR;
377       }
378
379       secArray[i-1] =  ms;
380
381       if (i == shstrndx) {
382         me->shstrtab = ms;
383       }
384     }
385
386
387     /* Find sh_link and sh_info dependencies (needs sections in original order) */
388     for (i = 0; i < numShdr - 1; i++) {
389       ElfuScn *ms = secArray[i];
390
391       switch (ms->shdr.sh_type) {
392         case SHT_REL:
393         case SHT_RELA:
394           if (ms->shdr.sh_info > 0) {
395             ms->infoptr = secArray[ms->shdr.sh_info - 1];
396           }
397         case SHT_DYNAMIC:
398         case SHT_HASH:
399         case SHT_SYMTAB:
400         case SHT_DYNSYM:
401         case SHT_GNU_versym:
402         case SHT_GNU_verdef:
403         case SHT_GNU_verneed:
404           if (ms->shdr.sh_link > 0) {
405             ms->linkptr = secArray[ms->shdr.sh_link - 1];
406           }
407       }
408     }
409
410
411     /* Parse symtabs (needs sections in original order) */
412     for (i = 0; i < numShdr - 1; i++) {
413       ElfuScn *ms = secArray[i];
414
415       switch (ms->shdr.sh_type) {
416         case SHT_SYMTAB:
417           me->symtab = ms;
418         case SHT_DYNSYM:
419           parseSymtab(me, ms, secArray);
420           break;
421       }
422     }
423
424
425     /* Parse relocations (needs sections in original order) */
426     for (i = 0; i < numShdr - 1; i++) {
427       ElfuScn *ms = secArray[i];
428
429       switch (ms->shdr.sh_type) {
430         case SHT_REL:
431           parseReltab(me, ms);
432           break;
433         case SHT_RELA:
434           parseRelatab(me, ms);
435           break;
436       }
437     }
438
439
440     /* Sort sections by file offset */
441     qsort(secArray, numShdr - 1, sizeof(*secArray), cmpScnOffs);
442
443
444     /* Find PHDR -> Section dependencies (needs sorted sections) */
445     for (i = 0; i < numShdr - 1; i++) {
446       ElfuScn *ms = secArray[i];
447
448       ElfuPhdr *parent = parentPhdr(me, ms);
449
450       if (parent) {
451         GElf_Off shaddr = parent->phdr.p_vaddr +
452                          (ms->shdr.sh_offset - parent->phdr.p_offset);
453
454         if (ms->shdr.sh_addr == 0) {
455           ms->shdr.sh_addr = shaddr;
456         } else {
457           assert(ms->shdr.sh_addr == shaddr);
458         }
459
460         CIRCLEQ_INSERT_TAIL(&parent->childScnList, ms, elemChildScn);
461       } else {
462         CIRCLEQ_INSERT_TAIL(&me->orphanScnList, ms, elemChildScn);
463       }
464     }
465   }
466
467
468   return me;
469
470
471   ERROR:
472   if (secArray) {
473     free(secArray);
474   }
475   if (me) {
476     // TODO: Free data structures
477   }
478
479   ELFU_WARN("elfu_mFromElf: Failed to load file.\n");
480   return NULL;
481 }