• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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