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