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	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
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	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
267int 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