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


XOS | Parent Directory | View | Download

/* Routines principales de l'editeur de lien dynamique. */
/* 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 "dynamic_link.h"
 
#include "reloc.h"
#include "load.h"
#include "vars.h"
#include "initfini.h"
#include "object_reference.h"
#include "object_info.h"
#include "error.h"
#include "xmalloc.h"
 
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
 
/* options de chargement des bibliotheques partagees */
#define LS_ACCEPT_NOT_FOUND 0x1
 
static void initializing_print_progname()
{
  fprintf(stderr, "%s: error while initializing shared objects", executable_invocation_name);
}
 
static void finalizing_print_progname()
{
  fprintf(stderr, "%s: error while finalizing shared objects", executable_invocation_name);
}
 
/* Recherche un nom de fichier dans une liste de chemins. */
static char *path_search_name(const char *name, const char *search_path, int accept_semicolons, struct stat *statbuf)
{
  char *filename;
  const char *p1;
  char *p2;
 
  filename = xmalloc(strlen(search_path) + strlen(name) + 2);
  if (*search_path) {
    p1 = search_path;
    while (1) {
      p2 = filename;
      while (*p1 && *p1 != ':' && (!accept_semicolons || *p1 != ';'))
        *p2++ = *p1++;
      if (p2 != filename)
        *p2++ = '/';
      strcpy(p2, name);
      if (stat(filename, statbuf) == 0)
        return filename;
      if (!*p1)
        break;
      if (*p1 == ':' || (accept_semicolons && *p1 == ';'))
        p1++;
    }
  }
  free(filename);
  return NULL;
}
 
/* Recherche le fichier correspondant a un nom de bibliotheque. */
static char *search_library_name(const char *name, const char *rpath, const char *runpath, struct stat *statbuf)
{
  char *filename;
 
  if (strchr(name, '/')) {
    if (stat(name, statbuf) == 0)
      return xstrdup(name);
  }
  else {
    if (rpath && !runpath)
      if ((filename = path_search_name(name, rpath, 0, statbuf)))
        return filename;
    if (library_path)
      if ((filename = path_search_name(name, library_path, 1, statbuf)))
        return filename;
    if (runpath)
      if ((filename = path_search_name(name, runpath, 0, statbuf)))
        return filename;
    if ((filename = path_search_name(name, "/lib", 0, statbuf)))
      return filename;
  }
  return NULL;
}
 
/* Insere les references a des objets partages. */
static void insert_references(struct object_info_struct *object_info, struct object_reference_list_struct *object_reference_list)
{
  int i;
 
  object_info->dependency_list = xmalloc(object_info->dynamic_data.needed_list_size * sizeof (struct object_info_struct *));
  object_info->dependency_list_size = object_info->dynamic_data.needed_list_size;
  for (i = 0; i < object_info->dynamic_data.needed_list_size; i++)
    object_info->dependency_list[i].object_reference = object_reference_list_insert(object_reference_list, object_info->dynamic_data.needed_list[i]);
}
 
/* Remplace les references a des objets partages par des liens vers ces objets.
 */
static void resolve_dependencies(struct object_info_struct *object_info)
{
  int idx;
  int i, j;
  const struct object_reference_struct *object_reference;
  struct object_info_struct *referenced_object_info;
  int found;
 
  idx = 0;
  for (i = 0; i < object_info->dependency_list_size; i++) {
    object_reference = object_info->dependency_list[i].object_reference;
    if (!object_reference->object_info)
      /* dependance non resolue ou dependance vers l'executable */
      continue;
    /* recherche si la dependance existe deja */
    found = 0;
    for (j = 0; j < idx; j++)
      if (object_info->dependency_list[j].object_reference == object_reference) {
        found = 1;
        break;
      }
    if (found)
      continue;
    /* remplacement de la reference par la dependance */
    referenced_object_info = object_reference->object_info;
    object_info->dependency_list[idx].object_info = referenced_object_info;
    referenced_object_info->reference_count++;
    idx++;
  }
  object_info->dependency_list_size = idx;
}
 
/* Charge en memoire les segments des objets partages necessaires a l'execution
 * du programme. */
