bd085f6c61558b75d564f5341c0ddbd02d71804c
[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 (ms->shdr.sh_addr >= mp->phdr.p_vaddr
42         && OFFS_END(ms->shdr.sh_addr, ms->shdr.sh_size) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) {
43       return mp;
44     } else if (ms->shdr.sh_offset >= mp->phdr.p_offset
45                && OFFS_END(ms->shdr.sh_offset, SCNFILESIZE(&ms->shdr)) <= OFFS_END(mp->phdr.p_offset, mp->phdr.p_filesz)
46                && OFFS_END(ms->shdr.sh_offset, ms->shdr.sh_size) <= OFFS_END(mp->phdr.p_offset, mp->phdr.p_memsz)) {
47       return mp;
48     }
49   }
50
51   return NULL;
52 }
53
54
55 static ElfuPhdr* modelFromPhdr(GElf_Phdr *phdr)
56 {
57   ElfuPhdr *mp;
58
59   assert(phdr);
60
61   mp = malloc(sizeof(ElfuPhdr));
62   if (!mp) {
63     ELFU_WARN("modelFromPhdr: malloc() failed for ElfuPhdr.\n");
64     return NULL;
65   }
66
67   mp->phdr = *phdr;
68
69   CIRCLEQ_INIT(&mp->phdrToScnList);
70
71   return mp;
72 }
73
74
75 static ElfuScn* modelFromSection(Elf_Scn *scn)
76 {
77   ElfuScn *ms;
78
79   assert(scn);
80
81   ms = malloc(sizeof(ElfuScn));
82   if (!ms) {
83     ELFU_WARN("modelFromSection: malloc() failed for ElfuScn.\n");
84     goto ERROR;
85   }
86
87
88   assert(gelf_getshdr(scn, &ms->shdr) == &ms->shdr);
89
90
91   /* Copy each data part in source segment */
92   ms->data.d_align = 1;
93   ms->data.d_buf  = NULL;
94   ms->data.d_off  = 0;
95   ms->data.d_type = ELF_T_BYTE;
96   ms->data.d_size = ms->shdr.sh_size;
97   ms->data.d_version = elf_version(EV_NONE);
98   if (ms->shdr.sh_type != SHT_NOBITS
99       && ms->shdr.sh_size > 0) {
100     Elf_Data *data;
101
102     ms->data.d_buf = malloc(ms->shdr.sh_size);
103     if (!ms->data.d_buf) {
104       ELFU_WARN("modelFromSection: malloc() failed for data buffer (%jx bytes).\n", ms->shdr.sh_size);
105       goto ERROR;
106     }
107
108     /* A non-empty section should contain at least one data block. */
109     data = elf_rawdata(scn, NULL);
110     assert(data);
111
112     ms->data.d_align = data->d_align;
113     ms->data.d_type = data->d_type;
114     ms->data.d_version = data->d_version;
115
116     while (data) {
117       if (data->d_off + data->d_size > ms->shdr.sh_size) {
118         ELFU_WARN("modelFromSection: libelf delivered a bogus data blob. Skipping\n");
119       } else {
120         memcpy(ms->data.d_buf + data->d_off, data->d_buf, data->d_size);
121       }
122
123       data = elf_rawdata(scn, data);
124     }
125   }
126
127   ms->linkptr = NULL;
128   ms->infoptr = NULL;
129
130
131   return ms;
132
133   ERROR:
134   if (ms) {
135     free(ms);
136   }
137   return NULL;
138 }
139
140
141
142
143 ElfuElf* elfu_mFromElf(Elf *e)
144 {
145   ElfuElf *me;
146   size_t shstrndx;
147   size_t i, numPhdr, numShdr;
148   ElfuScn **secArray = NULL;
149
150   assert(e);
151   if (elfu_eCheck(e)) {
152     goto ERROR;
153   }
154
155   me = malloc(sizeof(ElfuElf));
156   if (!me) {
157     ELFU_WARN("elfu_mFromElf: malloc() failed for ElfuElf.\n");
158     goto ERROR;
159   }
160
161
162   /* General stuff */
163   CIRCLEQ_INIT(&me->scnList);
164   CIRCLEQ_INIT(&me->phdrList);
165   me->shstrtab = NULL;
166
167   me->elfclass = gelf_getclass(e);
168   assert(me->elfclass != ELFCLASSNONE);
169   assert(gelf_getehdr(e, &me->ehdr) == &me->ehdr);
170
171
172   /* Get the section string table index */
173   if (elf_getshdrstrndx(e, &shstrndx) != 0) {
174     shstrndx = 0;
175   }
176
177
178   /* Load segments */
179   assert(!elf_getphdrnum(e, &numPhdr));
180   for (i = 0; i < numPhdr; i++) {
181     GElf_Phdr phdr;
182     ElfuPhdr *mp;
183
184     assert(gelf_getphdr(e, i, &phdr) == &phdr);
185
186     mp = modelFromPhdr(&phdr);
187     if (!mp) {
188       goto ERROR;
189     }
190
191     CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem);
192   }
193
194
195   /* Load sections */
196   assert(!elf_getshdrnum(e, &numShdr));
197   if (numShdr > 1) {
198     secArray = malloc((numShdr - 1) * sizeof(*secArray));
199     if (!secArray) {
200       ELFU_WARN("elfu_mFromElf: malloc() failed for secArray.\n");
201       goto ERROR;
202     }
203
204     for (i = 1; i < numShdr; i++) {
205       Elf_Scn *scn;
206       ElfuScn *ms;
207
208       scn = elf_getscn(e, i);
209       assert(scn);
210
211       ms = modelFromSection(scn);
212       if (!ms) {
213         goto ERROR;
214       }
215
216       secArray[i-1] =  ms;
217
218       if (i == shstrndx) {
219         me->shstrtab = ms;
220       }
221     }
222
223
224     /* Find sh_link dependencies */
225     for (i = 0; i < numShdr - 1; i++) {
226       ElfuScn *ms = secArray[i];
227
228       switch (ms->shdr.sh_type) {
229         case SHT_REL:
230         case SHT_RELA:
231           if (ms->shdr.sh_info > 0) {
232             ms->infoptr = secArray[ms->shdr.sh_info - 1];
233           }
234         case SHT_DYNAMIC:
235         case SHT_HASH:
236         case SHT_SYMTAB:
237         case SHT_DYNSYM:
238         case SHT_GNU_versym:
239         case SHT_GNU_verdef:
240         case SHT_GNU_verneed:
241           if (ms->shdr.sh_link > 0) {
242             ms->linkptr = secArray[ms->shdr.sh_link - 1];
243           }
244       }
245     }
246
247
248     /* Sort sections by file offset */
249     qsort(secArray, numShdr - 1, sizeof(*secArray), cmpScnOffs);
250
251
252     /* Find PHDR -> Section dependencies (needs sorted sections) */
253     for (i = 0; i < numShdr - 1; i++) {
254       ElfuScn *ms = secArray[i];
255
256       ElfuPhdr *parent = parentPhdr(me, ms);
257
258       if (parent) {
259         CIRCLEQ_INSERT_TAIL(&parent->phdrToScnList, ms, elemPhdrToScn);
260       }
261     }
262
263
264     /* Put sections into list of all sections */
265     for (i = 0; i < numShdr - 1; i++) {
266       CIRCLEQ_INSERT_TAIL(&me->scnList, secArray[i], elem);
267     }
268   }
269
270
271   return me;
272
273
274   ERROR:
275   if (secArray) {
276     free(secArray);
277   }
278   if (me) {
279     // TODO: Free data structures
280   }
281
282   ELFU_WARN("elfu_mFromElf: Failed to load file.\n");
283   return NULL;
284 }