View of xos/usr/lib/ld-so/elf.c


XOS | Parent Directory | View | Download

/* 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 <http://www.gnu.org/licenses/>. */
 
#include "elf.h"
 
#include "xmalloc.h"
#include "error.h"
#include "sysvars.h"
 
#include <elf_ext.h>
 
/* 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;
}