| /* 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; |
| } |