static void load_shared_objects(int flags)
{
  struct object_reference_list_struct object_reference_list;
  int executable_statbuf_available;
  struct stat executable_statbuf;
  struct object_info_struct *vdso_info;
  struct dynamic_data_struct dynamic_linker_dynamic_data;
  const char *dynamic_linker_name;
  const char *dynamic_linker_filename;
  struct object_info_struct *dynamic_linker_info;
 
  struct dynamic_data_struct dynamic_data;
  struct object_reference_struct *object_reference;
  const char *reference_name;
  struct object_info_struct *shared_object_info;
  struct stat statbuf;
  char *filename;
  struct loaded_object_info_struct loaded_object_info;
 
  object_reference_list_init(&object_reference_list);
 
  /* lecture des informations d'edition de liens dynamique de l'executable */
  object_info_init(&executable_info, executable_invocation_name, OI_EXEC);
  executable_info.base_address = executable_load_info.base_address;
  executable_info.load_address = executable_load_info.load_address;
  executable_info.segment_table = executable_load_info.segment_table;
  executable_info.segment_count = executable_load_info.segment_count;
  if (executable_load_info.dynamic_array) {
    read_dynamic_array(executable_load_info.dynamic_array, executable_load_info.base_address, &executable_info.dynamic_data);
    if (executable_info.dynamic_data.needed_list)
      insert_references(&executable_info, &object_reference_list);
  }
 
  /* initialisation de la liste des objets partages */
  object_info_list_init(&shared_object_info_list);
  if (!object_reference_list.first)
    /* Le programme ne necessite pas d'objets partages. */
    goto destroy_object_reference_list;
 
  /* recuperation de l'etat du fichier de l'executable */
  executable_statbuf_available = stat(executable_invocation_name, &executable_statbuf) == 0;
 
  /* lecture des informations d'edition de liens dynamique de l'objet virtuel partage */
  if (vdso_load_info.dynamic_array) {
    read_dynamic_array(vdso_load_info.dynamic_array, vdso_load_info.base_address, &dynamic_data);
    vdso_info = object_info_list_insert(&shared_object_info_list, dynamic_data.soname, OI_VDSO);
    vdso_info->base_address = vdso_load_info.base_address;
    vdso_info->load_address = vdso_load_info.load_address;
    vdso_info->segment_table = vdso_load_info.segment_table;
    vdso_info->segment_count = vdso_load_info.segment_count;
    vdso_info->dynamic_data = dynamic_data;
    if (vdso_info->dynamic_data.needed_list)
      insert_references(vdso_info, &object_reference_list);
  }
  else {
    if (vdso_load_info.segment_table)
      free(vdso_load_info.segment_table);
    vdso_info = NULL;
  }
 
  /* lecture des informations d'edition de liens dynamique de l'editeur de liens dynamique */
  read_dynamic_array(_DYNAMIC, dynamic_linker_load_info.base_address, &dynamic_linker_dynamic_data);
  dynamic_linker_name = executable_load_info.interpreter_name ? : dynamic_linker_invocation_name;
  dynamic_linker_filename = dynamic_linker_invocation_name ? : dynamic_linker_name;
  dynamic_linker_info = NULL;
 
  /* resolution des references et chargement des objets partages */
  for (object_reference = object_reference_list.first; object_reference; object_reference = object_reference->next) {
    reference_name = object_reference->name;
 
    /* recherche et chargement de l'objet partage reference */
    if (vdso_info && !strcmp(reference_name, vdso_info->dynamic_data.soname))
      /* dependance vers l'objet partage dynamique virtuel */
      shared_object_info = vdso_info;
    else if (!strcmp(reference_name, dynamic_linker_dynamic_data.soname)) {
      /* dependance vers l'editeur de liens dynamique */
      shared_object_info = object_info_list_insert(&shared_object_info_list, dynamic_linker_name, OI_DYNAMIC_LINKER, dynamic_linker_filename);
      if (shared_object_info->base_address == MAP_FAILED) {
        shared_object_info->base_address = dynamic_linker_load_info.base_address;
        shared_object_info->load_address = dynamic_linker_load_info.load_address;
        shared_object_info->segment_table = dynamic_linker_load_info.segment_table;
        shared_object_info->segment_count = dynamic_linker_load_info.segment_count;
        shared_object_info->dynamic_data = dynamic_linker_dynamic_data;
        dynamic_linker_info = shared_object_info;
      }
    }
    else if ((filename = search_library_name(reference_name, executable_info.dynamic_data.rpath, executable_info.dynamic_data.runpath, &statbuf))) {
      /* dependance vers un objet partage */
      if (executable_statbuf_available && statbuf.st_dev == executable_statbuf.st_dev && statbuf.st_ino == executable_statbuf.st_ino) {
        /* On ignore les dependances vers l'executable. */
        free(filename);
        continue;
      }
      shared_object_info = object_info_list_insert(&shared_object_info_list, reference_name, OI_FILE, filename, statbuf.st_dev, statbuf.st_ino);
      if (shared_object_info->base_address == MAP_FAILED) {
        /* chargement de la bibliotheque partagee */
        load_object_file(filename, LF_NOEXEC, &loaded_object_info);
        shared_object_info->base_address = loaded_object_info.base_address;
        shared_object_info->load_address = loaded_object_info.load_address;
        shared_object_info->segment_table = loaded_object_info.segment_table;
        shared_object_info->segment_count = loaded_object_info.segment_count;
 
        /* lecture des informations d'edition de liens dynamique */
        if (loaded_object_info.dynamic_array) {
          read_dynamic_array(loaded_object_info.dynamic_array, loaded_object_info.base_address, &shared_object_info->dynamic_data);
          if (shared_object_info->dynamic_data.needed_list)
            insert_references(shared_object_info, &object_reference_list);
        }
      }
      free(filename);
    }
    else {
      /* dependance non trouvee */
      if (flags & LS_ACCEPT_NOT_FOUND)
        object_info_list_insert(&shared_object_info_list, reference_name, OI_NOT_FOUND);
      else {
        errno = ENOENT;
        file_error_errno(reference_name, "cannot open shared object file");
      }
      continue;
    }
 
    /* resolution de la reference */
    object_reference->object_info = shared_object_info;
  }
  if (!dynamic_linker_info) {
    dispose_dynamic_data(&dynamic_linker_dynamic_data);
    if (dynamic_linker_load_info.segment_table)
      free(dynamic_linker_load_info.segment_table);
  }
 
  /* construction du graphe des dependances */
  resolve_dependencies(&executable_info);
  for (shared_object_info = shared_object_info_list.first; shared_object_info; shared_object_info = shared_object_info->next)
    resolve_dependencies(shared_object_info);
 
 destroy_object_reference_list:
  object_reference_list_destroy(&object_reference_list);
}
 
