Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2011 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 android.os.cts;
     18 
     19 import java.io.File;
     20 import java.io.IOException;
     21 import java.io.RandomAccessFile;
     22 import java.util.HashMap;
     23 import java.util.Map;
     24 
     25 /**
     26  * A poor man's implementation of the readelf command. This program is
     27  * designed to parse ELF (Executable and Linkable Format) files.
     28  */
     29 public class ReadElf implements AutoCloseable {
     30     /** The magic values for the ELF identification. */
     31     private static final byte[] ELF_IDENT = {
     32             (byte) 0x7F, (byte) 'E', (byte) 'L', (byte) 'F',
     33     };
     34 
     35     private static final int EI_CLASS = 4;
     36     private static final int EI_DATA = 5;
     37 
     38     private static final int EM_386 = 3;
     39     private static final int EM_MIPS = 8;
     40     private static final int EM_ARM = 40;
     41 
     42     /** Size of the e_ident[] structure in the ELF header. */
     43     private static final int EI_NIDENT = 16;
     44 
     45     /** Offset from end of ident structure in half-word sizes. */
     46     private static final int OFFSET_TYPE = 0;
     47 
     48     /** Machine type. */
     49     private static final int OFFSET_MACHINE = 1;
     50 
     51     /** ELF version. */
     52     private static final int OFFSET_VERSION = 2;
     53 
     54     /**
     55      * The offset to which the system transfers control. e.g., the first thing
     56      * executed.
     57      */
     58     private static final int OFFSET_ENTRY = 4;
     59 
     60     /** Program header offset in bytes. */
     61     private static final int OFFSET_PHOFF = 6;
     62 
     63     /** Segment header offset in bytes. */
     64     private static final int OFFSET_SHOFF = 8;
     65 
     66     /** Processor-specific flags for binary. */
     67     private static final int OFFSET_FLAGS = 10;
     68 
     69     /** ELF header size in bytes. */
     70     private static final int OFFSET_EHSIZE = 12;
     71 
     72     /** All program headers entry size in bytes. */
     73     private static final int OFFSET_PHENTSIZE = 13;
     74 
     75     /** Number of program headers in ELF. */
     76     private static final int OFFSET_PHNUM = 14;
     77 
     78     /** All segment headers entry size in bytes. */
     79     private static final int OFFSET_SHENTSIZE = 15;
     80 
     81     /** Number of segment headers in ELF. */
     82     private static final int OFFSET_SHNUM = 16;
     83 
     84     /** The section header index that refers to string table. */
     85     private static final int OFFSET_SHSTRNDX = 17;
     86 
     87     /** Program header offset for type of this program header. */
     88     private static final int PHOFF_TYPE = 0;
     89 
     90     /** Program header offset for absolute offset in file. */
     91     private static final int PHOFF_OFFSET = 2;
     92 
     93     /** Program header offset for virtual address. */
     94     private static final int PHOFF_VADDR = 4;
     95 
     96     /** Program header offset for physical address. */
     97     private static final int PHOFF_PADDR = 6;
     98 
     99     /** Program header offset for file size in bytes. */
    100     private static final int PHOFF_FILESZ = 8;
    101 
    102     /** Program header offset for memory size in bytes. */
    103     private static final int PHOFF_MEMSZ = 10;
    104 
    105     /** Program header offset for flags. */
    106     private static final int PHOFF_FLAGS = 12;
    107 
    108     /**
    109      * Program header offset for required alignment. 0 or 1 means no alignment
    110      * necessary.
    111      */
    112     private static final int PHOFF_ALIGN = 14;
    113 
    114     /** Index into string pool for segment name. */
    115     private static final long SHOFF_NAME = 0;
    116 
    117     /** Segment header offset for type (half-words) */
    118     private static final long SHOFF_TYPE = 2;
    119 
    120     /** Segment header offset for offset (meta!) (half-words) */
    121     private static final long SHOFF_OFFSET = 8;
    122 
    123     /** Segment header offset for size (half-words) */
    124     private static final long SHOFF_SIZE = 10;
    125 
    126     /** Data is presented in LSB format. */
    127     private static final int ELFDATA2LSB = 1;
    128 
    129     /** Date is presented in MSB format. */
    130     private static final int ELFDATA2MSB = 2;
    131 
    132     private static final int ELFCLASS32 = 1;
    133 
    134     private static final int ELFCLASS64 = 2;
    135 
    136     private static final long PT_LOAD = 1;
    137 
    138     /** Section Type: Symbol Table */
    139     private static final int SHT_SYMTAB = 2;
    140 
    141     /** Section Type: String Table */
    142     private static final int SHT_STRTAB = 3;
    143 
    144     /** Section Type: Dynamic **/
    145     private static final int SHT_DYNAMIC = 6;
    146 
    147     /** Section Type: Dynamic Symbol Table */
    148     private static final int SHT_DYNSYM = 11;
    149 
    150     /** Symbol Table Entry: Name offset */
    151     private static final int SYMTAB_NAME = 0;
    152 
    153     /** Symbol Table Entry: SymTab Info */
    154     private static final int SYMTAB_ST_INFO = 6;
    155 
    156     /** Symbol Table Entry size (half-words) */
    157     private static final int SYMTAB_ENTRY_HALFWORD_SIZE = 7;
    158 
    159     /**
    160      * Symbol Table Entry size (extra in bytes) to cover "st_info" and
    161      * "st_other"
    162      */
    163     private static final int SYMTAB_ENTRY_BYTE_EXTRA_SIZE = 2;
    164 
    165     public static class Symbol {
    166         public static final int STB_LOCAL = 0;
    167 
    168         public static final int STB_GLOBAL = 1;
    169 
    170         public static final int STB_WEAK = 2;
    171 
    172         public static final int STB_LOPROC = 13;
    173 
    174         public static final int STB_HIPROC = 15;
    175 
    176         public final String name;
    177 
    178         public final int bind;
    179 
    180         public final int type;
    181 
    182         Symbol(String name, int st_info) {
    183             this.name = name;
    184             this.bind = (st_info >> 4) & 0x0F;
    185             this.type = st_info & 0x0F;
    186         }
    187     };
    188 
    189     private final String mPath;
    190     private final RandomAccessFile mFile;
    191     private final byte[] mBuffer = new byte[512];
    192     private int mEndian;
    193     private boolean mIsDynamic;
    194     private boolean mIsPIE;
    195     private int mType;
    196     private int mWordSize;
    197     private int mHalfWordSize;
    198 
    199     /** Symbol Table offset */
    200     private long mSymTabOffset;
    201 
    202     /** Symbol Table size */
    203     private long mSymTabSize;
    204 
    205     /** Dynamic Symbol Table offset */
    206     private long mDynSymOffset;
    207 
    208     /** Dynamic Symbol Table size */
    209     private long mDynSymSize;
    210 
    211     /** Section Header String Table offset */
    212     private long mShStrTabOffset;
    213 
    214     /** Section Header String Table size */
    215     private long mShStrTabSize;
    216 
    217     /** String Table offset */
    218     private long mStrTabOffset;
    219 
    220     /** String Table size */
    221     private long mStrTabSize;
    222 
    223     /** Dynamic String Table offset */
    224     private long mDynStrOffset;
    225 
    226     /** Dynamic String Table size */
    227     private long mDynStrSize;
    228 
    229     /** Symbol Table symbol names */
    230     private Map<String, Symbol> mSymbols;
    231 
    232     /** Dynamic Symbol Table symbol names */
    233     private Map<String, Symbol> mDynamicSymbols;
    234 
    235     public static ReadElf read(File file) throws IOException {
    236         return new ReadElf(file);
    237     }
    238 
    239     public boolean isDynamic() {
    240         return mIsDynamic;
    241     }
    242 
    243     public int getType() {
    244         return mType;
    245     }
    246 
    247     public boolean isPIE() {
    248         return mIsPIE;
    249     }
    250 
    251     private ReadElf(File file) throws IOException {
    252         mPath = file.getPath();
    253         mFile = new RandomAccessFile(file, "r");
    254 
    255         if (mFile.length() < EI_NIDENT) {
    256             throw new IllegalArgumentException("Too small to be an ELF file: " + file);
    257         }
    258 
    259         readIdent();
    260         readHeader();
    261     }
    262 
    263     public void close() {
    264         try {
    265             mFile.close();
    266         } catch (IOException ignored) {
    267         }
    268     }
    269 
    270     protected void finalize() throws Throwable {
    271         try {
    272             close();
    273         } finally {
    274             super.finalize();
    275         }
    276     }
    277 
    278     private void readHeader() throws IOException {
    279         mType = readHalf(getHeaderOffset(OFFSET_TYPE));
    280         int e_machine = readHalf(getHeaderOffset(OFFSET_MACHINE));
    281         if (e_machine != EM_386 && e_machine != EM_MIPS && e_machine != EM_ARM) {
    282             throw new IOException("Invalid ELF e_machine: " + e_machine + ": " + mPath);
    283         }
    284 
    285         final long shOffset = readWord(getHeaderOffset(OFFSET_SHOFF));
    286         final int shNumber = readHalf(getHeaderOffset(OFFSET_SHNUM));
    287         final int shSize = readHalf(getHeaderOffset(OFFSET_SHENTSIZE));
    288         final int shStrIndex = readHalf(getHeaderOffset(OFFSET_SHSTRNDX));
    289 
    290         readSectionHeaders(shOffset, shNumber, shSize, shStrIndex);
    291 
    292         final long phOffset = readWord(getHeaderOffset(OFFSET_PHOFF));
    293         final int phNumber = readHalf(getHeaderOffset(OFFSET_PHNUM));
    294         final int phSize = readHalf(getHeaderOffset(OFFSET_PHENTSIZE));
    295 
    296         readProgramHeaders(phOffset, phNumber, phSize);
    297     }
    298 
    299     private void readSectionHeaders(long tableOffset, int shNumber, int shSize, int shStrIndex)
    300             throws IOException {
    301         // Read the Section Header String Table offset first.
    302         {
    303             final long shStrTabShOffset = tableOffset + shStrIndex * shSize;
    304             final long type = readWord(shStrTabShOffset + mHalfWordSize * SHOFF_TYPE);
    305 
    306             if (type == SHT_STRTAB) {
    307                 mShStrTabOffset = readWord(shStrTabShOffset + mHalfWordSize * SHOFF_OFFSET);
    308                 mShStrTabSize = readWord(shStrTabShOffset + mHalfWordSize * SHOFF_SIZE);
    309             }
    310         }
    311 
    312         for (int i = 0; i < shNumber; i++) {
    313             // Don't bother to re-read the Section Header StrTab.
    314             if (i == shStrIndex) {
    315                 continue;
    316             }
    317 
    318             final long shOffset = tableOffset + i * shSize;
    319 
    320             final long type = readWord(shOffset + mHalfWordSize * SHOFF_TYPE);
    321             if ((type == SHT_SYMTAB) || (type == SHT_DYNSYM)) {
    322                 final long nameOffset = readWord(shOffset + mHalfWordSize * SHOFF_NAME);
    323                 final long offset = readWord(shOffset + mHalfWordSize * SHOFF_OFFSET);
    324                 final long size = readWord(shOffset + mHalfWordSize * SHOFF_SIZE);
    325 
    326                 final String symTabName = readShStrTabEntry(nameOffset);
    327                 if (".symtab".equals(symTabName)) {
    328                     mSymTabOffset = offset;
    329                     mSymTabSize = size;
    330                 } else if (".dynsym".equals(symTabName)) {
    331                     mDynSymOffset = offset;
    332                     mDynSymSize = size;
    333                 }
    334             } else if (type == SHT_STRTAB) {
    335                 final long nameOffset = readWord(shOffset + mHalfWordSize * SHOFF_NAME);
    336                 final long offset = readWord(shOffset + mHalfWordSize * SHOFF_OFFSET);
    337                 final long size = readWord(shOffset + mHalfWordSize * SHOFF_SIZE);
    338 
    339                 final String strTabName = readShStrTabEntry(nameOffset);
    340                 if (".strtab".equals(strTabName)) {
    341                     mStrTabOffset = offset;
    342                     mStrTabSize = size;
    343                 } else if (".dynstr".equals(strTabName)) {
    344                     mDynStrOffset = offset;
    345                     mDynStrSize = size;
    346                 }
    347             } else if (type == SHT_DYNAMIC) {
    348                 mIsDynamic = true;
    349             }
    350         }
    351     }
    352 
    353     private void readProgramHeaders(long phOffset, int phNumber, int phSize) throws IOException {
    354         for (int i = 0; i < phNumber; i++) {
    355             final long baseOffset = phOffset + i * phSize;
    356             final long type = readWord(baseOffset);
    357             if (type == PT_LOAD) {
    358                 final long virtAddress = readWord(baseOffset + mHalfWordSize * PHOFF_VADDR);
    359                 if (virtAddress == 0) {
    360                     mIsPIE = true;
    361                 }
    362             }
    363         }
    364     }
    365 
    366     private void readSymbolTable(Map<String, Symbol> symbolMap, long symStrOffset, long symStrSize,
    367             long symOffset, long symSize) throws IOException {
    368         final long symEnd = symOffset + symSize;
    369         for (long off = symOffset; off < symEnd; off += SYMTAB_ENTRY_HALFWORD_SIZE * mHalfWordSize
    370                 + SYMTAB_ENTRY_BYTE_EXTRA_SIZE) {
    371             long strOffset = readWord(off + SYMTAB_NAME);
    372             if (strOffset == 0) {
    373                 continue;
    374             }
    375 
    376             final String symName = readStrTabEntry(symStrOffset, symStrSize, strOffset);
    377             if (symName != null) {
    378                 final int st_info = readByte(off + SYMTAB_ST_INFO);
    379                 symbolMap.put(symName, new Symbol(symName, st_info));
    380             }
    381         }
    382     }
    383 
    384     private String readShStrTabEntry(long strOffset) throws IOException {
    385         if ((mShStrTabOffset == 0) || (strOffset < 0) || (strOffset >= mShStrTabSize)) {
    386             return null;
    387         }
    388 
    389         return readString(mShStrTabOffset + strOffset);
    390     }
    391 
    392     private String readStrTabEntry(long tableOffset, long tableSize, long strOffset)
    393             throws IOException {
    394         if ((tableOffset == 0) || (strOffset < 0) || (strOffset >= tableSize)) {
    395             return null;
    396         }
    397 
    398         return readString(tableOffset + strOffset);
    399     }
    400 
    401     private int getHeaderOffset(int halfWorldOffset) {
    402         return EI_NIDENT + halfWorldOffset * mHalfWordSize;
    403     }
    404 
    405     private int readByte(long offset) throws IOException {
    406         mFile.seek(offset);
    407         mFile.readFully(mBuffer, 0, 1);
    408 
    409         return mBuffer[0];
    410     }
    411 
    412     private int readHalf(long offset) throws IOException {
    413         mFile.seek(offset);
    414         mFile.readFully(mBuffer, 0, mWordSize);
    415 
    416         final int answer;
    417         if (mEndian == ELFDATA2LSB) {
    418             answer = mBuffer[1] << 8 | mBuffer[0];
    419         } else {
    420             answer = mBuffer[0] << 8 | mBuffer[1];
    421         }
    422 
    423         return answer;
    424     }
    425 
    426     private long readWord(long offset) throws IOException {
    427         mFile.seek(offset);
    428         mFile.readFully(mBuffer, 0, mWordSize);
    429 
    430         int answer = 0;
    431         if (mEndian == ELFDATA2LSB) {
    432             for (int i = mWordSize - 1; i >= 0; i--) {
    433                 answer = (answer << 8) | (mBuffer[i] & 0xFF);
    434             }
    435         } else {
    436             final int N = mWordSize - 1;
    437             for (int i = 0; i <= N; i++) {
    438                 answer = (answer << 8) | mBuffer[i];
    439             }
    440         }
    441 
    442         return answer;
    443     }
    444 
    445     private String readString(long offset) throws IOException {
    446         mFile.seek(offset);
    447         mFile.readFully(mBuffer, 0, (int) Math.min(mBuffer.length, mFile.length() - offset));
    448 
    449         for (int i = 0; i < mBuffer.length; i++) {
    450             if (mBuffer[i] == 0) {
    451                 return new String(mBuffer, 0, i);
    452             }
    453         }
    454 
    455         return null;
    456     }
    457 
    458     private void readIdent() throws IOException {
    459         mFile.seek(0);
    460         mFile.readFully(mBuffer, 0, EI_NIDENT);
    461 
    462         if ((mBuffer[0] != ELF_IDENT[0]) || (mBuffer[1] != ELF_IDENT[1])
    463                 || (mBuffer[2] != ELF_IDENT[2]) || (mBuffer[3] != ELF_IDENT[3])) {
    464             throw new IllegalArgumentException("Invalid ELF file: " + mPath);
    465         }
    466 
    467         int elfClass = mBuffer[EI_CLASS];
    468         if (elfClass == ELFCLASS32) {
    469             mWordSize = 4;
    470             mHalfWordSize = 2;
    471         } else if (elfClass == ELFCLASS64) {
    472             throw new IOException("Unsupported ELFCLASS64 file: " + mPath);
    473         } else {
    474             throw new IOException("Invalid ELF EI_CLASS: " + elfClass + ": " + mPath);
    475         }
    476 
    477         mEndian = mBuffer[EI_DATA];
    478         if (mEndian == ELFDATA2LSB) {
    479         } else if (mEndian == ELFDATA2MSB) {
    480             throw new IOException("Unsupported ELFDATA2MSB file: " + mPath);
    481         } else {
    482             throw new IOException("Invalid ELF EI_DATA: " + mEndian + ": " + mPath);
    483         }
    484     }
    485 
    486     public Symbol getSymbol(String name) {
    487         if ((mSymTabOffset == 0) && (mSymTabSize == 0)) {
    488             return null;
    489         }
    490 
    491         if (mSymbols == null) {
    492             mSymbols = new HashMap<String, Symbol>();
    493             try {
    494                 readSymbolTable(mSymbols, mStrTabOffset, mStrTabSize, mSymTabOffset, mSymTabSize);
    495             } catch (IOException e) {
    496                 return null;
    497             }
    498         }
    499 
    500         return mSymbols.get(name);
    501     }
    502 
    503     public Symbol getDynamicSymbol(String name) {
    504         if ((mDynSymOffset == 0) && (mDynSymSize == 0)) {
    505             return null;
    506         }
    507 
    508         if (mDynamicSymbols == null) {
    509             mDynamicSymbols = new HashMap<String, Symbol>();
    510             try {
    511                 readSymbolTable(mDynamicSymbols, mDynStrOffset, mDynStrSize, mDynSymOffset,
    512                         mDynSymSize);
    513             } catch (IOException e) {
    514                 return null;
    515             }
    516         }
    517 
    518         return mDynamicSymbols.get(name);
    519     }
    520 }
    521