diff options
Diffstat (limited to 'tools/apriori/main.c')
-rw-r--r-- | tools/apriori/main.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/tools/apriori/main.c b/tools/apriori/main.c new file mode 100644 index 0000000..552392a --- /dev/null +++ b/tools/apriori/main.c @@ -0,0 +1,229 @@ +/* TODO: + 1. check the ARM EABI version--this works for versions 1 and 2. + 2. use a more-intelligent approach to finding the symbol table, + symbol-string table, and the .dynamic section. + 3. fix the determination of the host and ELF-file endianness + 4. write the help screen +*/ + +#include <stdio.h> +#include <common.h> +#include <debug.h> +#include <libelf.h> +#include <elf.h> +#include <gelf.h> +#include <cmdline.h> +#include <string.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <apriori.h> +#include <prelinkmap.h> + +/* Flag set by --verbose. This variable is global as it is accessed by the + macro INFO() in multiple compilation unites. */ +int verbose_flag = 0; +/* Flag set by --quiet. This variable is global as it is accessed by the + macro PRINT() in multiple compilation unites. */ +int quiet_flag = 0; +static void print_dynamic_symbols(Elf *elf, const char *symtab_name); + +static unsigned s_next_link_addr; +static off_t s_addr_increment; + +static void report_library_size_in_memory(const char *name, off_t fsize) +{ + ASSERT(s_next_link_addr != -1UL); + INFO("Setting next link address (current is at 0x%08x):\n", + s_next_link_addr); + if (s_addr_increment) { + FAILIF(s_addr_increment < fsize, + "Command-line-specified address increment of 0x%08llx (%lld) " + "less than file [%s]'s size of %lld bytes!\n", + s_addr_increment, s_addr_increment, name, fsize); + FAILIF(s_next_link_addr % 4096, + "User-provided address increment 0x%08lx " + "is not page-aligned!\n", + s_addr_increment); + INFO("\tignoring file size, adjusting by address increment.\n"); + s_next_link_addr += s_addr_increment; + } + else { + INFO("\tuser address increment is zero, adjusting by file size.\n"); + s_next_link_addr += fsize; + s_next_link_addr &= ~(4096 - 1); + } + INFO("\t[%s] file size 0x%08lx\n", + name, + fsize); + INFO("\tnext prelink address: 0x%08x\n", s_next_link_addr); + ASSERT(!(s_next_link_addr % 4096)); /* New address must be page-aligned */ +} + +static unsigned get_next_link_address(const char *name) { + return s_next_link_addr; +} + +int main(int argc, char **argv) { + /* Do not issue INFO() statements before you call get_options() to set + the verbose flag as necessary. + */ + + char **lookup_dirs, **default_libs; + char *mapfile, *output, *prelinkmap; + int start_addr, inc_addr, locals_only, num_lookup_dirs, + num_default_libs, dry_run; + int first = get_options(argc, argv, + &start_addr, &inc_addr, &locals_only, + &quiet_flag, + &dry_run, + &lookup_dirs, &num_lookup_dirs, + &default_libs, &num_default_libs, + &verbose_flag, + &mapfile, + &output, + &prelinkmap); + + /* Perform some command-line-parameter checks. */ + int cmdline_err = 0; + if (first == argc) { + ERROR("You must specify at least one input ELF file!\n"); + cmdline_err++; + } + /* We complain when the user does not specify a start address for + prelinking when the user does not pass the locals_only switch. The + reason is that we will have a collection of executables, which we always + prelink to zero, and shared libraries, which we prelink at the specified + prelink address. When the user passes the locals_only switch, we do not + fail if the user does not specify start_addr, because the file to + prelink may be an executable, and not a shared library. At this moment, + we do not know what the case is. We find that out when we call function + init_source(). + */ + if (!locals_only && start_addr == -1) { + ERROR("You must specify --start-addr!\n"); + cmdline_err++; + } + if (start_addr == -1 && inc_addr != -1) { + ERROR("You must provide a start address if you provide an " + "address increment!\n"); + cmdline_err++; + } + if (prelinkmap != NULL && start_addr != -1) { + ERROR("You may not provide a prelink-map file (-p) and use -s/-i " + "at the same time!\n"); + cmdline_err++; + } + if (inc_addr == 0) { + ERROR("You may not specify a link-address increment of zero!\n"); + cmdline_err++; + } + if (locals_only) { + if (argc - first == 1) { + if (inc_addr != -1) { + ERROR("You are prelinking a single file; there is no point in " + "specifying a prelink-address increment!\n"); + /* This is nonfatal error, but paranoia is healthy. */ + cmdline_err++; + } + } + if (lookup_dirs != NULL || default_libs != NULL) { + ERROR("You are prelinking local relocations only; there is " + "no point in specifying lookup directories!\n"); + /* This is nonfatal error, but paranoia is healthy. */ + cmdline_err++; + } + } + + /* If there is an output option, then that must specify a file, if there is + a single input file, or a directory, if there are multiple input + files. */ + if (output != NULL) { + struct stat output_st; + FAILIF(stat(output, &output_st) < 0 && errno != ENOENT, + "stat(%s): %s (%d)\n", + output, + strerror(errno), + errno); + + if (argc - first == 1) { + FAILIF(!errno && !S_ISREG(output_st.st_mode), + "you have a single input file: -o must specify a " + "file name!\n"); + } + else { + FAILIF(errno == ENOENT, + "you have multiple input files: -o must specify a " + "directory name, but %s does not exist!\n", + output); + FAILIF(!S_ISDIR(output_st.st_mode), + "you have multiple input files: -o must specify a " + "directory name, but %s is not a directory!\n", + output); + } + } + + if (cmdline_err) { + print_help(argv[0]); + FAILIF(1, "There are command-line-option errors.\n"); + } + + /* Check to see whether the ELF library is current. */ + FAILIF (elf_version(EV_CURRENT) == EV_NONE, "libelf is out of date!\n"); + + if (inc_addr < 0) { + if (!locals_only) + PRINT("User has not provided an increment address, " + "will use library size to calculate successive " + "prelink addresses.\n"); + inc_addr = 0; + } + + void (*func_report_library_size_in_memory)(const char *name, off_t fsize); + unsigned (*func_get_next_link_address)(const char *name); + + if (prelinkmap != NULL) { + INFO("Reading prelink addresses from prelink-map file [%s].\n", + prelinkmap); + pm_init(prelinkmap); + func_report_library_size_in_memory = pm_report_library_size_in_memory; + func_get_next_link_address = pm_get_next_link_address; + } + else { + INFO("Start address: 0x%x\n", start_addr); + INFO("Increment address: 0x%x\n", inc_addr); + s_next_link_addr = start_addr; + s_addr_increment = inc_addr; + func_report_library_size_in_memory = report_library_size_in_memory; + func_get_next_link_address = get_next_link_address; + } + + /* Prelink... */ + apriori(&argv[first], argc - first, output, + func_report_library_size_in_memory, func_get_next_link_address, + locals_only, + dry_run, + lookup_dirs, num_lookup_dirs, + default_libs, num_default_libs, + mapfile); + + FREEIF(mapfile); + FREEIF(output); + if (lookup_dirs) { + ASSERT(num_lookup_dirs); + while (num_lookup_dirs--) + FREE(lookup_dirs[num_lookup_dirs]); + FREE(lookup_dirs); + } + if (default_libs) { + ASSERT(num_default_libs); + while (num_default_libs--) + FREE(default_libs[num_default_libs]); + FREE(default_libs); + } + + return 0; +} |