/* Execute les relocations. */
static void perform_relocations()
{
  struct object_info_struct *shared_object_info;
 
  /* On effectue les relocations dans l'ordre inverse des dependences afin que
     les relocations de type COPY s'appliquent sur des objets deja relocalises.
  */
  for (shared_object_info = shared_object_info_list.last; shared_object_info; shared_object_info = shared_object_info->previous)
    if (shared_object_info->type != OI_DYNAMIC_LINKER)
      relocate_object(shared_object_info);
  relocate_object(&executable_info);
}
 
/* Trie les objets partages selon l'ordre d'execution des constructeurs et
 * destructeurs (tri topologique sur le graphe des dependances des objets
 * partages). */
static void sort_initfini()
{
  struct initfini_list_struct not_referenced_shared_object_list;
  struct initfini_list_struct referenced_shared_object_list;
  struct object_info_struct *shared_object_info;
  int i;
 
  /* construction des listes d'objets partages non references et references */
  initfini_list_init(&not_referenced_shared_object_list);
  initfini_list_init(&referenced_shared_object_list);
  for (shared_object_info = shared_object_info_list.first; shared_object_info; shared_object_info = shared_object_info->next) {
    if ((shared_object_info->initfini_ordering.reference_count = shared_object_info->reference_count) == 0)
      initfini_list_push_back(&not_referenced_shared_object_list, shared_object_info);
    else
      initfini_list_push_back(&referenced_shared_object_list, shared_object_info);
  }
 
  /* deduction des references des dependances directes de l'executable */
  for (i = 0; i < executable_info.dependency_list_size; i++) {
    executable_info.dependency_list[i].object_info->initfini_ordering.reference_count--;
    if (executable_info.dependency_list[i].object_info->initfini_ordering.reference_count == 0) {
      initfini_list_remove(&referenced_shared_object_list, executable_info.dependency_list[i].object_info);
      initfini_list_push_back(&not_referenced_shared_object_list, executable_info.dependency_list[i].object_info);
    }
  }
 
  /* construction de la liste ordonnee selon l'ordre d'execution des constructeurs */
  initfini_list_init(&shared_object_initfini_list);
  while ((shared_object_info = initfini_list_pop_front(&not_referenced_shared_object_list) ? : initfini_list_pop_front(&referenced_shared_object_list))) {
    shared_object_info->initfini_ordering.reference_count = -1; /* l'objet partage a ete retire des listes */
    if (shared_object_info->type != OI_DYNAMIC_LINKER) /* on ne doit pas executer les constructeurs et destructeurs de l'editeur de liens dynamique */
      initfini_list_push_back(&shared_object_initfini_list, shared_object_info);
 
    /* deduction des references des dependances directes */
    for (i = 0; i < shared_object_info->dependency_list_size; i++) {
      shared_object_info->dependency_list[i].object_info->initfini_ordering.reference_count--;
      if (shared_object_info->dependency_list[i].object_info->initfini_ordering.reference_count == 0) {
        initfini_list_remove(&referenced_shared_object_list, shared_object_info->dependency_list[i].object_info);
        initfini_list_push_back(&not_referenced_shared_object_list, shared_object_info->dependency_list[i].object_info);
      }
    }
  }
}
 
