-
Alessandro Di Federico authoredAlessandro Di Federico authored
elf-object.c 16.26 KiB
#include <string.h>
#include <stdlib.h>
#include "eld.h"
#include "support.h"
typedef unsigned long elf_hash_t;
typedef void (*t_init_function)(void);
typedef void (*t_fini_function)(void);
typedef Elf_Addr Elf_Addr_Unaligned __attribute__((aligned(1)));
/**
* ELF hash function for quick symbol lookup
*
* @param cursor the name of the symbol to hash.
*
* @return the hash of the input symbol name.
*/
static elf_hash_t eld_elf_hash(char *cursor) {
elf_hash_t result = 0;
while (*cursor) {
elf_hash_t tmp;
result = (result << 4) + *cursor++;
if ((tmp = result & 0xf0000000))
result ^= tmp >> 24;
result &= ~tmp;
}
return result;
}
elf_object_t * eld_elf_object_new(char *soname, int length) {
elf_object_t *new_elf;
new_elf = calloc(sizeof (char), sizeof (elf_object_t));
if (!new_elf) return NULL;
new_elf->soname = soname;
return new_elf;
}
void eld_elf_object_destroy(elf_object_t *this) {
if (!this) return;
// Call the finalization function of the shared object
t_fini_function fini_function =
(t_fini_function) this->dynamic_info.basic[DT_FINI].d_ptr;
if (fini_function && this->dynamic_info_section != &_DYNAMIC) {
DBG_MSG("Calling fini_function(): %p", fini_function);
fini_function();
DBG_MSG("fini_function called");
// Free the allocated memory
free(this->load_address);
}
free(this);
}
/**
* Get the symbol matching a specified name.
*
* @param this the input ELF object descriptor.
* @param target_name symbol name.
* @param target_hash hash of the symbol name.
* @param target_symbol [out] pointer where the matching symbol will be stored.
* @param weak_symbol [out] pointer where a weak matching symbol will be stored.
* @param weak_elf [out] pointer to where a weak matching ELF will be stored.
*
* @return zero, if success, non-zero otherwise.
*/
static int eld_elf_object_get_symbol(elf_object_t *this, char *target_name,
elf_hash_t target_hash,
Elf_Sym **target_symbol,
Elf_Sym **weak_symbol,
elf_object_t **weak_elf) {
CHECK_ARGS(this && target_name && target_symbol && weak_symbol && weak_elf &&
this->dynamic_info.hash_buckets && this->dynamic_info.hash_chains);
// TODO: lookup cache
// TOOD: gnu hash
Elf_Word item = this->dynamic_info.hash_buckets[target_hash %
this->dynamic_info.hash_nbuckets];
for (; item != STN_UNDEF; item = this->dynamic_info.hash_chains[item]) {
Elf_Sym *symbol = this->symtab + item;
if ((!symbol->st_value) ||
(ELF_ST_TYPE(symbol->st_info) != STT_NOTYPE &&
ELF_ST_TYPE(symbol->st_info) != STT_OBJECT &&
ELF_ST_TYPE(symbol->st_info) != STT_FUNC)) {
continue;
}
char *symbol_name = this->strtab + symbol->st_name;
// Not sure about the first part of the check, *target_symbol should be NULL
// in input
if (symbol != *target_symbol && strcmp(symbol_name, target_name)) {
continue;
}
// TODO: implement flags
/*
if (symbol->st_shndx == SHN_UNDEF) {
if ((flags & SYM_PLT) || symbol->st_value == 0 ||
ELF_ST_TYPE(symbol->st_info) != STT_FUNC)
continue;
}
*/
if (ELF_ST_BIND(symbol->st_info) == STB_GLOBAL) {
*target_symbol = symbol;
return SUCCESS;
} else if (ELF_ST_BIND(symbol->st_info) == STB_WEAK) {
//
if (!*weak_symbol) {
*weak_symbol = symbol;
*weak_elf = this;
return ERROR_WEAK_RESULT;
}
}
}
return ERROR_SYMBOL_NOT_FOUND;
}
/**
* Look for a symbol in the specified ELF or in one of those it is
* depending on.
*
* @param this the input ELF object descriptor.
* @param name symbol name to search.
* @param hash hash of the symbol name.
* @param match [out] pointer where the matching symbol will be stored.
* @param match_elf [out] pointer to where the matching ELF symbol
* will be stored.
*
* @return zero, if success, non-zero otherwise.
*/
static int eld_elf_object_find_symbol(elf_object_t *this, char *name,
elf_hash_t hash,
Elf_Sym **match,
elf_object_t **match_elf) {
CHECK_ARGS(name && match && match_elf);
// TODO: implement full search in the right order
// This is a simplified search order, first in the current library, then in
// all the others in load order
Elf_Sym *weak_match = NULL;
elf_object_t *weak_match_elf = NULL;
int result = ERROR_SYMBOL_NOT_FOUND;
if (this) {
DBG_MSG("Looking for symbol \"%s\" in the library \"%s\" itself", name,
this->soname);
if ((result =
eld_elf_object_get_symbol(this, name, hash, match,
&weak_match, &weak_match_elf)) == SUCCESS) {
// TODO: is the following correct?
*match_elf = this;
DBG_MSG("Symbol \"%s\" found in the library \"%s\" itself", name,
this->soname);
} else if (result != ERROR_SYMBOL_NOT_FOUND && result != ERROR_WEAK_RESULT) {
return result;
}
}
// Look in all the other elves
elf_object_t *loaded_elf = NULL;
SLIST_FOREACH(loaded_elf, &elves, next) {
// Don't check again the suggested ELF
if (loaded_elf == this) continue;
DBG_MSG("Looking for symbol \"%s\" in the \"%s\" library", name,
loaded_elf->soname);
if ((result = eld_elf_object_get_symbol(loaded_elf, name, hash, match,
&weak_match,
&weak_match_elf)) == SUCCESS) {
*match_elf = loaded_elf;
DBG_MSG("Symbol \"%s\" found in the \"%s\" library", name,
loaded_elf->soname);
break;
} else if (result != ERROR_SYMBOL_NOT_FOUND &&
result != ERROR_WEAK_RESULT) {
return result;
}
}
if (!*match && weak_match) {
DBG_MSG("Symbol \"%s\" has a weak match in \"%s\" library", name,
weak_match_elf->soname);
*match = weak_match;
*match_elf = weak_match_elf;
}
if (!match) DBG_MSG("Symbol \"%s\" not found", name);
return match ? SUCCESS : ERROR_SYMBOL_NOT_FOUND;
}
int eld_elf_object_find_symbol_by_name(elf_object_t *this, char *name,
Elf_Sym **match,
elf_object_t **match_elf) {
return eld_elf_object_find_symbol(this, name, eld_elf_hash(name), match,
match_elf);
}
/**
* Relocate symbols listed in an ELF section.
*
* @param this the input ELF object descriptor.
* @param reloc_index index of the relocation section.
* @param reloc_size_index size of the relocation section.
*
* @return zero, if success, non-zero otherwise.
*/
static int eld_elf_object_relocate(elf_object_t *this,
int reloc_index, int reloc_size_index) {
CHECK_ARGS(this);
int result = SUCCESS;
// We only support relocation with addend
int reloc_count =
this->dynamic_info.basic[reloc_size_index].d_val / sizeof (Elf_Rela);
int relative_reloc_count = (reloc_index == DT_RELATIVE_RELOC) ?
this->dynamic_info.relative_reloc_count : 0;
Elf_Rela *first_reloc =
(Elf_Rela *) this->dynamic_info.basic[reloc_index].d_ptr;
DBG_MSG("Relocating section %d (size: %d)", reloc_index, reloc_count);
int i = 0;
Elf_Rela *reloc = first_reloc;
Elf_Addr *patch_location = NULL;
for (; i < relative_reloc_count && i < reloc_count; reloc++, i++) {
#ifndef NDEBUG
if (ELF_R_TYPE(reloc->r_info) != R_OR1K_RELATIVE) {
DBG_MSG("The %dth relocation is not relative, while the first %d should.",
i, relative_reloc_count);
return ERROR_GENERIC;
}
#endif
patch_location = (Elf_Addr *) (reloc->r_offset + this->elf_offset);
*patch_location += (uintptr_t) this->elf_offset;
}
// Continue from the index left from the previous loop
for (; i < reloc_count; reloc++, i++) {
int type = ELF_R_TYPE(reloc->r_info);
Elf_Sym *symbol = &this->symtab[ELF_R_SYM(reloc->r_info)];
char *name = this->strtab + symbol->st_name;
patch_location = (Elf_Addr *) (reloc->r_offset + this->elf_offset);
// Compute the hash
elf_hash_t hash = eld_elf_hash(name);
// Look for the symbol
Elf_Sym *match = NULL;
elf_object_t *match_elf = NULL;
RETURN_ON_ERROR(eld_elf_object_find_symbol(this, name, hash, &match,
&match_elf));
Elf_Addr symbol_address = (Elf_Addr) (match_elf->elf_offset + match->st_value);
switch (type) {
case R_OR1K_NONE:
break;
case R_OR1K_8:
case R_OR1K_16:
case R_OR1K_32:
// Support relocations on misaligned offsets
*((Elf_Addr_Unaligned *) patch_location) = symbol_address +
reloc->r_addend;
break;
case R_OR1K_8_PCREL:
case R_OR1K_16_PCREL:
case R_OR1K_32_PCREL:
case R_OR1K_INSN_REL_26:
*patch_location = symbol_address + reloc->r_addend;
break;
case R_OR1K_GLOB_DAT:
case R_OR1K_JMP_SLOT:
*patch_location = symbol_address + reloc->r_addend;
break;
case R_OR1K_COPY:
if (symbol_address) {
memcpy(patch_location, (void *) symbol_address, match->st_size);
}
break;
default:
return ERROR_UNKNOWN_RELOCATION_TYPE;
}
}
return SUCCESS;
}
int eld_elf_object_check(elf_object_t *this) {
CHECK_ARGS(this && this->file_address);
Elf_Ehdr *elf_header = (Elf_Ehdr *) this->file_address;
if (elf_header->e_ident[EI_MAG0] != ELFMAG0 ||
elf_header->e_ident[EI_MAG1] != ELFMAG1 ||
elf_header->e_ident[EI_MAG2] != ELFMAG2 ||
elf_header->e_ident[EI_MAG3] != ELFMAG3 ||
elf_header->e_type != ET_DYN ||
elf_header->e_machine != EM_OPENRISC ||
elf_header->e_ident[EI_DATA] != ELFDATA2MSB) {
DBG_MSG("Not an OpenRISC big-endian ELF dynamic shared object.");
return ERROR_UNEXPECTED_FORMAT;
}
return SUCCESS;
}
int eld_elf_object_load(elf_object_t *this) {
CHECK_ARGS(this && this->file_address);
Elf_Ehdr *elf_header = (Elf_Ehdr *) this->file_address;
Elf_Phdr *program_header_begin =
(Elf_Phdr *) (this->file_address + elf_header->e_phoff);
Elf_Phdr *program_header_end = program_header_begin + elf_header->e_phnum;
Elf_Addr min_address = 0, max_address = 0;
this->dynamic_info_section = NULL;
for (Elf_Phdr *program_header = program_header_begin;
program_header < program_header_end; program_header++) {
switch (program_header->p_type) {
case PT_LOAD:
if (program_header->p_vaddr < min_address) {
min_address = program_header->p_vaddr;
}
if (program_header->p_vaddr + program_header->p_memsz > max_address) {
max_address = program_header->p_vaddr + program_header->p_memsz;
}
break;
case PT_DYNAMIC:
this->dynamic_info_section = (Elf_Dyn *) program_header->p_vaddr;
break;
default:
// Ignore
break;
}
}
if (!max_address) {
DBG_MSG("There's nothing to load in the ELF");
return ERROR_GENERIC;
}
Elf_MemSz to_allocate = max_address - min_address;
this->load_address = malloc(to_allocate);
if (!this->load_address) {
DBG_MSG("Cannot allocate the necessary memory (0x%x bytes)",
(unsigned int) to_allocate);
return ERROR_OUT_OF_MEMORY;
} else {
DBG_MSG("The library has been loaded at %p", this->load_address);
}
this->elf_offset = this->load_address - min_address;
// Load from file
for (Elf_Phdr *program_header = program_header_begin;
program_header < program_header_end; program_header++) {
if (program_header->p_type == PT_LOAD) {
// Do we have something to take from the file?
if (program_header->p_filesz > 0) {
memcpy(this->elf_offset + program_header->p_vaddr,
this->file_address + program_header->p_offset,
program_header->p_filesz);
}
// If there's nothing to take from the file or in any case less than
// what must be in memory, zero-fill
if (program_header->p_filesz < program_header->p_memsz) {
memset(this->elf_offset + program_header->p_vaddr +
program_header->p_filesz,
0, program_header->p_memsz - program_header->p_filesz);
}
}
}
// Update pointers
this->dynamic_info_section = (Elf_Dyn *) (this->elf_offset +
(Elf_Addr) this->dynamic_info_section);
return SUCCESS;
}
int eld_elf_object_handle_dyn(elf_object_t *this) {
CHECK_ARGS(this && this->dynamic_info_section);
int result = SUCCESS;
// First pass over the dynamic entries
for (Elf_Dyn *dynamic_info_entry = this->dynamic_info_section;
dynamic_info_entry->d_tag != DT_NULL;
dynamic_info_entry++) {
// Store in an array (< DT_NUM) or structure
if (dynamic_info_entry->d_tag < DT_NUM) {
this->dynamic_info.basic[dynamic_info_entry->d_tag].d_ptr =
dynamic_info_entry->d_un.d_ptr;
} else if (dynamic_info_entry->d_tag == DT_RELATIVE_RELOC_COUNT) {
this->dynamic_info.relative_reloc_count = dynamic_info_entry->d_un.d_val;
}
}
// These entries are addresses and therefore require relocation
int to_rebase[] = {DT_PLTGOT, DT_HASH, DT_STRTAB, DT_SYMTAB, DT_RELA,
DT_REL, DT_INIT, DT_FINI, DT_JMPREL};
for (unsigned int counter = 0;
counter < sizeof (to_rebase) / sizeof (int); counter++) {
if (this->dynamic_info.basic[to_rebase[counter]].d_ptr) {
this->dynamic_info.basic[to_rebase[counter]].d_ptr +=
(Elf_Addr) this->elf_offset;
}
}
this->strtab = (char *) this->dynamic_info.basic[DT_STRTAB].d_ptr;
this->symtab = (Elf_Sym *) this->dynamic_info.basic[DT_SYMTAB].d_ptr;
// Lookups in the string table
if (this->dynamic_info.basic[DT_SONAME].d_ptr) {
this->dynamic_info.basic[DT_SONAME].d_ptr += (Elf_Addr) this->strtab;
this->soname = (char *) this->dynamic_info.basic[DT_SONAME].d_ptr;
DBG_MSG("Loading \"%s\"", (char *) this->soname);
}
if (this->dynamic_info.basic[DT_RPATH].d_ptr) {
this->dynamic_info.basic[DT_RPATH].d_ptr += (Elf_Addr) this->strtab;
}
// Look for dependencies
for (Elf_Dyn *dynamic_info_entry = this->dynamic_info_section;
dynamic_info_entry->d_tag != DT_NULL;
dynamic_info_entry++) {
// If there's a dependency
if (dynamic_info_entry->d_tag == DT_NEEDED &&
dynamic_info_entry->d_un.d_ptr) {
int lib_found = 0;
// Take the name of the library from the string table
char *needed_lib_name = this->strtab + dynamic_info_entry->d_un.d_ptr;
// Look for the library in the already loaded ELVes
elf_object_t *loaded_elf = NULL;
SLIST_FOREACH(loaded_elf, &elves, next) {
if (loaded_elf->soname &&
strncmp(loaded_elf->soname, needed_lib_name, 1024) == 0) {
lib_found = 1;
break;
}
}
if (!lib_found) {
DBG_MSG("Library %s has not been loaded", needed_lib_name);
return ERROR_LIB_NOT_FOUND;
}
}
}
if (this->dynamic_info.basic[DT_HASH].d_ptr) {
Elf_Word *hashtab = (Elf_Word *) this->dynamic_info.basic[DT_HASH].d_ptr;
this->dynamic_info.hash_nbuckets = hashtab[0];
this->dynamic_info.hash_nchains = hashtab[1];
this->dynamic_info.hash_buckets = hashtab + 2;
this->dynamic_info.hash_chains = this->dynamic_info.hash_buckets +
this->dynamic_info.hash_nbuckets;
}
// TODO: handle errors
RETURN_ON_ERROR(eld_elf_object_relocate(this, DT_REL, DT_RELSZ));
RETURN_ON_ERROR(eld_elf_object_relocate(this, DT_RELA, DT_RELASZ));
// TODO: implement lazy loading (see _dl_md_reloc_got)
RETURN_ON_ERROR(eld_elf_object_relocate(this, DT_JMPREL, DT_PLTRELSZ));
t_init_function init_function =
(t_init_function) this->dynamic_info.basic[DT_INIT].d_ptr;
if (init_function && this->dynamic_info_section != &_DYNAMIC) {
DBG_MSG("Calling init_function(): %p", init_function);
init_function();
DBG_MSG("init_function called");
}
return SUCCESS;
}
int eld_elf_object_is_registered(elf_object_t *this) {
elf_object_t *loaded_elf = NULL;
SLIST_FOREACH(loaded_elf, &elves, next) {
if (loaded_elf == this) return SUCCESS;
}
return ERROR_LIB_NOT_FOUND;
}
int eld_elf_object_close(elf_object_t *this) {
CHECK_ARGS(this);
int result = SUCCESS;
RETURN_ON_ERROR(eld_elf_object_is_registered(this));
SLIST_REMOVE(&elves, this, elf_object, next);
eld_elf_object_destroy(this);
return SUCCESS;
}