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 com.android.compatibility.common.util;
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 designed
27  * 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[] ELFMAG = {
32             (byte) 0x7F, (byte) 'E', (byte) 'L', (byte) 'F', };
33 
34     private static final int EI_NIDENT = 16;
35 
36     private static final int EI_CLASS = 4;
37     private static final int EI_DATA = 5;
38 
39     private static final int EM_386 = 3;
40     private static final int EM_MIPS = 8;
41     private static final int EM_ARM = 40;
42     private static final int EM_X86_64 = 62;
43     // http://en.wikipedia.org/wiki/Qualcomm_Hexagon
44     private static final int EM_QDSP6 = 164;
45     private static final int EM_AARCH64 = 183;
46 
47     private static final int ELFCLASS32 = 1;
48     private static final int ELFCLASS64 = 2;
49 
50     private static final int ELFDATA2LSB = 1;
51     private static final int ELFDATA2MSB = 2;
52 
53     private static final int EV_CURRENT = 1;
54 
55     private static final long PT_LOAD = 1;
56 
57     private static final int SHT_SYMTAB = 2;
58     private static final int SHT_STRTAB = 3;
59     private static final int SHT_DYNAMIC = 6;
60     private static final int SHT_DYNSYM = 11;
61 
62     public static class Symbol {
63         public static final int STB_LOCAL = 0;
64         public static final int STB_GLOBAL = 1;
65         public static final int STB_WEAK = 2;
66         public static final int STB_LOPROC = 13;
67         public static final int STB_HIPROC = 15;
68 
69         public static final int STT_NOTYPE = 0;
70         public static final int STT_OBJECT = 1;
71         public static final int STT_FUNC = 2;
72         public static final int STT_SECTION = 3;
73         public static final int STT_FILE = 4;
74         public static final int STT_COMMON = 5;
75         public static final int STT_TLS = 6;
76 
77         public final String name;
78         public final int bind;
79         public final int type;
80 
Symbol(String name, int st_info)81         Symbol(String name, int st_info) {
82             this.name = name;
83             this.bind = (st_info >> 4) & 0x0F;
84             this.type = st_info & 0x0F;
85         }
86 
87         @Override
toString()88         public String toString() {
89             return "Symbol[" + name + "," + toBind() + "," + toType() + "]";
90         }
91 
toBind()92         private String toBind() {
93             switch (bind) {
94                 case STB_LOCAL:
95                     return "LOCAL";
96                 case STB_GLOBAL:
97                     return "GLOBAL";
98                 case STB_WEAK:
99                     return "WEAK";
100             }
101             return "STB_??? (" + bind + ")";
102         }
103 
toType()104         private String toType() {
105             switch (type) {
106                 case STT_NOTYPE:
107                     return "NOTYPE";
108                 case STT_OBJECT:
109                     return "OBJECT";
110                 case STT_FUNC:
111                     return "FUNC";
112                 case STT_SECTION:
113                     return "SECTION";
114                 case STT_FILE:
115                     return "FILE";
116                 case STT_COMMON:
117                     return "COMMON";
118                 case STT_TLS:
119                     return "TLS";
120             }
121             return "STT_??? (" + type + ")";
122         }
123     }
124 
125     private final String mPath;
126     private final RandomAccessFile mFile;
127     private final byte[] mBuffer = new byte[512];
128     private int mEndian;
129     private boolean mIsDynamic;
130     private boolean mIsPIE;
131     private int mType;
132     private int mAddrSize;
133 
134     /** Symbol Table offset */
135     private long mSymTabOffset;
136 
137     /** Symbol Table size */
138     private long mSymTabSize;
139 
140     /** Dynamic Symbol Table offset */
141     private long mDynSymOffset;
142 
143     /** Dynamic Symbol Table size */
144     private long mDynSymSize;
145 
146     /** Section Header String Table offset */
147     private long mShStrTabOffset;
148 
149     /** Section Header String Table size */
150     private long mShStrTabSize;
151 
152     /** String Table offset */
153     private long mStrTabOffset;
154 
155     /** String Table size */
156     private long mStrTabSize;
157 
158     /** Dynamic String Table offset */
159     private long mDynStrOffset;
160 
161     /** Dynamic String Table size */
162     private long mDynStrSize;
163 
164     /** Symbol Table symbol names */
165     private Map<String, Symbol> mSymbols;
166 
167     /** Dynamic Symbol Table symbol names */
168     private Map<String, Symbol> mDynamicSymbols;
169 
read(File file)170     public static ReadElf read(File file) throws IOException {
171         return new ReadElf(file);
172     }
173 
main(String[] args)174     public static void main(String[] args) throws IOException {
175         for (String arg : args) {
176             ReadElf re = new ReadElf(new File(arg));
177             re.getSymbol("x");
178             re.getDynamicSymbol("x");
179             re.close();
180         }
181     }
182 
isDynamic()183     public boolean isDynamic() {
184         return mIsDynamic;
185     }
186 
getType()187     public int getType() {
188         return mType;
189     }
190 
isPIE()191     public boolean isPIE() {
192         return mIsPIE;
193     }
194 
ReadElf(File file)195     private ReadElf(File file) throws IOException {
196         mPath = file.getPath();
197         mFile = new RandomAccessFile(file, "r");
198 
199         if (mFile.length() < EI_NIDENT) {
200             throw new IllegalArgumentException("Too small to be an ELF file: " + file);
201         }
202 
203         readHeader();
204     }
205 
206     @Override
close()207     public void close() {
208         try {
209             mFile.close();
210         } catch (IOException ignored) {
211         }
212     }
213 
214     @Override
finalize()215     protected void finalize() throws Throwable {
216         try {
217             close();
218         } finally {
219             super.finalize();
220         }
221     }
222 
readHeader()223     private void readHeader() throws IOException {
224         mFile.seek(0);
225         mFile.readFully(mBuffer, 0, EI_NIDENT);
226 
227         if (mBuffer[0] != ELFMAG[0] || mBuffer[1] != ELFMAG[1] ||
228                 mBuffer[2] != ELFMAG[2] || mBuffer[3] != ELFMAG[3]) {
229             throw new IllegalArgumentException("Invalid ELF file: " + mPath);
230         }
231 
232         int elfClass = mBuffer[EI_CLASS];
233         if (elfClass == ELFCLASS32) {
234             mAddrSize = 4;
235         } else if (elfClass == ELFCLASS64) {
236             mAddrSize = 8;
237         } else {
238             throw new IOException("Invalid ELF EI_CLASS: " + elfClass + ": " + mPath);
239         }
240 
241         mEndian = mBuffer[EI_DATA];
242         if (mEndian == ELFDATA2LSB) {
243         } else if (mEndian == ELFDATA2MSB) {
244             throw new IOException("Unsupported ELFDATA2MSB file: " + mPath);
245         } else {
246             throw new IOException("Invalid ELF EI_DATA: " + mEndian + ": " + mPath);
247         }
248 
249         mType = readHalf();
250 
251         int e_machine = readHalf();
252         if (e_machine != EM_386 && e_machine != EM_X86_64 &&
253                 e_machine != EM_AARCH64 && e_machine != EM_ARM &&
254                 e_machine != EM_MIPS &&
255                 e_machine != EM_QDSP6) {
256             throw new IOException("Invalid ELF e_machine: " + e_machine + ": " + mPath);
257         }
258 
259         // AbiTest relies on us rejecting any unsupported combinations.
260         if ((e_machine == EM_386 && elfClass != ELFCLASS32) ||
261                 (e_machine == EM_X86_64 && elfClass != ELFCLASS64) ||
262                 (e_machine == EM_AARCH64 && elfClass != ELFCLASS64) ||
263                 (e_machine == EM_ARM && elfClass != ELFCLASS32) ||
264                 (e_machine == EM_QDSP6 && elfClass != ELFCLASS32)) {
265             throw new IOException("Invalid e_machine/EI_CLASS ELF combination: " +
266                     e_machine + "/" + elfClass + ": " + mPath);
267         }
268 
269         long e_version = readWord();
270         if (e_version != EV_CURRENT) {
271             throw new IOException("Invalid e_version: " + e_version + ": " + mPath);
272         }
273 
274         long e_entry = readAddr();
275 
276         long ph_off = readOff();
277         long sh_off = readOff();
278 
279         long e_flags = readWord();
280         int e_ehsize = readHalf();
281         int e_phentsize = readHalf();
282         int e_phnum = readHalf();
283         int e_shentsize = readHalf();
284         int e_shnum = readHalf();
285         int e_shstrndx = readHalf();
286 
287         readSectionHeaders(sh_off, e_shnum, e_shentsize, e_shstrndx);
288         readProgramHeaders(ph_off, e_phnum, e_phentsize);
289     }
290 
readSectionHeaders(long sh_off, int e_shnum, int e_shentsize, int e_shstrndx)291     private void readSectionHeaders(long sh_off, int e_shnum, int e_shentsize, int e_shstrndx)
292             throws IOException {
293         // Read the Section Header String Table offset first.
294         {
295             mFile.seek(sh_off + e_shstrndx * e_shentsize);
296 
297             long sh_name = readWord();
298             long sh_type = readWord();
299             long sh_flags = readX(mAddrSize);
300             long sh_addr = readAddr();
301             long sh_offset = readOff();
302             long sh_size = readX(mAddrSize);
303             // ...
304 
305             if (sh_type == SHT_STRTAB) {
306                 mShStrTabOffset = sh_offset;
307                 mShStrTabSize = sh_size;
308             }
309         }
310 
311         for (int i = 0; i < e_shnum; ++i) {
312             // Don't bother to re-read the Section Header StrTab.
313             if (i == e_shstrndx) {
314                 continue;
315             }
316 
317             mFile.seek(sh_off + i * e_shentsize);
318 
319             long sh_name = readWord();
320             long sh_type = readWord();
321             long sh_flags = readX(mAddrSize);
322             long sh_addr = readAddr();
323             long sh_offset = readOff();
324             long sh_size = readX(mAddrSize);
325 
326             if (sh_type == SHT_SYMTAB || sh_type == SHT_DYNSYM) {
327                 final String symTabName = readShStrTabEntry(sh_name);
328                 if (".symtab".equals(symTabName)) {
329                     mSymTabOffset = sh_offset;
330                     mSymTabSize = sh_size;
331                 } else if (".dynsym".equals(symTabName)) {
332                     mDynSymOffset = sh_offset;
333                     mDynSymSize = sh_size;
334                 }
335             } else if (sh_type == SHT_STRTAB) {
336                 final String strTabName = readShStrTabEntry(sh_name);
337                 if (".strtab".equals(strTabName)) {
338                     mStrTabOffset = sh_offset;
339                     mStrTabSize = sh_size;
340                 } else if (".dynstr".equals(strTabName)) {
341                     mDynStrOffset = sh_offset;
342                     mDynStrSize = sh_size;
343                 }
344             } else if (sh_type == SHT_DYNAMIC) {
345                 mIsDynamic = true;
346             }
347         }
348     }
349 
readProgramHeaders(long ph_off, int e_phnum, int e_phentsize)350     private void readProgramHeaders(long ph_off, int e_phnum, int e_phentsize) throws IOException {
351         for (int i = 0; i < e_phnum; ++i) {
352             mFile.seek(ph_off + i * e_phentsize);
353 
354             long p_type = readWord();
355             if (p_type == PT_LOAD) {
356                 if (mAddrSize == 8) {
357                     // Only in Elf64_phdr; in Elf32_phdr p_flags is at the end.
358                     long p_flags = readWord();
359                 }
360                 long p_offset = readOff();
361                 long p_vaddr = readAddr();
362                 // ...
363 
364                 if (p_vaddr == 0) {
365                     mIsPIE = true;
366                 }
367             }
368         }
369     }
370 
readSymbolTable(long symStrOffset, long symStrSize, long tableOffset, long tableSize)371     private HashMap<String, Symbol> readSymbolTable(long symStrOffset, long symStrSize,
372             long tableOffset, long tableSize) throws IOException {
373         HashMap<String, Symbol> result = new HashMap<String, Symbol>();
374         mFile.seek(tableOffset);
375         while (mFile.getFilePointer() < tableOffset + tableSize) {
376             long st_name = readWord();
377             int st_info;
378             if (mAddrSize == 8) {
379                 st_info = readByte();
380                 int st_other = readByte();
381                 int st_shndx = readHalf();
382                 long st_value = readAddr();
383                 long st_size = readX(mAddrSize);
384             } else {
385                 long st_value = readAddr();
386                 long st_size = readWord();
387                 st_info = readByte();
388                 int st_other = readByte();
389                 int st_shndx = readHalf();
390             }
391             if (st_name == 0) {
392                 continue;
393             }
394 
395             final String symName = readStrTabEntry(symStrOffset, symStrSize, st_name);
396             if (symName != null) {
397                 Symbol s = new Symbol(symName, st_info);
398                 result.put(symName, s);
399             }
400         }
401         return result;
402     }
403 
readShStrTabEntry(long strOffset)404     private String readShStrTabEntry(long strOffset) throws IOException {
405         if (mShStrTabOffset == 0 || strOffset < 0 || strOffset >= mShStrTabSize) {
406             return null;
407         }
408         return readString(mShStrTabOffset + strOffset);
409     }
410 
readStrTabEntry(long tableOffset, long tableSize, long strOffset)411     private String readStrTabEntry(long tableOffset, long tableSize, long strOffset)
412             throws IOException {
413         if (tableOffset == 0 || strOffset < 0 || strOffset >= tableSize) {
414             return null;
415         }
416         return readString(tableOffset + strOffset);
417     }
418 
readHalf()419     private int readHalf() throws IOException {
420         return (int) readX(2);
421     }
422 
readWord()423     private long readWord() throws IOException {
424         return readX(4);
425     }
426 
readOff()427     private long readOff() throws IOException {
428         return readX(mAddrSize);
429     }
430 
readAddr()431     private long readAddr() throws IOException {
432         return readX(mAddrSize);
433     }
434 
readX(int byteCount)435     private long readX(int byteCount) throws IOException {
436         mFile.readFully(mBuffer, 0, byteCount);
437 
438         int answer = 0;
439         if (mEndian == ELFDATA2LSB) {
440             for (int i = byteCount - 1; i >= 0; i--) {
441                 answer = (answer << 8) | (mBuffer[i] & 0xff);
442             }
443         } else {
444             final int N = byteCount - 1;
445             for (int i = 0; i <= N; ++i) {
446                 answer = (answer << 8) | (mBuffer[i] & 0xff);
447             }
448         }
449 
450         return answer;
451     }
452 
readString(long offset)453     private String readString(long offset) throws IOException {
454         long originalOffset = mFile.getFilePointer();
455         mFile.seek(offset);
456         mFile.readFully(mBuffer, 0, (int) Math.min(mBuffer.length, mFile.length() - offset));
457         mFile.seek(originalOffset);
458 
459         for (int i = 0; i < mBuffer.length; ++i) {
460             if (mBuffer[i] == 0) {
461                 return new String(mBuffer, 0, i);
462             }
463         }
464 
465         return null;
466     }
467 
readByte()468     private int readByte() throws IOException {
469         return mFile.read() & 0xff;
470     }
471 
getSymbol(String name)472     public Symbol getSymbol(String name) {
473         if (mSymbols == null) {
474             try {
475                 mSymbols = readSymbolTable(mStrTabOffset, mStrTabSize, mSymTabOffset, mSymTabSize);
476             } catch (IOException e) {
477                 return null;
478             }
479         }
480         return mSymbols.get(name);
481     }
482 
getDynamicSymbol(String name)483     public Symbol getDynamicSymbol(String name) {
484         if (mDynamicSymbols == null) {
485             try {
486                 mDynamicSymbols = readSymbolTable(
487                         mDynStrOffset, mDynStrSize, mDynSymOffset, mDynSymSize);
488             } catch (IOException e) {
489                 return null;
490             }
491         }
492         return mDynamicSymbols.get(name);
493     }
494 }
495