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


XOS | Parent Directory | View | Download

/* Chargement de fichiers objets. */
/* 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 "load.h"
 
#include "object_info.h"
#include "elf.h"
#include "error.h"
#include "util.h"
#include "xmalloc.h"
 
#include <string.h>
#include <sys/mman.h>
#include <errno.h>
 
static int get_prot(int flags)
{
  int prot;
 
  prot = 0;
  if (flags & PF_X)
    prot |= PROT_EXEC;
  if (flags & PF_W)
    prot |= PROT_WRITE;
  if (flags & PF_R)
    prot |= PROT_READ;
  if (!prot)
    prot = PROT_NONE;
  return prot;
}
 
static void get_segments(Elf32_Phdr *program_header_table, int program_header_count, void *base_address, struct segment_info_struct **segment_table, int *segment_count)
{
  int i;
  Elf32_Phdr *program_header;
  off_t page_offset;
  Elf32_Addr vaddr;
  size_t mem_size;
 
  *segment_table = xmalloc(program_header_count * sizeof (struct segment_info_struct));
  *segment_count = 0;
  for (i = 0; i < program_header_count; i++)
    if ((program_header = &program_header_table[i])->p_type == PT_LOAD) {
      /* ajustement des adresses */
      page_offset = PAGE_OFFSET(program_header->p_vaddr);
      vaddr = program_header->p_vaddr - page_offset;
      mem_size = program_header->p_memsz + page_offset;
 
      /* ajout du segment */
      (*segment_table)[*segment_count].load_address = base_address + vaddr;
      (*segment_table)[*segment_count].size = mem_size;
      (*segment_table)[*segment_count].prot = get_prot(program_header->p_flags);
      (*segment_count)++;
    }
}
 
static void examine_bottom_half(Elf32_Phdr *program_header_table, int program_header_count, const struct program_data_struct *program_data, void *base_address, void *entry_point, const char *invocation_name, struct loaded_object_info_struct *loaded_object_info)
{
  struct segment_info_struct *segment_table;
  int segment_count;
 
  get_segments(program_header_table, program_header_count, base_address, &segment_table, &segment_count);
 
  loaded_object_info->base_address = base_address;
  loaded_object_info->load_address = base_address + PAGE(program_data->load_vaddr);
  loaded_object_info->segment_table = segment_table;
  loaded_object_info->segment_count = segment_count;
  loaded_object_info->entry_point = entry_point;
  loaded_object_info->dynamic_array = program_data->contents.dynamic_array ? base_address + program_data->dynamic_array_vaddr : NULL;
  loaded_object_info->interpreter_name = program_data->contents.interpreter_name ? base_address + program_data->interpreter_name_vaddr : NULL;
}
 
void examine_executable(Elf32_Phdr *program_header_table, int program_header_count, void *entry_point, const char *invocation_name, struct loaded_object_info_struct *loaded_object_info)
{
  struct program_data_struct program_data;
  void *base_address;
 
  read_program_header_table(program_header_table, program_header_count, &program_data, invocation_name);
  base_address = program_data.contents.program_header_table ? (void *)program_header_table - program_data.program_header_table_vaddr : NULL;
  examine_bottom_half(program_header_table, program_header_count, &program_data, base_address, entry_point, invocation_name, loaded_object_info);
}
 
void examine_dynamic_linker(Elf32_Phdr *program_header_table, int program_header_count, void *base_address, void *entry_point, struct loaded_object_info_struct *loaded_object_info)
{
  const char *invocation_name = "(dynamic linker)";
 
  struct program_data_struct program_data;
 
  read_program_header_table(program_header_table, program_header_count, &program_data, invocation_name);
  examine_bottom_half(program_header_table, program_header_count, &program_data, base_address, entry_point, invocation_name, loaded_object_info);
}
 
void examine_vdso(void *load_address, struct loaded_object_info_struct *loaded_object_info)
{
  Elf32_Ehdr *elf_header;
  Elf32_Phdr *program_header_table;
  struct program_data_struct program_data;
  void *base_address;
  struct segment_info_struct *segment_table;
  int segment_count;
 
  elf_header = load_address;
  program_header_table = load_address + elf_header->e_phoff;
  read_program_header_table(program_header_table, elf_header->e_phnum, &program_data, "(vdso)");
  base_address = load_address - PAGE(program_data.load_vaddr);
  get_segments(program_header_table, elf_header->e_phnum, base_address, &segment_table, &segment_count);
 
  loaded_object_info->base_address = base_address;
  loaded_object_info->load_address = load_address;
  loaded_object_info->segment_table = segment_table;
  loaded_object_info->segment_count = segment_count;
  loaded_object_info->entry_point = elf_header->e_entry ? base_address + elf_header->e_entry : NULL;
  loaded_object_info->dynamic_array = program_data.contents.dynamic_array ? base_address + program_data.dynamic_array_vaddr : NULL;
  loaded_object_info->interpreter_name = program_data.contents.interpreter_name ? base_address + program_data.interpreter_name_vaddr : NULL;
}
 
