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 */
29int 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
183out:
184	// Free up allocated memory
185	if (pht != NULL)
186		free(pht);
187
188	return res;
189}
190
191int 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
267int 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