The Android Open Source Project | 88b6079 | 2009-03-03 19:28:42 -0800 | [diff] [blame] | 1 | /* TODO: |
| 2 | 1. check the ARM EABI version--this works for versions 1 and 2. |
| 3 | 2. use a more-intelligent approach to finding the symbol table, symbol-string |
| 4 | table, and the .dynamic section. |
| 5 | 3. fix the determination of the host and ELF-file endianness |
| 6 | 4. write the help screen |
| 7 | */ |
| 8 | |
| 9 | #include <stdio.h> |
| 10 | #include <common.h> |
| 11 | #include <debug.h> |
| 12 | #include <hash.h> |
| 13 | #include <libelf.h> |
| 14 | #include <elf.h> |
| 15 | #include <gelf.h> |
| 16 | #include <cmdline.h> |
| 17 | #include <string.h> |
| 18 | #include <errno.h> |
| 19 | #include <string.h> |
| 20 | #include <sys/types.h> |
| 21 | #include <sys/stat.h> |
| 22 | #include <fcntl.h> |
| 23 | #include <unistd.h> |
| 24 | #include <soslim.h> |
| 25 | #include <symfilter.h> |
| 26 | #ifdef SUPPORT_ANDROID_PRELINK_TAGS |
| 27 | #include <prelink_info.h> |
| 28 | #endif |
| 29 | |
| 30 | /* Flag set by --verbose. This variable is global as it is accessed by the |
| 31 | macro INFO() in multiple compilation unites. */ |
| 32 | int verbose_flag = 0; |
| 33 | /* Flag set by --quiet. This variable is global as it is accessed by the |
| 34 | macro PRINT() in multiple compilation unites. */ |
| 35 | int quiet_flag = 0; |
| 36 | static void print_dynamic_symbols(Elf *elf, const char *symtab_name); |
| 37 | |
| 38 | int main(int argc, char **argv) |
| 39 | { |
| 40 | int elf_fd = -1, newelf_fd = -1; |
| 41 | Elf *elf = NULL, *newelf = NULL; |
| 42 | char *infile = NULL; |
| 43 | char *outfile = NULL; |
| 44 | char *symsfile_name = NULL; |
| 45 | int print_symtab = 0; |
| 46 | int shady = 0; |
| 47 | int dry_run = 0; |
| 48 | int strip_debug = 0; |
| 49 | |
| 50 | /* Do not issue INFO() statements before you call get_options() to set |
| 51 | the verbose flag as necessary. |
| 52 | */ |
| 53 | |
| 54 | int first = get_options(argc, argv, |
| 55 | &outfile, |
| 56 | &symsfile_name, |
| 57 | &print_symtab, |
| 58 | &verbose_flag, |
| 59 | &quiet_flag, |
| 60 | &shady, |
| 61 | &dry_run, |
| 62 | &strip_debug); |
| 63 | |
| 64 | if ((print_symtab && (first == argc)) || |
| 65 | (!print_symtab && first + 1 != argc)) { |
| 66 | print_help(); |
| 67 | FAILIF(1, "You must specify an input ELF file!\n"); |
| 68 | } |
| 69 | FAILIF(print_symtab && (outfile || symsfile_name || shady), |
| 70 | "You cannot provide --print and --outfile, --filter options, or " |
| 71 | "--shady simultaneously!\n"); |
| 72 | FAILIF(dry_run && outfile, |
| 73 | "You cannot have a dry run and output a file at the same time."); |
| 74 | |
| 75 | /* Check to see whether the ELF library is current. */ |
| 76 | FAILIF (elf_version(EV_CURRENT) == EV_NONE, "libelf is out of date!\n"); |
| 77 | |
| 78 | if (print_symtab) { |
| 79 | |
| 80 | while (first < argc) { |
| 81 | infile = argv[first++]; |
| 82 | |
| 83 | INFO("Opening %s...\n", infile); |
| 84 | elf_fd = open(infile, O_RDONLY); |
| 85 | FAILIF(elf_fd < 0, "open(%s): %s (%d)\n", |
| 86 | infile, |
| 87 | strerror(errno), |
| 88 | errno); |
| 89 | INFO("Calling elf_begin(%s)...\n", infile); |
| 90 | elf = elf_begin(elf_fd, ELF_C_READ, NULL); |
| 91 | FAILIF_LIBELF(elf == NULL, elf_begin); |
| 92 | |
| 93 | /* libelf can recognize COFF and A.OUT formats, but we handle only |
| 94 | ELF. */ |
| 95 | FAILIF(elf_kind(elf) != ELF_K_ELF, |
| 96 | "Input file %s is not in ELF format!\n", |
| 97 | infile); |
| 98 | |
| 99 | /* Make sure this is a shared library or an executable. */ |
| 100 | { |
| 101 | GElf_Ehdr elf_hdr; |
| 102 | INFO("Making sure %s is a shared library or an executable.\n", |
| 103 | infile); |
| 104 | FAILIF_LIBELF(0 == gelf_getehdr(elf, &elf_hdr), gelf_getehdr); |
| 105 | FAILIF(elf_hdr.e_type != ET_DYN && |
| 106 | elf_hdr.e_type != ET_EXEC, |
| 107 | "%s must be a shared library or an executable " |
| 108 | "(elf type is %d).\n", |
| 109 | infile, |
| 110 | elf_hdr.e_type); |
| 111 | } |
| 112 | |
| 113 | print_dynamic_symbols(elf, infile); |
| 114 | |
| 115 | FAILIF_LIBELF(elf_end(elf), elf_end); |
| 116 | FAILIF(close(elf_fd) < 0, "Could not close file %s: %s (%d)!\n", |
| 117 | infile, strerror(errno), errno); |
| 118 | } |
| 119 | } |
| 120 | else { |
| 121 | int elf_fd = -1; |
| 122 | Elf *elf = NULL; |
| 123 | infile = argv[first]; |
| 124 | |
| 125 | INFO("Opening %s...\n", infile); |
| 126 | elf_fd = open(infile, ((outfile == NULL && dry_run == 0) ? O_RDWR : O_RDONLY)); |
| 127 | FAILIF(elf_fd < 0, "open(%s): %s (%d)\n", |
| 128 | infile, |
| 129 | strerror(errno), |
| 130 | errno); |
| 131 | INFO("Calling elf_begin(%s)...\n", infile); |
| 132 | elf = elf_begin(elf_fd, |
| 133 | ((outfile == NULL && dry_run == 0) ? ELF_C_RDWR : ELF_C_READ), |
| 134 | NULL); |
| 135 | FAILIF_LIBELF(elf == NULL, elf_begin); |
| 136 | |
| 137 | /* libelf can recognize COFF and A.OUT formats, but we handle only ELF. */ |
| 138 | FAILIF(elf_kind(elf) != ELF_K_ELF, |
| 139 | "Input file %s is not in ELF format!\n", |
| 140 | infile); |
| 141 | |
| 142 | /* We run a better check in adjust_elf() itself. It is permissible to call adjust_elf() |
| 143 | on an executable if we are only stripping sections from the executable, not rearranging |
| 144 | or moving sections. |
| 145 | */ |
| 146 | if (0) { |
| 147 | /* Make sure this is a shared library. */ |
| 148 | GElf_Ehdr elf_hdr; |
| 149 | INFO("Making sure %s is a shared library...\n", infile); |
| 150 | FAILIF_LIBELF(0 == gelf_getehdr(elf, &elf_hdr), gelf_getehdr); |
| 151 | FAILIF(elf_hdr.e_type != ET_DYN, |
| 152 | "%s must be a shared library (elf type is %d, expecting %d).\n", |
| 153 | infile, |
| 154 | elf_hdr.e_type, |
| 155 | ET_DYN); |
| 156 | } |
| 157 | |
| 158 | if (outfile != NULL) { |
| 159 | ASSERT(!dry_run); |
| 160 | struct stat st; |
| 161 | FAILIF(fstat (elf_fd, &st) != 0, |
| 162 | "Cannot stat input file %s: %s (%d)!\n", |
| 163 | infile, strerror(errno), errno); |
| 164 | newelf_fd = open (outfile, O_RDWR | O_CREAT | O_TRUNC, |
| 165 | st.st_mode & ACCESSPERMS); |
| 166 | FAILIF(newelf_fd < 0, "Cannot create file %s: %s (%d)!\n", |
| 167 | outfile, strerror(errno), errno); |
| 168 | INFO("Output file is [%s].\n", outfile); |
| 169 | newelf = elf_begin(newelf_fd, ELF_C_WRITE_MMAP, NULL); |
| 170 | } else { |
| 171 | INFO("Modifying [%s] in-place.\n", infile); |
| 172 | newelf = elf_clone(elf, ELF_C_EMPTY); |
| 173 | } |
| 174 | |
| 175 | symfilter_t symfilter; |
| 176 | |
| 177 | symfilter.symbols_to_keep = NULL; |
| 178 | symfilter.num_symbols_to_keep = 0; |
| 179 | if (symsfile_name) { |
| 180 | /* Make sure that the file is not empty. */ |
| 181 | struct stat s; |
| 182 | FAILIF(stat(symsfile_name, &s) < 0, |
| 183 | "Cannot stat file %s.\n", symsfile_name); |
| 184 | if (s.st_size) { |
| 185 | INFO("Building symbol filter.\n"); |
| 186 | build_symfilter(symsfile_name, elf, &symfilter, s.st_size); |
| 187 | } |
| 188 | else INFO("Not building symbol filter, filter file is empty.\n"); |
| 189 | } |
| 190 | #ifdef SUPPORT_ANDROID_PRELINK_TAGS |
| 191 | int prelinked = 0; |
| 192 | int elf_little; /* valid if prelinked != 0 */ |
| 193 | long prelink_addr; /* valid if prelinked != 0 */ |
| 194 | #endif |
| 195 | clone_elf(elf, newelf, |
| 196 | infile, outfile, |
| 197 | symfilter.symbols_to_keep, |
| 198 | symfilter.num_symbols_to_keep, |
| 199 | shady |
| 200 | #ifdef SUPPORT_ANDROID_PRELINK_TAGS |
| 201 | , &prelinked, |
| 202 | &elf_little, |
| 203 | &prelink_addr |
| 204 | #endif |
| 205 | , |
| 206 | true, /* rebuild the section-header-strings table */ |
| 207 | strip_debug, |
| 208 | dry_run); |
| 209 | |
| 210 | if (symsfile_name && symfilter.symbols_to_keep != NULL) { |
| 211 | destroy_symfilter(&symfilter); |
| 212 | } |
| 213 | |
| 214 | if (outfile != NULL) INFO("Closing %s...\n", outfile); |
| 215 | FAILIF_LIBELF(elf_end (newelf) != 0, elf_end); |
| 216 | FAILIF(newelf_fd >= 0 && close(newelf_fd) < 0, |
| 217 | "Could not close file %s: %s (%d)!\n", |
| 218 | outfile, strerror(errno), errno); |
| 219 | |
| 220 | INFO("Closing %s...\n", infile); |
| 221 | FAILIF_LIBELF(elf_end(elf), elf_end); |
| 222 | FAILIF(close(elf_fd) < 0, "Could not close file %s: %s (%d)!\n", |
| 223 | infile, strerror(errno), errno); |
| 224 | |
| 225 | #ifdef SUPPORT_ANDROID_PRELINK_TAGS |
| 226 | if (prelinked) { |
| 227 | INFO("File is prelinked, putting prelink TAG back in place.\n"); |
| 228 | setup_prelink_info(outfile != NULL ? outfile : infile, |
| 229 | elf_little, |
| 230 | prelink_addr); |
| 231 | } |
| 232 | #endif |
| 233 | } |
| 234 | |
| 235 | FREEIF(outfile); |
| 236 | return 0; |
| 237 | } |
| 238 | |
| 239 | static void print_dynamic_symbols(Elf *elf, const char *file) |
| 240 | { |
| 241 | Elf_Scn *scn = NULL; |
| 242 | GElf_Shdr shdr; |
| 243 | |
| 244 | GElf_Ehdr ehdr; |
| 245 | FAILIF_LIBELF(0 == gelf_getehdr(elf, &ehdr), gelf_getehdr); |
| 246 | while ((scn = elf_nextscn (elf, scn)) != NULL) { |
| 247 | FAILIF_LIBELF(NULL == gelf_getshdr(scn, &shdr), gelf_getshdr); |
| 248 | if (SHT_DYNSYM == shdr.sh_type) { |
| 249 | /* This failure is too restrictive. There is no reason why |
| 250 | the symbol table couldn't be called something else, but |
| 251 | there is a standard name, and chances are that if we don't |
| 252 | see it, there's something wrong. |
| 253 | */ |
| 254 | size_t shstrndx; |
| 255 | FAILIF_LIBELF(elf_getshstrndx(elf, &shstrndx) < 0, |
| 256 | elf_getshstrndx); |
| 257 | /* Now print the symbols. */ |
| 258 | { |
| 259 | Elf_Data *symdata; |
| 260 | size_t elsize; |
| 261 | symdata = elf_getdata (scn, NULL); /* get the symbol data */ |
| 262 | FAILIF_LIBELF(NULL == symdata, elf_getdata); |
| 263 | /* Get the number of section. We need to compare agains this |
| 264 | value for symbols that have special info in their section |
| 265 | references */ |
| 266 | size_t shnum; |
| 267 | FAILIF_LIBELF(elf_getshnum (elf, &shnum) < 0, elf_getshnum); |
| 268 | /* Retrieve the size of a symbol entry */ |
| 269 | elsize = gelf_fsize(elf, ELF_T_SYM, 1, ehdr.e_version); |
| 270 | |
| 271 | size_t index; |
| 272 | for (index = 0; index < symdata->d_size / elsize; index++) { |
| 273 | GElf_Sym sym_mem; |
| 274 | GElf_Sym *sym; |
| 275 | /* Get the symbol. */ |
| 276 | sym = gelf_getsymshndx (symdata, NULL, |
| 277 | index, &sym_mem, NULL); |
| 278 | FAILIF_LIBELF(sym == NULL, gelf_getsymshndx); |
| 279 | /* Print the symbol. */ |
| 280 | char bind = '?'; |
| 281 | switch(ELF32_ST_BIND(sym->st_info)) |
| 282 | { |
| 283 | case STB_LOCAL: bind = 'l'; break; |
| 284 | case STB_GLOBAL: bind = 'g'; break; |
| 285 | case STB_WEAK: bind = 'w'; break; |
| 286 | default: break; |
| 287 | } |
| 288 | char type = '?'; |
| 289 | switch(ELF32_ST_TYPE(sym->st_info)) |
| 290 | { |
| 291 | case STT_NOTYPE: /* Symbol type is unspecified */ |
| 292 | type = '?'; |
| 293 | break; |
| 294 | case STT_OBJECT: /* Symbol is a data object */ |
| 295 | type = 'o'; |
| 296 | break; |
| 297 | case STT_FUNC: /* Symbol is a code object */ |
| 298 | type = 'f'; |
| 299 | break; |
| 300 | case STT_SECTION:/* Symbol associated with a section */ |
| 301 | type = 's'; |
| 302 | break; |
| 303 | case STT_FILE: /* Symbol's name is file name */ |
| 304 | type = 'f'; |
| 305 | break; |
| 306 | case STT_COMMON: /* Symbol is a common data object */ |
| 307 | type = 'c'; |
| 308 | break; |
| 309 | case STT_TLS: /* Symbol is thread-local data object*/ |
| 310 | type = 't'; |
| 311 | break; |
| 312 | } |
| 313 | { |
| 314 | int till_lineno; |
| 315 | int lineno; |
| 316 | const char *section_name = "(unknown)"; |
| 317 | FAILIF(sym->st_shndx == SHN_XINDEX, |
| 318 | "Can't handle symbol's st_shndx == SHN_XINDEX!\n"); |
| 319 | if (sym->st_shndx != SHN_UNDEF && |
| 320 | sym->st_shndx < shnum) { |
| 321 | Elf_Scn *symscn = elf_getscn(elf, sym->st_shndx); |
| 322 | FAILIF_LIBELF(NULL == symscn, elf_getscn); |
| 323 | GElf_Shdr symscn_shdr; |
| 324 | FAILIF_LIBELF(NULL == gelf_getshdr(symscn, |
| 325 | &symscn_shdr), |
| 326 | gelf_getshdr); |
| 327 | section_name = elf_strptr(elf, shstrndx, |
| 328 | symscn_shdr.sh_name); |
| 329 | } |
| 330 | else if (sym->st_shndx == SHN_ABS) { |
| 331 | section_name = "SHN_ABS"; |
| 332 | } |
| 333 | else if (sym->st_shndx == SHN_COMMON) { |
| 334 | section_name = "SHN_COMMON"; |
| 335 | } |
| 336 | else if (sym->st_shndx == SHN_UNDEF) { |
| 337 | section_name = "(undefined)"; |
| 338 | } |
| 339 | /* value size binding type section symname */ |
Kenny Root | afb36e7 | 2010-06-26 22:39:38 -0700 | [diff] [blame] | 340 | PRINT("%-15s %8zd: %08llx %08llx %c%c %5d %n%s%n", |
The Android Open Source Project | 88b6079 | 2009-03-03 19:28:42 -0800 | [diff] [blame] | 341 | file, |
| 342 | index, |
| 343 | sym->st_value, sym->st_size, bind, type, |
| 344 | sym->st_shndx, |
| 345 | &till_lineno, |
| 346 | section_name, |
| 347 | &lineno); |
| 348 | lineno -= till_lineno; |
| 349 | /* Create padding for section names of 15 chars. |
| 350 | This limit is somewhat arbitratry. */ |
| 351 | while (lineno++ < 15) PRINT(" "); |
| 352 | PRINT("(%d) %s\n", |
| 353 | sym->st_name, |
| 354 | elf_strptr(elf, shdr.sh_link, sym->st_name)); |
| 355 | } |
| 356 | } |
| 357 | } |
| 358 | } /* if (shdr.sh_type = SHT_DYNSYM) */ |
| 359 | } /* while ((scn = elf_nextscn (elf, scn)) != NULL) */ |
| 360 | } |