/* Fonctions exportees */
 
void verify_error(int status, int errnum, const char *format, ...)
{
  exit(1);
}
 
void verify_executable()
{
  if (!executable_load_info.dynamic_array)
    exit(1);
  if (!executable_load_info.interpreter_name)
    exit(2);
  exit(0);
}
 
void list_dependencies()
{
  struct object_info_struct *object_info;
 
  /* chargement des bibliotheques partagees */
  load_shared_objects(LS_ACCEPT_NOT_FOUND);
 
  /* affichage de la liste des dependances */
  if (shared_object_info_list.first)
    for (object_info = shared_object_info_list.first; object_info; object_info = object_info->next)
      object_info_print(object_info);
  else
    printf("\tstatically linked\n");
}
 
void prepare_program()
{
  /* chargement des bibliotheques partagees */
  load_shared_objects(0);
 
  /* execution des relocations */
  perform_relocations(&shared_object_info_list);
 
  /* construction de la liste d'initialisation */
  sort_initfini();
}
 
void initialize_shared_objects(void *initial_stack_ptr)
{
  int argc;
  char **argv;
  char **envp;
  struct object_info_struct *shared_object_info;
  void *p;
  int i;
 
  error_print_progname = initializing_print_progname;
 
  p = initial_stack_ptr;
  argc = *(int *)p;
  p += sizeof (int);
  argv = p;
  p += (argc + 1) * sizeof (char *);
  envp = p;
 
  if (executable_info.dynamic_data.preinit_func_array)
    for (i = 0; i < executable_info.dynamic_data.preinit_func_array_size; i++)
      executable_info.dynamic_data.preinit_func_array[i](argc, argv, envp);
  for (shared_object_info = shared_object_initfini_list.last; shared_object_info; shared_object_info = shared_object_info->initfini_previous) {
    if (shared_object_info->dynamic_data.init_func)
      shared_object_info->dynamic_data.init_func(argc, argv, envp);
    if (shared_object_info->dynamic_data.init_func_array)
      for (i = 0; i < shared_object_info->dynamic_data.init_func_array_size; i++)
        shared_object_info->dynamic_data.init_func_array[i](argc, argv, envp);
  }
}
 
void finalize_shared_objects()
{
  struct object_info_struct *shared_object_info;
  int i;
 
  error_print_progname = finalizing_print_progname;
  if (executable_info.dynamic_data.fini_func_array)
    for (i = executable_info.dynamic_data.fini_func_array_size; i-- > 0;)
      executable_info.dynamic_data.fini_func_array[i]();
  if (executable_info.dynamic_data.fini_func)
    executable_info.dynamic_data.fini_func();
  for (shared_object_info = shared_object_initfini_list.first; shared_object_info; shared_object_info = shared_object_info->initfini_next) {
    if (shared_object_info->dynamic_data.fini_func_array)
      for (i = shared_object_info->dynamic_data.fini_func_array_size; i-- > 0;)
        shared_object_info->dynamic_data.fini_func_array[i]();
    if (shared_object_info->dynamic_data.fini_func)
      shared_object_info->dynamic_data.fini_func();
  }
}