/* 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 . */ #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 #include #include #include /* 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(¬_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(¬_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(¬_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(¬_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(¬_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(); } }