| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.compatibility.common.util; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.RandomAccessFile; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * A poor man's implementation of the readelf command. This program is designed to parse ELF |
| * (Executable and Linkable Format) files. |
| */ |
| // ToDo: consolidate with com.android.compatibility.common.util |
| public class ReadElf implements AutoCloseable { |
| /** 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_NIDENT = 16; |
| |
| private static final int EI_CLASS = 4; |
| private static final int EI_DATA = 5; |
| |
| public static final int ET_DYN = 3; |
| public static final int EM_386 = 3; |
| public static final int EM_MIPS = 8; |
| public static final int EM_ARM = 40; |
| public static final int EM_X86_64 = 62; |
| // http://en.wikipedia.org/wiki/Qualcomm_Hexagon |
| public static final int EM_QDSP6 = 164; |
| public static final int EM_AARCH64 = 183; |
| |
| public static final String ARCH_ARM = "arm"; |
| public static final String ARCH_X86 = "x86"; |
| public static final String ARCH_MIPS = "mips"; |
| public static final String ARCH_UNKNOWN = "unknown"; |
| private static final String RODATA = ".rodata"; |
| |
| private static final int ELFCLASS32 = 1; |
| private static final int ELFCLASS64 = 2; |
| |
| private static final int ELFDATA2LSB = 1; |
| private static final int ELFDATA2MSB = 2; |
| |
| private static final int EV_CURRENT = 1; |
| |
| private static final long PT_LOAD = 1; |
| |
| // https://en.wikipedia.org/wiki/Executable_and_Linkable_Format |
| private static final int SHT_PROGBITS = 1; |
| 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; |
| private static final int SHT_GNU_VERDEF = 0x6ffffffd; |
| private static final int SHT_GNU_VERNEED = 0x6ffffffe; |
| private static final int SHT_GNU_VERSYM = 0x6fffffff; |
| |
| 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; |
| |
| 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; |
| |
| public static final int SHN_UNDEF = 0; |
| public static final int SHN_ABS = 0xfff1; |
| |
| public final String name; |
| public final int bind; |
| public final int type; |
| public final int shndx; |
| public final long value; |
| public final long size; |
| public final int other; |
| |
| public VerNeed mVerNeed; |
| public VerDef mVerDef; |
| |
| Symbol(String name, int st_info, int st_shndx, long st_value, long st_size, int st_other) { |
| this.name = name; |
| this.bind = (st_info >> 4) & 0x0F; |
| this.type = st_info & 0x0F; |
| this.shndx = st_shndx; |
| this.value = st_value; |
| this.size = st_size; |
| this.other = st_other; |
| } |
| |
| @Override |
| public String toString() { |
| return String.format( |
| "%s, %s, %s, %s, %s, %s", |
| name, |
| toBind(), |
| toType(), |
| toShndx(), |
| getExternalLibFileName(), |
| getExternalLibName()); |
| } |
| |
| public String toBind() { |
| switch (bind) { |
| case STB_LOCAL: |
| return "LOCAL"; |
| case STB_GLOBAL: |
| return "GLOBAL"; |
| case STB_WEAK: |
| return "WEAK"; |
| } |
| return "STB_??? (" + bind + ")"; |
| } |
| |
| public 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 + ")"; |
| } |
| |
| public String toShndx() { |
| switch (shndx) { |
| case SHN_ABS: |
| return "ABS"; |
| case SHN_UNDEF: |
| return "UND"; |
| } |
| return String.valueOf(shndx); |
| } |
| |
| // if a symbol is not define locally |
| public boolean isGlobalUnd() { |
| return (bind != STB_LOCAL && shndx == SHN_UNDEF); |
| } |
| |
| // if a symbol is extern |
| public boolean isExtern() { |
| return (bind != STB_LOCAL && shndx != SHN_UNDEF); |
| } |
| |
| public String getExternalLibFileName() { |
| if (mVerNeed != null) { |
| return mVerNeed.vn_file_name; |
| } |
| return null; |
| } |
| |
| public String getExternalLibName() { |
| if (mVerNeed != null) { |
| return mVerNeed.vn_vernaux[0].vna_lib_name; |
| } |
| return null; |
| } |
| |
| public int getExternalLibVer() { |
| if (mVerNeed != null) { |
| return mVerNeed.vn_vernaux[0].vna_other; |
| } |
| return -1; |
| } |
| |
| public String getVerDefLibName() { |
| if (mVerDef != null) { |
| return mVerDef.vd_verdaux[0].vda_lib_name; |
| } |
| return null; |
| } |
| |
| public int getVerDefVersion() { |
| if (mVerDef != null) { |
| return mVerDef.vd_version; |
| } |
| return -1; |
| } |
| } |
| |
| public static class SecHeader { |
| public final long sh_name; |
| public final long sh_type; |
| public final long sh_flags; |
| public final long sh_addr; |
| public final long sh_offset; |
| public final long sh_size; |
| public final long sh_link; |
| public final long sh_info; |
| public final long sh_addralign; |
| public final long sh_entsize; |
| |
| SecHeader( |
| long name, |
| long type, |
| long flags, |
| long addr, |
| long offset, |
| long size, |
| long link, |
| long info, |
| long addralign, |
| long entsize) { |
| this.sh_name = name; |
| this.sh_type = type; |
| this.sh_flags = flags; |
| this.sh_addr = addr; |
| this.sh_offset = offset; |
| this.sh_size = size; |
| this.sh_link = link; |
| this.sh_info = info; |
| this.sh_addralign = addralign; |
| this.sh_entsize = entsize; |
| } |
| |
| @Override |
| public String toString() { |
| return String.format( |
| "%d, %d, %d, %d, %d, %d, %d, %d, %d, %d", |
| this.sh_name, |
| this.sh_type, |
| this.sh_flags, |
| this.sh_addr, |
| this.sh_offset, |
| this.sh_size, |
| this.sh_link, |
| this.sh_info, |
| this.sh_addralign, |
| this.sh_entsize); |
| } |
| } |
| |
| public static class VerNeed { |
| public final int vn_version; |
| public final int vn_cnt; |
| public final long vn_file; |
| public final long vn_aux; |
| public final long vn_next; |
| public String vn_file_name; |
| public VerNAux[] vn_vernaux; |
| |
| VerNeed(String file_name, String lib_name, int ndx) { |
| this.vn_file_name = file_name.toLowerCase(); |
| this.vn_vernaux = new VerNAux[1]; |
| this.vn_vernaux[0] = new VerNAux(lib_name, ndx); |
| |
| this.vn_version = 0; |
| this.vn_cnt = 0; |
| this.vn_file = 0; |
| this.vn_aux = 0; |
| this.vn_next = 0; |
| } |
| |
| VerNeed(int ver, int cnt, long file, long aux, long next) { |
| this.vn_version = ver; |
| this.vn_cnt = cnt; |
| this.vn_file = file; |
| this.vn_aux = aux; |
| this.vn_next = next; |
| } |
| |
| @Override |
| public String toString() { |
| String vernauxStr = ""; |
| for (int i = 0; i < this.vn_cnt; i++) { |
| vernauxStr += String.format(" %s\n", this.vn_vernaux[i].toString()); |
| } |
| return String.format( |
| "%s, %d, %d, %d, %d, %d \n%s", |
| this.vn_file_name, |
| this.vn_version, |
| this.vn_cnt, |
| this.vn_file, |
| this.vn_aux, |
| this.vn_next, |
| vernauxStr); |
| } |
| } |
| |
| public static class VerNAux { |
| public final long vna_hash; |
| public final int vna_flags; |
| public final int vna_other; |
| public final long vna_name; |
| public final long vna_next; |
| public String vna_lib_name; |
| |
| VerNAux(String lib_name, int ndx) { |
| this.vna_lib_name = lib_name; |
| |
| this.vna_hash = 0; |
| this.vna_flags = 0; |
| this.vna_other = ndx; |
| this.vna_name = 0; |
| this.vna_next = 0; |
| } |
| |
| VerNAux(long hash, int flags, int other, long name, long next) { |
| this.vna_hash = hash; |
| this.vna_flags = flags; |
| this.vna_other = other; |
| this.vna_name = name; |
| this.vna_next = next; |
| } |
| |
| @Override |
| public String toString() { |
| return String.format( |
| "%s, %d, %d, %d, %d, %d", |
| this.vna_lib_name, |
| this.vna_hash, |
| this.vna_flags, |
| this.vna_other, |
| this.vna_name, |
| this.vna_next); |
| } |
| } |
| |
| public static class VerDef { |
| public final int vd_version; |
| public final int vd_flags; |
| public final int vd_ndx; |
| public final int vd_cnt; |
| public final long vd_hash; |
| public final long vd_aux; |
| public final long vd_next; |
| public VerDAux[] vd_verdaux; |
| |
| VerDef(String lib_name) { |
| this.vd_verdaux = new VerDAux[1]; |
| this.vd_verdaux[0] = new VerDAux(lib_name); |
| |
| this.vd_version = 0; |
| this.vd_flags = 0; |
| this.vd_ndx = 0; |
| this.vd_cnt = 0; |
| this.vd_hash = 0; |
| this.vd_aux = 0; |
| this.vd_next = 0; |
| } |
| |
| VerDef(int ver, int flags, int ndx, int cnt, long hash, long aux, long next) { |
| this.vd_version = ver; |
| this.vd_flags = flags; |
| this.vd_ndx = ndx; |
| this.vd_cnt = cnt; |
| this.vd_hash = hash; |
| this.vd_aux = aux; |
| this.vd_next = next; |
| } |
| |
| @Override |
| public String toString() { |
| String vStr = ""; |
| for (int i = 0; i < this.vd_cnt; i++) { |
| vStr += String.format(" %s\n", this.vd_verdaux[i].toString()); |
| } |
| return String.format( |
| "%s, %d, %d, %d, %d, %d \n%s", |
| this.vd_verdaux[0].vda_lib_name, |
| this.vd_version, |
| this.vd_flags, |
| this.vd_ndx, |
| this.vd_cnt, |
| this.vd_hash, |
| vStr); |
| } |
| } |
| |
| public static class VerDAux { |
| public final long vda_name; |
| public final long vda_next; |
| public String vda_lib_name; |
| |
| VerDAux(String lib_name) { |
| this.vda_lib_name = lib_name.toLowerCase(); |
| |
| this.vda_name = 0; |
| this.vda_next = 0; |
| } |
| |
| VerDAux(long name, long next) { |
| this.vda_name = name; |
| this.vda_next = next; |
| } |
| |
| @Override |
| public String toString() { |
| return String.format("%s, %d, %d", this.vda_lib_name, this.vda_name, this.vda_next); |
| } |
| } |
| |
| // Dynamic Section Entry |
| public static class DynamicEntry { |
| private static final int DT_NEEDED = 1; |
| public final long mTag; |
| public final long mValue; |
| |
| DynamicEntry(long tag, long value) { |
| mTag = tag; |
| mValue = value; |
| } |
| |
| public boolean isNeeded() { |
| if (mTag == DT_NEEDED) { |
| return true; |
| } else { |
| // System.err.println(String.format("Not Needed: %d, %d", mTag, mValue)); |
| return false; |
| } |
| } |
| |
| public long getValue() { |
| return mValue; |
| } |
| |
| @Override |
| public String toString() { |
| return String.format("%d, %d", this.mTag, this.mValue); |
| } |
| } |
| |
| 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; |
| private int mMachine; |
| |
| /** Symbol Table offset */ |
| private long mSymTabOffset; |
| |
| /** Symbol Table size */ |
| private long mSymTabSize; |
| |
| /** Symbol entry count */ |
| private int mSymEntCnt; |
| |
| /** Dynamic Symbol Table offset */ |
| private long mDynSymOffset; |
| |
| /** Dynamic Symbol Table size */ |
| private long mDynSymSize; |
| |
| /** Dynamic entry count */ |
| private int mDynSymEntCnt; |
| |
| /** 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; |
| |
| /** Dynamic Table offset */ |
| private long mDynamicTabOffset; |
| |
| /** Dynamic Table size */ |
| private long mDynamicTabSize; |
| |
| /** Version Symbols Table offset */ |
| private long mVerSymTabOffset; |
| |
| /** Version Symbols Table size */ |
| private long mVerSymTabSize; |
| |
| /** Version Needs Table offset */ |
| private long mVerNeedTabOffset; |
| |
| /** Version Definition Table size */ |
| private long mVerNeedTabSize; |
| |
| private int mVerNeedEntryCnt; |
| |
| /** Version Definition Table offset */ |
| private long mVerDefTabOffset; |
| |
| /** Version Needs Table size */ |
| private long mVerDefTabSize; |
| |
| private int mVerDefEntryCnt; |
| |
| /** Symbol Table symbol names */ |
| private Map<String, Symbol> mSymbols; |
| |
| /** Symbol Table symbol array */ |
| private Symbol[] mSymArr; |
| |
| /** Dynamic Symbol Table symbol names */ |
| private Map<String, Symbol> mDynamicSymbols; |
| |
| /** Dynamic Symbol Table symbol array */ |
| private Symbol[] mDynSymArr; |
| |
| /** Version Symbols Table */ |
| private int[] mVerSym; |
| |
| /** Version Needed Table */ |
| private VerNeed[] mVerNeedArr; |
| |
| /** Version Definition Table */ |
| private VerDef[] mVerDefArr; |
| |
| /** Dynamic Table */ |
| private List<DynamicEntry> mDynamicArr; |
| |
| /** Rodata offset */ |
| private boolean mHasRodata; |
| |
| /** Rodata offset */ |
| private long mRodataOffset; |
| |
| /** Rodata size */ |
| private int mRodataSize; |
| |
| /** Rodata String List */ |
| private List<String> mRoStrings; |
| |
| /** Rodata byte[] */ |
| private byte[] mRoData; |
| |
| public static ReadElf read(File file) throws IOException { |
| return new ReadElf(file); |
| } |
| |
| public static void main(String[] args) throws IOException { |
| for (String arg : args) { |
| ReadElf elf = ReadElf.read(new File(arg)); |
| elf.getDynamicSymbol("x"); |
| elf.getSymbol("x"); |
| |
| Symbol[] symArr; |
| System.out.println("===Symbol==="); |
| symArr = elf.getSymArr(); |
| for (int i = 0; i < symArr.length; i++) { |
| System.out.println(String.format("%8x: %s", i, symArr[i].toString())); |
| } |
| System.out.println("===Dynamic Symbol==="); |
| symArr = elf.getDynSymArr(); |
| for (int i = 0; i < symArr.length; i++) { |
| if (elf.mVerNeedEntryCnt > 0) { |
| System.out.println( |
| String.format( |
| "%8x: %s, %s, %s - %d", |
| i, |
| symArr[i].toString(), |
| symArr[i].getExternalLibName(), |
| symArr[i].getExternalLibFileName(), |
| symArr[i].getExternalLibVer())); |
| } else { |
| System.out.println( |
| String.format( |
| "%8x: %s, %s - %d", |
| i, |
| symArr[i].toString(), |
| symArr[i].getVerDefLibName(), |
| symArr[i].getVerDefVersion())); |
| } |
| } |
| |
| System.out.println("===Dynamic Dependencies==="); |
| for (String DynDepEntry : elf.getDynamicDependencies()) { |
| System.out.println(DynDepEntry); |
| } |
| |
| System.out.println("===Strings in Read Only(.rodata) section==="); |
| for (String roStr : elf.getRoStrings()) { |
| System.out.println(roStr); |
| } |
| |
| elf.close(); |
| } |
| } |
| |
| public static boolean isElf(File file) { |
| try { |
| if (file.length() < EI_NIDENT) { |
| throw new IllegalArgumentException( |
| "Too small to be an ELF file: " + file.getCanonicalPath()); |
| } |
| |
| RandomAccessFile raFile = new RandomAccessFile(file, "r"); |
| byte[] buffer = new byte[512]; |
| raFile.seek(0); |
| raFile.readFully(buffer, 0, EI_NIDENT); |
| if (buffer[0] != ELFMAG[0] |
| || buffer[1] != ELFMAG[1] |
| || buffer[2] != ELFMAG[2] |
| || buffer[3] != ELFMAG[3]) { |
| throw new IllegalArgumentException("Invalid ELF file: " + file.getCanonicalPath()); |
| } |
| raFile.close(); |
| ; |
| return true; |
| } catch (Exception e) { |
| return false; |
| } |
| } |
| |
| public int getBits() { |
| if (mMachine == EM_386 |
| || mMachine == EM_MIPS |
| || mMachine == EM_ARM |
| || mMachine == EM_QDSP6) { |
| return 32; |
| } else if (mMachine == EM_AARCH64 || mMachine == EM_X86_64) { |
| return 64; |
| } else { |
| return -1; |
| } |
| } |
| |
| public String getArchitecture() { |
| if (mMachine == EM_ARM || mMachine == EM_AARCH64) { |
| return ARCH_ARM; |
| } else if (mMachine == EM_386 || mMachine == EM_X86_64) { |
| return ARCH_X86; |
| } else if (mMachine == EM_MIPS) { |
| return ARCH_MIPS; |
| } else { |
| return ARCH_UNKNOWN; |
| } |
| } |
| |
| public Map<String, Symbol> getSymbols() throws IOException { |
| if (mSymbols == null) { |
| getSymbol(""); |
| } |
| return mSymbols; |
| } |
| |
| public Symbol[] getSymArr() throws IOException { |
| if (mSymArr == null) { |
| getSymbol(""); |
| } |
| return mSymArr; |
| } |
| |
| public Map<String, Symbol> getDynamicSymbols() throws IOException { |
| if (mDynamicSymbols == null) { |
| getDynamicSymbol(""); |
| } |
| return mDynamicSymbols; |
| } |
| |
| public Symbol[] getDynSymArr() throws IOException { |
| if (mDynSymArr == null) { |
| getDynamicSymbol(""); |
| } |
| return mDynSymArr; |
| } |
| |
| public boolean isDynamic() { |
| return mIsDynamic; |
| } |
| |
| public int getType() { |
| return mType; |
| } |
| |
| public boolean isPIE() { |
| return mIsPIE; |
| } |
| |
| private ReadElf(File file) throws IOException { |
| mHasRodata = false; |
| mRoData = null; |
| mPath = file.getPath(); |
| mFile = new RandomAccessFile(file, "r"); |
| |
| if (mFile.length() < EI_NIDENT) { |
| throw new IllegalArgumentException("Too small to be an ELF file: " + file); |
| } |
| |
| readHeader(); |
| } |
| |
| @Override |
| public void close() { |
| try { |
| mFile.close(); |
| } catch (IOException ignored) { |
| } |
| } |
| |
| @Override |
| protected void finalize() throws Throwable { |
| try { |
| close(); |
| } finally { |
| super.finalize(); |
| } |
| } |
| |
| private void readHeader() throws IOException { |
| mFile.seek(0); |
| mFile.readFully(mBuffer, 0, EI_NIDENT); |
| |
| if (mBuffer[0] != ELFMAG[0] |
| || mBuffer[1] != ELFMAG[1] |
| || mBuffer[2] != ELFMAG[2] |
| || mBuffer[3] != ELFMAG[3]) { |
| throw new IllegalArgumentException("Invalid ELF file: " + mPath); |
| } |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| mType = readHalf(); |
| |
| 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); |
| } |
| |
| // 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); |
| } |
| |
| mMachine = e_machine; |
| long e_version = readWord(); |
| if (e_version != EV_CURRENT) { |
| throw new IOException("Invalid e_version: " + e_version + ": " + mPath); |
| } |
| |
| long e_entry = readAddr(); |
| |
| long ph_off = readOff(); |
| long sh_off = readOff(); |
| |
| 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(); |
| |
| 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); |
| long sh_link = readWord(); |
| long sh_info = readWord(); |
| long sh_addralign = readX(mAddrSize); |
| ; |
| long sh_entsize = 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; |
| mSymEntCnt = (int) (sh_size / sh_entsize); |
| } else if (".dynsym".equals(symTabName)) { |
| mDynSymOffset = sh_offset; |
| mDynSymSize = sh_size; |
| mDynSymEntCnt = (int) (sh_size / sh_entsize); |
| } |
| System.out.println( |
| String.format( |
| "%s, %d, %d, %d, %d, %d", |
| symTabName, sh_offset, sh_size, sh_link, sh_info, sh_entsize)); |
| } else if (sh_type == SHT_STRTAB) { |
| final String strTabName = readShStrTabEntry(sh_name); |
| if (".strtab".equals(strTabName)) { |
| mStrTabOffset = sh_offset; |
| mStrTabSize = sh_size; |
| System.out.println( |
| String.format( |
| "%s, %d, %d, %d, %d", |
| strTabName, sh_offset, sh_size, sh_link, sh_info)); |
| } else if (".dynstr".equals(strTabName)) { |
| mDynStrOffset = sh_offset; |
| mDynStrSize = sh_size; |
| System.out.println( |
| String.format( |
| "%s, %d, %d, %d, %d", |
| strTabName, sh_offset, sh_size, sh_link, sh_info)); |
| } |
| } else if (sh_type == SHT_DYNAMIC) { |
| mIsDynamic = true; |
| final String strTabName = readShStrTabEntry(sh_name); |
| mDynamicTabOffset = sh_offset; |
| mDynamicTabSize = sh_size; |
| System.out.println( |
| String.format( |
| "%s, %d, %d, %d, %d", |
| strTabName, sh_offset, sh_size, sh_link, sh_info)); |
| } else if (sh_type == SHT_GNU_VERSYM) { |
| final String strTabName = readShStrTabEntry(sh_name); |
| if (".gnu.version".equals(strTabName)) { |
| mVerSymTabOffset = sh_offset; |
| mVerSymTabSize = sh_size; |
| } |
| System.out.println( |
| String.format( |
| "%s, %d, %d, %d, %d", |
| strTabName, sh_offset, sh_size, sh_link, sh_info)); |
| } else if (sh_type == SHT_GNU_VERNEED) { |
| final String strTabName = readShStrTabEntry(sh_name); |
| if (".gnu.version_r".equals(strTabName)) { |
| mVerNeedTabOffset = sh_offset; |
| mVerNeedTabSize = sh_size; |
| mVerNeedEntryCnt = (int) sh_info; |
| } |
| System.out.println( |
| String.format( |
| "%s, %d, %d, %d, %d", |
| strTabName, sh_offset, sh_size, sh_link, sh_info)); |
| } else if (sh_type == SHT_GNU_VERDEF) { |
| final String strTabName = readShStrTabEntry(sh_name); |
| if (".gnu.version_d".equals(strTabName)) { |
| mVerDefTabOffset = sh_offset; |
| mVerDefTabSize = sh_size; |
| mVerDefEntryCnt = (int) sh_info; |
| } |
| System.out.println( |
| String.format( |
| "%s, %d, %d, %d, %d", |
| strTabName, sh_offset, sh_size, sh_link, sh_info)); |
| } else if (sh_type == SHT_PROGBITS) { |
| final String strTabName = readShStrTabEntry(sh_name); |
| if (".rodata".equals(strTabName)) { |
| mHasRodata = true; |
| mRodataOffset = sh_offset; |
| mRodataSize = (int) sh_size; |
| } |
| System.out.println( |
| String.format( |
| "%s, %d, %d, %d, %d", |
| strTabName, sh_offset, sh_size, sh_link, sh_info)); |
| } |
| } |
| } |
| |
| 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) { |
| // Only in Elf64_phdr; in Elf32_phdr p_flags is at the end. |
| long p_flags = readWord(); |
| } |
| long p_offset = readOff(); |
| long p_vaddr = readAddr(); |
| // ... |
| |
| if (p_vaddr == 0) { |
| mIsPIE = true; |
| } |
| } |
| } |
| } |
| |
| private HashMap<String, Symbol> readSymbolTable( |
| Symbol[] symArr, |
| boolean isDynSym, |
| long symStrOffset, |
| long symStrSize, |
| long tableOffset, |
| long tableSize) |
| throws IOException { |
| HashMap<String, Symbol> result = new HashMap<String, Symbol>(); |
| mFile.seek(tableOffset); |
| int i = 0; |
| while (mFile.getFilePointer() < tableOffset + tableSize) { |
| long st_name = readWord(); |
| int st_info; |
| int st_shndx; |
| long st_value; |
| long st_size; |
| int st_other; |
| if (mAddrSize == 8) { |
| st_info = readByte(); |
| st_other = readByte(); |
| st_shndx = readHalf(); |
| st_value = readAddr(); |
| st_size = readX(mAddrSize); |
| } else { |
| st_value = readAddr(); |
| st_size = readWord(); |
| st_info = readByte(); |
| st_other = readByte(); |
| st_shndx = readHalf(); |
| } |
| |
| String symName; |
| if (st_name == 0) { |
| symName = ""; |
| } else { |
| symName = readStrTabEntry(symStrOffset, symStrSize, st_name); |
| } |
| |
| Symbol sym = new Symbol(symName, st_info, st_shndx, st_value, st_size, st_other); |
| if (!symName.equals("")) { |
| result.put(symName, sym); |
| } |
| if (isDynSym) { |
| if (mVerNeedEntryCnt > 0) { |
| if (sym.type == Symbol.STT_NOTYPE) { |
| sym.mVerNeed = mVerNeedArr[0]; |
| } else { |
| sym.mVerNeed = getVerNeed(mVerSym[i]); |
| } |
| } else if (mVerDefEntryCnt > 0) { |
| sym.mVerDef = mVerDefArr[mVerSym[i]]; |
| } |
| } |
| symArr[i] = sym; |
| i++; |
| } |
| System.out.println( |
| String.format( |
| "Info readSymbolTable: %s, isDynSym %b, symbol# %d", |
| mPath, isDynSym, symArr.length)); |
| 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 String readDynStrTabEntry(long strOffset) throws IOException { |
| if (mDynStrOffset == 0 || strOffset < 0 || strOffset >= mDynStrSize) { |
| return null; |
| } |
| return readString(mDynStrOffset + strOffset); |
| } |
| |
| private int[] getVerSym() throws IOException { |
| if (mVerSym == null) { |
| mFile.seek(mVerSymTabOffset); |
| int cnt = (int) mVerSymTabSize / 2; |
| mVerSym = new int[cnt]; |
| for (int i = 0; i < cnt; i++) { |
| mVerSym[i] = readHalf(); |
| //System.out.println(String.format("%d, %d", i, mVerSym[i])); |
| } |
| } |
| return mVerSym; |
| } |
| |
| public VerNeed getVerNeed(int ndx) throws IOException { |
| // vna_other Contains version index unique for the file which is used in the version symbol table. |
| if (ndx < 2) { |
| return this.mVerNeedArr[ndx]; |
| } |
| |
| for (int i = 2; i < this.mVerNeedEntryCnt + 2; i++) { |
| for (int j = 0; j < this.mVerNeedArr[i].vn_cnt; j++) { |
| if (this.mVerNeedArr[i].vn_vernaux[j].vna_other == ndx) { |
| return this.mVerNeedArr[i]; |
| } |
| } |
| } |
| System.out.println(String.format("no VerNeed found: %d", ndx)); |
| return null; |
| } |
| |
| private VerNeed[] getVerNeedArr() throws IOException { |
| if (mVerNeedArr == null) { |
| mVerNeedArr = new VerNeed[mVerNeedEntryCnt + 2]; |
| |
| // SHT_GNU_versym 0: local |
| mVerNeedArr[0] = new VerNeed("*local*", "*local*", 0); |
| // HT_GNU_versym 1: global |
| mVerNeedArr[1] = new VerNeed("*global*", "*global*", 1); |
| |
| long idx = mVerNeedTabOffset; |
| for (int i = 2; i < mVerNeedEntryCnt + 2; i++) { |
| mFile.seek(idx); |
| mVerNeedArr[i] = |
| new VerNeed(readHalf(), readHalf(), readWord(), readWord(), readWord()); |
| mVerNeedArr[i].vn_file_name = readDynStrTabEntry(mVerNeedArr[i].vn_file).toLowerCase(); |
| |
| mVerNeedArr[i].vn_vernaux = new VerNAux[mVerNeedArr[i].vn_cnt]; |
| long idxAux = idx + mVerNeedArr[i].vn_aux; |
| for (int j = 0; j < mVerNeedArr[i].vn_cnt; j++) { |
| mFile.seek(idxAux); |
| mVerNeedArr[i].vn_vernaux[j] = |
| new VerNAux(readWord(), readHalf(), readHalf(), readWord(), readWord()); |
| mVerNeedArr[i].vn_vernaux[j].vna_lib_name = |
| readDynStrTabEntry(mVerNeedArr[i].vn_vernaux[j].vna_name); |
| idxAux += mVerNeedArr[i].vn_vernaux[j].vna_next; |
| } |
| idx += mVerNeedArr[i].vn_next; |
| System.out.println(mVerNeedArr[i]); |
| } |
| } |
| |
| return mVerNeedArr; |
| } |
| |
| private VerDef[] getVerDef() throws IOException { |
| if (mVerDefArr == null) { |
| mVerDefArr = new VerDef[mVerDefEntryCnt + 2]; |
| |
| // SHT_GNU_versym 0: local |
| mVerDefArr[0] = new VerDef("*local*"); |
| // HT_GNU_versym 1: global |
| mVerDefArr[1] = new VerDef("*global*"); |
| |
| long idx = mVerDefTabOffset; |
| for (int i = 2; i < mVerDefEntryCnt + 2; i++) { |
| mFile.seek(idx); |
| mVerDefArr[i] = |
| new VerDef( |
| readHalf(), |
| readHalf(), |
| readHalf(), |
| readHalf(), |
| readWord(), |
| readWord(), |
| readWord()); |
| |
| mVerDefArr[i].vd_verdaux = new VerDAux[mVerDefArr[i].vd_cnt]; |
| long idxAux = idx + mVerDefArr[i].vd_aux; |
| for (int j = 0; j < mVerDefArr[i].vd_cnt; j++) { |
| mFile.seek(idxAux); |
| mVerDefArr[i].vd_verdaux[j] = new VerDAux(readWord(), readWord()); |
| mVerDefArr[i].vd_verdaux[j].vda_lib_name = |
| readDynStrTabEntry(mVerDefArr[i].vd_verdaux[j].vda_name).toLowerCase(); |
| idxAux += mVerDefArr[i].vd_verdaux[j].vda_next; |
| } |
| idx += mVerDefArr[i].vd_next; |
| System.out.println(mVerDefArr[i]); |
| } |
| } |
| return mVerDefArr; |
| } |
| |
| 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); |
| } |
| } |
| |
| return answer; |
| } |
| |
| 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); |
| |
| for (int i = 0; i < mBuffer.length; ++i) { |
| if (mBuffer[i] == 0) { |
| return new String(mBuffer, 0, i); |
| } |
| } |
| |
| return null; |
| } |
| |
| private int readByte() throws IOException { |
| return mFile.read() & 0xff; |
| } |
| |
| public Symbol getSymbol(String name) { |
| if (mSymbols == null) { |
| try { |
| mSymArr = new Symbol[mSymEntCnt]; |
| mSymbols = |
| readSymbolTable( |
| mSymArr, |
| false, |
| mStrTabOffset, |
| mStrTabSize, |
| mSymTabOffset, |
| mSymTabSize); |
| } catch (IOException e) { |
| return null; |
| } |
| } |
| return mSymbols.get(name); |
| } |
| |
| public Symbol getDynamicSymbol(String name) throws IOException { |
| if (mDynamicSymbols == null) { |
| try { |
| int[] verSmyArr = this.getVerSym(); |
| VerNeed[] verNeedArr = this.getVerNeedArr(); |
| VerDef[] verDefArr = this.getVerDef(); |
| mDynSymArr = new Symbol[mDynSymEntCnt]; |
| mDynamicSymbols = |
| readSymbolTable( |
| mDynSymArr, |
| true, |
| mDynStrOffset, |
| mDynStrSize, |
| mDynSymOffset, |
| mDynSymSize); |
| } catch (IOException e) { |
| return null; |
| } |
| } |
| return mDynamicSymbols.get(name); |
| } |
| |
| // Get Dynamic Linking Dependency List |
| public List<String> getDynamicDependencies() throws IOException { |
| List<String> result = new ArrayList<>(); |
| for (DynamicEntry entry : getDynamicList()) { |
| if (entry.isNeeded()) { |
| result.add(readDynStr(entry.getValue())); |
| } |
| } |
| return result; |
| } |
| |
| private List<DynamicEntry> getDynamicList() throws IOException { |
| if (mDynamicArr == null) { |
| int entryNo = 0; |
| mDynamicArr = new ArrayList<>(); |
| mFile.seek(mDynamicTabOffset); |
| System.out.println( |
| String.format( |
| "mDynamicTabOffset 0x%x, mDynamicTabSize %d", |
| mDynamicTabOffset, mDynamicTabSize)); |
| while (true) { |
| long tag = readX(mAddrSize); |
| long value = readX(mAddrSize); |
| // System.out.println(String.format("%d: 0x%x, %d", entryNo, tag, value)); |
| mDynamicArr.add(new DynamicEntry(tag, value)); |
| if (tag == 0) { |
| break; |
| } |
| entryNo++; |
| } |
| } |
| return mDynamicArr; |
| } |
| |
| private String readDynStr(long strOffset) throws IOException { |
| int offset = (int) (strOffset & 0xFFFFFFFF); |
| if (mDynStrOffset == 0 || offset < 0 || offset >= mDynStrSize) { |
| System.err.println( |
| String.format( |
| "err mDynStrOffset: %d, mDynStrSize: %d, offset: %d", |
| mDynStrOffset, mDynStrSize, offset)); |
| return String.format("%d", offset); |
| } |
| return readString(mDynStrOffset + offset); |
| } |
| |
| /** |
| * Gets a list of string from .rodata section |
| * |
| * @return a String list .rodata section |
| */ |
| public List<String> getRoStrings() throws IOException { |
| if (mRoStrings == null) { |
| mRoStrings = new ArrayList<>(); |
| byte[] byteArr = getRoData(); |
| if (byteArr != null) { |
| int strOffset = 0; |
| for (int i = 0; i < mRodataSize; i++) { |
| if (byteArr[i] == 0) { |
| // skip null string |
| if (i != strOffset) { |
| String str = new String(byteArr, strOffset, i - strOffset); |
| mRoStrings.add(str); |
| } |
| strOffset = i + 1; |
| } |
| } |
| } |
| } |
| return mRoStrings; |
| } |
| |
| /** |
| * Gets .rodata section |
| * |
| * @return byte [] of .rodata or null if there is none |
| */ |
| public byte[] getRoData() throws IOException { |
| if (mHasRodata && mRoData == null) { |
| mRoData = new byte[mRodataSize]; |
| mFile.seek(mRodataOffset); |
| mFile.readFully(mRoData); |
| } |
| |
| return mRoData; |
| } |
| } |