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