9c25e291dc5450729ed6523b0e0e5d08386e8287
[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 = malloc(sizeof(ElfuPhdr));
189   if (!mp) {
190     ELFU_WARN("modelFromPhdr: malloc() failed for ElfuPhdr.\n");
191     return NULL;
192   }
193
194   mp->phdr = *phdr;
195
196   CIRCLEQ_INIT(&mp->childScnList);
197   CIRCLEQ_INIT(&mp->childPhdrList);
198
199   return mp;
200 }
201
202
203 static ElfuScn* modelFromSection(Elf_Scn *scn)
204 {
205   ElfuScn *ms;
206
207   assert(scn);
208
209   ms = elfu_mScnAlloc();
210   if (!ms) {
211     goto ERROR;
212   }
213
214   /* Copy SHDR */
215   assert(gelf_getshdr(scn, &ms->shdr) == &ms->shdr);
216
217   /* Copy each data part in source segment */
218   if (SCNFILESIZE(&ms->shdr) > 0) {
219     Elf_Data *data;
220
221     ms->databuf = malloc(ms->shdr.sh_size);
222     if (!ms->databuf) {
223       ELFU_WARN("modelFromSection: malloc() failed for data buffer (%x bytes).\n", (unsigned)ms->shdr.sh_size);
224       goto ERROR;
225     }
226
227     /* A non-empty section should contain at least one data block. */
228     data = elf_rawdata(scn, NULL);
229     assert(data);
230
231     /* elf_rawdata() always returns ELF_T_BYTE */
232     assert(data->d_type == ELF_T_BYTE);
233
234     while (data) {
235       if (data->d_off + data->d_size > ms->shdr.sh_size) {
236         ELFU_WARN("modelFromSection: libelf delivered a bogus data blob. Skipping\n");
237       } else {
238         memcpy((char*)ms->databuf + data->d_off, data->d_buf, data->d_size);
239       }
240
241       data = elf_rawdata(scn, data);
242     }
243   }
244
245   return ms;
246
247   ERROR:
248   if (ms) {
249     free(ms);
250   }
251   return NULL;
252 }
253
254
255
256
257 ElfuElf* elfu_mFromElf(Elf *e)
258 {
259   ElfuElf *me;
260   size_t shstrndx;
261   size_t i, numPhdr, numShdr;
262   ElfuScn **secArray = NULL;
263
264   assert(e);
265   if (elfu_eCheck(e)) {
266     goto ERROR;
267   }
268
269   me = malloc(sizeof(ElfuElf));
270   if (!me) {
271     ELFU_WARN("elfu_mFromElf: malloc() failed for ElfuElf.\n");
272     goto ERROR;
273   }
274
275
276   /* General stuff */
277   CIRCLEQ_INIT(&me->phdrList);
278   CIRCLEQ_INIT(&me->orphanScnList);
279   me->shstrtab = NULL;
280   me->symtab = NULL;
281
282   me->elfclass = gelf_getclass(e);
283   assert(me->elfclass != ELFCLASSNONE);
284   assert(gelf_getehdr(e, &me->ehdr) == &me->ehdr);
285
286
287   /* Get the section string table index */
288   if (elf_getshdrstrndx(e, &shstrndx) != 0) {
289     shstrndx = 0;
290   }
291
292
293   /* Load segments */
294   assert(!elf_getphdrnum(e, &numPhdr));
295   for (i = 0; i < numPhdr; i++) {
296     GElf_Phdr phdr;
297     ElfuPhdr *mp;
298
299     assert(gelf_getphdr(e, i, &phdr) == &phdr);
300
301     mp = modelFromPhdr(&phdr);
302     if (!mp) {
303       goto ERROR;
304     }
305
306     CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem);
307   }
308
309   if (numPhdr > 0) {
310     ElfuPhdr *mp;
311
312     /* Find PHDR -> PHDR dependencies (needs sorted sections) */
313     CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
314       ElfuPhdr *mp2;
315
316       if (mp->phdr.p_type != PT_LOAD) {
317         continue;
318       }
319
320       CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
321         if (mp2 == mp) {
322           continue;
323         }
324
325         if (mp->phdr.p_vaddr <= mp2->phdr.p_vaddr
326             && OFFS_END(mp2->phdr.p_vaddr, mp2->phdr.p_memsz) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) {
327           CIRCLEQ_INSERT_TAIL(&mp->childPhdrList, mp2, elemChildPhdr);
328         }
329       }
330     }
331   }
332
333
334   /* Load sections */
335   assert(!elf_getshdrnum(e, &numShdr));
336   if (numShdr > 1) {
337     secArray = malloc((numShdr - 1) * sizeof(*secArray));
338     if (!secArray) {
339       ELFU_WARN("elfu_mFromElf: malloc() failed for secArray.\n");
340       goto ERROR;
341     }
342
343     for (i = 1; i < numShdr; i++) {
344       Elf_Scn *scn;
345       ElfuScn *ms;
346
347       scn = elf_getscn(e, i);
348       assert(scn);
349
350       ms = modelFromSection(scn);
351       if (!ms) {
352         goto ERROR;
353       }
354
355       secArray[i-1] =  ms;
356
357       if (i == shstrndx) {
358         me->shstrtab = ms;
359       }
360     }
361
362
363     /* Find sh_link and sh_info dependencies (needs sections in original order) */
364     for (i = 0; i < numShdr - 1; i++) {
365       ElfuScn *ms = secArray[i];
366
367       switch (ms->shdr.sh_type) {
368         case SHT_REL:
369         case SHT_RELA:
370           if (ms->shdr.sh_info > 0) {
371             ms->infoptr = secArray[ms->shdr.sh_info - 1];
372           }
373         case SHT_DYNAMIC:
374         case SHT_HASH:
375         case SHT_SYMTAB:
376         case SHT_DYNSYM:
377         case SHT_GNU_versym:
378         case SHT_GNU_verdef:
379         case SHT_GNU_verneed:
380           if (ms->shdr.sh_link > 0) {
381             ms->linkptr = secArray[ms->shdr.sh_link - 1];
382           }
383       }
384     }
385
386
387     /* Parse symtabs (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_SYMTAB:
393           me->symtab = ms;
394         case SHT_DYNSYM:
395           parseSymtab(me, ms, secArray);
396           break;
397       }
398     }
399
400
401     /* Parse relocations */
402     for (i = 0; i < numShdr - 1; i++) {
403       ElfuScn *ms = secArray[i];
404
405       switch (ms->shdr.sh_type) {
406         case SHT_REL:
407           if (me->elfclass == ELFCLASS32) {
408             parseReltab32(ms);
409           } else if (me->elfclass == ELFCLASS64) {
410             /* Not used on x86-64 */
411             assert(0);
412           }
413           break;
414         case SHT_RELA:
415           if (me->elfclass == ELFCLASS32) {
416             /* Not used on x86-32 */
417             assert(0);
418           } else if (me->elfclass == ELFCLASS64) {
419             parseRelatab64(ms);
420           }
421           break;
422       }
423     }
424
425
426     /* Sort sections by file offset */
427     qsort(secArray, numShdr - 1, sizeof(*secArray), cmpScnOffs);
428
429
430     /* Find PHDR -> Section dependencies (needs sorted sections) */
431     for (i = 0; i < numShdr - 1; i++) {
432       ElfuScn *ms = secArray[i];
433
434       ElfuPhdr *parent = parentPhdr(me, ms);
435
436       if (parent) {
437         GElf_Off shaddr = parent->phdr.p_vaddr +
438                          (ms->shdr.sh_offset - parent->phdr.p_offset);
439
440         if (ms->shdr.sh_addr == 0) {
441           ms->shdr.sh_addr = shaddr;
442         } else {
443           assert(ms->shdr.sh_addr == shaddr);
444         }
445
446         CIRCLEQ_INSERT_TAIL(&parent->childScnList, ms, elemChildScn);
447       } else {
448         CIRCLEQ_INSERT_TAIL(&me->orphanScnList, ms, elemChildScn);
449       }
450     }
451   }
452
453
454   return me;
455
456
457   ERROR:
458   if (secArray) {
459     free(secArray);
460   }
461   if (me) {
462     // TODO: Free data structures
463   }
464
465   ELFU_WARN("elfu_mFromElf: Failed to load file.\n");
466   return NULL;
467 }