View of xos/usr/lib/ld-so/ld.so.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 "dynamic_link.h"
#include "load.h"
#include "vars.h"
#include "elf.h"
#include "auxv.h"
#include "xmalloc.h"
#include "error.h"
#include "sysvars.h"
 
#include <getopt.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
 
#define cannot_handle_program_error() fatal_error("cannot handle program")
#define usage_error(...)              nonfatal_error(__VA_ARGS__)
 
void _start();
 
/* identifiants d'options longues */
enum {LO_LIST, LO_VERIFY, LO_LIBRARY_PATH};
 
/* modes */
enum {LM_VERIFY, LM_LIST, LM_RUN};
 
int program_skip_args;
void *program_entry_point;
 
void main(int argc, char **argv, char **envp, unsigned long *auxv);
 
static void __attribute__ ((noreturn)) die_bad_usage()
{
  fprintf(stderr, "Try `%s --help' for more information.\n", program_invocation_name);
  exit(EXIT_FAILURE);
}
 
static void print_help()
{
  printf("Usage: %s [OPTION]... EXECUTABLE-FILE [ARGS...]\n", program_invocation_name);
  puts("Dynamic linker/loader.");
  putchar('\n');
  puts("      --list               list all dependencies and how they are resolved");
  puts("      --verify             verify that given object really is a dynamically");
  puts("                           linked object we can handle");
  puts("      --library-path PATH  use given PATH instead of content of the environment");
  puts("                           variable LD_LIBRARY_PATH");
  puts("  -h, --help               display this help and exit");
}
 
static void loading_libraries_print_progname()
{
  fprintf(stderr, "%s: error while loading shared libraries", executable_invocation_name);
}
 
