Support 64-bit ELF files.

Also add the EM_AARCH64 and EM_X86_64 machine types, and explicitly
exclude cases like x32.

Bug: 17663159
Change-Id: If28631efc6d212d77c4383bffd0c2c99997cc5c6
diff --git a/tests/tests/os/src/android/os/cts/ReadElf.java b/tests/tests/os/src/android/os/cts/ReadElf.java
index 89e9d48..2f64e03 100644
--- a/tests/tests/os/src/android/os/cts/ReadElf.java
+++ b/tests/tests/os/src/android/os/cts/ReadElf.java
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -27,497 +28,449 @@
  * designed to parse ELF (Executable and Linkable Format) files.
  */
 public class ReadElf implements AutoCloseable {
-    /** The magic values for the ELF identification. */
-    private static final byte[] ELF_IDENT = {
-            (byte) 0x7F, (byte) 'E', (byte) 'L', (byte) 'F',
-    };
+  /** The magic values for the ELF identification. */
+  private static final byte[] ELFMAG = { (byte) 0x7F, (byte) 'E', (byte) 'L', (byte) 'F', };
 
-    private static final int EI_CLASS = 4;
-    private static final int EI_DATA = 5;
+  private static final int EI_NIDENT = 16;
 
-    private static final int EM_386 = 3;
-    private static final int EM_MIPS = 8;
-    private static final int EM_ARM = 40;
-    // http://en.wikipedia.org/wiki/Qualcomm_Hexagon
-    private static final int EM_QDSP6 = 164;
+  private static final int EI_CLASS = 4;
+  private static final int EI_DATA = 5;
 
-    /** Size of the e_ident[] structure in the ELF header. */
-    private static final int EI_NIDENT = 16;
+  private static final int EM_386 = 3;
+  private static final int EM_MIPS = 8;
+  private static final int EM_ARM = 40;
+  private static final int EM_X86_64 = 62;
+  // http://en.wikipedia.org/wiki/Qualcomm_Hexagon
+  private static final int EM_QDSP6 = 164;
+  private static final int EM_AARCH64 = 183;
 
-    /** Offset from end of ident structure in half-word sizes. */
-    private static final int OFFSET_TYPE = 0;
+  private static final int ELFCLASS32 = 1;
+  private static final int ELFCLASS64 = 2;
 
-    /** Machine type. */
-    private static final int OFFSET_MACHINE = 1;
+  private static final int ELFDATA2LSB = 1;
+  private static final int ELFDATA2MSB = 2;
 
-    /** ELF version. */
-    private static final int OFFSET_VERSION = 2;
+  private static final int EV_CURRENT = 1;
 
-    /**
-     * The offset to which the system transfers control. e.g., the first thing
-     * executed.
-     */
-    private static final int OFFSET_ENTRY = 4;
+  private static final long PT_LOAD = 1;
 
-    /** Program header offset in bytes. */
-    private static final int OFFSET_PHOFF = 6;
+  private static final int SHT_SYMTAB = 2;
+  private static final int SHT_STRTAB = 3;
+  private static final int SHT_DYNAMIC = 6;
+  private static final int SHT_DYNSYM = 11;
 
-    /** Segment header offset in bytes. */
-    private static final int OFFSET_SHOFF = 8;
+  public static class Symbol {
+    public static final int STB_LOCAL = 0;
+    public static final int STB_GLOBAL = 1;
+    public static final int STB_WEAK = 2;
+    public static final int STB_LOPROC = 13;
+    public static final int STB_HIPROC = 15;
 
-    /** Processor-specific flags for binary. */
-    private static final int OFFSET_FLAGS = 10;
+    public static final int STT_NOTYPE = 0;
+    public static final int STT_OBJECT = 1;
+    public static final int STT_FUNC = 2;
+    public static final int STT_SECTION = 3;
+    public static final int STT_FILE = 4;
+    public static final int STT_COMMON = 5;
+    public static final int STT_TLS = 6;
 
-    /** ELF header size in bytes. */
-    private static final int OFFSET_EHSIZE = 12;
+    public final String name;
+    public final int bind;
+    public final int type;
 
-    /** All program headers entry size in bytes. */
-    private static final int OFFSET_PHENTSIZE = 13;
+    Symbol(String name, int st_info) {
+      this.name = name;
+      this.bind = (st_info >> 4) & 0x0F;
+      this.type = st_info & 0x0F;
+    }
 
-    /** Number of program headers in ELF. */
-    private static final int OFFSET_PHNUM = 14;
+    public String toString() {
+      return "Symbol[" + name + "," + toBind() + "," + toType() + "]";
+    }
 
-    /** All segment headers entry size in bytes. */
-    private static final int OFFSET_SHENTSIZE = 15;
+    private String toBind() {
+      switch (bind) {
+        case STB_LOCAL: return "LOCAL";
+        case STB_GLOBAL: return "GLOBAL";
+        case STB_WEAK: return "WEAK";
+      }
+      return "STB_??? (" + bind + ")";
+    }
 
-    /** Number of segment headers in ELF. */
-    private static final int OFFSET_SHNUM = 16;
+    private String toType() {
+      switch (type) {
+        case STT_NOTYPE: return "NOTYPE";
+        case STT_OBJECT: return "OBJECT";
+        case STT_FUNC: return "FUNC";
+        case STT_SECTION: return "SECTION";
+        case STT_FILE: return "FILE";
+        case STT_COMMON: return "COMMON";
+        case STT_TLS: return "TLS";
+      }
+      return "STT_??? (" + type + ")";
+    }
+  }
 
-    /** The section header index that refers to string table. */
-    private static final int OFFSET_SHSTRNDX = 17;
+  private final String mPath;
+  private final RandomAccessFile mFile;
+  private final byte[] mBuffer = new byte[512];
+  private int mEndian;
+  private boolean mIsDynamic;
+  private boolean mIsPIE;
+  private int mType;
+  private int mAddrSize;
 
-    /** Program header offset for type of this program header. */
-    private static final int PHOFF_TYPE = 0;
+  /** Symbol Table offset */
+  private long mSymTabOffset;
 
-    /** Program header offset for absolute offset in file. */
-    private static final int PHOFF_OFFSET = 2;
+  /** Symbol Table size */
+  private long mSymTabSize;
 
-    /** Program header offset for virtual address. */
-    private static final int PHOFF_VADDR = 4;
+  /** Dynamic Symbol Table offset */
+  private long mDynSymOffset;
 
-    /** Program header offset for physical address. */
-    private static final int PHOFF_PADDR = 6;
+  /** Dynamic Symbol Table size */
+  private long mDynSymSize;
 
-    /** Program header offset for file size in bytes. */
-    private static final int PHOFF_FILESZ = 8;
+  /** Section Header String Table offset */
+  private long mShStrTabOffset;
 
-    /** Program header offset for memory size in bytes. */
-    private static final int PHOFF_MEMSZ = 10;
+  /** Section Header String Table size */
+  private long mShStrTabSize;
 
-    /** Program header offset for flags. */
-    private static final int PHOFF_FLAGS = 12;
+  /** String Table offset */
+  private long mStrTabOffset;
 
-    /**
-     * Program header offset for required alignment. 0 or 1 means no alignment
-     * necessary.
-     */
-    private static final int PHOFF_ALIGN = 14;
+  /** String Table size */
+  private long mStrTabSize;
 
-    /** Index into string pool for segment name. */
-    private static final long SHOFF_NAME = 0;
+  /** Dynamic String Table offset */
+  private long mDynStrOffset;
 
-    /** Segment header offset for type (half-words) */
-    private static final long SHOFF_TYPE = 2;
+  /** Dynamic String Table size */
+  private long mDynStrSize;
 
-    /** Segment header offset for offset (meta!) (half-words) */
-    private static final long SHOFF_OFFSET = 8;
+  /** Symbol Table symbol names */
+  private Map<String, Symbol> mSymbols;
 
-    /** Segment header offset for size (half-words) */
-    private static final long SHOFF_SIZE = 10;
+  /** Dynamic Symbol Table symbol names */
+  private Map<String, Symbol> mDynamicSymbols;
 
-    /** Data is presented in LSB format. */
-    private static final int ELFDATA2LSB = 1;
+  public static ReadElf read(File file) throws IOException {
+    return new ReadElf(file);
+  }
 
-    /** Date is presented in MSB format. */
-    private static final int ELFDATA2MSB = 2;
+  public static void main(String[] args) throws IOException {
+    for (String arg : args) {
+      ReadElf re = new ReadElf(new File(arg));
+      re.getSymbol("x");
+      re.getDynamicSymbol("x");
+    }
+  }
 
-    private static final int ELFCLASS32 = 1;
+  public boolean isDynamic() {
+    return mIsDynamic;
+  }
 
-    private static final int ELFCLASS64 = 2;
+  public int getType() {
+    return mType;
+  }
 
-    private static final long PT_LOAD = 1;
+  public boolean isPIE() {
+    return mIsPIE;
+  }
 
-    /** Section Type: Symbol Table */
-    private static final int SHT_SYMTAB = 2;
+  private ReadElf(File file) throws IOException {
+    mPath = file.getPath();
+    mFile = new RandomAccessFile(file, "r");
 
-    /** Section Type: String Table */
-    private static final int SHT_STRTAB = 3;
+    if (mFile.length() < EI_NIDENT) {
+      throw new IllegalArgumentException("Too small to be an ELF file: " + file);
+    }
 
-    /** Section Type: Dynamic **/
-    private static final int SHT_DYNAMIC = 6;
+    readHeader();
+  }
 
-    /** Section Type: Dynamic Symbol Table */
-    private static final int SHT_DYNSYM = 11;
+  public void close() {
+    try {
+      mFile.close();
+    } catch (IOException ignored) {
+    }
+  }
 
-    /** Symbol Table Entry: Name offset */
-    private static final int SYMTAB_NAME = 0;
+  protected void finalize() throws Throwable {
+    try {
+      close();
+    } finally {
+      super.finalize();
+    }
+  }
 
-    /** Symbol Table Entry: SymTab Info */
-    private static final int SYMTAB_ST_INFO = 6;
+  private void readHeader() throws IOException {
+    mFile.seek(0);
+    mFile.readFully(mBuffer, 0, EI_NIDENT);
 
-    /** Symbol Table Entry size (half-words) */
-    private static final int SYMTAB_ENTRY_HALFWORD_SIZE = 7;
+    if (mBuffer[0] != ELFMAG[0] || mBuffer[1] != ELFMAG[1] ||
+        mBuffer[2] != ELFMAG[2] || mBuffer[3] != ELFMAG[3]) {
+      throw new IllegalArgumentException("Invalid ELF file: " + mPath);
+    }
 
-    /**
-     * Symbol Table Entry size (extra in bytes) to cover "st_info" and
-     * "st_other"
-     */
-    private static final int SYMTAB_ENTRY_BYTE_EXTRA_SIZE = 2;
+    int elfClass = mBuffer[EI_CLASS];
+    if (elfClass == ELFCLASS32) {
+      mAddrSize = 4;
+    } else if (elfClass == ELFCLASS64) {
+      mAddrSize = 8;
+    } else {
+      throw new IOException("Invalid ELF EI_CLASS: " + elfClass + ": " + mPath);
+    }
 
-    public static class Symbol {
-        public static final int STB_LOCAL = 0;
+    mEndian = mBuffer[EI_DATA];
+    if (mEndian == ELFDATA2LSB) {
+    } else if (mEndian == ELFDATA2MSB) {
+      throw new IOException("Unsupported ELFDATA2MSB file: " + mPath);
+    } else {
+      throw new IOException("Invalid ELF EI_DATA: " + mEndian + ": " + mPath);
+    }
 
-        public static final int STB_GLOBAL = 1;
+    mType = readHalf();
 
-        public static final int STB_WEAK = 2;
+    int e_machine = readHalf();
+    if (e_machine != EM_386 && e_machine != EM_X86_64 &&
+        e_machine != EM_AARCH64 && e_machine != EM_ARM &&
+        e_machine != EM_MIPS &&
+        e_machine != EM_QDSP6) {
+      throw new IOException("Invalid ELF e_machine: " + e_machine + ": " + mPath);
+    }
 
-        public static final int STB_LOPROC = 13;
+    // AbiTest relies on us rejecting any unsupported combinations.
+    if ((e_machine == EM_386 && elfClass != ELFCLASS32) ||
+        (e_machine == EM_X86_64 && elfClass != ELFCLASS64) ||
+        (e_machine == EM_AARCH64 && elfClass != ELFCLASS64) ||
+        (e_machine == EM_ARM && elfClass != ELFCLASS32) ||
+        (e_machine == EM_QDSP6 && elfClass != ELFCLASS32)) {
+      throw new IOException("Invalid e_machine/EI_CLASS ELF combination: " +
+                            e_machine + "/" + elfClass + ": " + mPath);
+    }
 
-        public static final int STB_HIPROC = 15;
+    long e_version = readWord();
+    if (e_version != EV_CURRENT) {
+      throw new IOException("Invalid e_version: " + e_version + ": " + mPath);
+    }
 
-        public final String name;
+    long e_entry = readAddr();
 
-        public final int bind;
+    long ph_off = readOff();
+    long sh_off = readOff();
 
-        public final int type;
+    long e_flags = readWord();
+    int e_ehsize = readHalf();
+    int e_phentsize = readHalf();
+    int e_phnum = readHalf();
+    int e_shentsize = readHalf();
+    int e_shnum = readHalf();
+    int e_shstrndx = readHalf();
 
-        Symbol(String name, int st_info) {
-            this.name = name;
-            this.bind = (st_info >> 4) & 0x0F;
-            this.type = st_info & 0x0F;
+    readSectionHeaders(sh_off, e_shnum, e_shentsize, e_shstrndx);
+    readProgramHeaders(ph_off, e_phnum, e_phentsize);
+  }
+
+  private void readSectionHeaders(long sh_off, int e_shnum, int e_shentsize, int e_shstrndx) throws IOException {
+    // Read the Section Header String Table offset first.
+    {
+      mFile.seek(sh_off + e_shstrndx * e_shentsize);
+
+      long sh_name = readWord();
+      long sh_type = readWord();
+      long sh_flags = readX(mAddrSize);
+      long sh_addr = readAddr();
+      long sh_offset = readOff();
+      long sh_size = readX(mAddrSize);
+      // ...
+
+      if (sh_type == SHT_STRTAB) {
+        mShStrTabOffset = sh_offset;
+        mShStrTabSize = sh_size;
+      }
+    }
+
+    for (int i = 0; i < e_shnum; ++i) {
+      // Don't bother to re-read the Section Header StrTab.
+      if (i == e_shstrndx) {
+        continue;
+      }
+
+      mFile.seek(sh_off + i * e_shentsize);
+
+      long sh_name = readWord();
+      long sh_type = readWord();
+      long sh_flags = readX(mAddrSize);
+      long sh_addr = readAddr();
+      long sh_offset = readOff();
+      long sh_size = readX(mAddrSize);
+
+      if (sh_type == SHT_SYMTAB || sh_type == SHT_DYNSYM) {
+        final String symTabName = readShStrTabEntry(sh_name);
+        if (".symtab".equals(symTabName)) {
+          mSymTabOffset = sh_offset;
+          mSymTabSize = sh_size;
+        } else if (".dynsym".equals(symTabName)) {
+          mDynSymOffset = sh_offset;
+          mDynSymSize = sh_size;
         }
-    };
-
-    private final String mPath;
-    private final RandomAccessFile mFile;
-    private final byte[] mBuffer = new byte[512];
-    private int mEndian;
-    private boolean mIsDynamic;
-    private boolean mIsPIE;
-    private int mType;
-    private int mWordSize;
-    private int mHalfWordSize;
-
-    /** Symbol Table offset */
-    private long mSymTabOffset;
-
-    /** Symbol Table size */
-    private long mSymTabSize;
-
-    /** Dynamic Symbol Table offset */
-    private long mDynSymOffset;
-
-    /** Dynamic Symbol Table size */
-    private long mDynSymSize;
-
-    /** Section Header String Table offset */
-    private long mShStrTabOffset;
-
-    /** Section Header String Table size */
-    private long mShStrTabSize;
-
-    /** String Table offset */
-    private long mStrTabOffset;
-
-    /** String Table size */
-    private long mStrTabSize;
-
-    /** Dynamic String Table offset */
-    private long mDynStrOffset;
-
-    /** Dynamic String Table size */
-    private long mDynStrSize;
-
-    /** Symbol Table symbol names */
-    private Map<String, Symbol> mSymbols;
-
-    /** Dynamic Symbol Table symbol names */
-    private Map<String, Symbol> mDynamicSymbols;
-
-    public static ReadElf read(File file) throws IOException {
-        return new ReadElf(file);
-    }
-
-    public boolean isDynamic() {
-        return mIsDynamic;
-    }
-
-    public int getType() {
-        return mType;
-    }
-
-    public boolean isPIE() {
-        return mIsPIE;
-    }
-
-    private ReadElf(File file) throws IOException {
-        mPath = file.getPath();
-        mFile = new RandomAccessFile(file, "r");
-
-        if (mFile.length() < EI_NIDENT) {
-            throw new IllegalArgumentException("Too small to be an ELF file: " + file);
+      } else if (sh_type == SHT_STRTAB) {
+        final String strTabName = readShStrTabEntry(sh_name);
+        if (".strtab".equals(strTabName)) {
+          mStrTabOffset = sh_offset;
+          mStrTabSize = sh_size;
+        } else if (".dynstr".equals(strTabName)) {
+          mDynStrOffset = sh_offset;
+          mDynStrSize = sh_size;
         }
-
-        readIdent();
-        readHeader();
+      } else if (sh_type == SHT_DYNAMIC) {
+        mIsDynamic = true;
+      }
     }
+  }
 
-    public void close() {
-        try {
-            mFile.close();
-        } catch (IOException ignored) {
+  private void readProgramHeaders(long ph_off, int e_phnum, int e_phentsize) throws IOException {
+    for (int i = 0; i < e_phnum; ++i) {
+      mFile.seek(ph_off + i * e_phentsize);
+
+      long p_type = readWord();
+      if (p_type == PT_LOAD) {
+        if (mAddrSize == 8) {
+          long p_flags = readWord(); // Only in Elf64_phdr; in Elf32_phdr p_flags is at the end.
         }
-    }
+        long p_offset = readOff();
+        long p_vaddr = readAddr();
+        // ...
 
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
+        if (p_vaddr == 0) {
+          mIsPIE = true;
         }
+      }
+    }
+  }
+
+  private HashMap<String, Symbol> readSymbolTable(long symStrOffset, long symStrSize,
+                                                  long tableOffset, long tableSize) throws IOException {
+    HashMap<String, Symbol> result = new HashMap<String, Symbol>();
+    mFile.seek(tableOffset);
+    while (mFile.getFilePointer() < tableOffset + tableSize) {
+      long st_name = readWord();
+      int st_info;
+      if (mAddrSize == 8) {
+        st_info = readByte();
+        int st_other = readByte();
+        int st_shndx = readHalf();
+        long st_value = readAddr();
+        long st_size = readX(mAddrSize);
+      } else {
+        long st_value = readAddr();
+        long st_size = readWord();
+        st_info = readByte();
+        int st_other = readByte();
+        int st_shndx = readHalf();
+      }
+      if (st_name == 0) {
+        continue;
+      }
+
+      final String symName = readStrTabEntry(symStrOffset, symStrSize, st_name);
+      if (symName != null) {
+        Symbol s = new Symbol(symName, st_info);
+        result.put(symName, s);
+      }
+    }
+    return result;
+  }
+
+  private String readShStrTabEntry(long strOffset) throws IOException {
+    if (mShStrTabOffset == 0 || strOffset < 0 || strOffset >= mShStrTabSize) {
+      return null;
+    }
+    return readString(mShStrTabOffset + strOffset);
+  }
+
+  private String readStrTabEntry(long tableOffset, long tableSize, long strOffset) throws IOException {
+    if (tableOffset == 0 || strOffset < 0 || strOffset >= tableSize) {
+      return null;
+    }
+    return readString(tableOffset + strOffset);
+  }
+
+  private int readHalf() throws IOException {
+    return (int) readX(2);
+  }
+
+  private long readWord() throws IOException {
+    return readX(4);
+  }
+
+  private long readOff() throws IOException {
+    return readX(mAddrSize);
+  }
+
+  private long readAddr() throws IOException {
+    return readX(mAddrSize);
+  }
+
+  private long readX(int byteCount) throws IOException {
+    mFile.readFully(mBuffer, 0, byteCount);
+
+    int answer = 0;
+    if (mEndian == ELFDATA2LSB) {
+      for (int i = byteCount - 1; i >= 0; i--) {
+        answer = (answer << 8) | (mBuffer[i] & 0xff);
+      }
+    } else {
+      final int N = byteCount - 1;
+      for (int i = 0; i <= N; ++i) {
+        answer = (answer << 8) | (mBuffer[i] & 0xff);
+      }
     }
 
-    private void readHeader() throws IOException {
-        mType = readHalf(getHeaderOffset(OFFSET_TYPE));
-        int e_machine = readHalf(getHeaderOffset(OFFSET_MACHINE));
-        if (e_machine != EM_386 && e_machine != EM_MIPS && e_machine != EM_ARM &&
-                e_machine != EM_QDSP6) {
-            throw new IOException("Invalid ELF e_machine: " + e_machine + ": " + mPath);
-        }
+    return answer;
+  }
 
-        final long shOffset = readWord(getHeaderOffset(OFFSET_SHOFF));
-        final int shNumber = readHalf(getHeaderOffset(OFFSET_SHNUM));
-        final int shSize = readHalf(getHeaderOffset(OFFSET_SHENTSIZE));
-        final int shStrIndex = readHalf(getHeaderOffset(OFFSET_SHSTRNDX));
+  private String readString(long offset) throws IOException {
+    long originalOffset = mFile.getFilePointer();
+    mFile.seek(offset);
+    mFile.readFully(mBuffer, 0, (int) Math.min(mBuffer.length, mFile.length() - offset));
+    mFile.seek(originalOffset);
 
-        readSectionHeaders(shOffset, shNumber, shSize, shStrIndex);
-
-        final long phOffset = readWord(getHeaderOffset(OFFSET_PHOFF));
-        final int phNumber = readHalf(getHeaderOffset(OFFSET_PHNUM));
-        final int phSize = readHalf(getHeaderOffset(OFFSET_PHENTSIZE));
-
-        readProgramHeaders(phOffset, phNumber, phSize);
+    for (int i = 0; i < mBuffer.length; ++i) {
+      if (mBuffer[i] == 0) {
+        return new String(mBuffer, 0, i);
+      }
     }
 
-    private void readSectionHeaders(long tableOffset, int shNumber, int shSize, int shStrIndex)
-            throws IOException {
-        // Read the Section Header String Table offset first.
-        {
-            final long shStrTabShOffset = tableOffset + shStrIndex * shSize;
-            final long type = readWord(shStrTabShOffset + mHalfWordSize * SHOFF_TYPE);
+    return null;
+  }
 
-            if (type == SHT_STRTAB) {
-                mShStrTabOffset = readWord(shStrTabShOffset + mHalfWordSize * SHOFF_OFFSET);
-                mShStrTabSize = readWord(shStrTabShOffset + mHalfWordSize * SHOFF_SIZE);
-            }
-        }
+  private int readByte() throws IOException {
+    return mFile.read() & 0xff;
+  }
 
-        for (int i = 0; i < shNumber; i++) {
-            // Don't bother to re-read the Section Header StrTab.
-            if (i == shStrIndex) {
-                continue;
-            }
-
-            final long shOffset = tableOffset + i * shSize;
-
-            final long type = readWord(shOffset + mHalfWordSize * SHOFF_TYPE);
-            if ((type == SHT_SYMTAB) || (type == SHT_DYNSYM)) {
-                final long nameOffset = readWord(shOffset + mHalfWordSize * SHOFF_NAME);
-                final long offset = readWord(shOffset + mHalfWordSize * SHOFF_OFFSET);
-                final long size = readWord(shOffset + mHalfWordSize * SHOFF_SIZE);
-
-                final String symTabName = readShStrTabEntry(nameOffset);
-                if (".symtab".equals(symTabName)) {
-                    mSymTabOffset = offset;
-                    mSymTabSize = size;
-                } else if (".dynsym".equals(symTabName)) {
-                    mDynSymOffset = offset;
-                    mDynSymSize = size;
-                }
-            } else if (type == SHT_STRTAB) {
-                final long nameOffset = readWord(shOffset + mHalfWordSize * SHOFF_NAME);
-                final long offset = readWord(shOffset + mHalfWordSize * SHOFF_OFFSET);
-                final long size = readWord(shOffset + mHalfWordSize * SHOFF_SIZE);
-
-                final String strTabName = readShStrTabEntry(nameOffset);
-                if (".strtab".equals(strTabName)) {
-                    mStrTabOffset = offset;
-                    mStrTabSize = size;
-                } else if (".dynstr".equals(strTabName)) {
-                    mDynStrOffset = offset;
-                    mDynStrSize = size;
-                }
-            } else if (type == SHT_DYNAMIC) {
-                mIsDynamic = true;
-            }
-        }
-    }
-
-    private void readProgramHeaders(long phOffset, int phNumber, int phSize) throws IOException {
-        for (int i = 0; i < phNumber; i++) {
-            final long baseOffset = phOffset + i * phSize;
-            final long type = readWord(baseOffset);
-            if (type == PT_LOAD) {
-                final long virtAddress = readWord(baseOffset + mHalfWordSize * PHOFF_VADDR);
-                if (virtAddress == 0) {
-                    mIsPIE = true;
-                }
-            }
-        }
-    }
-
-    private void readSymbolTable(Map<String, Symbol> symbolMap, long symStrOffset, long symStrSize,
-            long symOffset, long symSize) throws IOException {
-        final long symEnd = symOffset + symSize;
-        for (long off = symOffset; off < symEnd; off += SYMTAB_ENTRY_HALFWORD_SIZE * mHalfWordSize
-                + SYMTAB_ENTRY_BYTE_EXTRA_SIZE) {
-            long strOffset = readWord(off + SYMTAB_NAME);
-            if (strOffset == 0) {
-                continue;
-            }
-
-            final String symName = readStrTabEntry(symStrOffset, symStrSize, strOffset);
-            if (symName != null) {
-                final int st_info = readByte(off + SYMTAB_ST_INFO);
-                symbolMap.put(symName, new Symbol(symName, st_info));
-            }
-        }
-    }
-
-    private String readShStrTabEntry(long strOffset) throws IOException {
-        if ((mShStrTabOffset == 0) || (strOffset < 0) || (strOffset >= mShStrTabSize)) {
-            return null;
-        }
-
-        return readString(mShStrTabOffset + strOffset);
-    }
-
-    private String readStrTabEntry(long tableOffset, long tableSize, long strOffset)
-            throws IOException {
-        if ((tableOffset == 0) || (strOffset < 0) || (strOffset >= tableSize)) {
-            return null;
-        }
-
-        return readString(tableOffset + strOffset);
-    }
-
-    private int getHeaderOffset(int halfWorldOffset) {
-        return EI_NIDENT + halfWorldOffset * mHalfWordSize;
-    }
-
-    private int readByte(long offset) throws IOException {
-        mFile.seek(offset);
-        mFile.readFully(mBuffer, 0, 1);
-
-        return mBuffer[0] & 0xff;
-    }
-
-    private int readHalf(long offset) throws IOException {
-        mFile.seek(offset);
-        mFile.readFully(mBuffer, 0, mWordSize);
-
-        final int answer;
-        if (mEndian == ELFDATA2LSB) {
-            answer = mBuffer[1] << 8 | (mBuffer[0] & 0xff);
-        } else {
-            answer = mBuffer[0] << 8 | (mBuffer[1] & 0xff);
-        }
-
-        return answer;
-    }
-
-    private long readWord(long offset) throws IOException {
-        mFile.seek(offset);
-        mFile.readFully(mBuffer, 0, mWordSize);
-
-        int answer = 0;
-        if (mEndian == ELFDATA2LSB) {
-            for (int i = mWordSize - 1; i >= 0; i--) {
-                answer = (answer << 8) | (mBuffer[i] & 0xff);
-            }
-        } else {
-            final int N = mWordSize - 1;
-            for (int i = 0; i <= N; i++) {
-                answer = (answer << 8) | (mBuffer[i] & 0xff);
-            }
-        }
-
-        return answer;
-    }
-
-    private String readString(long offset) throws IOException {
-        mFile.seek(offset);
-        mFile.readFully(mBuffer, 0, (int) Math.min(mBuffer.length, mFile.length() - offset));
-
-        for (int i = 0; i < mBuffer.length; i++) {
-            if (mBuffer[i] == 0) {
-                return new String(mBuffer, 0, i);
-            }
-        }
-
+  public Symbol getSymbol(String name) {
+    if (mSymbols == null) {
+      try {
+        mSymbols = readSymbolTable(mStrTabOffset, mStrTabSize, mSymTabOffset, mSymTabSize);
+      } catch (IOException e) {
         return null;
+      }
     }
+    return mSymbols.get(name);
+  }
 
-    private void readIdent() throws IOException {
-        mFile.seek(0);
-        mFile.readFully(mBuffer, 0, EI_NIDENT);
-
-        if ((mBuffer[0] != ELF_IDENT[0]) || (mBuffer[1] != ELF_IDENT[1])
-                || (mBuffer[2] != ELF_IDENT[2]) || (mBuffer[3] != ELF_IDENT[3])) {
-            throw new IllegalArgumentException("Invalid ELF file: " + mPath);
-        }
-
-        int elfClass = mBuffer[EI_CLASS];
-        if (elfClass == ELFCLASS32) {
-            mWordSize = 4;
-            mHalfWordSize = 2;
-        } else if (elfClass == ELFCLASS64) {
-            throw new IOException("Unsupported ELFCLASS64 file: " + mPath);
-        } else {
-            throw new IOException("Invalid ELF EI_CLASS: " + elfClass + ": " + mPath);
-        }
-
-        mEndian = mBuffer[EI_DATA];
-        if (mEndian == ELFDATA2LSB) {
-        } else if (mEndian == ELFDATA2MSB) {
-            throw new IOException("Unsupported ELFDATA2MSB file: " + mPath);
-        } else {
-            throw new IOException("Invalid ELF EI_DATA: " + mEndian + ": " + mPath);
-        }
+  public Symbol getDynamicSymbol(String name) {
+    if (mDynamicSymbols == null) {
+      try {
+        mDynamicSymbols = readSymbolTable(mDynStrOffset, mDynStrSize, mDynSymOffset, mDynSymSize);
+      } catch (IOException e) {
+        return null;
+      }
     }
-
-    public Symbol getSymbol(String name) {
-        if ((mSymTabOffset == 0) && (mSymTabSize == 0)) {
-            return null;
-        }
-
-        if (mSymbols == null) {
-            mSymbols = new HashMap<String, Symbol>();
-            try {
-                readSymbolTable(mSymbols, mStrTabOffset, mStrTabSize, mSymTabOffset, mSymTabSize);
-            } catch (IOException e) {
-                return null;
-            }
-        }
-
-        return mSymbols.get(name);
-    }
-
-    public Symbol getDynamicSymbol(String name) {
-        if ((mDynSymOffset == 0) && (mDynSymSize == 0)) {
-            return null;
-        }
-
-        if (mDynamicSymbols == null) {
-            mDynamicSymbols = new HashMap<String, Symbol>();
-            try {
-                readSymbolTable(mDynamicSymbols, mDynStrOffset, mDynStrSize, mDynSymOffset,
-                        mDynSymSize);
-            } catch (IOException e) {
-                return null;
-            }
-        }
-
-        return mDynamicSymbols.get(name);
-    }
+    return mDynamicSymbols.get(name);
+  }
 }