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