/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SUPPORT_ANDROID_PRELINK_TAGS #include #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) */ }