360 lines
14 KiB
C
360 lines
14 KiB
C
/* 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 %8d: %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) */
|
|
}
|