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 | */ |
---|
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 | Elf32_Phdr *cr_pht; |
---|
35 | Elf32_Shdr *cr_sht; |
---|
36 | |
---|
37 | Elf32_Addr min_addr = 0x00000000; // Min. ELF vaddr |
---|
38 | Elf32_Addr max_addr = 0x00000000; // Max. ELF vaddr |
---|
39 | Elf32_Word max_align = sizeof(void*); // Min. align of posix_memalign() |
---|
40 | Elf32_Addr min_alloc, max_alloc; // Min. and max. aligned allocables |
---|
41 | |
---|
42 | Elf32_Addr dyn_addr = 0x00000000; |
---|
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 = (Elf32_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 = (Elf32_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 = (Elf32_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 | Elf32_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 | (Elf32_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 = (Elf32_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 | |
---|
191 | int perform_relocation(struct elf_module *module, Elf_Rel *rel) { |
---|
192 | Elf32_Word *dest = module_get_absolute(rel->r_offset, module); |
---|
193 | |
---|
194 | // The symbol reference index |
---|
195 | Elf32_Word sym = ELF32_R_SYM(rel->r_info); |
---|
196 | unsigned char type = ELF32_R_TYPE(rel->r_info); |
---|
197 | |
---|
198 | // The symbol definition (if applicable) |
---|
199 | Elf32_Sym *sym_def = NULL; |
---|
200 | struct elf_module *sym_module = NULL; |
---|
201 | Elf32_Addr sym_addr = 0x0; |
---|
202 | |
---|
203 | if (sym > 0) { |
---|
204 | // Find out details about the symbol |
---|
205 | |
---|
206 | // The symbol reference |
---|
207 | Elf32_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 (ELF32_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 = (Elf32_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_386_NONE: |
---|
238 | // Do nothing |
---|
239 | break; |
---|
240 | case R_386_32: |
---|
241 | *dest += sym_addr; |
---|
242 | break; |
---|
243 | case R_386_PC32: |
---|
244 | *dest += sym_addr - (Elf32_Addr)dest; |
---|
245 | break; |
---|
246 | case R_386_COPY: |
---|
247 | if (sym_addr > 0) { |
---|
248 | memcpy((void*)dest, (void*)sym_addr, sym_def->st_size); |
---|
249 | } |
---|
250 | break; |
---|
251 | case R_386_GLOB_DAT: |
---|
252 | case R_386_JMP_SLOT: |
---|
253 | // Maybe TODO: Keep track of the GOT entries allocations |
---|
254 | *dest = sym_addr; |
---|
255 | break; |
---|
256 | case R_386_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 | |
---|
267 | int resolve_symbols(struct elf_module *module) { |
---|
268 | Elf32_Dyn *dyn_entry = module->dyn_table; |
---|
269 | unsigned int i; |
---|
270 | int res; |
---|
271 | |
---|
272 | Elf32_Word plt_rel_size = 0; |
---|
273 | char *plt_rel = NULL; |
---|
274 | |
---|
275 | char *rel = NULL; |
---|
276 | Elf32_Word rel_size = 0; |
---|
277 | Elf32_Word rel_entry = 0; |
---|
278 | |
---|
279 | // The current relocation |
---|
280 | Elf32_Rel *crt_rel; |
---|
281 | |
---|
282 | while (dyn_entry->d_tag != DT_NULL) { |
---|
283 | switch(dyn_entry->d_tag) { |
---|
284 | |
---|
285 | // PLT relocation information |
---|
286 | case DT_PLTRELSZ: |
---|
287 | plt_rel_size = dyn_entry->d_un.d_val; |
---|
288 | break; |
---|
289 | case DT_PLTREL: |
---|
290 | if (dyn_entry->d_un.d_val != DT_REL) { |
---|
291 | DBG_PRINT("Unsupported PLT relocation\n"); |
---|
292 | return -1; |
---|
293 | } |
---|
294 | case DT_JMPREL: |
---|
295 | plt_rel = module_get_absolute(dyn_entry->d_un.d_ptr, module); |
---|
296 | break; |
---|
297 | |
---|
298 | // Standard relocation information |
---|
299 | case DT_REL: |
---|
300 | rel = module_get_absolute(dyn_entry->d_un.d_ptr, module); |
---|
301 | break; |
---|
302 | case DT_RELSZ: |
---|
303 | rel_size = dyn_entry->d_un.d_val; |
---|
304 | break; |
---|
305 | case DT_RELENT: |
---|
306 | rel_entry = dyn_entry->d_un.d_val; |
---|
307 | break; |
---|
308 | |
---|
309 | // Module initialization and termination |
---|
310 | case DT_INIT: |
---|
311 | // TODO Implement initialization functions |
---|
312 | break; |
---|
313 | case DT_FINI: |
---|
314 | // TODO Implement finalization functions |
---|
315 | break; |
---|
316 | } |
---|
317 | |
---|
318 | dyn_entry++; |
---|
319 | } |
---|
320 | |
---|
321 | if (rel_size > 0) { |
---|
322 | // Process standard relocations |
---|
323 | for (i = 0; i < rel_size/rel_entry; i++) { |
---|
324 | crt_rel = (Elf32_Rel*)(rel + i*rel_entry); |
---|
325 | |
---|
326 | res = perform_relocation(module, crt_rel); |
---|
327 | |
---|
328 | if (res < 0) |
---|
329 | return res; |
---|
330 | } |
---|
331 | |
---|
332 | } |
---|
333 | |
---|
334 | if (plt_rel_size > 0) { |
---|
335 | // TODO: Permit this lazily |
---|
336 | // Process PLT relocations |
---|
337 | for (i = 0; i < plt_rel_size/sizeof(Elf32_Rel); i++) { |
---|
338 | crt_rel = (Elf32_Rel*)(plt_rel + i*sizeof(Elf32_Rel)); |
---|
339 | |
---|
340 | res = perform_relocation(module, crt_rel); |
---|
341 | |
---|
342 | if (res < 0) |
---|
343 | return res; |
---|
344 | } |
---|
345 | } |
---|
346 | |
---|
347 | return 0; |
---|
348 | } |
---|
349 | |
---|