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
report_library_size_in_memory(const char * name,off_t fsize)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
get_next_link_address(const char * name)66 static unsigned get_next_link_address(const char *name) {
67 return s_next_link_addr;
68 }
69
main(int argc,char ** argv)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