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