void main(int argc, char **argv, char **envp, unsigned long *auxv)
{
  int mode;
  struct auxiliary_vector_data_struct auxiliary_vector_data;
  struct program_data_struct program_data;
  int long_option_id;
  const struct option longopts[] = {
    {"list", no_argument, &long_option_id, LO_LIST},
    {"verify", no_argument, &long_option_id, LO_VERIFY},
    {"library-path", required_argument, &long_option_id, LO_LIBRARY_PATH},
    {"help", no_argument, NULL, 'h'},
    {NULL, 0, NULL, 0}
  };
  int c;
 
  /* configuration */
  error_func = error;
 
  mode = LM_RUN;
 
  /* lecture du vecteur auxiliaire ELF */
  read_auxiliary_vector(auxv, &auxiliary_vector_data);
  if (auxiliary_vector_data.contents.not_elf)
    fatal_error("program is not ELF");
 
  /* parametres systeme */
  page_size = auxiliary_vector_data.contents.page_size ? auxiliary_vector_data.page_size : (unsigned long)getpagesize();
 
  /* environnement */
  library_path = getenv("LD_LIBRARY_PATH") ? xstrdup(getenv("LD_LIBRARY_PATH")) : NULL;
 
  /* object partage dynamique virtuel */
  if (auxiliary_vector_data.contents.vdso_elf_header)
    examine_vdso(auxiliary_vector_data.vdso_elf_header, &vdso_load_info);
  else {
    vdso_load_info.base_address = MAP_FAILED;
    vdso_load_info.load_address = MAP_FAILED;
    vdso_load_info.segment_table = NULL;
    vdso_load_info.segment_count = 0;
    vdso_load_info.entry_point = NULL;
    vdso_load_info.dynamic_array = NULL;
    vdso_load_info.interpreter_name = NULL;
  }
 
  if (auxiliary_vector_data.contents.program_entry_point && auxiliary_vector_data.program_entry_point != &_start) {
    /* ld.so a ete invoque par le systeme comme interpreteur d'un autre programme. */
    if (!auxiliary_vector_data.contents.program_header_table
        || (auxiliary_vector_data.contents.program_header_entry_size && auxiliary_vector_data.program_header_entry_size != sizeof (Elf32_Phdr))
        || !auxiliary_vector_data.contents.program_header_entry_number
        || !auxiliary_vector_data.contents.interpreter_base_address)
      cannot_handle_program_error();
    program_skip_args = 0;
    program_entry_point = auxiliary_vector_data.program_entry_point;
 
    /* executable */
    executable_invocation_name = argv[0];
    examine_executable(auxiliary_vector_data.program_header_table, auxiliary_vector_data.program_header_entry_number, program_entry_point, executable_invocation_name, &executable_load_info);
 
    /* editeur de liens dynamique */
    dynamic_linker_invocation_name = NULL;
    dynamic_linker_load_info.base_address = auxiliary_vector_data.interpreter_base_address;
    /* Je ne sais pas comment determiner l'adresse de chargement de
       l'interpeteur. Elle devrait etre egale a l'adresse de base augmentee de
       l'adresse virtuelle (vaddr) du premier segment. Comme nous sommes un
       objet partage (ET_DYN), vaddr a toutes les chances d'etre nulle et
       l'adresse de chargement de coincider avec l'adresse de base.
       La seule utilisation de dynamic_linker_load_address est dans l'affichage
       des dependances lorsque l'editeur de liens dynamique est appele avec
       l'option --list. A ce jour, ce mode n'est pas accessible par
       l'interpreteur. */
    dynamic_linker_load_info.load_address = dynamic_linker_load_info.base_address;
    dynamic_linker_load_info.segment_table = NULL;
    dynamic_linker_load_info.segment_count = 0;
    dynamic_linker_load_info.entry_point = &_start;
    dynamic_linker_load_info.dynamic_array = NULL;
    dynamic_linker_load_info.interpreter_name = NULL;
 
    if (mode == LM_VERIFY)
      error_func = verify_error;
    else
      error_print_progname = loading_libraries_print_progname;
  }
  else {
    /* ld.so a ete invoque explicitement. */
    if (!auxiliary_vector_data.contents.program_header_table
        || (auxiliary_vector_data.contents.program_header_entry_size && auxiliary_vector_data.program_header_entry_size != sizeof (Elf32_Phdr))
        || !auxiliary_vector_data.contents.program_header_entry_number)
      cannot_handle_program_error();
 
    /* editeur de liens dynamique */
    dynamic_linker_invocation_name = argv[0];
    read_program_header_table(auxiliary_vector_data.program_header_table, auxiliary_vector_data.program_header_entry_number, &program_data, dynamic_linker_invocation_name);
    if (!program_data.contents.dynamic_array)
      cannot_handle_program_error();
    examine_dynamic_linker(auxiliary_vector_data.program_header_table, auxiliary_vector_data.program_header_entry_number, (void *)_DYNAMIC - program_data.dynamic_array_vaddr, program_entry_point, &dynamic_linker_load_info);
 
    /* lecture des arguments de la ligne de commande */
    while ((c = getopt_long(argc, argv, "h", longopts, NULL)) != -1)
      switch (c) {
      case 0:
        switch (long_option_id) {
        case LO_LIST:
          mode = LM_LIST;
          break;
        case LO_VERIFY:
          mode = LM_VERIFY;
          break;
        case LO_LIBRARY_PATH:
          library_path = optarg;
          break;
        }
        break;
      case 'h':
        print_help();
        exit(EXIT_SUCCESS);
      case '?':
        die_bad_usage();
      }
    if (optind == argc) {
      usage_error("missing filename operand");
      die_bad_usage();
    }
    program_skip_args = optind;
 
    /* executable */
    executable_invocation_name = argv[optind];
 
    if (mode == LM_VERIFY)
      error_func = verify_error;
    else
      error_print_progname = loading_libraries_print_progname;
 
    /* chargement de l'executable */
    load_object_file(executable_invocation_name, 0, &executable_load_info);
    program_entry_point = executable_load_info.entry_point;
  }
 
  switch (mode) {
  case LM_VERIFY:
    verify_executable();
  case LM_LIST:
    list_dependencies();
    exit(EXIT_SUCCESS);
  case LM_RUN:
    prepare_program();
    break;
  }
}