diff options
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..c879d62 --- /dev/null +++ b/src/main.c @@ -0,0 +1,570 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2010, 2011 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + 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 2, 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, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <error.h> +#include <argp.h> +#include <stdlib.h> +#include <unistd.h> + +#include "prelink.h" + +#define PRELINK_CONF "/etc/prelink.conf" +#define PRELINK_CACHE "/etc/prelink.cache" + +int all; +int force; +int verbose; +int print_cache; +int reloc_only; +GElf_Addr reloc_base; +int no_update; +int random_base; +int conserve_memory; +int libs_only; +int dry_run; +int dereference; +int one_file_system; +int enable_cxx_optimizations = 1; +int exec_shield; +int undo, verify; +enum verify_method_t verify_method; +int quick; +int compute_checksum; +long long seed; +GElf_Addr mmap_reg_start = ~(GElf_Addr) 0; +GElf_Addr mmap_reg_end = ~(GElf_Addr) 0; +GElf_Addr layout_page_size = 0; +const char *dynamic_linker; +const char *ld_library_path; +const char *prelink_conf = PRELINK_CONF; +const char *prelink_cache = PRELINK_CACHE; +const char *undo_output; +char *ld_preload = NULL; +int noreexecinit; +time_t initctime; + +const char *argp_program_version = PRELINK_PROG PKGVERSION " 1.0"; + +const char *argp_program_bug_address = REPORT_BUGS_TO; + +static char argp_doc[] = PRELINK_PROG " -- program to relocate and prelink ELF shared libraries and programs"; + +#define OPT_DYNAMIC_LINKER 0x80 +#define OPT_LD_LIBRARY_PATH 0x81 +#define OPT_LIBS_ONLY 0x82 +#define OPT_CXX_DISABLE 0x83 +#define OPT_MMAP_REG_START 0x84 +#define OPT_MMAP_REG_END 0x85 +#define OPT_EXEC_SHIELD 0x86 +#define OPT_NO_EXEC_SHIELD 0x87 +#define OPT_SEED 0x88 +#define OPT_MD5 0x89 +#define OPT_SHA 0x8a +#define OPT_COMPUTE_CHECKSUM 0x8b +#define OPT_LAYOUT_PAGE_SIZE 0x8c +#define OPT_SYSROOT 0x8d +#define OPT_RTLD 0x8e +#define OPT_ALLOW_TEXTREL 0x8f +#define OPT_LD_PRELOAD 0x90 + +static struct argp_option options[] = { + {"all", 'a', 0, 0, "Prelink all binaries" }, + {"black-list", 'b', "PATH", 0, "Blacklist path" }, + {"cache-file", 'C', "CACHE", 0, "Use CACHE as cache file" }, + {"config-file", 'c', "CONF", 0, "Use CONF as configuration file" }, + {"force", 'f', 0, 0, "Force prelinking" }, + {"dereference", 'h', 0, 0, "Follow symlinks when processing directory trees from command line" }, + {"one-file-system", 'l', 0, 0, "Stay in local file system when processing directories from command line" }, + {"conserve-memory", 'm', 0, 0, "Allow libraries to overlap as long as they never appear in the same program" }, + {"no-update-cache", 'N', 0, 0, "Don't update prelink cache" }, + {"dry-run", 'n', 0, 0, "Don't actually prelink anything" }, + {"undo-output", 'o', "FILE", 0, "Undo output file" }, + {"print-cache", 'p', 0, 0, "Print prelink cache" }, + {"quick", 'q', 0, 0, "Quick scan" }, + {"random", 'R', 0, 0, "Choose random base for libraries" }, + {"reloc-only", 'r', "BASE_ADDRESS", 0, "Relocate library to given address, don't prelink" }, + {"root", OPT_SYSROOT, "ROOT_PATH", 0, "Prefix all paths with ROOT_PATH" }, + {"undo", 'u', 0, 0, "Undo prelink" }, + {"verbose", 'v', 0, 0, "Produce verbose output" }, + {"verify", 'y', 0, 0, "Verify file consistency by undoing and redoing prelink and printing original to standard output" }, + {"md5", OPT_MD5, 0, 0, "For verify print MD5 sum of original to standard output instead of content" }, + {"sha", OPT_SHA, 0, 0, "For verify print SHA sum of original to standard output instead of content" }, + {"dynamic-linker", OPT_DYNAMIC_LINKER, "DYNAMIC_LINKER", + 0, "Special dynamic linker path" }, + {"exec-shield", OPT_EXEC_SHIELD, 0, 0, "Lay out libraries for exec-shield on IA-32" }, + {"no-exec-shield", OPT_NO_EXEC_SHIELD, 0, 0, "Don't lay out libraries for exec-shield on IA-32" }, + {"ld-library-path", OPT_LD_LIBRARY_PATH, "PATHLIST", + 0, "What LD_LIBRARY_PATH should be used" }, + {"ld-preload", OPT_LD_PRELOAD, "PATHLIST", 0, "What LD_PRELOAD should be used" }, + {"libs-only", OPT_LIBS_ONLY, 0, 0, "Prelink only libraries, no binaries" }, + {"layout-page-size", OPT_LAYOUT_PAGE_SIZE, "SIZE", 0, "Layout start of libraries at given boundary" }, + {"disable-c++-optimizations", OPT_CXX_DISABLE, 0, OPTION_HIDDEN, "" }, + {"mmap-region-start", OPT_MMAP_REG_START, "BASE_ADDRESS", OPTION_HIDDEN, "" }, + {"mmap-region-end", OPT_MMAP_REG_END, "BASE_ADDRESS", OPTION_HIDDEN, "" }, + {"seed", OPT_SEED, "SEED", OPTION_HIDDEN, "" }, + {"compute-checksum", OPT_COMPUTE_CHECKSUM, 0, OPTION_HIDDEN, "" }, + {"rtld", OPT_RTLD, "RTLD", OPTION_HIDDEN, "" }, + {"init", 'i', 0, 0, "Do not re-execute init" }, + {"allow-textrel", OPT_ALLOW_TEXTREL, 0, 0, "Allow text relocations even on architectures where they may not work" }, + { 0 } +}; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + char *endarg; + + switch (key) + { + case 'a': + all = 1; + break; + case 'b': + if (add_to_blacklist (arg, dereference, one_file_system)) + exit (EXIT_FAILURE); + break; + case 'f': + force = 1; + break; + case 'p': + print_cache = 1; + break; + case 'q': + quick = 1; + break; + case 'v': + ++verbose; + break; + case 'R': + random_base |= 1; + break; + case OPT_SEED: + random_base |= 2; + seed = strtoull (arg, &endarg, 0); + if (endarg != strchr (arg, '\0')) + error (EXIT_FAILURE, 0, "--seed option requires numberic argument"); + break; + case 'r': + reloc_only = 1; + reloc_base = strtoull (arg, &endarg, 0); + if (endarg != strchr (arg, '\0')) + error (EXIT_FAILURE, 0, "-r option requires numberic argument"); + break; + case 'h': + dereference = 1; + break; + case 'l': + one_file_system = 1; + break; + case 'm': + conserve_memory = 1; + break; + case 'N': + no_update = 1; + break; + case 'n': + dry_run = 1; + break; + case 'C': + prelink_cache = arg; + break; + case 'c': + prelink_conf = arg; + break; + case 'u': + undo = 1; + break; + case 'y': + verify = 1; + break; + case 'o': + undo_output = arg; + break; + case OPT_DYNAMIC_LINKER: + dynamic_linker = arg; + break; + case OPT_LD_LIBRARY_PATH: + ld_library_path = arg; + break; + case OPT_LIBS_ONLY: + libs_only = 1; + break; + case OPT_MD5: + verify_method = VERIFY_MD5; + break; + case OPT_SHA: + verify_method = VERIFY_SHA; + break; + case OPT_CXX_DISABLE: + enable_cxx_optimizations = 0; + break; + case OPT_MMAP_REG_START: + mmap_reg_start = strtoull (arg, &endarg, 0); + if (endarg != strchr (arg, '\0')) + error (EXIT_FAILURE, 0, "--mmap-region-start option requires numberic argument"); + break; + case OPT_MMAP_REG_END: + mmap_reg_end = strtoull (arg, &endarg, 0); + if (endarg != strchr (arg, '\0')) + error (EXIT_FAILURE, 0, "--mmap-region-end option requires numberic argument"); + break; + case OPT_EXEC_SHIELD: + exec_shield = 1; + break; + case OPT_NO_EXEC_SHIELD: + exec_shield = 0; + break; + case OPT_COMPUTE_CHECKSUM: + compute_checksum = 1; + break; + case OPT_LAYOUT_PAGE_SIZE: + layout_page_size = strtoull (arg, &endarg, 0); + if (endarg != strchr (arg, '\0') || (layout_page_size & (layout_page_size - 1))) + error (EXIT_FAILURE, 0, "--layout-page-size option requires numberic power-of-two argument"); + case OPT_SYSROOT: + sysroot = arg; + break; + case OPT_RTLD: + prelink_rtld = arg; + break; + case 'i': + noreexecinit=1; + break; + case OPT_ALLOW_TEXTREL: + allow_bad_textrel = 1; + break; + case OPT_LD_PRELOAD: + ld_preload = arg; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +time_t get_ctime(const char *file) { + struct stat st; + if(stat(file,&st) == 0) + return st.st_ctime; + return 0; +} + +void checkinit() { + if(initctime != get_ctime("/sbin/init")) { + printf("Executing /sbin/init U\n"); + system("/sbin/init U"); + } +} + +static struct argp argp = { options, parse_opt, "[FILES]", argp_doc }; + +const char *prelink_rtld = NULL; + +/* Disable detection, this is not appropriate when cross prelinking. */ +#if 0 && (defined (__i386__) || defined (__x86_64__)) && defined (__GNUC__) +static void +set_default_layout_page_size (void) +{ + /* From gcc.dg/20020523-1.c test in gcc 3.2 testsuite. */ + int fl1, fl2; + +#ifndef __x86_64__ + /* See if we can use cpuid. */ + __asm__ ("pushfl; pushfl; popl %0; movl %0,%1; xorl %2,%0;" + "pushl %0; popfl; pushfl; popl %0; popfl" + : "=&r" (fl1), "=&r" (fl2) + : "i" (0x00200000)); + if (((fl1 ^ fl2) & 0x00200000) == 0) + return; +#define cpuid(fl1, fl2, fn) \ + __asm__ ("movl %%ebx, %1; cpuid; xchgl %%ebx, %1" \ + : "=a" (fl1), "=r" (fl2) : "0" (fn) : "ecx", "edx") +#else +#define cpuid(fl1, fl2, fn) \ + __asm__ ("cpuid" : "=a" (fl1), "=b" (fl2) : "0" (fn) : "ecx", "edx") +#endif + + /* See if CPUID gives capabilities. */ + cpuid (fl1, fl2, 0); + if (fl1 < 1 || fl2 != 0x68747541 /* Auth - AMD */) + return; + + /* CPUID 1. */ + cpuid (fl1, fl2, 1); + if (((fl1 >> 8) & 0x0f) + ((fl1 >> 20) & 0xff) == 0x15 /* Family */) + /* On AMD Bulldozer CPUs default to --layout-page-size=0x8000. */ + layout_page_size = 0x8000; +} +#else +# define set_default_layout_page_size() +#endif + +int +main (int argc, char *argv[]) +{ + int remaining, failures = 0; + + setlocale (LC_ALL, ""); + + exec_shield = 2; + + set_default_layout_page_size (); + + prelink_init_cache (); + + elf_version (EV_CURRENT); + + argp_parse (&argp, argc, argv, 0, &remaining, 0); + + if(!noreexecinit) { + initctime = get_ctime("/sbin/init"); + atexit(checkinit); + } + + if (ld_library_path == NULL) + ld_library_path = getenv ("LD_LIBRARY_PATH"); + + if (all && reloc_only) + error (EXIT_FAILURE, 0, "--all and --reloc-only options are incompatible"); + if ((undo || verify) && reloc_only) + error (EXIT_FAILURE, 0, "--undo and --reloc-only options are incompatible"); + if (verify && (undo || all)) + error (EXIT_FAILURE, 0, "--verify and either --undo or --all options are incompatible"); + if (dry_run && verify) + error (EXIT_FAILURE, 0, "--dry-run and --verify options are incompatible"); + if ((undo || verify) && quick) + error (EXIT_FAILURE, 0, "--undo and --quick options are incompatible"); + + /* Set the default for exec_shield. */ + if (exec_shield == 2) + { + if (sysroot == NULL && ! access ("/proc/sys/kernel/exec-shield", F_OK)) + exec_shield = 1; + else + exec_shield = 0; + } + +#ifdef DEFAULT_SYSROOT + if (sysroot == NULL) + { + extern char *make_relative_prefix (const char *, const char *, const char *); + sysroot = make_relative_prefix (argv[0], BINDIR, DEFAULT_SYSROOT); + } +#endif + + if (sysroot) + { + sysroot = canonicalize_file_name (sysroot); + if (sysroot == NULL) + error (EXIT_FAILURE, 0, "Could not canonicalize --root argument"); + asprintf ((char **) &prelink_conf, "%s%s", sysroot, prelink_conf); + } + if (prelink_rtld != NULL && prelink_rtld[0] == 0) + prelink_rtld = NULL; + else + if (prelink_rtld == NULL) + { + extern char *make_relative_prefix (const char *, const char *, const char *); + const char *path = make_relative_prefix (argv[0], BINDIR, BINDIR); + asprintf ((char **) &prelink_rtld, "%s/%s", path, + PRELINK_RTLD_PROG EXEEXT); + } + + if (print_cache) + { + prelink_load_cache (); + prelink_print_cache (); + return 0; + } + + if (remaining == argc && ! all) + error (EXIT_FAILURE, 0, "no files given and --all not used"); + + if (undo_output && (!undo || all)) + error (EXIT_FAILURE, 0, "-o can be only specified together with -u and without -a"); + + if (undo_output && remaining + 1 != argc) + error (EXIT_FAILURE, 0, "-o can only be used when undoing a single object"); + + if (compute_checksum) + { + while (remaining < argc) + { + DSO *dso = open_dso (argv[remaining++]); + + if (dso == NULL || reopen_dso (dso, NULL, NULL) + || prelink_set_checksum (dso)) + error (0, 0, "could not recompute checksum of %s", dso->filename); + close_dso (dso); + error (0, 0, "%08x %s\n", (unsigned int) dso->info_DT_CHECKSUM, dso->filename); + } + exit (0); + } + + if (verify) + { + if (remaining + 1 != argc) + error (EXIT_FAILURE, 0, "only one library or binary can be verified in a single command"); + return prelink_verify (argv[remaining]); + } + + if (reloc_only || (undo && ! all)) + { + while (remaining < argc) + { + DSO *dso = open_dso (argv[remaining++]); + int ret; + + if (dso == NULL) + { + ++failures; + continue; + } + + if (dso->ehdr.e_type != ET_DYN + && (reloc_only || dso->ehdr.e_type != ET_EXEC)) + { + ++failures; + error (0, 0, "%s is not a shared library", dso->filename); + continue; + } + + if (undo) + ret = prelink_undo (dso); + else + ret = relocate_dso (dso, reloc_base); + + if (ret) + { + ++failures; + close_dso (dso); + continue; + } + + if (dynamic_info_is_set (dso, DT_CHECKSUM_BIT) + && dso_is_rdwr (dso) + && prelink_set_checksum (dso)) + { + ++failures; + close_dso (dso); + continue; + } + + if (dry_run) + { + close_dso (dso); + continue; + } + + if (reloc_only) + dso->permissive = 1; + else if (undo_output) + { + const char *output, *orig_filename; + + if (!dso_is_rdwr (dso)) + { + struct stat64 st; + int err; + + if (fstat64 (dso->fd, &st) < 0) + { + error (0, errno, "Could not stat %s", dso->filename); + ++failures; + close_dso (dso); + continue; + } + err = copy_fd_to_file (dso->fd, undo_output, &st); + if (err) + { + error (0, err, "Could not undo %s to %s", dso->filename, + undo_output); + ++failures; + } + close_dso (dso); + continue; + } + + output = strdup (undo_output); + if (!output) + { + ++failures; + close_dso (dso); + continue; + } + if (dso->filename != dso->soname) + orig_filename = dso->filename; + else + orig_filename = strdup (dso->filename); + if (!orig_filename) + { + ++failures; + close_dso (dso); + continue; + } + dso->filename = output; + if (update_dso (dso, orig_filename)) + ++failures; + free ((char *) orig_filename); + continue; + } + + if (update_dso (dso, NULL)) + ++failures; + } + + return failures; + } + + if (read_config (prelink_conf)) + return EXIT_FAILURE; + + if (blacklist_from_config ()) + return EXIT_FAILURE; + + if (quick) + prelink_load_cache (); + + if (gather_config ()) + return EXIT_FAILURE; + + while (remaining < argc) + if (gather_object (argv[remaining++], dereference, one_file_system)) + return EXIT_FAILURE; + + if (gather_check_libs ()) + return EXIT_FAILURE; + + if (undo) + return undo_all (); + + if (! all && ! quick) + prelink_load_cache (); + + layout_libs (); + prelink_all (); + + if (! no_update && ! dry_run) + prelink_save_cache (all); + return 0; +} |