1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.compatibility.common.util; 18 19 import com.google.errorprone.annotations.CanIgnoreReturnValue; 20 21 import java.io.File; 22 import java.io.IOException; 23 import java.io.RandomAccessFile; 24 import java.util.ArrayList; 25 import java.util.HashMap; 26 import java.util.List; 27 import java.util.Map; 28 29 /** 30 * A poor man's implementation of the readelf command. This program is designed to parse ELF 31 * (Executable and Linkable Format) files. 32 */ 33 // ToDo: consolidate with com.android.compatibility.common.util 34 public class ReadElf implements AutoCloseable { 35 /** The magic values for the ELF identification. */ 36 private static final byte[] ELFMAG = { 37 (byte) 0x7F, (byte) 'E', (byte) 'L', (byte) 'F', 38 }; 39 40 private static final int EI_NIDENT = 16; 41 42 private static final int EI_CLASS = 4; 43 private static final int EI_DATA = 5; 44 45 public static final int ET_DYN = 3; 46 public static final int EM_386 = 3; 47 public static final int EM_ARM = 40; 48 public static final int EM_X86_64 = 62; 49 // http://en.wikipedia.org/wiki/Qualcomm_Hexagon 50 public static final int EM_QDSP6 = 164; 51 public static final int EM_AARCH64 = 183; 52 public static final int EM_RISCV = 243; 53 54 public static final String ARCH_ARM = "arm"; 55 public static final String ARCH_X86 = "x86"; 56 public static final String ARCH_RISCV = "riscv"; 57 public static final String ARCH_UNKNOWN = "unknown"; 58 private static final String RODATA = ".rodata"; 59 60 private static final int ELFCLASS32 = 1; 61 private static final int ELFCLASS64 = 2; 62 63 private static final int ELFDATA2LSB = 1; 64 private static final int ELFDATA2MSB = 2; 65 66 private static final int EV_CURRENT = 1; 67 68 private static final long PT_LOAD = 1; 69 70 // https://en.wikipedia.org/wiki/Executable_and_Linkable_Format 71 private static final int SHT_PROGBITS = 1; 72 private static final int SHT_SYMTAB = 2; 73 private static final int SHT_STRTAB = 3; 74 private static final int SHT_DYNAMIC = 6; 75 private static final int SHT_DYNSYM = 11; 76 private static final int SHT_GNU_VERDEF = 0x6ffffffd; 77 private static final int SHT_GNU_VERNEED = 0x6ffffffe; 78 private static final int SHT_GNU_VERSYM = 0x6fffffff; 79 80 public static class Symbol { 81 public static final int STB_LOCAL = 0; 82 public static final int STB_GLOBAL = 1; 83 public static final int STB_WEAK = 2; 84 public static final int STB_LOPROC = 13; 85 public static final int STB_HIPROC = 15; 86 87 public static final int STT_NOTYPE = 0; 88 public static final int STT_OBJECT = 1; 89 public static final int STT_FUNC = 2; 90 public static final int STT_SECTION = 3; 91 public static final int STT_FILE = 4; 92 public static final int STT_COMMON = 5; 93 public static final int STT_TLS = 6; 94 95 public static final int SHN_UNDEF = 0; 96 public static final int SHN_ABS = 0xfff1; 97 98 public final String name; 99 public final int bind; 100 public final int type; 101 public final int shndx; 102 public final long value; 103 public final long size; 104 public final int other; 105 106 public VerNeed mVerNeed; 107 public VerDef mVerDef; 108 Symbol(String name, int st_info, int st_shndx, long st_value, long st_size, int st_other)109 Symbol(String name, int st_info, int st_shndx, long st_value, long st_size, int st_other) { 110 this.name = name; 111 this.bind = (st_info >> 4) & 0x0F; 112 this.type = st_info & 0x0F; 113 this.shndx = st_shndx; 114 this.value = st_value; 115 this.size = st_size; 116 this.other = st_other; 117 } 118 119 @Override toString()120 public String toString() { 121 return String.format( 122 "%s, %s, %s, %s, %s, %s", 123 name, 124 toBind(), 125 toType(), 126 toShndx(), 127 getExternalLibFileName(), 128 getExternalLibName()); 129 } 130 toBind()131 public String toBind() { 132 switch (bind) { 133 case STB_LOCAL: 134 return "LOCAL"; 135 case STB_GLOBAL: 136 return "GLOBAL"; 137 case STB_WEAK: 138 return "WEAK"; 139 } 140 return "STB_??? (" + bind + ")"; 141 } 142 toType()143 public String toType() { 144 switch (type) { 145 case STT_NOTYPE: 146 return "NOTYPE"; 147 case STT_OBJECT: 148 return "OBJECT"; 149 case STT_FUNC: 150 return "FUNC"; 151 case STT_SECTION: 152 return "SECTION"; 153 case STT_FILE: 154 return "FILE"; 155 case STT_COMMON: 156 return "COMMON"; 157 case STT_TLS: 158 return "TLS"; 159 } 160 return "STT_??? (" + type + ")"; 161 } 162 toShndx()163 public String toShndx() { 164 switch (shndx) { 165 case SHN_ABS: 166 return "ABS"; 167 case SHN_UNDEF: 168 return "UND"; 169 } 170 return String.valueOf(shndx); 171 } 172 173 // if a symbol is not define locally isGlobalUnd()174 public boolean isGlobalUnd() { 175 return (bind != STB_LOCAL && shndx == SHN_UNDEF); 176 } 177 178 // if a symbol is extern isExtern()179 public boolean isExtern() { 180 return (bind != STB_LOCAL && shndx != SHN_UNDEF); 181 } 182 getExternalLibFileName()183 public String getExternalLibFileName() { 184 if (mVerNeed != null) { 185 return mVerNeed.vn_file_name; 186 } 187 return null; 188 } 189 getExternalLibName()190 public String getExternalLibName() { 191 if (mVerNeed != null) { 192 return mVerNeed.vn_vernaux[0].vna_lib_name; 193 } 194 return null; 195 } 196 getExternalLibVer()197 public int getExternalLibVer() { 198 if (mVerNeed != null) { 199 return mVerNeed.vn_vernaux[0].vna_other; 200 } 201 return -1; 202 } 203 getVerDefLibName()204 public String getVerDefLibName() { 205 if (mVerDef != null) { 206 return mVerDef.vd_verdaux[0].vda_lib_name; 207 } 208 return null; 209 } 210 getVerDefVersion()211 public int getVerDefVersion() { 212 if (mVerDef != null) { 213 return mVerDef.vd_version; 214 } 215 return -1; 216 } 217 } 218 219 public static class SecHeader { 220 public final long sh_name; 221 public final long sh_type; 222 public final long sh_flags; 223 public final long sh_addr; 224 public final long sh_offset; 225 public final long sh_size; 226 public final long sh_link; 227 public final long sh_info; 228 public final long sh_addralign; 229 public final long sh_entsize; 230 SecHeader( long name, long type, long flags, long addr, long offset, long size, long link, long info, long addralign, long entsize)231 SecHeader( 232 long name, 233 long type, 234 long flags, 235 long addr, 236 long offset, 237 long size, 238 long link, 239 long info, 240 long addralign, 241 long entsize) { 242 this.sh_name = name; 243 this.sh_type = type; 244 this.sh_flags = flags; 245 this.sh_addr = addr; 246 this.sh_offset = offset; 247 this.sh_size = size; 248 this.sh_link = link; 249 this.sh_info = info; 250 this.sh_addralign = addralign; 251 this.sh_entsize = entsize; 252 } 253 254 @Override toString()255 public String toString() { 256 return String.format( 257 "%d, %d, %d, %d, %d, %d, %d, %d, %d, %d", 258 this.sh_name, 259 this.sh_type, 260 this.sh_flags, 261 this.sh_addr, 262 this.sh_offset, 263 this.sh_size, 264 this.sh_link, 265 this.sh_info, 266 this.sh_addralign, 267 this.sh_entsize); 268 } 269 } 270 271 public static class VerNeed { 272 public final int vn_version; 273 public final int vn_cnt; 274 public final long vn_file; 275 public final long vn_aux; 276 public final long vn_next; 277 public String vn_file_name; 278 public VerNAux[] vn_vernaux; 279 VerNeed(String file_name, String lib_name, int ndx)280 VerNeed(String file_name, String lib_name, int ndx) { 281 this.vn_file_name = file_name.toLowerCase(); 282 this.vn_vernaux = new VerNAux[1]; 283 this.vn_vernaux[0] = new VerNAux(lib_name, ndx); 284 285 this.vn_version = 0; 286 this.vn_cnt = 0; 287 this.vn_file = 0; 288 this.vn_aux = 0; 289 this.vn_next = 0; 290 } 291 VerNeed(int ver, int cnt, long file, long aux, long next)292 VerNeed(int ver, int cnt, long file, long aux, long next) { 293 this.vn_version = ver; 294 this.vn_cnt = cnt; 295 this.vn_file = file; 296 this.vn_aux = aux; 297 this.vn_next = next; 298 } 299 300 @Override toString()301 public String toString() { 302 String vernauxStr = ""; 303 for (int i = 0; i < this.vn_cnt; i++) { 304 vernauxStr += String.format(" %s\n", this.vn_vernaux[i].toString()); 305 } 306 return String.format( 307 "%s, %d, %d, %d, %d, %d \n%s", 308 this.vn_file_name, 309 this.vn_version, 310 this.vn_cnt, 311 this.vn_file, 312 this.vn_aux, 313 this.vn_next, 314 vernauxStr); 315 } 316 } 317 318 public static class VerNAux { 319 public final long vna_hash; 320 public final int vna_flags; 321 public final int vna_other; 322 public final long vna_name; 323 public final long vna_next; 324 public String vna_lib_name; 325 VerNAux(String lib_name, int ndx)326 VerNAux(String lib_name, int ndx) { 327 this.vna_lib_name = lib_name; 328 329 this.vna_hash = 0; 330 this.vna_flags = 0; 331 this.vna_other = ndx; 332 this.vna_name = 0; 333 this.vna_next = 0; 334 } 335 VerNAux(long hash, int flags, int other, long name, long next)336 VerNAux(long hash, int flags, int other, long name, long next) { 337 this.vna_hash = hash; 338 this.vna_flags = flags; 339 this.vna_other = other; 340 this.vna_name = name; 341 this.vna_next = next; 342 } 343 344 @Override toString()345 public String toString() { 346 return String.format( 347 "%s, %d, %d, %d, %d, %d", 348 this.vna_lib_name, 349 this.vna_hash, 350 this.vna_flags, 351 this.vna_other, 352 this.vna_name, 353 this.vna_next); 354 } 355 } 356 357 public static class VerDef { 358 public final int vd_version; 359 public final int vd_flags; 360 public final int vd_ndx; 361 public final int vd_cnt; 362 public final long vd_hash; 363 public final long vd_aux; 364 public final long vd_next; 365 public VerDAux[] vd_verdaux; 366 VerDef(String lib_name)367 VerDef(String lib_name) { 368 this.vd_verdaux = new VerDAux[1]; 369 this.vd_verdaux[0] = new VerDAux(lib_name); 370 371 this.vd_version = 0; 372 this.vd_flags = 0; 373 this.vd_ndx = 0; 374 this.vd_cnt = 0; 375 this.vd_hash = 0; 376 this.vd_aux = 0; 377 this.vd_next = 0; 378 } 379 VerDef(int ver, int flags, int ndx, int cnt, long hash, long aux, long next)380 VerDef(int ver, int flags, int ndx, int cnt, long hash, long aux, long next) { 381 this.vd_version = ver; 382 this.vd_flags = flags; 383 this.vd_ndx = ndx; 384 this.vd_cnt = cnt; 385 this.vd_hash = hash; 386 this.vd_aux = aux; 387 this.vd_next = next; 388 } 389 390 @Override toString()391 public String toString() { 392 String vStr = ""; 393 for (int i = 0; i < this.vd_cnt; i++) { 394 vStr += String.format(" %s\n", this.vd_verdaux[i].toString()); 395 } 396 return String.format( 397 "%s, %d, %d, %d, %d, %d \n%s", 398 this.vd_verdaux[0].vda_lib_name, 399 this.vd_version, 400 this.vd_flags, 401 this.vd_ndx, 402 this.vd_cnt, 403 this.vd_hash, 404 vStr); 405 } 406 } 407 408 public static class VerDAux { 409 public final long vda_name; 410 public final long vda_next; 411 public String vda_lib_name; 412 VerDAux(String lib_name)413 VerDAux(String lib_name) { 414 this.vda_lib_name = lib_name.toLowerCase(); 415 416 this.vda_name = 0; 417 this.vda_next = 0; 418 } 419 VerDAux(long name, long next)420 VerDAux(long name, long next) { 421 this.vda_name = name; 422 this.vda_next = next; 423 } 424 425 @Override toString()426 public String toString() { 427 return String.format("%s, %d, %d", this.vda_lib_name, this.vda_name, this.vda_next); 428 } 429 } 430 431 // Dynamic Section Entry 432 public static class DynamicEntry { 433 private static final int DT_NEEDED = 1; 434 public final long mTag; 435 public final long mValue; 436 DynamicEntry(long tag, long value)437 DynamicEntry(long tag, long value) { 438 mTag = tag; 439 mValue = value; 440 } 441 isNeeded()442 public boolean isNeeded() { 443 if (mTag == DT_NEEDED) { 444 return true; 445 } else { 446 // System.err.println(String.format("Not Needed: %d, %d", mTag, mValue)); 447 return false; 448 } 449 } 450 getValue()451 public long getValue() { 452 return mValue; 453 } 454 455 @Override toString()456 public String toString() { 457 return String.format("%d, %d", this.mTag, this.mValue); 458 } 459 } 460 461 private final String mPath; 462 private final RandomAccessFile mFile; 463 private final byte[] mBuffer = new byte[512]; 464 private int mEndian; 465 private boolean mIsDynamic; 466 private boolean mIsPIE; 467 private int mType; 468 private int mAddrSize; 469 private int mMachine; 470 471 /** Symbol Table offset */ 472 private long mSymTabOffset; 473 474 /** Symbol Table size */ 475 private long mSymTabSize; 476 477 /** Symbol entry count */ 478 private int mSymEntCnt; 479 480 /** Dynamic Symbol Table offset */ 481 private long mDynSymOffset; 482 483 /** Dynamic Symbol Table size */ 484 private long mDynSymSize; 485 486 /** Dynamic entry count */ 487 private int mDynSymEntCnt; 488 489 /** Section Header String Table offset */ 490 private long mShStrTabOffset; 491 492 /** Section Header String Table size */ 493 private long mShStrTabSize; 494 495 /** String Table offset */ 496 private long mStrTabOffset; 497 498 /** String Table size */ 499 private long mStrTabSize; 500 501 /** Dynamic String Table offset */ 502 private long mDynStrOffset; 503 504 /** Dynamic String Table size */ 505 private long mDynStrSize; 506 507 /** Dynamic Table offset */ 508 private long mDynamicTabOffset; 509 510 /** Dynamic Table size */ 511 private long mDynamicTabSize; 512 513 /** Version Symbols Table offset */ 514 private long mVerSymTabOffset; 515 516 /** Version Symbols Table size */ 517 private long mVerSymTabSize; 518 519 /** Version Needs Table offset */ 520 private long mVerNeedTabOffset; 521 522 /** Version Definition Table size */ 523 private long mVerNeedTabSize; 524 525 private int mVerNeedEntryCnt; 526 527 /** Version Definition Table offset */ 528 private long mVerDefTabOffset; 529 530 /** Version Needs Table size */ 531 private long mVerDefTabSize; 532 533 private int mVerDefEntryCnt; 534 535 /** Symbol Table symbol names */ 536 private Map<String, Symbol> mSymbols; 537 538 /** Symbol Table symbol array */ 539 private Symbol[] mSymArr; 540 541 /** Dynamic Symbol Table symbol names */ 542 private Map<String, Symbol> mDynamicSymbols; 543 544 /** Dynamic Symbol Table symbol array */ 545 private Symbol[] mDynSymArr; 546 547 /** Version Symbols Table */ 548 private int[] mVerSym; 549 550 /** Version Needed Table */ 551 private VerNeed[] mVerNeedArr; 552 553 /** Version Definition Table */ 554 private VerDef[] mVerDefArr; 555 556 /** Dynamic Table */ 557 private List<DynamicEntry> mDynamicArr; 558 559 /** Rodata offset */ 560 private boolean mHasRodata; 561 562 /** Rodata offset */ 563 private long mRodataOffset; 564 565 /** Rodata size */ 566 private int mRodataSize; 567 568 /** Rodata String List */ 569 private List<String> mRoStrings; 570 571 /** Rodata byte[] */ 572 private byte[] mRoData; 573 read(File file)574 public static ReadElf read(File file) throws IOException { 575 return new ReadElf(file); 576 } 577 main(String[] args)578 public static void main(String[] args) throws IOException { 579 for (String arg : args) { 580 ReadElf elf = ReadElf.read(new File(arg)); 581 elf.getDynamicSymbol("x"); 582 elf.getSymbol("x"); 583 584 Symbol[] symArr; 585 System.out.println("===Symbol==="); 586 symArr = elf.getSymArr(); 587 for (int i = 0; i < symArr.length; i++) { 588 System.out.println(String.format("%8x: %s", i, symArr[i].toString())); 589 } 590 System.out.println("===Dynamic Symbol==="); 591 symArr = elf.getDynSymArr(); 592 for (int i = 0; i < symArr.length; i++) { 593 if (elf.mVerNeedEntryCnt > 0) { 594 System.out.println( 595 String.format( 596 "%8x: %s, %s, %s - %d", 597 i, 598 symArr[i].toString(), 599 symArr[i].getExternalLibName(), 600 symArr[i].getExternalLibFileName(), 601 symArr[i].getExternalLibVer())); 602 } else { 603 System.out.println( 604 String.format( 605 "%8x: %s, %s - %d", 606 i, 607 symArr[i].toString(), 608 symArr[i].getVerDefLibName(), 609 symArr[i].getVerDefVersion())); 610 } 611 } 612 613 System.out.println("===Dynamic Dependencies==="); 614 for (String DynDepEntry : elf.getDynamicDependencies()) { 615 System.out.println(DynDepEntry); 616 } 617 618 System.out.println("===Strings in Read Only(.rodata) section==="); 619 for (String roStr : elf.getRoStrings()) { 620 System.out.println(roStr); 621 } 622 623 elf.close(); 624 } 625 } 626 isElf(File file)627 public static boolean isElf(File file) { 628 try { 629 if (file.length() < EI_NIDENT) { 630 throw new IllegalArgumentException( 631 "Too small to be an ELF file: " + file.getCanonicalPath()); 632 } 633 634 RandomAccessFile raFile = new RandomAccessFile(file, "r"); 635 byte[] buffer = new byte[512]; 636 raFile.seek(0); 637 raFile.readFully(buffer, 0, EI_NIDENT); 638 if (buffer[0] != ELFMAG[0] 639 || buffer[1] != ELFMAG[1] 640 || buffer[2] != ELFMAG[2] 641 || buffer[3] != ELFMAG[3]) { 642 throw new IllegalArgumentException("Invalid ELF file: " + file.getCanonicalPath()); 643 } 644 raFile.close(); 645 ; 646 return true; 647 } catch (Exception e) { 648 return false; 649 } 650 } 651 getBits()652 public int getBits() { 653 if (mMachine == EM_386 || mMachine == EM_ARM || mMachine == EM_QDSP6) { 654 return 32; 655 } else if (mMachine == EM_AARCH64 || mMachine == EM_X86_64 || mMachine == EM_RISCV) { 656 return 64; 657 } else { 658 return -1; 659 } 660 } 661 getArchitecture()662 public String getArchitecture() { 663 if (mMachine == EM_ARM || mMachine == EM_AARCH64) { 664 return ARCH_ARM; 665 } else if (mMachine == EM_386 || mMachine == EM_X86_64) { 666 return ARCH_X86; 667 } else if (mMachine == EM_RISCV) { 668 return ARCH_RISCV; 669 } else { 670 return ARCH_UNKNOWN; 671 } 672 } 673 getSymbols()674 public Map<String, Symbol> getSymbols() throws IOException { 675 if (mSymbols == null) { 676 getSymbol(""); 677 } 678 return mSymbols; 679 } 680 getSymArr()681 public Symbol[] getSymArr() throws IOException { 682 if (mSymArr == null) { 683 getSymbol(""); 684 } 685 return mSymArr; 686 } 687 getDynamicSymbols()688 public Map<String, Symbol> getDynamicSymbols() throws IOException { 689 if (mDynamicSymbols == null) { 690 getDynamicSymbol(""); 691 } 692 return mDynamicSymbols; 693 } 694 getDynSymArr()695 public Symbol[] getDynSymArr() throws IOException { 696 if (mDynSymArr == null) { 697 getDynamicSymbol(""); 698 } 699 return mDynSymArr; 700 } 701 isDynamic()702 public boolean isDynamic() { 703 return mIsDynamic; 704 } 705 getType()706 public int getType() { 707 return mType; 708 } 709 isPIE()710 public boolean isPIE() { 711 return mIsPIE; 712 } 713 ReadElf(File file)714 private ReadElf(File file) throws IOException { 715 mHasRodata = false; 716 mRoData = null; 717 mPath = file.getPath(); 718 mFile = new RandomAccessFile(file, "r"); 719 720 if (mFile.length() < EI_NIDENT) { 721 throw new IllegalArgumentException("Too small to be an ELF file: " + file); 722 } 723 724 readHeader(); 725 } 726 727 @Override close()728 public void close() { 729 try { 730 mFile.close(); 731 } catch (IOException ignored) { 732 } 733 } 734 735 @Override finalize()736 protected void finalize() throws Throwable { 737 try { 738 close(); 739 } finally { 740 super.finalize(); 741 } 742 } 743 readHeader()744 private void readHeader() throws IOException { 745 mFile.seek(0); 746 mFile.readFully(mBuffer, 0, EI_NIDENT); 747 748 if (mBuffer[0] != ELFMAG[0] 749 || mBuffer[1] != ELFMAG[1] 750 || mBuffer[2] != ELFMAG[2] 751 || mBuffer[3] != ELFMAG[3]) { 752 throw new IllegalArgumentException("Invalid ELF file: " + mPath); 753 } 754 755 int elfClass = mBuffer[EI_CLASS]; 756 if (elfClass == ELFCLASS32) { 757 mAddrSize = 4; 758 } else if (elfClass == ELFCLASS64) { 759 mAddrSize = 8; 760 } else { 761 throw new IOException("Invalid ELF EI_CLASS: " + elfClass + ": " + mPath); 762 } 763 764 mEndian = mBuffer[EI_DATA]; 765 if (mEndian == ELFDATA2LSB) { 766 } else if (mEndian == ELFDATA2MSB) { 767 throw new IOException("Unsupported ELFDATA2MSB file: " + mPath); 768 } else { 769 throw new IOException("Invalid ELF EI_DATA: " + mEndian + ": " + mPath); 770 } 771 772 mType = readHalf(); 773 774 int e_machine = readHalf(); 775 if (e_machine != EM_386 776 && e_machine != EM_X86_64 777 && e_machine != EM_AARCH64 778 && e_machine != EM_ARM 779 && e_machine != EM_RISCV 780 && e_machine != EM_QDSP6) { 781 throw new IOException("Invalid ELF e_machine: " + e_machine + ": " + mPath); 782 } 783 784 // AbiTest relies on us rejecting any unsupported combinations. 785 if ((e_machine == EM_386 && elfClass != ELFCLASS32) 786 || (e_machine == EM_X86_64 && elfClass != ELFCLASS64) 787 || (e_machine == EM_AARCH64 && elfClass != ELFCLASS64) 788 || (e_machine == EM_ARM && elfClass != ELFCLASS32) 789 || (e_machine == EM_QDSP6 && elfClass != ELFCLASS32)) { 790 throw new IOException( 791 "Invalid e_machine/EI_CLASS ELF combination: " 792 + e_machine 793 + "/" 794 + elfClass 795 + ": " 796 + mPath); 797 } 798 799 mMachine = e_machine; 800 long e_version = readWord(); 801 if (e_version != EV_CURRENT) { 802 throw new IOException("Invalid e_version: " + e_version + ": " + mPath); 803 } 804 805 long e_entry = readAddr(); 806 807 long ph_off = readOff(); 808 long sh_off = readOff(); 809 810 long e_flags = readWord(); 811 int e_ehsize = readHalf(); 812 int e_phentsize = readHalf(); 813 int e_phnum = readHalf(); 814 int e_shentsize = readHalf(); 815 int e_shnum = readHalf(); 816 int e_shstrndx = readHalf(); 817 818 readSectionHeaders(sh_off, e_shnum, e_shentsize, e_shstrndx); 819 readProgramHeaders(ph_off, e_phnum, e_phentsize); 820 } 821 readSectionHeaders(long sh_off, int e_shnum, int e_shentsize, int e_shstrndx)822 private void readSectionHeaders(long sh_off, int e_shnum, int e_shentsize, int e_shstrndx) 823 throws IOException { 824 // Read the Section Header String Table offset first. 825 { 826 mFile.seek(sh_off + e_shstrndx * e_shentsize); 827 828 long sh_name = readWord(); 829 long sh_type = readWord(); 830 long sh_flags = readX(mAddrSize); 831 long sh_addr = readAddr(); 832 long sh_offset = readOff(); 833 long sh_size = readX(mAddrSize); 834 // ... 835 836 if (sh_type == SHT_STRTAB) { 837 mShStrTabOffset = sh_offset; 838 mShStrTabSize = sh_size; 839 } 840 } 841 842 for (int i = 0; i < e_shnum; ++i) { 843 // Don't bother to re-read the Section Header StrTab. 844 if (i == e_shstrndx) { 845 continue; 846 } 847 848 mFile.seek(sh_off + i * e_shentsize); 849 850 long sh_name = readWord(); 851 long sh_type = readWord(); 852 long sh_flags = readX(mAddrSize); 853 long sh_addr = readAddr(); 854 long sh_offset = readOff(); 855 long sh_size = readX(mAddrSize); 856 long sh_link = readWord(); 857 long sh_info = readWord(); 858 long sh_addralign = readX(mAddrSize); 859 ; 860 long sh_entsize = readX(mAddrSize); 861 ; 862 863 if (sh_type == SHT_SYMTAB || sh_type == SHT_DYNSYM) { 864 final String symTabName = readShStrTabEntry(sh_name); 865 if (".symtab".equals(symTabName)) { 866 mSymTabOffset = sh_offset; 867 mSymTabSize = sh_size; 868 mSymEntCnt = (int) (sh_size / sh_entsize); 869 } else if (".dynsym".equals(symTabName)) { 870 mDynSymOffset = sh_offset; 871 mDynSymSize = sh_size; 872 mDynSymEntCnt = (int) (sh_size / sh_entsize); 873 } 874 System.out.println( 875 String.format( 876 "%s, %d, %d, %d, %d, %d", 877 symTabName, sh_offset, sh_size, sh_link, sh_info, sh_entsize)); 878 } else if (sh_type == SHT_STRTAB) { 879 final String strTabName = readShStrTabEntry(sh_name); 880 if (".strtab".equals(strTabName)) { 881 mStrTabOffset = sh_offset; 882 mStrTabSize = sh_size; 883 System.out.println( 884 String.format( 885 "%s, %d, %d, %d, %d", 886 strTabName, sh_offset, sh_size, sh_link, sh_info)); 887 } else if (".dynstr".equals(strTabName)) { 888 mDynStrOffset = sh_offset; 889 mDynStrSize = sh_size; 890 System.out.println( 891 String.format( 892 "%s, %d, %d, %d, %d", 893 strTabName, sh_offset, sh_size, sh_link, sh_info)); 894 } 895 } else if (sh_type == SHT_DYNAMIC) { 896 mIsDynamic = true; 897 final String strTabName = readShStrTabEntry(sh_name); 898 mDynamicTabOffset = sh_offset; 899 mDynamicTabSize = sh_size; 900 System.out.println( 901 String.format( 902 "%s, %d, %d, %d, %d", 903 strTabName, sh_offset, sh_size, sh_link, sh_info)); 904 } else if (sh_type == SHT_GNU_VERSYM) { 905 final String strTabName = readShStrTabEntry(sh_name); 906 if (".gnu.version".equals(strTabName)) { 907 mVerSymTabOffset = sh_offset; 908 mVerSymTabSize = sh_size; 909 } 910 System.out.println( 911 String.format( 912 "%s, %d, %d, %d, %d", 913 strTabName, sh_offset, sh_size, sh_link, sh_info)); 914 } else if (sh_type == SHT_GNU_VERNEED) { 915 final String strTabName = readShStrTabEntry(sh_name); 916 if (".gnu.version_r".equals(strTabName)) { 917 mVerNeedTabOffset = sh_offset; 918 mVerNeedTabSize = sh_size; 919 mVerNeedEntryCnt = (int) sh_info; 920 } 921 System.out.println( 922 String.format( 923 "%s, %d, %d, %d, %d", 924 strTabName, sh_offset, sh_size, sh_link, sh_info)); 925 } else if (sh_type == SHT_GNU_VERDEF) { 926 final String strTabName = readShStrTabEntry(sh_name); 927 if (".gnu.version_d".equals(strTabName)) { 928 mVerDefTabOffset = sh_offset; 929 mVerDefTabSize = sh_size; 930 mVerDefEntryCnt = (int) sh_info; 931 } 932 System.out.println( 933 String.format( 934 "%s, %d, %d, %d, %d", 935 strTabName, sh_offset, sh_size, sh_link, sh_info)); 936 } else if (sh_type == SHT_PROGBITS) { 937 final String strTabName = readShStrTabEntry(sh_name); 938 if (".rodata".equals(strTabName)) { 939 mHasRodata = true; 940 mRodataOffset = sh_offset; 941 mRodataSize = (int) sh_size; 942 } 943 System.out.println( 944 String.format( 945 "%s, %d, %d, %d, %d", 946 strTabName, sh_offset, sh_size, sh_link, sh_info)); 947 } 948 } 949 } 950 readProgramHeaders(long ph_off, int e_phnum, int e_phentsize)951 private void readProgramHeaders(long ph_off, int e_phnum, int e_phentsize) throws IOException { 952 for (int i = 0; i < e_phnum; ++i) { 953 mFile.seek(ph_off + i * e_phentsize); 954 955 long p_type = readWord(); 956 if (p_type == PT_LOAD) { 957 if (mAddrSize == 8) { 958 // Only in Elf64_phdr; in Elf32_phdr p_flags is at the end. 959 long p_flags = readWord(); 960 } 961 long p_offset = readOff(); 962 long p_vaddr = readAddr(); 963 // ... 964 965 if (p_vaddr == 0) { 966 mIsPIE = true; 967 } 968 } 969 } 970 } 971 readSymbolTable( Symbol[] symArr, boolean isDynSym, long symStrOffset, long symStrSize, long tableOffset, long tableSize)972 private HashMap<String, Symbol> readSymbolTable( 973 Symbol[] symArr, 974 boolean isDynSym, 975 long symStrOffset, 976 long symStrSize, 977 long tableOffset, 978 long tableSize) 979 throws IOException { 980 HashMap<String, Symbol> result = new HashMap<String, Symbol>(); 981 mFile.seek(tableOffset); 982 int i = 0; 983 while (mFile.getFilePointer() < tableOffset + tableSize) { 984 long st_name = readWord(); 985 int st_info; 986 int st_shndx; 987 long st_value; 988 long st_size; 989 int st_other; 990 if (mAddrSize == 8) { 991 st_info = readByte(); 992 st_other = readByte(); 993 st_shndx = readHalf(); 994 st_value = readAddr(); 995 st_size = readX(mAddrSize); 996 } else { 997 st_value = readAddr(); 998 st_size = readWord(); 999 st_info = readByte(); 1000 st_other = readByte(); 1001 st_shndx = readHalf(); 1002 } 1003 1004 String symName; 1005 if (st_name == 0) { 1006 symName = ""; 1007 } else { 1008 symName = readStrTabEntry(symStrOffset, symStrSize, st_name); 1009 } 1010 1011 Symbol sym = new Symbol(symName, st_info, st_shndx, st_value, st_size, st_other); 1012 if (!symName.equals("")) { 1013 result.put(symName, sym); 1014 } 1015 if (isDynSym) { 1016 if (mVerNeedEntryCnt > 0) { 1017 if (sym.type == Symbol.STT_NOTYPE) { 1018 sym.mVerNeed = mVerNeedArr[0]; 1019 } else { 1020 sym.mVerNeed = getVerNeed(mVerSym[i]); 1021 } 1022 } else if (mVerDefEntryCnt > 0) { 1023 sym.mVerDef = mVerDefArr[mVerSym[i]]; 1024 } 1025 } 1026 symArr[i] = sym; 1027 i++; 1028 } 1029 System.out.println( 1030 String.format( 1031 "Info readSymbolTable: %s, isDynSym %b, symbol# %d", 1032 mPath, isDynSym, symArr.length)); 1033 return result; 1034 } 1035 readShStrTabEntry(long strOffset)1036 private String readShStrTabEntry(long strOffset) throws IOException { 1037 if (mShStrTabOffset == 0 || strOffset < 0 || strOffset >= mShStrTabSize) { 1038 return null; 1039 } 1040 return readString(mShStrTabOffset + strOffset); 1041 } 1042 readStrTabEntry(long tableOffset, long tableSize, long strOffset)1043 private String readStrTabEntry(long tableOffset, long tableSize, long strOffset) 1044 throws IOException { 1045 if (tableOffset == 0 || strOffset < 0 || strOffset >= tableSize) { 1046 return null; 1047 } 1048 return readString(tableOffset + strOffset); 1049 } 1050 readDynStrTabEntry(long strOffset)1051 private String readDynStrTabEntry(long strOffset) throws IOException { 1052 if (mDynStrOffset == 0 || strOffset < 0 || strOffset >= mDynStrSize) { 1053 return null; 1054 } 1055 return readString(mDynStrOffset + strOffset); 1056 } 1057 getVerSym()1058 private int[] getVerSym() throws IOException { 1059 if (mVerSym == null) { 1060 mFile.seek(mVerSymTabOffset); 1061 int cnt = (int) mVerSymTabSize / 2; 1062 mVerSym = new int[cnt]; 1063 for (int i = 0; i < cnt; i++) { 1064 mVerSym[i] = readHalf(); 1065 //System.out.println(String.format("%d, %d", i, mVerSym[i])); 1066 } 1067 } 1068 return mVerSym; 1069 } 1070 getVerNeed(int ndx)1071 public VerNeed getVerNeed(int ndx) throws IOException { 1072 // vna_other Contains version index unique for the file which is used in the version symbol table. 1073 if (ndx < 2) { 1074 return this.mVerNeedArr[ndx]; 1075 } 1076 1077 for (int i = 2; i < this.mVerNeedEntryCnt + 2; i++) { 1078 for (int j = 0; j < this.mVerNeedArr[i].vn_cnt; j++) { 1079 if (this.mVerNeedArr[i].vn_vernaux[j].vna_other == ndx) { 1080 return this.mVerNeedArr[i]; 1081 } 1082 } 1083 } 1084 System.out.println(String.format("no VerNeed found: %d", ndx)); 1085 return null; 1086 } 1087 getVerNeedArr()1088 private VerNeed[] getVerNeedArr() throws IOException { 1089 if (mVerNeedArr == null) { 1090 mVerNeedArr = new VerNeed[mVerNeedEntryCnt + 2]; 1091 1092 // SHT_GNU_versym 0: local 1093 mVerNeedArr[0] = new VerNeed("*local*", "*local*", 0); 1094 // HT_GNU_versym 1: global 1095 mVerNeedArr[1] = new VerNeed("*global*", "*global*", 1); 1096 1097 long idx = mVerNeedTabOffset; 1098 for (int i = 2; i < mVerNeedEntryCnt + 2; i++) { 1099 mFile.seek(idx); 1100 mVerNeedArr[i] = 1101 new VerNeed(readHalf(), readHalf(), readWord(), readWord(), readWord()); 1102 mVerNeedArr[i].vn_file_name = readDynStrTabEntry(mVerNeedArr[i].vn_file).toLowerCase(); 1103 1104 mVerNeedArr[i].vn_vernaux = new VerNAux[mVerNeedArr[i].vn_cnt]; 1105 long idxAux = idx + mVerNeedArr[i].vn_aux; 1106 for (int j = 0; j < mVerNeedArr[i].vn_cnt; j++) { 1107 mFile.seek(idxAux); 1108 mVerNeedArr[i].vn_vernaux[j] = 1109 new VerNAux(readWord(), readHalf(), readHalf(), readWord(), readWord()); 1110 mVerNeedArr[i].vn_vernaux[j].vna_lib_name = 1111 readDynStrTabEntry(mVerNeedArr[i].vn_vernaux[j].vna_name); 1112 idxAux += mVerNeedArr[i].vn_vernaux[j].vna_next; 1113 } 1114 idx += mVerNeedArr[i].vn_next; 1115 System.out.println(mVerNeedArr[i]); 1116 } 1117 } 1118 1119 return mVerNeedArr; 1120 } 1121 getVerDef()1122 private VerDef[] getVerDef() throws IOException { 1123 if (mVerDefArr == null) { 1124 mVerDefArr = new VerDef[mVerDefEntryCnt + 2]; 1125 1126 // SHT_GNU_versym 0: local 1127 mVerDefArr[0] = new VerDef("*local*"); 1128 // HT_GNU_versym 1: global 1129 mVerDefArr[1] = new VerDef("*global*"); 1130 1131 long idx = mVerDefTabOffset; 1132 for (int i = 2; i < mVerDefEntryCnt + 2; i++) { 1133 mFile.seek(idx); 1134 mVerDefArr[i] = 1135 new VerDef( 1136 readHalf(), 1137 readHalf(), 1138 readHalf(), 1139 readHalf(), 1140 readWord(), 1141 readWord(), 1142 readWord()); 1143 1144 mVerDefArr[i].vd_verdaux = new VerDAux[mVerDefArr[i].vd_cnt]; 1145 long idxAux = idx + mVerDefArr[i].vd_aux; 1146 for (int j = 0; j < mVerDefArr[i].vd_cnt; j++) { 1147 mFile.seek(idxAux); 1148 mVerDefArr[i].vd_verdaux[j] = new VerDAux(readWord(), readWord()); 1149 mVerDefArr[i].vd_verdaux[j].vda_lib_name = 1150 readDynStrTabEntry(mVerDefArr[i].vd_verdaux[j].vda_name).toLowerCase(); 1151 idxAux += mVerDefArr[i].vd_verdaux[j].vda_next; 1152 } 1153 idx += mVerDefArr[i].vd_next; 1154 System.out.println(mVerDefArr[i]); 1155 } 1156 } 1157 return mVerDefArr; 1158 } 1159 readHalf()1160 private int readHalf() throws IOException { 1161 return (int) readX(2); 1162 } 1163 readWord()1164 private long readWord() throws IOException { 1165 return readX(4); 1166 } 1167 readOff()1168 private long readOff() throws IOException { 1169 return readX(mAddrSize); 1170 } 1171 readAddr()1172 private long readAddr() throws IOException { 1173 return readX(mAddrSize); 1174 } 1175 readX(int byteCount)1176 private long readX(int byteCount) throws IOException { 1177 mFile.readFully(mBuffer, 0, byteCount); 1178 1179 int answer = 0; 1180 if (mEndian == ELFDATA2LSB) { 1181 for (int i = byteCount - 1; i >= 0; i--) { 1182 answer = (answer << 8) | (mBuffer[i] & 0xff); 1183 } 1184 } else { 1185 final int N = byteCount - 1; 1186 for (int i = 0; i <= N; ++i) { 1187 answer = (answer << 8) | (mBuffer[i] & 0xff); 1188 } 1189 } 1190 1191 return answer; 1192 } 1193 readString(long offset)1194 private String readString(long offset) throws IOException { 1195 long originalOffset = mFile.getFilePointer(); 1196 mFile.seek(offset); 1197 mFile.readFully(mBuffer, 0, (int) Math.min(mBuffer.length, mFile.length() - offset)); 1198 mFile.seek(originalOffset); 1199 1200 for (int i = 0; i < mBuffer.length; ++i) { 1201 if (mBuffer[i] == 0) { 1202 return new String(mBuffer, 0, i); 1203 } 1204 } 1205 1206 return null; 1207 } 1208 readByte()1209 private int readByte() throws IOException { 1210 return mFile.read() & 0xff; 1211 } 1212 1213 /** Gets the symbol by name. */ 1214 @CanIgnoreReturnValue getSymbol(String name)1215 public Symbol getSymbol(String name) { 1216 if (mSymbols == null) { 1217 try { 1218 mSymArr = new Symbol[mSymEntCnt]; 1219 mSymbols = 1220 readSymbolTable( 1221 mSymArr, 1222 false, 1223 mStrTabOffset, 1224 mStrTabSize, 1225 mSymTabOffset, 1226 mSymTabSize); 1227 } catch (IOException e) { 1228 return null; 1229 } 1230 } 1231 return mSymbols.get(name); 1232 } 1233 1234 /** Gets a dynamic symbol by name. */ 1235 @CanIgnoreReturnValue getDynamicSymbol(String name)1236 public Symbol getDynamicSymbol(String name) throws IOException { 1237 if (mDynamicSymbols == null) { 1238 try { 1239 int[] verSmyArr = this.getVerSym(); 1240 VerNeed[] verNeedArr = this.getVerNeedArr(); 1241 VerDef[] verDefArr = this.getVerDef(); 1242 mDynSymArr = new Symbol[mDynSymEntCnt]; 1243 mDynamicSymbols = 1244 readSymbolTable( 1245 mDynSymArr, 1246 true, 1247 mDynStrOffset, 1248 mDynStrSize, 1249 mDynSymOffset, 1250 mDynSymSize); 1251 } catch (IOException e) { 1252 return null; 1253 } 1254 } 1255 return mDynamicSymbols.get(name); 1256 } 1257 1258 // Get Dynamic Linking Dependency List getDynamicDependencies()1259 public List<String> getDynamicDependencies() throws IOException { 1260 List<String> result = new ArrayList<>(); 1261 for (DynamicEntry entry : getDynamicList()) { 1262 if (entry.isNeeded()) { 1263 result.add(readDynStr(entry.getValue())); 1264 } 1265 } 1266 return result; 1267 } 1268 getDynamicList()1269 private List<DynamicEntry> getDynamicList() throws IOException { 1270 if (mDynamicArr == null) { 1271 int entryNo = 0; 1272 mDynamicArr = new ArrayList<>(); 1273 mFile.seek(mDynamicTabOffset); 1274 System.out.println( 1275 String.format( 1276 "mDynamicTabOffset 0x%x, mDynamicTabSize %d", 1277 mDynamicTabOffset, mDynamicTabSize)); 1278 while (true) { 1279 long tag = readX(mAddrSize); 1280 long value = readX(mAddrSize); 1281 // System.out.println(String.format("%d: 0x%x, %d", entryNo, tag, value)); 1282 mDynamicArr.add(new DynamicEntry(tag, value)); 1283 if (tag == 0) { 1284 break; 1285 } 1286 entryNo++; 1287 } 1288 } 1289 return mDynamicArr; 1290 } 1291 readDynStr(long strOffset)1292 private String readDynStr(long strOffset) throws IOException { 1293 int offset = (int) (strOffset & 0xFFFFFFFF); 1294 if (mDynStrOffset == 0 || offset < 0 || offset >= mDynStrSize) { 1295 System.err.println( 1296 String.format( 1297 "err mDynStrOffset: %d, mDynStrSize: %d, offset: %d", 1298 mDynStrOffset, mDynStrSize, offset)); 1299 return String.format("%d", offset); 1300 } 1301 return readString(mDynStrOffset + offset); 1302 } 1303 1304 /** 1305 * Gets a list of string from .rodata section 1306 * 1307 * @return a String list .rodata section 1308 */ getRoStrings()1309 public List<String> getRoStrings() throws IOException { 1310 if (mRoStrings == null) { 1311 mRoStrings = new ArrayList<>(); 1312 byte[] byteArr = getRoData(); 1313 if (byteArr != null) { 1314 int strOffset = 0; 1315 for (int i = 0; i < mRodataSize; i++) { 1316 if (byteArr[i] == 0) { 1317 // skip null string 1318 if (i != strOffset) { 1319 String str = new String(byteArr, strOffset, i - strOffset); 1320 mRoStrings.add(str); 1321 } 1322 strOffset = i + 1; 1323 } 1324 } 1325 } 1326 } 1327 return mRoStrings; 1328 } 1329 1330 /** 1331 * Gets .rodata section 1332 * 1333 * @return byte [] of .rodata or null if there is none 1334 */ getRoData()1335 public byte[] getRoData() throws IOException { 1336 if (mHasRodata && mRoData == null) { 1337 mRoData = new byte[mRodataSize]; 1338 mFile.seek(mRodataOffset); 1339 mFile.readFully(mRoData); 1340 } 1341 1342 return mRoData; 1343 } 1344 } 1345