summaryrefslogtreecommitdiffstats
path: root/tools/apriori/main.c
blob: 552392a1cefdfaaf8ba28865f385c548ffb320b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
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;
}