/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include "elf.h"
#include "xmalloc.h"
#include "error.h"
#include "sysvars.h"
#include
/* Lecture et validation des structures ELF depuis un fichier */
static void fread_object(void *ptr, size_t size, size_t count, FILE *fp, const char *filename)
{
if (fread(ptr, size, count, fp) < count) {
if (!ferror(fp) && feof(fp))
file_error(filename, "file too short");
else
file_error(filename, "read error");
}
}
void fread_elf_header(Elf32_Ehdr *elf_header, FILE *fp, const char *filename)
{
fread_object(elf_header, sizeof (Elf32_Ehdr), 1, fp, filename);
/* verification de la validite du fichier */
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)
file_error(filename, "not an ELF file");
if (elf_header->e_ident[EI_CLASS] != ELFCLASS32
|| elf_header->e_ident[EI_DATA] != ELFDATA2LSB
|| elf_header->e_ident[EI_VERSION] != EV_CURRENT
|| elf_header->e_machine != EM_386
|| elf_header->e_version != EV_CURRENT)
file_error(filename, "cannot handle object file");
}
void fread_program_header_table(Elf32_Phdr *program_header_table, size_t count, FILE *fp, const char *filename)
{
fread_object(program_header_table, sizeof (Elf32_Phdr), count, fp, filename);
}
/* Lecture des structures ELF */
void clear_program_data(struct program_data_struct *program_data)
{
program_data->contents.program_header_table = 0;
program_data->contents.dynamic_array = 0;
program_data->contents.interpreter_name = 0;
}
void read_program_header_table(const Elf32_Phdr *program_header_table, int count, struct program_data_struct *program_data, const char *filename)
{
int loadable_segment_found;
int i;
const Elf32_Phdr *program_header;
clear_program_data(program_data);
loadable_segment_found = 0;
for (i = 0; i < count; i++)
switch ((program_header = &program_header_table[i])->p_type) {
case PT_NULL:
break;
case PT_LOAD:
if (program_header->p_align & (page_size - 1))
file_error(filename, "bad load alignment");
if ((program_header->p_vaddr - program_header->p_offset) & (program_header->p_align - 1))
file_error(filename, "bad segment alignment");
if (program_header->p_filesz > program_header->p_memsz)
file_error(filename, "bad segment size");
if (!loadable_segment_found) {
loadable_segment_found = 1;
program_data->load_vaddr = program_header->p_vaddr;
}
program_data->mem_size = program_header->p_vaddr + program_header->p_memsz - program_data->load_vaddr;
break;
case PT_DYNAMIC:
program_data->contents.dynamic_array = 1;
program_data->dynamic_array_vaddr = program_header->p_vaddr;
break;
case PT_INTERP:
program_data->contents.interpreter_name = 1;
program_data->interpreter_name_vaddr = program_header->p_vaddr;
break;
case PT_NOTE:
/* ignore */
break;
case PT_PHDR:
program_data->contents.program_header_table = 1;
program_data->program_header_table_vaddr = program_header->p_vaddr;
break;
case PT_TLS:
/* ignore */
break;
}
if (!loadable_segment_found)
file_error(filename, "no loadable segments");
}
void clear_dynamic_data(struct dynamic_data_struct *dynamic_data)
{
dynamic_data->string_table = NULL;
dynamic_data->soname = NULL;
dynamic_data->needed_list = NULL;
dynamic_data->needed_list_size = 0;
dynamic_data->rpath = NULL;
dynamic_data->runpath = NULL;
dynamic_data->symbol_table = NULL;
dynamic_data->symbol_hash_table = NULL;
dynamic_data->global_offset_table = NULL;
dynamic_data->symbolic = 0;
dynamic_data->textrel = 0;
dynamic_data->bind_now = 0;
dynamic_data->rel_relocation_table = NULL;
dynamic_data->rel_relocation_table_size = 0;
dynamic_data->rela_relocation_table = NULL;
dynamic_data->rela_relocation_table_size = 0;
dynamic_data->plt_relocation_type = RT_NONE;
dynamic_data->preinit_func_array = NULL;
dynamic_data->preinit_func_array_size = 0;
dynamic_data->init_func = NULL;
dynamic_data->init_func_array = NULL;
dynamic_data->init_func_array_size = 0;
dynamic_data->fini_func = NULL;
dynamic_data->fini_func_array = NULL;
dynamic_data->fini_func_array_size = 0;
}
void dispose_dynamic_data(struct dynamic_data_struct *dynamic_data)
{
if (dynamic_data->fini_func_array)
free(dynamic_data->fini_func_array);
if (dynamic_data->init_func_array)
free(dynamic_data->init_func_array);
if (dynamic_data->preinit_func_array)
free(dynamic_data->preinit_func_array);
if (dynamic_data->needed_list)
free(dynamic_data->needed_list);
}
void read_dynamic_array(const Elf32_Dyn *dynamic_array, void *base_address, struct dynamic_data_struct *dynamic_data)
{
const char *string_table;
int needed_idx;
int plt_relocation_type;
int preinit_array_size, init_array_size, fini_array_size;
int symbolic, symbolic_flag;
int textrel, textrel_flag;
int bind_now, bind_now_flag;
const Elf32_Dyn *dyn_entry;
const Elf32_Addr *initfini_array;
int flags;
int i;
/* Premiere passe: recuperation d'informations preliminaires. */
string_table = NULL;
needed_idx = 0;
plt_relocation_type = RT_NONE;
preinit_array_size = 0;
init_array_size = 0;
fini_array_size = 0;
for (dyn_entry = dynamic_array; dyn_entry->d_tag != DT_NULL; dyn_entry++)
switch (dyn_entry->d_tag) {
case DT_NEEDED:
needed_idx++;
break;
case DT_STRTAB:
string_table = base_address + dyn_entry->d_un.d_ptr;
break;
case DT_PLTREL:
switch (dyn_entry->d_un.d_val) {
case DT_REL:
plt_relocation_type = RT_REL;
break;
case DT_RELA:
plt_relocation_type = RT_RELA;
break;
}
break;
case DT_INIT_ARRAYSZ:
init_array_size = dyn_entry->d_un.d_val;
break;
case DT_FINI_ARRAYSZ:
fini_array_size = dyn_entry->d_un.d_val;
break;
case DT_PREINIT_ARRAYSZ:
preinit_array_size = dyn_entry->d_un.d_val;
break;
}
/* Seconde passe: remplissage de dynamic_data. */
clear_dynamic_data(dynamic_data);
dynamic_data->string_table = string_table;
if (needed_idx) {
dynamic_data->needed_list = xmalloc(needed_idx * sizeof (char *));
dynamic_data->needed_list_size = needed_idx;
}
dynamic_data->plt_relocation_type = plt_relocation_type;
switch (plt_relocation_type) {
case RT_REL:
dynamic_data->plt_relocation_table.rel = NULL;
break;
case RT_RELA:
dynamic_data->plt_relocation_table.rela = NULL;
break;
}
if (preinit_array_size) {
dynamic_data->preinit_func_array = xmalloc(init_array_size);
dynamic_data->preinit_func_array_size = preinit_array_size / sizeof (void (*)());
for (i = 0; i < dynamic_data->preinit_func_array_size; i++)
dynamic_data->preinit_func_array[i] = NULL;
}
if (init_array_size) {
dynamic_data->init_func_array = xmalloc(init_array_size);
dynamic_data->init_func_array_size = init_array_size / sizeof (void (*)());
for (i = 0; i < dynamic_data->init_func_array_size; i++)
dynamic_data->init_func_array[i] = NULL;
}
if (fini_array_size) {
dynamic_data->fini_func_array = xmalloc(fini_array_size);
dynamic_data->fini_func_array_size = fini_array_size / sizeof (void (*)());
for (i = 0; i < dynamic_data->fini_func_array_size; i++)
dynamic_data->fini_func_array[i] = NULL;
}
needed_idx = 0;
symbolic = symbolic_flag = 0;
textrel = textrel_flag = 0;
bind_now = bind_now_flag = 0;
for (dyn_entry = dynamic_array; dyn_entry->d_tag != DT_NULL; dyn_entry++)
switch (dyn_entry->d_tag) {
case DT_NEEDED:
dynamic_data->needed_list[needed_idx++] = string_table + dyn_entry->d_un.d_val;
break;
case DT_PLTRELSZ:
dynamic_data->plt_relocation_table_size = dyn_entry->d_un.d_val / sizeof (Elf32_Rel);
break;
case DT_PLTGOT:
dynamic_data->global_offset_table = base_address + dyn_entry->d_un.d_ptr;
break;
case DT_HASH:
dynamic_data->symbol_hash_table = base_address + dyn_entry->d_un.d_ptr;
break;
case DT_SYMTAB:
dynamic_data->symbol_table = base_address + dyn_entry->d_un.d_ptr;
break;
case DT_RELA:
dynamic_data->rela_relocation_table = base_address + dyn_entry->d_un.d_ptr;
break;
case DT_RELASZ:
dynamic_data->rela_relocation_table_size = dyn_entry->d_un.d_val / sizeof (Elf32_Rel);
break;
case DT_RELAENT:
/* ignore */
break;
case DT_STRSZ:
/* ignore */
break;
case DT_SYMENT:
/* ignore */
break;
case DT_INIT:
dynamic_data->init_func = base_address + dyn_entry->d_un.d_ptr;
break;
case DT_FINI:
dynamic_data->fini_func = base_address + dyn_entry->d_un.d_ptr;
break;
case DT_SONAME:
dynamic_data->soname = string_table + dyn_entry->d_un.d_val;
break;
case DT_RPATH:
dynamic_data->rpath = string_table + dyn_entry->d_un.d_val;
break;
case DT_SYMBOLIC:
symbolic = 1;
break;
case DT_REL:
dynamic_data->rel_relocation_table = base_address + dyn_entry->d_un.d_ptr;
break;
case DT_RELSZ:
dynamic_data->rel_relocation_table_size = dyn_entry->d_un.d_val / sizeof (Elf32_Rel);
break;
case DT_RELENT:
/* ignore */
break;
case DT_DEBUG:
/* ignore */
break;
case DT_TEXTREL:
textrel = 1;
break;
case DT_JMPREL:
switch (plt_relocation_type) {
case RT_REL:
dynamic_data->plt_relocation_table.rel = base_address + dyn_entry->d_un.d_ptr;
break;
case RT_RELA:
dynamic_data->plt_relocation_table.rela = base_address + dyn_entry->d_un.d_ptr;
break;
}
break;
case DT_BIND_NOW:
bind_now = 1;
break;
case DT_INIT_ARRAY:
initfini_array = base_address + dyn_entry->d_un.d_ptr;
for (i = 0; i < dynamic_data->init_func_array_size; i++)
dynamic_data->init_func_array[i] = base_address + initfini_array[i];
break;
case DT_FINI_ARRAY:
initfini_array = base_address + dyn_entry->d_un.d_ptr;
for (i = 0; i < dynamic_data->fini_func_array_size; i++)
dynamic_data->fini_func_array[i] = base_address + initfini_array[i];
break;
case DT_RUNPATH:
dynamic_data->runpath = string_table + dyn_entry->d_un.d_val;
break;
case DT_FLAGS:
flags = dyn_entry->d_un.d_val;
/* DF_ORIGIN ignore */
if (flags & DF_SYMBOLIC)
symbolic_flag = 1;
if (flags & DF_TEXTREL)
textrel_flag = 1;
if (flags & DF_BIND_NOW)
bind_now_flag = 1;
/* DF_STATIC_TLS ignore */
break;
case DT_PREINIT_ARRAY:
initfini_array = base_address + dyn_entry->d_un.d_ptr;
for (i = 0; i < dynamic_data->preinit_func_array_size; i++)
dynamic_data->preinit_func_array[i] = base_address + initfini_array[i];
break;
}
dynamic_data->symbolic = symbolic_flag ? : symbolic;
dynamic_data->textrel = textrel_flag ? : textrel;
dynamic_data->bind_now = bind_now_flag ? : bind_now;
}