#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>
#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);
}
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;
}
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;
}
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]);
}
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)
continue;
found = 0;
for (j = 0; j < idx; j++)
if (object_info->dependency_list[j].object_reference == object_reference) {
found = 1;
break;
}
if (found)
continue;
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;
}
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);
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);
}
object_info_list_init(&shared_object_info_list);
if (!object_reference_list.first)
goto destroy_object_reference_list;
executable_statbuf_available = stat(executable_invocation_name, &executable_statbuf) == 0;
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;
}
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;
for (object_reference = object_reference_list.first; object_reference; object_reference = object_reference->next) {
reference_name = object_reference->name;
if (vdso_info && !strcmp(reference_name, vdso_info->dynamic_data.soname))
shared_object_info = vdso_info;
else if (!strcmp(reference_name, dynamic_linker_dynamic_data.soname)) {
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))) {
if (executable_statbuf_available && statbuf.st_dev == executable_statbuf.st_dev && statbuf.st_ino == executable_statbuf.st_ino) {
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) {
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;
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 {
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;
}
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);
}
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);
}
static void perform_relocations()
{
struct object_info_struct *shared_object_info;
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);
}
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;
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);
}
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);
}
}
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;
if (shared_object_info->type != OI_DYNAMIC_LINKER)
initfini_list_push_back(&shared_object_initfini_list, shared_object_info);
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);
}
}
}
}
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;
load_shared_objects(LS_ACCEPT_NOT_FOUND);
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()
{
load_shared_objects(0);
perform_relocations(&shared_object_info_list);
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();
}
}