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 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
perform_relocation(struct elf_module * module,Elf_Rel * rel)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
resolve_symbols(struct elf_module * module)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
350