/* 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; }