void load_object_file(const char *filename, int flags, struct loaded_object_info_struct *loaded_object_info)
{
  FILE *fp;
  int fd;
  Elf32_Ehdr elf_header;
  void *base_address;
  Elf32_Phdr *program_header_table, *program_header;
  struct program_data_struct program_data;
  struct segment_info_struct *segment_table;
  int segment_count;
  int i;
  off_t page_offset;
  off_t offset;
  Elf32_Addr vaddr;
  size_t file_size, mem_size, total_mem_size;
  int prot;
  size_t map_file_size, map_mem_size;
  void *load_addr;
 
  if (!(fp = fopen(filename, "r")))
    file_error_errno(filename, "cannot open object file");
  fd = fileno(fp);
 
  /* lecture de l'en-tete ELF */
  fread_elf_header(&elf_header, fp, filename);
  switch (elf_header.e_type) {
  case ET_EXEC:
    if (flags & LF_NOEXEC)
      file_error(filename, "cannot dynamically load executable");
    base_address = NULL;
    break;
  case ET_DYN:
    /* L'adresse de base d'un objet repositionnable sera determinee lors du
       chargement du premier segment. */
    base_address = MAP_FAILED;
    break;
  default:
    file_error(filename, "object file cannot be loaded");
  }
 
  /* lecture de la table des en-tetes de programme */
  if (elf_header.e_phentsize != sizeof (Elf32_Phdr))
    file_error(filename, "cannot read program header table");
  program_header_table = xmalloc(elf_header.e_phnum * sizeof (Elf32_Phdr));
  if (fseek(fp, elf_header.e_phoff, SEEK_SET) == -1)
    file_error_errno("seek error", filename);
  fread_program_header_table(program_header_table, elf_header.e_phnum, fp, filename);
 
  /* lecture des en-tetes de programme */
  read_program_header_table(program_header_table, elf_header.e_phnum, &program_data, filename);
 
  /* chargement des segments */
  segment_table= xmalloc(elf_header.e_phnum * sizeof (struct segment_info_struct));
  segment_count = 0;
  for (i = 0; i < elf_header.e_phnum; i++)
    if ((program_header = &program_header_table[i])->p_type == PT_LOAD) {
      /* ajustement des adresses */
      page_offset = PAGE_OFFSET(program_header->p_vaddr);
      offset = program_header->p_offset - page_offset;
      vaddr = program_header->p_vaddr - page_offset;
      file_size = program_header->p_filesz + page_offset;
      mem_size = program_header->p_memsz + page_offset;
 
      /* permissions */
      prot = get_prot(program_header->p_flags);
 
      /* chargement du segment du fichier */
      map_file_size = PAGE_ALIGN(file_size);
      map_mem_size = min(map_file_size, mem_size);
      if (base_address == MAP_FAILED) { /* premier segment d'un objet repositionnable */
        total_mem_size = program_data.mem_size + page_offset;
        if (total_mem_size == 0)
          file_error(filename, "cannot load object file");
        if ((load_addr = mmap(NULL, total_mem_size, prot, MAP_PRIVATE, fd, offset)) == MAP_FAILED)
          file_error_errno(filename, "failed to map segment from object file");
        base_address = load_addr - vaddr;
        if (PAGE_ALIGN(map_mem_size) < total_mem_size)
          mprotect(load_addr + PAGE_ALIGN(map_mem_size), total_mem_size - PAGE_ALIGN(map_mem_size), PROT_NONE);
      }
      else {
        if (map_mem_size > 0)
          if (mmap(base_address + vaddr, map_mem_size, prot, MAP_PRIVATE | MAP_FIXED, fd, offset) == MAP_FAILED)
            file_error_errno(filename, "failed to map segment from object file");
        load_addr = base_address + vaddr;
      }
      if (file_size < map_mem_size)
        memset(load_addr + file_size, 0, map_mem_size - file_size);
 
      /* completion du segment avec des octets nuls  */
      if (mem_size > map_file_size)
        if (mmap(load_addr + map_file_size, mem_size - map_file_size, prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == MAP_FAILED)
          file_error_errno(filename, "failed to map zero-fill pages");
 
      /* ajout du segment */
      segment_table[segment_count].load_address = load_addr;
      segment_table[segment_count].size = mem_size;
      segment_table[segment_count].prot = prot;
      segment_count++;
    }
 
  free(program_header_table);
  fclose(fp);
 
  loaded_object_info->base_address = base_address;
  loaded_object_info->load_address = base_address + PAGE(program_data.load_vaddr);
  loaded_object_info->segment_table = segment_table;
  loaded_object_info->segment_count = segment_count;
  loaded_object_info->entry_point = elf_header.e_entry ? base_address + elf_header.e_entry : NULL;
  loaded_object_info->dynamic_array = program_data.contents.dynamic_array ? base_address + program_data.dynamic_array_vaddr : NULL;
  loaded_object_info->interpreter_name = program_data.contents.interpreter_name ? base_address + program_data.interpreter_name_vaddr : NULL;
}
 
void make_segments_writable(struct object_info_struct *object_info)
{
  int i;
 
  if (!object_info->segment_table)
    return;
 
  for (i = 0; i < object_info->segment_count; i++)
    if (!(object_info->segment_table[i].prot & PROT_WRITE))
      if (mprotect(object_info->segment_table[i].load_address, object_info->segment_table[i].size, PROT_READ | PROT_WRITE) == -1)
        fatal_sys_error(errno, "failed to make segment writable for relocation");
}
 
void restore_segments_prot(struct object_info_struct *object_info)
{
  int i;
 
  if (!object_info->segment_table)
    return;
 
  for (i = 0; i < object_info->segment_count; i++)
    if (!(object_info->segment_table[i].prot & PROT_WRITE))
      if (mprotect(object_info->segment_table[i].load_address, object_info->segment_table[i].size, object_info->segment_table[i].prot) == -1)
        fatal_sys_error(errno, "failed to restore segment protection");
}