7b8bfd31d756fc87a2e85cda36d1874d8f7abcec
[centaur.git] / src / model / fromFile.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <libelf/libelf.h>
6 #include <libelf/gelf.h>
7 #include <libelfu/libelfu.h>
8
9
10 static int cmpScnOffs(const void *ms1, const void *ms2)
11 {
12   assert(ms1);
13   assert(ms2);
14
15   ElfuScn *s1 = *(ElfuScn**)ms1;
16   ElfuScn *s2 = *(ElfuScn**)ms2;
17
18   assert(s1);
19   assert(s2);
20
21
22   if (s1->shdr.sh_offset < s2->shdr.sh_offset) {
23     return -1;
24   } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) {
25     return 0;
26   } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ {
27     return 1;
28   }
29 }
30
31
32
33 static ElfuPhdr* parentPhdr(ElfuElf *me, ElfuScn *ms)
34 {
35   ElfuPhdr *mp;
36
37   assert(me);
38   assert(ms);
39
40   CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
41     if (mp->phdr.p_type != PT_LOAD) {
42       continue;
43     }
44
45     if (PHDR_CONTAINS_SCN_IN_MEMORY(&mp->phdr, &ms->shdr)) {
46       return mp;
47     }
48
49     /* Give sections a second chance if they do not have any sh_addr
50      * at all. */
51     /* Actually we don't, because it's ambiguous.
52      * Re-enable for experiments with strangely-formatted files.
53     if (ms->shdr.sh_addr == 0
54         && PHDR_CONTAINS_SCN_IN_FILE(&mp->phdr, &ms->shdr)
55         && OFFS_END(ms->shdr.sh_offset, ms->shdr.sh_size)
56             <= OFFS_END(mp->phdr.p_offset, mp->phdr.p_memsz)) {
57       return mp;
58     }
59     */
60   }
61
62   return NULL;
63 }
64
65
66 static ElfuPhdr* modelFromPhdr(GElf_Phdr *phdr)
67 {
68   ElfuPhdr *mp;
69
70   assert(phdr);
71
72   mp = malloc(sizeof(ElfuPhdr));
73   if (!mp) {
74     ELFU_WARN("modelFromPhdr: malloc() failed for ElfuPhdr.\n");
75     return NULL;
76   }
77
78   mp->phdr = *phdr;
79
80   CIRCLEQ_INIT(&mp->childScnList);
81   CIRCLEQ_INIT(&mp->childPhdrList);
82
83   return mp;
84 }
85
86
87 static ElfuScn* modelFromSection(Elf_Scn *scn)
88 {
89   ElfuScn *ms;
90
91   assert(scn);
92
93   ms = malloc(sizeof(ElfuScn));
94   if (!ms) {
95     ELFU_WARN("modelFromSection: malloc() failed for ElfuScn.\n");
96     goto ERROR;
97   }
98
99
100   assert(gelf_getshdr(scn, &ms->shdr) == &ms->shdr);
101
102
103   /* Copy each data part in source segment */
104   ms->data.d_align = 1;
105   ms->data.d_buf  = NULL;
106   ms->data.d_off  = 0;
107   ms->data.d_type = ELF_T_BYTE;
108   ms->data.d_size = ms->shdr.sh_size;
109   ms->data.d_version = elf_version(EV_NONE);
110   if (ms->shdr.sh_type != SHT_NOBITS
111       && ms->shdr.sh_size > 0) {
112     Elf_Data *data;
113
114     ms->data.d_buf = malloc(ms->shdr.sh_size);
115     if (!ms->data.d_buf) {
116       ELFU_WARN("modelFromSection: malloc() failed for data buffer (%jx bytes).\n", ms->shdr.sh_size);
117       goto ERROR;
118     }
119
120     /* A non-empty section should contain at least one data block. */
121     data = elf_rawdata(scn, NULL);
122     assert(data);
123
124     ms->data.d_align = data->d_align;
125     ms->data.d_type = data->d_type;
126     ms->data.d_version = data->d_version;
127
128     while (data) {
129       if (data->d_off + data->d_size > ms->shdr.sh_size) {
130         ELFU_WARN("modelFromSection: libelf delivered a bogus data blob. Skipping\n");
131       } else {
132         memcpy(ms->data.d_buf + data->d_off, data->d_buf, data->d_size);
133       }
134
135       data = elf_rawdata(scn, data);
136     }
137   }
138
139   ms->linkptr = NULL;
140   ms->infoptr = NULL;
141
142   ms->oldptr = NULL;
143
144
145   return ms;
146
147   ERROR:
148   if (ms) {
149     free(ms);
150   }
151   return NULL;
152 }
153
154
155
156
157 ElfuElf* elfu_mFromElf(Elf *e)
158 {
159   ElfuElf *me;
160   size_t shstrndx;
161   size_t i, numPhdr, numShdr;
162   ElfuScn **secArray = NULL;
163
164   assert(e);
165   if (elfu_eCheck(e)) {
166     goto ERROR;
167   }
168
169   me = malloc(sizeof(ElfuElf));
170   if (!me) {
171     ELFU_WARN("elfu_mFromElf: malloc() failed for ElfuElf.\n");
172     goto ERROR;
173   }
174
175
176   /* General stuff */
177   CIRCLEQ_INIT(&me->phdrList);
178   CIRCLEQ_INIT(&me->orphanScnList);
179   me->shstrtab = NULL;
180
181   me->elfclass = gelf_getclass(e);
182   assert(me->elfclass != ELFCLASSNONE);
183   assert(gelf_getehdr(e, &me->ehdr) == &me->ehdr);
184
185
186   /* Get the section string table index */
187   if (elf_getshdrstrndx(e, &shstrndx) != 0) {
188     shstrndx = 0;
189   }
190
191
192   /* Load segments */
193   assert(!elf_getphdrnum(e, &numPhdr));
194   for (i = 0; i < numPhdr; i++) {
195     GElf_Phdr phdr;
196     ElfuPhdr *mp;
197
198     assert(gelf_getphdr(e, i, &phdr) == &phdr);
199
200     mp = modelFromPhdr(&phdr);
201     if (!mp) {
202       goto ERROR;
203     }
204
205     CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem);
206   }
207
208   if (numPhdr > 0) {
209     ElfuPhdr *mp;
210
211     /* Find PHDR -> PHDR dependencies (needs sorted sections) */
212     CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
213       ElfuPhdr *mp2;
214
215       if (mp->phdr.p_type != PT_LOAD) {
216         continue;
217       }
218
219       CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
220         if (mp2 == mp) {
221           continue;
222         }
223
224         if (mp->phdr.p_vaddr <= mp2->phdr.p_vaddr
225             && OFFS_END(mp2->phdr.p_vaddr, mp2->phdr.p_memsz) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) {
226           CIRCLEQ_INSERT_TAIL(&mp->childPhdrList, mp2, elemChildPhdr);
227         }
228       }
229     }
230   }
231
232
233   /* Load sections */
234   assert(!elf_getshdrnum(e, &numShdr));
235   if (numShdr > 1) {
236     secArray = malloc((numShdr - 1) * sizeof(*secArray));
237     if (!secArray) {
238       ELFU_WARN("elfu_mFromElf: malloc() failed for secArray.\n");
239       goto ERROR;
240     }
241
242     for (i = 1; i < numShdr; i++) {
243       Elf_Scn *scn;
244       ElfuScn *ms;
245
246       scn = elf_getscn(e, i);
247       assert(scn);
248
249       ms = modelFromSection(scn);
250       if (!ms) {
251         goto ERROR;
252       }
253
254       secArray[i-1] =  ms;
255
256       if (i == shstrndx) {
257         me->shstrtab = ms;
258       }
259     }
260
261
262     /* Find sh_link dependencies */
263     for (i = 0; i < numShdr - 1; i++) {
264       ElfuScn *ms = secArray[i];
265
266       switch (ms->shdr.sh_type) {
267         case SHT_REL:
268         case SHT_RELA:
269           if (ms->shdr.sh_info > 0) {
270             ms->infoptr = secArray[ms->shdr.sh_info - 1];
271           }
272         case SHT_DYNAMIC:
273         case SHT_HASH:
274         case SHT_SYMTAB:
275         case SHT_DYNSYM:
276         case SHT_GNU_versym:
277         case SHT_GNU_verdef:
278         case SHT_GNU_verneed:
279           if (ms->shdr.sh_link > 0) {
280             ms->linkptr = secArray[ms->shdr.sh_link - 1];
281           }
282       }
283     }
284
285
286     /* Sort sections by file offset */
287     qsort(secArray, numShdr - 1, sizeof(*secArray), cmpScnOffs);
288
289
290     /* Find PHDR -> Section dependencies (needs sorted sections) */
291     for (i = 0; i < numShdr - 1; i++) {
292       ElfuScn *ms = secArray[i];
293
294       ElfuPhdr *parent = parentPhdr(me, ms);
295
296       if (parent) {
297         GElf_Off shaddr = parent->phdr.p_vaddr +
298                          (ms->shdr.sh_offset - parent->phdr.p_offset);
299
300         if (ms->shdr.sh_addr == 0) {
301           ms->shdr.sh_addr = shaddr;
302         } else {
303           assert(ms->shdr.sh_addr == shaddr);
304         }
305
306         CIRCLEQ_INSERT_TAIL(&parent->childScnList, ms, elemChildScn);
307       } else {
308         CIRCLEQ_INSERT_TAIL(&me->orphanScnList, ms, elemChildScn);
309       }
310     }
311   }
312
313
314   return me;
315
316
317   ERROR:
318   if (secArray) {
319     free(secArray);
320   }
321   if (me) {
322     // TODO: Free data structures
323   }
324
325   ELFU_WARN("elfu_mFromElf: Failed to load file.\n");
326   return NULL;
327 }