Home | History | Annotate | Download | only in apriori
      1 /* TODO:
      2    1. check the ARM EABI version--this works for versions 1 and 2.
      3    2. use a more-intelligent approach to finding the symbol table,
      4       symbol-string table, and the .dynamic section.
      5    3. fix the determination of the host and ELF-file endianness
      6    4. write the help screen
      7 */
      8 
      9 #include <stdio.h>
     10 #include <common.h>
     11 #include <debug.h>
     12 #include <libelf.h>
     13 #include <elf.h>
     14 #include <gelf.h>
     15 #include <cmdline.h>
     16 #include <string.h>
     17 #include <errno.h>
     18 #include <string.h>
     19 #include <sys/types.h>
     20 #include <sys/stat.h>
     21 #include <fcntl.h>
     22 #include <unistd.h>
     23 #include <apriori.h>
     24 #include <prelinkmap.h>
     25 
     26 /* Flag set by --verbose.  This variable is global as it is accessed by the
     27    macro INFO() in multiple compilation unites. */
     28 int verbose_flag = 0;
     29 /* Flag set by --quiet.  This variable is global as it is accessed by the
     30    macro PRINT() in multiple compilation unites. */
     31 int quiet_flag = 0;
     32 static void print_dynamic_symbols(Elf *elf, const char *symtab_name);
     33 
     34 static unsigned s_next_link_addr;
     35 static off_t s_addr_increment;
     36 
     37 static void report_library_size_in_memory(const char *name, off_t fsize)
     38 {
     39     ASSERT(s_next_link_addr != -1UL);
     40 	INFO("Setting next link address (current is at 0x%08x):\n",
     41          s_next_link_addr);
     42 	if (s_addr_increment) {
     43 		FAILIF(s_addr_increment < fsize,
     44 			   "Command-line-specified address increment of 0x%08llx (%lld) "
     45                "less than file [%s]'s size of %lld bytes!\n",
     46 			   s_addr_increment, s_addr_increment, name, fsize);
     47 		FAILIF(s_next_link_addr % 4096,
     48 			   "User-provided address increment 0x%08lx "
     49                "is not page-aligned!\n",
     50 			   s_addr_increment);
     51 		INFO("\tignoring file size, adjusting by address increment.\n");
     52 		s_next_link_addr += s_addr_increment;
     53 	}
     54 	else {
     55 		INFO("\tuser address increment is zero, adjusting by file size.\n");
     56 		s_next_link_addr += fsize;
     57 		s_next_link_addr &= ~(4096 - 1);
     58 	}
     59 	INFO("\t[%s] file size 0x%08lx\n",
     60 		 name,
     61 		 fsize);
     62 	INFO("\tnext prelink address: 0x%08x\n", s_next_link_addr);
     63 	ASSERT(!(s_next_link_addr % 4096)); /* New address must be page-aligned */
     64 }
     65 
     66 static unsigned get_next_link_address(const char *name) {
     67     return s_next_link_addr;
     68 }
     69 
     70 int main(int argc, char **argv) {
     71     /* Do not issue INFO() statements before you call get_options() to set
     72        the verbose flag as necessary.
     73     */
     74 
     75     char **lookup_dirs, **default_libs;
     76 	char *mapfile, *output, *prelinkmap;
     77     int start_addr, inc_addr, locals_only, num_lookup_dirs,
     78         num_default_libs, dry_run;
     79     int first = get_options(argc, argv,
     80                             &start_addr, &inc_addr, &locals_only,
     81                             &quiet_flag,
     82                             &dry_run,
     83                             &lookup_dirs, &num_lookup_dirs,
     84                             &default_libs, &num_default_libs,
     85                             &verbose_flag,
     86 							&mapfile,
     87                             &output,
     88                             &prelinkmap);
     89 
     90     /* Perform some command-line-parameter checks. */
     91     int cmdline_err = 0;
     92     if (first == argc) {
     93         ERROR("You must specify at least one input ELF file!\n");
     94         cmdline_err++;
     95     }
     96     /* We complain when the user does not specify a start address for
     97        prelinking when the user does not pass the locals_only switch.  The
     98        reason is that we will have a collection of executables, which we always
     99        prelink to zero, and shared libraries, which we prelink at the specified
    100        prelink address.  When the user passes the locals_only switch, we do not
    101        fail if the user does not specify start_addr, because the file to
    102        prelink may be an executable, and not a shared library.  At this moment,
    103        we do not know what the case is.  We find that out when we call function
    104        init_source().
    105     */
    106     if (!locals_only && start_addr == -1) {
    107         ERROR("You must specify --start-addr!\n");
    108         cmdline_err++;
    109     }
    110     if (start_addr == -1 && inc_addr != -1) {
    111         ERROR("You must provide a start address if you provide an "
    112               "address increment!\n");
    113         cmdline_err++;
    114     }
    115     if (prelinkmap != NULL && start_addr != -1) {
    116         ERROR("You may not provide a prelink-map file (-p) and use -s/-i "
    117               "at the same time!\n");
    118         cmdline_err++;
    119     }
    120     if (inc_addr == 0) {
    121         ERROR("You may not specify a link-address increment of zero!\n");
    122         cmdline_err++;
    123     }
    124     if (locals_only) {
    125         if (argc - first == 1) {
    126             if (inc_addr != -1) {
    127                 ERROR("You are prelinking a single file; there is no point in "
    128                       "specifying a prelink-address increment!\n");
    129                 /* This is nonfatal error, but paranoia is healthy. */
    130                 cmdline_err++;
    131             }
    132         }
    133         if (lookup_dirs != NULL || default_libs != NULL) {
    134             ERROR("You are prelinking local relocations only; there is "
    135                   "no point in specifying lookup directories!\n");
    136             /* This is nonfatal error, but paranoia is healthy. */
    137             cmdline_err++;
    138         }
    139     }
    140 
    141     /* If there is an output option, then that must specify a file, if there is
    142        a single input file, or a directory, if there are multiple input
    143        files. */
    144     if (output != NULL) {
    145         struct stat output_st;
    146         FAILIF(stat(output, &output_st) < 0 && errno != ENOENT,
    147                "stat(%s): %s (%d)\n",
    148                output,
    149                strerror(errno),
    150                errno);
    151 
    152         if (argc - first == 1) {
    153             FAILIF(!errno && !S_ISREG(output_st.st_mode),
    154                    "you have a single input file: -o must specify a "
    155                    "file name!\n");
    156         }
    157         else {
    158             FAILIF(errno == ENOENT,
    159                    "you have multiple input files: -o must specify a "
    160                    "directory name, but %s does not exist!\n",
    161                    output);
    162             FAILIF(!S_ISDIR(output_st.st_mode),
    163                    "you have multiple input files: -o must specify a "
    164                    "directory name, but %s is not a directory!\n",
    165                    output);
    166         }
    167     }
    168 
    169     if (cmdline_err) {
    170         print_help(argv[0]);
    171         FAILIF(1, "There are command-line-option errors.\n");
    172     }
    173 
    174     /* Check to see whether the ELF library is current. */
    175     FAILIF (elf_version(EV_CURRENT) == EV_NONE, "libelf is out of date!\n");
    176 
    177 	if (inc_addr < 0) {
    178         if (!locals_only)
    179             PRINT("User has not provided an increment address, "
    180                   "will use library size to calculate successive "
    181                   "prelink addresses.\n");
    182         inc_addr = 0;
    183 	}
    184 
    185     void (*func_report_library_size_in_memory)(const char *name, off_t fsize);
    186     unsigned (*func_get_next_link_address)(const char *name);
    187 
    188     if (prelinkmap != NULL) {
    189         INFO("Reading prelink addresses from prelink-map file [%s].\n",
    190              prelinkmap);
    191         pm_init(prelinkmap);
    192         func_report_library_size_in_memory = pm_report_library_size_in_memory;
    193         func_get_next_link_address = pm_get_next_link_address;
    194     }
    195     else {
    196         INFO("Start address: 0x%x\n", start_addr);
    197         INFO("Increment address: 0x%x\n", inc_addr);
    198         s_next_link_addr = start_addr;
    199         s_addr_increment = inc_addr;
    200         func_report_library_size_in_memory = report_library_size_in_memory;
    201         func_get_next_link_address = get_next_link_address;
    202     }
    203 
    204     /* Prelink... */
    205     apriori(&argv[first], argc - first, output,
    206             func_report_library_size_in_memory, func_get_next_link_address,
    207             locals_only,
    208             dry_run,
    209             lookup_dirs, num_lookup_dirs,
    210             default_libs, num_default_libs,
    211 			mapfile);
    212 
    213 	FREEIF(mapfile);
    214     FREEIF(output);
    215 	if (lookup_dirs) {
    216 		ASSERT(num_lookup_dirs);
    217 		while (num_lookup_dirs--)
    218 			FREE(lookup_dirs[num_lookup_dirs]);
    219 		FREE(lookup_dirs);
    220 	}
    221 	if (default_libs) {
    222 		ASSERT(num_default_libs);
    223 		while (num_default_libs--)
    224 			FREE(default_libs[num_default_libs]);
    225 		FREE(default_libs);
    226 	}
    227 
    228     return 0;
    229 }
    230