summaryrefslogtreecommitdiffstats
path: root/tools/apriori/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/apriori/main.c')
-rw-r--r--tools/apriori/main.c229
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;
+}