/*
* elf_module.c
*
* Created on: Aug 11, 2008
* Author: Stefan Bucur <stefanb@zytor.com>
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <elf.h>
#include <dprintf.h>
#include <core.h>
#include <linux/list.h>
#include <sys/module.h>
#include <sys/exec.h>
#include "elfutils.h"
#include "common.h"
static int check_header(Elf_Ehdr *elf_hdr) {
int res;
res = check_header_common(elf_hdr);
if (res != 0)
return res;
if (elf_hdr->e_type != MODULE_ELF_TYPE) {
dprintf("The ELF file must be a shared object\n");
return -1;
}
if (elf_hdr->e_phoff == 0x00000000) {
dprintf("PHT missing\n");
return -1;
}
return 0;
}
/*
*
* The implementation assumes that the loadable segments are present
* in the PHT sorted by their offsets, so that only forward seeks would
* be necessary.
*/
extern int load_segments(struct elf_module *module, Elf_Ehdr *elf_hdr);
static int prepare_dynlinking(struct elf_module *module) {
Elf_Dyn *dyn_entry = module->dyn_table;
while (dyn_entry->d_tag != DT_NULL) {
switch (dyn_entry->d_tag) {
case DT_NEEDED:
/*
* It's unlikely there'll be more than
* MAX_NR_DEPS DT_NEEDED entries but if there
* are then inform the user that we ran out of
* space.
*/
if (module->nr_needed < MAX_NR_DEPS)
module->needed[module->nr_needed++] = dyn_entry->d_un.d_ptr;
else {
printf("Too many dependencies!\n");
return -1;
}
break;
case DT_HASH:
module->hash_table =
(Elf_Word*)module_get_absolute(dyn_entry->d_un.d_ptr, module);
break;
case DT_GNU_HASH:
module->ghash_table =
(Elf_Word*)module_get_absolute(dyn_entry->d_un.d_ptr, module);
break;
case DT_STRTAB:
module->str_table =
(char*)module_get_absolute(dyn_entry->d_un.d_ptr, module);
break;
case DT_SYMTAB:
module->sym_table =
module_get_absolute(dyn_entry->d_un.d_ptr, module);
break;
case DT_STRSZ:
module->strtable_size = dyn_entry->d_un.d_val;
break;
case DT_SYMENT:
module->syment_size = dyn_entry->d_un.d_val;
break;
case DT_PLTGOT: // The first entry in the GOT
module->got = module_get_absolute(dyn_entry->d_un.d_ptr, module);
break;
}
dyn_entry++;
}
return 0;
}
void undefined_symbol(void)
{
printf("Error: An undefined symbol was referenced\n");
kaboom();
}
extern int perform_relocation(struct elf_module *module, Elf_Rel *rel);
extern int resolve_symbols(struct elf_module *module);
static int extract_operations(struct elf_module *module) {
Elf_Sym *ctors_start, *ctors_end;
Elf_Sym *dtors_start, *dtors_end;
module_ctor_t *ctors = NULL;
module_ctor_t *dtors = NULL;
ctors_start = module_find_symbol("__ctors_start", module);
ctors_end = module_find_symbol("__ctors_end", module);
if (ctors_start && ctors_end) {
module_ctor_t *start, *end;
int nr_ctors = 0;
int i, size;
start = module_get_absolute(ctors_start->st_value, module);
end = module_get_absolute(ctors_end->st_value, module);
nr_ctors = end - start;
size = nr_ctors * sizeof(module_ctor_t);
size += sizeof(module_ctor_t); /* NULL entry */
ctors = malloc(size);
if (!ctors) {
printf("Unable to alloc memory for ctors\n");
return -1;
}
memset(ctors, 0, size);
for (i = 0; i < nr_ctors; i++)
ctors[i] = start[i];
module->ctors = ctors;
}
dtors_start = module_find_symbol("__dtors_start", module);
dtors_end = module_find_symbol("__dtors_end", module);
if (dtors_start && dtors_end) {
module_ctor_t *start, *end;
int nr_dtors = 0;
int i, size;
start = module_get_absolute(dtors_start->st_value, module);
end = module_get_absolute(dtors_end->st_value, module);
nr_dtors = end - start;
size = nr_dtors * sizeof(module_ctor_t);
size += sizeof(module_ctor_t); /* NULL entry */
dtors = malloc(size);
if (!dtors) {
printf("Unable to alloc memory for dtors\n");
free(ctors);
return -1;
}
memset(dtors, 0, size);
for (i = 0; i < nr_dtors; i++)
dtors[i] = start[i];
module->dtors = dtors;
}
return 0;
}
// Loads the module into the system
int module_load(struct elf_module *module) {
int res;
Elf_Sym *main_sym;
Elf_Ehdr elf_hdr;
module_ctor_t *ctor;
struct elf_module *head = NULL;
// Do not allow duplicate modules
if (module_find(module->name) != NULL) {
dprintf("Module %s is already loaded.\n", module->name);
return EEXIST;
}
// Get a mapping/copy of the ELF file in memory
res = image_load(module);
if (res < 0) {
dprintf("Image load failed for %s\n", module->name);
return res;
}
// The module is a fully featured dynamic library
module->shallow = 0;
CHECKED(res, image_read(&elf_hdr, sizeof(Elf_Ehdr), module), error);
//printf("check... 1\n");
//print_elf_ehdr(&elf_hdr);
// Checking the header signature and members
CHECKED(res, check_header(&elf_hdr), error);
//printf("check... 2\n");
// Load the segments in the memory
CHECKED(res, load_segments(module, &elf_hdr), error);
//printf("bleah... 3\n");
// Obtain dynamic linking information
CHECKED(res, prepare_dynlinking(module), error);
//printf("check... 4\n");
head = module_current();
/* Find modules we need to load as dependencies */
if (module->str_table) {
int i;
/*
* Note that we have to load the dependencies in
* reverse order.
*/
for (i = module->nr_needed - 1; i >= 0; i--) {
char *dep, *p;
char *argv[2] = { NULL, NULL };
dep = module->str_table + module->needed[i];
/* strip everything but the last component */
if (!strlen(dep))
continue;
if (strchr(dep, '/')) {
p = strrchr(dep, '/');
p++;
} else
p = dep;
argv[0] = p;
res = spawn_load(p, 1, argv);
if (res < 0) {
printf("Failed to load %s\n", p);
goto error;
}
}
}
// Check the symbols for duplicates / missing definitions
CHECKED(res, check_symbols(module), error);
//printf("check... 5\n");
main_sym = module_find_symbol("main", module);
if (main_sym)
module->main_func =
module_get_absolute(main_sym->st_value, module);
//printf("check... 6\n");
// Add the module at the beginning of the module list
list_add(&module->list, &modules_head);
// Perform the relocations
resolve_symbols(module);
// Obtain constructors and destructors
CHECKED(res, extract_operations(module), error);
//dprintf("module->symtable_size = %d\n", module->symtable_size);
//print_elf_symbols(module);
// The file image is no longer needed
image_unload(module);
/*
dprintf("MODULE %s LOADED SUCCESSFULLY (main@%p, init@%p, exit@%p)\n",
module->name,
(module->main_func == NULL) ? NULL : *(module->main_func),
(module->init_func == NULL) ? NULL : *(module->init_func),
(module->exit_func == NULL) ? NULL : *(module->exit_func));
*/
for (ctor = module->ctors; ctor && *ctor; ctor++)
(*ctor) ();
return 0;
error:
if (head)
unload_modules_since(head->name);
// Remove the module from the module list (if applicable)
list_del_init(&module->list);
if (module->module_addr != NULL) {
elf_free(module->module_addr);
module->module_addr = NULL;
}
image_unload(module);
// Clear the execution part of the module buffer
memset(&module->u, 0, sizeof module->u);
return res;
}