1 /*
2 * elf_module.c
3 *
4 * Created on: Aug 11, 2008
5 * Author: Stefan Bucur <stefanb@zytor.com>
6 */
7
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <elf.h>
13 #include <dprintf.h>
14 #include <core.h>
15
16 #include <linux/list.h>
17 #include <sys/module.h>
18 #include <sys/exec.h>
19
20 #include "elfutils.h"
21 #include "../common.h"
22
23 /*
24 *
25 * The implementation assumes that the loadable segments are present
26 * in the PHT sorted by their offsets, so that only forward seeks would
27 * be necessary.
28 */
load_segments(struct elf_module * module,Elf_Ehdr * elf_hdr)29 int load_segments(struct elf_module *module, Elf_Ehdr *elf_hdr) {
30 int i;
31 int res = 0;
32 char *pht = NULL;
33 char *sht = NULL;
34 Elf64_Phdr *cr_pht;
35 Elf64_Shdr *cr_sht;
36
37 Elf64_Addr min_addr = 0x0000000000000000; // Min. ELF vaddr
38 Elf64_Addr max_addr = 0x0000000000000000; // Max. ELF vaddr
39 Elf64_Word max_align = sizeof(void*); // Min. align of posix_memalign()
40 Elf64_Addr min_alloc, max_alloc; // Min. and max. aligned allocables
41
42 Elf64_Addr dyn_addr = 0x0000000000000000;
43
44 // Get to the PHT
45 image_seek(elf_hdr->e_phoff, module);
46
47 // Load the PHT
48 pht = malloc(elf_hdr->e_phnum * elf_hdr->e_phentsize);
49 if (!pht)
50 return -1;
51
52 image_read(pht, elf_hdr->e_phnum * elf_hdr->e_phentsize, module);
53
54 // Compute the memory needings of the module
55 for (i=0; i < elf_hdr->e_phnum; i++) {
56 cr_pht = (Elf64_Phdr*)(pht + i * elf_hdr->e_phentsize);
57
58 switch (cr_pht->p_type) {
59 case PT_LOAD:
60 if (i == 0) {
61 min_addr = cr_pht->p_vaddr;
62 } else {
63 min_addr = MIN(min_addr, cr_pht->p_vaddr);
64 }
65
66 max_addr = MAX(max_addr, cr_pht->p_vaddr + cr_pht->p_memsz);
67 max_align = MAX(max_align, cr_pht->p_align);
68 break;
69 case PT_DYNAMIC:
70 dyn_addr = cr_pht->p_vaddr;
71 break;
72 default:
73 // Unsupported - ignore
74 break;
75 }
76 }
77
78 if (max_addr - min_addr == 0) {
79 // No loadable segments
80 DBG_PRINT("No loadable segments found\n");
81 goto out;
82 }
83
84 if (dyn_addr == 0) {
85 DBG_PRINT("No dynamic information segment found\n");
86 goto out;
87 }
88
89 // The minimum address that should be allocated
90 min_alloc = min_addr - (min_addr % max_align);
91
92 // The maximum address that should be allocated
93 max_alloc = max_addr - (max_addr % max_align);
94 if (max_addr % max_align > 0)
95 max_alloc += max_align;
96
97
98 if (elf_malloc(&module->module_addr,
99 max_align,
100 max_alloc-min_alloc) != 0) {
101
102 DBG_PRINT("Could not allocate segments\n");
103 goto out;
104 }
105
106 module->base_addr = (Elf64_Addr)(module->module_addr) - min_alloc;
107 module->module_size = max_alloc - min_alloc;
108
109 // Zero-initialize the memory
110 memset(module->module_addr, 0, module->module_size);
111
112 for (i = 0; i < elf_hdr->e_phnum; i++) {
113 cr_pht = (Elf64_Phdr*)(pht + i * elf_hdr->e_phentsize);
114
115 if (cr_pht->p_type == PT_LOAD) {
116 // Copy the segment at its destination
117 if (cr_pht->p_offset < module->u.l._cr_offset) {
118 // The segment contains data before the current offset
119 // It can be discarded without worry - it would contain only
120 // headers
121 Elf64_Off aux_off = module->u.l._cr_offset - cr_pht->p_offset;
122
123 if (image_read((char *)module_get_absolute(cr_pht->p_vaddr, module) + aux_off,
124 cr_pht->p_filesz - aux_off, module) < 0) {
125 res = -1;
126 goto out;
127 }
128 } else {
129 if (image_seek(cr_pht->p_offset, module) < 0) {
130 res = -1;
131 goto out;
132 }
133
134 if (image_read(module_get_absolute(cr_pht->p_vaddr, module),
135 cr_pht->p_filesz, module) < 0) {
136 res = -1;
137 goto out;
138 }
139 }
140
141 /*
142 DBG_PRINT("Loadable segment of size 0x%08x copied from vaddr 0x%08x at 0x%08x\n",
143 cr_pht->p_filesz,
144 cr_pht->p_vaddr,
145 (Elf64_Addr)module_get_absolute(cr_pht->p_vaddr, module));
146 */
147 }
148 }
149
150 // Get to the SHT
151 image_seek(elf_hdr->e_shoff, module);
152
153 // Load the SHT
154 sht = malloc(elf_hdr->e_shnum * elf_hdr->e_shentsize);
155 if (!sht) {
156 res = -1;
157 goto out;
158 }
159
160 image_read(sht, elf_hdr->e_shnum * elf_hdr->e_shentsize, module);
161
162 // Setup the symtable size
163 for (i = 0; i < elf_hdr->e_shnum; i++) {
164 cr_sht = (Elf64_Shdr*)(sht + i * elf_hdr->e_shentsize);
165
166 if (cr_sht->sh_type == SHT_DYNSYM) {
167 module->symtable_size = cr_sht->sh_size;
168 break;
169 }
170 }
171
172 free(sht);
173
174 // Setup dynamic segment location
175 module->dyn_table = module_get_absolute(dyn_addr, module);
176
177 /*
178 DBG_PRINT("Base address: 0x%08x, aligned at 0x%08x\n", module->base_addr,
179 max_align);
180 DBG_PRINT("Module size: 0x%08x\n", module->module_size);
181 */
182
183 out:
184 // Free up allocated memory
185 if (pht != NULL)
186 free(pht);
187
188 return res;
189 }
190
perform_relocation(struct elf_module * module,Elf_Rel * rel)191 int perform_relocation(struct elf_module *module, Elf_Rel *rel) {
192 Elf64_Xword *dest = module_get_absolute(rel->r_offset, module);
193
194 // The symbol reference index
195 Elf64_Word sym = ELF64_R_SYM(rel->r_info);
196 unsigned char type = ELF64_R_TYPE(rel->r_info);
197
198 // The symbol definition (if applicable)
199 Elf64_Sym *sym_def = NULL;
200 struct elf_module *sym_module = NULL;
201 Elf64_Addr sym_addr = 0x0;
202
203 if (sym > 0) {
204 // Find out details about the symbol
205
206 // The symbol reference
207 Elf64_Sym *sym_ref = symbol_get_entry(module, sym);
208
209 // The symbol definition
210 sym_def =
211 global_find_symbol(module->str_table + sym_ref->st_name,
212 &sym_module);
213
214 if (sym_def == NULL) {
215 DBG_PRINT("Cannot perform relocation for symbol %s\n",
216 module->str_table + sym_ref->st_name);
217
218 if (ELF64_ST_BIND(sym_ref->st_info) != STB_WEAK)
219 return -1;
220
221 // This must be a derivative-specific
222 // function. We're OK as long as we never
223 // execute the function.
224 sym_def = global_find_symbol("undefined_symbol", &sym_module);
225 }
226
227 // Compute the absolute symbol virtual address
228 sym_addr = (Elf64_Addr)module_get_absolute(sym_def->st_value, sym_module);
229
230 if (sym_module != module) {
231 // Create a dependency
232 enforce_dependency(sym_module, module);
233 }
234 }
235
236 switch (type) {
237 case R_X86_64_NONE:
238 // Do nothing
239 break;
240 case R_X86_64_64:
241 *dest += sym_addr;
242 break;
243 case R_X86_64_PC32:
244 *dest += sym_addr - (Elf32_Addr)dest;
245 break;
246 case R_X86_64_COPY:
247 if (sym_addr > 0) {
248 memcpy((void*)dest, (void*)sym_addr, sym_def->st_size);
249 }
250 break;
251 case R_X86_64_GLOB_DAT:
252 case R_X86_64_JUMP_SLOT:
253 //Maybe TODO: Keep track of the GOT entries allocations
254 *dest = sym_addr;
255 break;
256 case R_X86_64_RELATIVE:
257 *dest += module->base_addr;
258 break;
259 default:
260 DBG_PRINT("Relocation type %d not supported\n", type);
261 return -1;
262 }
263
264 return 0;
265 }
266
resolve_symbols(struct elf_module * module)267 int resolve_symbols(struct elf_module *module) {
268 Elf64_Dyn *dyn_entry = module->dyn_table;
269 unsigned int i;
270 int res;
271
272 Elf64_Word plt_rel_size = 0;
273 void *plt_rel = NULL;
274
275 void *rel = NULL;
276 Elf64_Word rel_size = 0;
277 Elf64_Word rel_entry = 0;
278 Elf64_Xword rela_size = 0;
279 Elf64_Xword rela_entry = 0;
280 Elf64_Xword sym_ent = 0;
281
282 // The current relocation
283 Elf64_Rel *crt_rel;
284
285 while (dyn_entry->d_tag != DT_NULL) {
286 switch(dyn_entry->d_tag) {
287
288 // PLT relocation information
289 case DT_PLTRELSZ:
290 plt_rel_size = dyn_entry->d_un.d_val;
291 break;
292 case DT_PLTREL:
293 if (dyn_entry->d_un.d_val != DT_REL && dyn_entry->d_un.d_val != DT_RELA) {
294 DBG_PRINT("Unsupported PLT relocation\n");
295 return -1;
296 }
297 //break;
298 case DT_JMPREL:
299 plt_rel = module_get_absolute(dyn_entry->d_un.d_ptr, module);
300 break;
301
302 // Standard relocation information
303 case DT_REL:
304 rel = module_get_absolute(dyn_entry->d_un.d_ptr, module);
305 break;
306 case DT_RELA:
307 rel = module_get_absolute(dyn_entry->d_un.d_ptr, module);
308 break;
309 case DT_RELSZ:
310 rel_size = dyn_entry->d_un.d_val;
311 break;
312 case DT_RELASZ:
313 rela_size = dyn_entry->d_un.d_val;
314 break;
315 case DT_RELENT:
316 rel_entry = dyn_entry->d_un.d_val;
317 break;
318 case DT_RELAENT:
319 rela_entry = dyn_entry->d_un.d_val;
320 break;
321 /* FIXME: We may need to rely upon SYMENT if DT_RELAENT is missing in the object file */
322 case DT_SYMENT:
323 sym_ent = dyn_entry->d_un.d_val;
324 break;
325
326 // Module initialization and termination
327 case DT_INIT:
328 // TODO Implement initialization functions
329 break;
330 case DT_FINI:
331 // TODO Implement finalization functions
332 break;
333 }
334
335 dyn_entry++;
336 }
337
338 if (rel_size > 0) {
339 // Process standard relocations
340 for (i = 0; i < rel_size/rel_entry; i++) {
341 crt_rel = (Elf64_Rel*)(rel + i*rel_entry);
342
343 res = perform_relocation(module, crt_rel);
344
345 if (res < 0)
346 return res;
347 }
348
349 }
350
351 if (rela_size > 0) {
352 // Process standard relocations
353 for (i = 0; i < rela_size/rela_entry; i++) {
354 crt_rel = (Elf64_Rel*)(rel + i*rela_entry);
355
356 res = perform_relocation(module, crt_rel);
357
358 if (res < 0)
359 return res;
360 }
361 }
362 if (plt_rel_size > 0) {
363 // TODO: Permit this lazily
364 // Process PLT relocations
365 /* some modules do not have DT_SYMENT, set it sym_ent in such cases */
366 if (!rela_entry) rela_entry = sym_ent;
367 //for (i = 0; i < plt_rel_size/sizeof(Elf64_Rel); i++) {
368 for (i = 0; i < plt_rel_size/rela_entry; i++) {
369 //crt_rel = (Elf64_Rel*)(plt_rel + i*sizeof(Elf64_Rel));
370 crt_rel = (Elf64_Rel*)(plt_rel + i*rela_entry);
371
372 res = perform_relocation(module, crt_rel);
373
374 if (res < 0)
375 return res;
376 }
377 }
378
379 return 0;
380 }
381