/* 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 <hash.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 <soslim.h>
#include <symfilter.h>
#ifdef SUPPORT_ANDROID_PRELINK_TAGS
#include <prelink_info.h>
#endif
/* 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);
int main(int argc, char **argv)
{
int elf_fd = -1, newelf_fd = -1;
Elf *elf = NULL, *newelf = NULL;
char *infile = NULL;
char *outfile = NULL;
char *symsfile_name = NULL;
int print_symtab = 0;
int shady = 0;
int dry_run = 0;
int strip_debug = 0;
/* Do not issue INFO() statements before you call get_options() to set
the verbose flag as necessary.
*/
int first = get_options(argc, argv,
&outfile,
&symsfile_name,
&print_symtab,
&verbose_flag,
&quiet_flag,
&shady,
&dry_run,
&strip_debug);
if ((print_symtab && (first == argc)) ||
(!print_symtab && first + 1 != argc)) {
print_help();
FAILIF(1, "You must specify an input ELF file!\n");
}
FAILIF(print_symtab && (outfile || symsfile_name || shady),
"You cannot provide --print and --outfile, --filter options, or "
"--shady simultaneously!\n");
FAILIF(dry_run && outfile,
"You cannot have a dry run and output a file at the same time.");
/* Check to see whether the ELF library is current. */
FAILIF (elf_version(EV_CURRENT) == EV_NONE, "libelf is out of date!\n");
if (print_symtab) {
while (first < argc) {
infile = argv[first++];
INFO("Opening %s...\n", infile);
elf_fd = open(infile, O_RDONLY);
FAILIF(elf_fd < 0, "open(%s): %s (%d)\n",
infile,
strerror(errno),
errno);
INFO("Calling elf_begin(%s)...\n", infile);
elf = elf_begin(elf_fd, ELF_C_READ, NULL);
FAILIF_LIBELF(elf == NULL, elf_begin);
/* libelf can recognize COFF and A.OUT formats, but we handle only
ELF. */
FAILIF(elf_kind(elf) != ELF_K_ELF,
"Input file %s is not in ELF format!\n",
infile);
/* Make sure this is a shared library or an executable. */
{
GElf_Ehdr elf_hdr;
INFO("Making sure %s is a shared library or an executable.\n",
infile);
FAILIF_LIBELF(0 == gelf_getehdr(elf, &elf_hdr), gelf_getehdr);
FAILIF(elf_hdr.e_type != ET_DYN &&
elf_hdr.e_type != ET_EXEC,
"%s must be a shared library or an executable "
"(elf type is %d).\n",
infile,
elf_hdr.e_type);
}
print_dynamic_symbols(elf, infile);
FAILIF_LIBELF(elf_end(elf), elf_end);
FAILIF(close(elf_fd) < 0, "Could not close file %s: %s (%d)!\n",
infile, strerror(errno), errno);
}
}
else {
int elf_fd = -1;
Elf *elf = NULL;
infile = argv[first];
INFO("Opening %s...\n", infile);
elf_fd = open(infile, ((outfile == NULL && dry_run == 0) ? O_RDWR : O_RDONLY));
FAILIF(elf_fd < 0, "open(%s): %s (%d)\n",
infile,
strerror(errno),
errno);
INFO("Calling elf_begin(%s)...\n", infile);
elf = elf_begin(elf_fd,
((outfile == NULL && dry_run == 0) ? ELF_C_RDWR : ELF_C_READ),
NULL);
FAILIF_LIBELF(elf == NULL, elf_begin);
/* libelf can recognize COFF and A.OUT formats, but we handle only ELF. */
FAILIF(elf_kind(elf) != ELF_K_ELF,
"Input file %s is not in ELF format!\n",
infile);
/* We run a better check in adjust_elf() itself. It is permissible to call adjust_elf()
on an executable if we are only stripping sections from the executable, not rearranging
or moving sections.
*/
if (0) {
/* Make sure this is a shared library. */
GElf_Ehdr elf_hdr;
INFO("Making sure %s is a shared library...\n", infile);
FAILIF_LIBELF(0 == gelf_getehdr(elf, &elf_hdr), gelf_getehdr);
FAILIF(elf_hdr.e_type != ET_DYN,
"%s must be a shared library (elf type is %d, expecting %d).\n",
infile,
elf_hdr.e_type,
ET_DYN);
}
if (outfile != NULL) {
ASSERT(!dry_run);
struct stat st;
FAILIF(fstat (elf_fd, &st) != 0,
"Cannot stat input file %s: %s (%d)!\n",
infile, strerror(errno), errno);
newelf_fd = open (outfile, O_RDWR | O_CREAT | O_TRUNC,
st.st_mode & ACCESSPERMS);
FAILIF(newelf_fd < 0, "Cannot create file %s: %s (%d)!\n",
outfile, strerror(errno), errno);
INFO("Output file is [%s].\n", outfile);
newelf = elf_begin(newelf_fd, ELF_C_WRITE_MMAP, NULL);
} else {
INFO("Modifying [%s] in-place.\n", infile);
newelf = elf_clone(elf, ELF_C_EMPTY);
}
symfilter_t symfilter;
symfilter.symbols_to_keep = NULL;
symfilter.num_symbols_to_keep = 0;
if (symsfile_name) {
/* Make sure that the file is not empty. */
struct stat s;
FAILIF(stat(symsfile_name, &s) < 0,
"Cannot stat file %s.\n", symsfile_name);
if (s.st_size) {
INFO("Building symbol filter.\n");
build_symfilter(symsfile_name, elf, &symfilter, s.st_size);
}
else INFO("Not building symbol filter, filter file is empty.\n");
}
#ifdef SUPPORT_ANDROID_PRELINK_TAGS
int prelinked = 0;
int elf_little; /* valid if prelinked != 0 */
long prelink_addr; /* valid if prelinked != 0 */
#endif
clone_elf(elf, newelf,
infile, outfile,
symfilter.symbols_to_keep,
symfilter.num_symbols_to_keep,
shady
#ifdef SUPPORT_ANDROID_PRELINK_TAGS
, &prelinked,
&elf_little,
&prelink_addr
#endif
,
true, /* rebuild the section-header-strings table */
strip_debug,
dry_run);
if (symsfile_name && symfilter.symbols_to_keep != NULL) {
destroy_symfilter(&symfilter);
}
if (outfile != NULL) INFO("Closing %s...\n", outfile);
FAILIF_LIBELF(elf_end (newelf) != 0, elf_end);
FAILIF(newelf_fd >= 0 && close(newelf_fd) < 0,
"Could not close file %s: %s (%d)!\n",
outfile, strerror(errno), errno);
INFO("Closing %s...\n", infile);
FAILIF_LIBELF(elf_end(elf), elf_end);
FAILIF(close(elf_fd) < 0, "Could not close file %s: %s (%d)!\n",
infile, strerror(errno), errno);
#ifdef SUPPORT_ANDROID_PRELINK_TAGS
if (prelinked) {
INFO("File is prelinked, putting prelink TAG back in place.\n");
setup_prelink_info(outfile != NULL ? outfile : infile,
elf_little,
prelink_addr);
}
#endif
}
FREEIF(outfile);
return 0;
}
static void print_dynamic_symbols(Elf *elf, const char *file)
{
Elf_Scn *scn = NULL;
GElf_Shdr shdr;
GElf_Ehdr ehdr;
FAILIF_LIBELF(0 == gelf_getehdr(elf, &ehdr), gelf_getehdr);
while ((scn = elf_nextscn (elf, scn)) != NULL) {
FAILIF_LIBELF(NULL == gelf_getshdr(scn, &shdr), gelf_getshdr);
if (SHT_DYNSYM == shdr.sh_type) {
/* This failure is too restrictive. There is no reason why
the symbol table couldn't be called something else, but
there is a standard name, and chances are that if we don't
see it, there's something wrong.
*/
size_t shstrndx;
FAILIF_LIBELF(elf_getshstrndx(elf, &shstrndx) < 0,
elf_getshstrndx);
/* Now print the symbols. */
{
Elf_Data *symdata;
size_t elsize;
symdata = elf_getdata (scn, NULL); /* get the symbol data */
FAILIF_LIBELF(NULL == symdata, elf_getdata);
/* Get the number of section. We need to compare agains this
value for symbols that have special info in their section
references */
size_t shnum;
FAILIF_LIBELF(elf_getshnum (elf, &shnum) < 0, elf_getshnum);
/* Retrieve the size of a symbol entry */
elsize = gelf_fsize(elf, ELF_T_SYM, 1, ehdr.e_version);
size_t index;
for (index = 0; index < symdata->d_size / elsize; index++) {
GElf_Sym sym_mem;
GElf_Sym *sym;
/* Get the symbol. */
sym = gelf_getsymshndx (symdata, NULL,
index, &sym_mem, NULL);
FAILIF_LIBELF(sym == NULL, gelf_getsymshndx);
/* Print the symbol. */
char bind = '?';
switch(ELF32_ST_BIND(sym->st_info))
{
case STB_LOCAL: bind = 'l'; break;
case STB_GLOBAL: bind = 'g'; break;
case STB_WEAK: bind = 'w'; break;
default: break;
}
char type = '?';
switch(ELF32_ST_TYPE(sym->st_info))
{
case STT_NOTYPE: /* Symbol type is unspecified */
type = '?';
break;
case STT_OBJECT: /* Symbol is a data object */
type = 'o';
break;
case STT_FUNC: /* Symbol is a code object */
type = 'f';
break;
case STT_SECTION:/* Symbol associated with a section */
type = 's';
break;
case STT_FILE: /* Symbol's name is file name */
type = 'f';
break;
case STT_COMMON: /* Symbol is a common data object */
type = 'c';
break;
case STT_TLS: /* Symbol is thread-local data object*/
type = 't';
break;
}
{
int till_lineno;
int lineno;
const char *section_name = "(unknown)";
FAILIF(sym->st_shndx == SHN_XINDEX,
"Can't handle symbol's st_shndx == SHN_XINDEX!\n");
if (sym->st_shndx != SHN_UNDEF &&
sym->st_shndx < shnum) {
Elf_Scn *symscn = elf_getscn(elf, sym->st_shndx);
FAILIF_LIBELF(NULL == symscn, elf_getscn);
GElf_Shdr symscn_shdr;
FAILIF_LIBELF(NULL == gelf_getshdr(symscn,
&symscn_shdr),
gelf_getshdr);
section_name = elf_strptr(elf, shstrndx,
symscn_shdr.sh_name);
}
else if (sym->st_shndx == SHN_ABS) {
section_name = "SHN_ABS";
}
else if (sym->st_shndx == SHN_COMMON) {
section_name = "SHN_COMMON";
}
else if (sym->st_shndx == SHN_UNDEF) {
section_name = "(undefined)";
}
/* value size binding type section symname */
PRINT("%-15s %8zd: %08llx %08llx %c%c %5d %n%s%n",
file,
index,
sym->st_value, sym->st_size, bind, type,
sym->st_shndx,
&till_lineno,
section_name,
&lineno);
lineno -= till_lineno;
/* Create padding for section names of 15 chars.
This limit is somewhat arbitratry. */
while (lineno++ < 15) PRINT(" ");
PRINT("(%d) %s\n",
sym->st_name,
elf_strptr(elf, shdr.sh_link, sym->st_name));
}
}
}
} /* if (shdr.sh_type = SHT_DYNSYM) */
} /* while ((scn = elf_nextscn (elf, scn)) != NULL) */
}