1 /*
2  * Copyright (C) 2017 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 com.google.errorprone.annotations.CanIgnoreReturnValue;
20 
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.RandomAccessFile;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 
29 /**
30  * A poor man's implementation of the readelf command. This program is designed to parse ELF
31  * (Executable and Linkable Format) files.
32  */
33 // ToDo: consolidate with com.android.compatibility.common.util
34 public class ReadElf implements AutoCloseable {
35     /** The magic values for the ELF identification. */
36     private static final byte[] ELFMAG = {
37         (byte) 0x7F, (byte) 'E', (byte) 'L', (byte) 'F',
38     };
39 
40     private static final int EI_NIDENT = 16;
41 
42     private static final int EI_CLASS = 4;
43     private static final int EI_DATA = 5;
44 
45     public static final int ET_DYN = 3;
46     public static final int EM_386 = 3;
47     public static final int EM_ARM = 40;
48     public static final int EM_X86_64 = 62;
49     // http://en.wikipedia.org/wiki/Qualcomm_Hexagon
50     public static final int EM_QDSP6 = 164;
51     public static final int EM_AARCH64 = 183;
52     public static final int EM_RISCV = 243;
53 
54     public static final String ARCH_ARM = "arm";
55     public static final String ARCH_X86 = "x86";
56     public static final String ARCH_RISCV = "riscv";
57     public static final String ARCH_UNKNOWN = "unknown";
58     private static final String RODATA = ".rodata";
59 
60     private static final int ELFCLASS32 = 1;
61     private static final int ELFCLASS64 = 2;
62 
63     private static final int ELFDATA2LSB = 1;
64     private static final int ELFDATA2MSB = 2;
65 
66     private static final int EV_CURRENT = 1;
67 
68     private static final long PT_LOAD = 1;
69 
70     // https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
71     private static final int SHT_PROGBITS = 1;
72     private static final int SHT_SYMTAB = 2;
73     private static final int SHT_STRTAB = 3;
74     private static final int SHT_DYNAMIC = 6;
75     private static final int SHT_DYNSYM = 11;
76     private static final int SHT_GNU_VERDEF = 0x6ffffffd;
77     private static final int SHT_GNU_VERNEED = 0x6ffffffe;
78     private static final int SHT_GNU_VERSYM = 0x6fffffff;
79 
80     public static class Symbol {
81         public static final int STB_LOCAL = 0;
82         public static final int STB_GLOBAL = 1;
83         public static final int STB_WEAK = 2;
84         public static final int STB_LOPROC = 13;
85         public static final int STB_HIPROC = 15;
86 
87         public static final int STT_NOTYPE = 0;
88         public static final int STT_OBJECT = 1;
89         public static final int STT_FUNC = 2;
90         public static final int STT_SECTION = 3;
91         public static final int STT_FILE = 4;
92         public static final int STT_COMMON = 5;
93         public static final int STT_TLS = 6;
94 
95         public static final int SHN_UNDEF = 0;
96         public static final int SHN_ABS = 0xfff1;
97 
98         public final String name;
99         public final int bind;
100         public final int type;
101         public final int shndx;
102         public final long value;
103         public final long size;
104         public final int other;
105 
106         public VerNeed mVerNeed;
107         public VerDef mVerDef;
108 
Symbol(String name, int st_info, int st_shndx, long st_value, long st_size, int st_other)109         Symbol(String name, int st_info, int st_shndx, long st_value, long st_size, int st_other) {
110             this.name = name;
111             this.bind = (st_info >> 4) & 0x0F;
112             this.type = st_info & 0x0F;
113             this.shndx = st_shndx;
114             this.value = st_value;
115             this.size = st_size;
116             this.other = st_other;
117         }
118 
119         @Override
toString()120         public String toString() {
121             return String.format(
122                     "%s, %s, %s, %s, %s, %s",
123                     name,
124                     toBind(),
125                     toType(),
126                     toShndx(),
127                     getExternalLibFileName(),
128                     getExternalLibName());
129         }
130 
toBind()131         public String toBind() {
132             switch (bind) {
133                 case STB_LOCAL:
134                     return "LOCAL";
135                 case STB_GLOBAL:
136                     return "GLOBAL";
137                 case STB_WEAK:
138                     return "WEAK";
139             }
140             return "STB_??? (" + bind + ")";
141         }
142 
toType()143         public String toType() {
144             switch (type) {
145                 case STT_NOTYPE:
146                     return "NOTYPE";
147                 case STT_OBJECT:
148                     return "OBJECT";
149                 case STT_FUNC:
150                     return "FUNC";
151                 case STT_SECTION:
152                     return "SECTION";
153                 case STT_FILE:
154                     return "FILE";
155                 case STT_COMMON:
156                     return "COMMON";
157                 case STT_TLS:
158                     return "TLS";
159             }
160             return "STT_??? (" + type + ")";
161         }
162 
toShndx()163         public String toShndx() {
164             switch (shndx) {
165                 case SHN_ABS:
166                     return "ABS";
167                 case SHN_UNDEF:
168                     return "UND";
169             }
170             return String.valueOf(shndx);
171         }
172 
173         // if a symbol is not define locally
isGlobalUnd()174         public boolean isGlobalUnd() {
175             return (bind != STB_LOCAL && shndx == SHN_UNDEF);
176         }
177 
178         // if a symbol is extern
isExtern()179         public boolean isExtern() {
180             return (bind != STB_LOCAL && shndx != SHN_UNDEF);
181         }
182 
getExternalLibFileName()183         public String getExternalLibFileName() {
184             if (mVerNeed != null) {
185                 return mVerNeed.vn_file_name;
186             }
187             return null;
188         }
189 
getExternalLibName()190         public String getExternalLibName() {
191             if (mVerNeed != null) {
192                 return mVerNeed.vn_vernaux[0].vna_lib_name;
193             }
194             return null;
195         }
196 
getExternalLibVer()197         public int getExternalLibVer() {
198             if (mVerNeed != null) {
199                 return mVerNeed.vn_vernaux[0].vna_other;
200             }
201             return -1;
202         }
203 
getVerDefLibName()204         public String getVerDefLibName() {
205             if (mVerDef != null) {
206                 return mVerDef.vd_verdaux[0].vda_lib_name;
207             }
208             return null;
209         }
210 
getVerDefVersion()211         public int getVerDefVersion() {
212             if (mVerDef != null) {
213                 return mVerDef.vd_version;
214             }
215             return -1;
216         }
217     }
218 
219     public static class SecHeader {
220         public final long sh_name;
221         public final long sh_type;
222         public final long sh_flags;
223         public final long sh_addr;
224         public final long sh_offset;
225         public final long sh_size;
226         public final long sh_link;
227         public final long sh_info;
228         public final long sh_addralign;
229         public final long sh_entsize;
230 
SecHeader( long name, long type, long flags, long addr, long offset, long size, long link, long info, long addralign, long entsize)231         SecHeader(
232                 long name,
233                 long type,
234                 long flags,
235                 long addr,
236                 long offset,
237                 long size,
238                 long link,
239                 long info,
240                 long addralign,
241                 long entsize) {
242             this.sh_name = name;
243             this.sh_type = type;
244             this.sh_flags = flags;
245             this.sh_addr = addr;
246             this.sh_offset = offset;
247             this.sh_size = size;
248             this.sh_link = link;
249             this.sh_info = info;
250             this.sh_addralign = addralign;
251             this.sh_entsize = entsize;
252         }
253 
254         @Override
toString()255         public String toString() {
256             return String.format(
257                     "%d, %d, %d, %d, %d, %d, %d, %d, %d, %d",
258                     this.sh_name,
259                     this.sh_type,
260                     this.sh_flags,
261                     this.sh_addr,
262                     this.sh_offset,
263                     this.sh_size,
264                     this.sh_link,
265                     this.sh_info,
266                     this.sh_addralign,
267                     this.sh_entsize);
268         }
269     }
270 
271     public static class VerNeed {
272         public final int vn_version;
273         public final int vn_cnt;
274         public final long vn_file;
275         public final long vn_aux;
276         public final long vn_next;
277         public String vn_file_name;
278         public VerNAux[] vn_vernaux;
279 
VerNeed(String file_name, String lib_name, int ndx)280         VerNeed(String file_name, String lib_name, int ndx) {
281             this.vn_file_name = file_name.toLowerCase();
282             this.vn_vernaux = new VerNAux[1];
283             this.vn_vernaux[0] = new VerNAux(lib_name, ndx);
284 
285             this.vn_version = 0;
286             this.vn_cnt = 0;
287             this.vn_file = 0;
288             this.vn_aux = 0;
289             this.vn_next = 0;
290         }
291 
VerNeed(int ver, int cnt, long file, long aux, long next)292         VerNeed(int ver, int cnt, long file, long aux, long next) {
293             this.vn_version = ver;
294             this.vn_cnt = cnt;
295             this.vn_file = file;
296             this.vn_aux = aux;
297             this.vn_next = next;
298         }
299 
300         @Override
toString()301         public String toString() {
302             String vernauxStr = "";
303             for (int i = 0; i < this.vn_cnt; i++) {
304                 vernauxStr += String.format("    %s\n", this.vn_vernaux[i].toString());
305             }
306             return String.format(
307                     "%s, %d, %d, %d, %d, %d \n%s",
308                     this.vn_file_name,
309                     this.vn_version,
310                     this.vn_cnt,
311                     this.vn_file,
312                     this.vn_aux,
313                     this.vn_next,
314                     vernauxStr);
315         }
316     }
317 
318     public static class VerNAux {
319         public final long vna_hash;
320         public final int vna_flags;
321         public final int vna_other;
322         public final long vna_name;
323         public final long vna_next;
324         public String vna_lib_name;
325 
VerNAux(String lib_name, int ndx)326         VerNAux(String lib_name, int ndx) {
327             this.vna_lib_name = lib_name;
328 
329             this.vna_hash = 0;
330             this.vna_flags = 0;
331             this.vna_other = ndx;
332             this.vna_name = 0;
333             this.vna_next = 0;
334         }
335 
VerNAux(long hash, int flags, int other, long name, long next)336         VerNAux(long hash, int flags, int other, long name, long next) {
337             this.vna_hash = hash;
338             this.vna_flags = flags;
339             this.vna_other = other;
340             this.vna_name = name;
341             this.vna_next = next;
342         }
343 
344         @Override
toString()345         public String toString() {
346             return String.format(
347                     "%s, %d, %d, %d, %d, %d",
348                     this.vna_lib_name,
349                     this.vna_hash,
350                     this.vna_flags,
351                     this.vna_other,
352                     this.vna_name,
353                     this.vna_next);
354         }
355     }
356 
357     public static class VerDef {
358         public final int vd_version;
359         public final int vd_flags;
360         public final int vd_ndx;
361         public final int vd_cnt;
362         public final long vd_hash;
363         public final long vd_aux;
364         public final long vd_next;
365         public VerDAux[] vd_verdaux;
366 
VerDef(String lib_name)367         VerDef(String lib_name) {
368             this.vd_verdaux = new VerDAux[1];
369             this.vd_verdaux[0] = new VerDAux(lib_name);
370 
371             this.vd_version = 0;
372             this.vd_flags = 0;
373             this.vd_ndx = 0;
374             this.vd_cnt = 0;
375             this.vd_hash = 0;
376             this.vd_aux = 0;
377             this.vd_next = 0;
378         }
379 
VerDef(int ver, int flags, int ndx, int cnt, long hash, long aux, long next)380         VerDef(int ver, int flags, int ndx, int cnt, long hash, long aux, long next) {
381             this.vd_version = ver;
382             this.vd_flags = flags;
383             this.vd_ndx = ndx;
384             this.vd_cnt = cnt;
385             this.vd_hash = hash;
386             this.vd_aux = aux;
387             this.vd_next = next;
388         }
389 
390         @Override
toString()391         public String toString() {
392             String vStr = "";
393             for (int i = 0; i < this.vd_cnt; i++) {
394                 vStr += String.format("    %s\n", this.vd_verdaux[i].toString());
395             }
396             return String.format(
397                     "%s, %d, %d, %d, %d, %d \n%s",
398                     this.vd_verdaux[0].vda_lib_name,
399                     this.vd_version,
400                     this.vd_flags,
401                     this.vd_ndx,
402                     this.vd_cnt,
403                     this.vd_hash,
404                     vStr);
405         }
406     }
407 
408     public static class VerDAux {
409         public final long vda_name;
410         public final long vda_next;
411         public String vda_lib_name;
412 
VerDAux(String lib_name)413         VerDAux(String lib_name) {
414             this.vda_lib_name = lib_name.toLowerCase();
415 
416             this.vda_name = 0;
417             this.vda_next = 0;
418         }
419 
VerDAux(long name, long next)420         VerDAux(long name, long next) {
421             this.vda_name = name;
422             this.vda_next = next;
423         }
424 
425         @Override
toString()426         public String toString() {
427             return String.format("%s, %d, %d", this.vda_lib_name, this.vda_name, this.vda_next);
428         }
429     }
430 
431     // Dynamic Section Entry
432     public static class DynamicEntry {
433         private static final int DT_NEEDED = 1;
434         public final long mTag;
435         public final long mValue;
436 
DynamicEntry(long tag, long value)437         DynamicEntry(long tag, long value) {
438             mTag = tag;
439             mValue = value;
440         }
441 
isNeeded()442         public boolean isNeeded() {
443             if (mTag == DT_NEEDED) {
444                 return true;
445             } else {
446                 // System.err.println(String.format("Not Needed: %d, %d", mTag, mValue));
447                 return false;
448             }
449         }
450 
getValue()451         public long getValue() {
452             return mValue;
453         }
454 
455         @Override
toString()456         public String toString() {
457             return String.format("%d, %d", this.mTag, this.mValue);
458         }
459     }
460 
461     private final String mPath;
462     private final RandomAccessFile mFile;
463     private final byte[] mBuffer = new byte[512];
464     private int mEndian;
465     private boolean mIsDynamic;
466     private boolean mIsPIE;
467     private int mType;
468     private int mAddrSize;
469     private int mMachine;
470 
471     /** Symbol Table offset */
472     private long mSymTabOffset;
473 
474     /** Symbol Table size */
475     private long mSymTabSize;
476 
477     /** Symbol entry count */
478     private int mSymEntCnt;
479 
480     /** Dynamic Symbol Table offset */
481     private long mDynSymOffset;
482 
483     /** Dynamic Symbol Table size */
484     private long mDynSymSize;
485 
486     /** Dynamic entry count */
487     private int mDynSymEntCnt;
488 
489     /** Section Header String Table offset */
490     private long mShStrTabOffset;
491 
492     /** Section Header String Table size */
493     private long mShStrTabSize;
494 
495     /** String Table offset */
496     private long mStrTabOffset;
497 
498     /** String Table size */
499     private long mStrTabSize;
500 
501     /** Dynamic String Table offset */
502     private long mDynStrOffset;
503 
504     /** Dynamic String Table size */
505     private long mDynStrSize;
506 
507     /** Dynamic Table offset */
508     private long mDynamicTabOffset;
509 
510     /** Dynamic Table size */
511     private long mDynamicTabSize;
512 
513     /** Version Symbols Table offset */
514     private long mVerSymTabOffset;
515 
516     /** Version Symbols Table size */
517     private long mVerSymTabSize;
518 
519     /** Version Needs Table offset */
520     private long mVerNeedTabOffset;
521 
522     /** Version Definition Table size */
523     private long mVerNeedTabSize;
524 
525     private int mVerNeedEntryCnt;
526 
527     /** Version Definition Table offset */
528     private long mVerDefTabOffset;
529 
530     /** Version Needs Table size */
531     private long mVerDefTabSize;
532 
533     private int mVerDefEntryCnt;
534 
535     /** Symbol Table symbol names */
536     private Map<String, Symbol> mSymbols;
537 
538     /** Symbol Table symbol array */
539     private Symbol[] mSymArr;
540 
541     /** Dynamic Symbol Table symbol names */
542     private Map<String, Symbol> mDynamicSymbols;
543 
544     /** Dynamic Symbol Table symbol array */
545     private Symbol[] mDynSymArr;
546 
547     /** Version Symbols Table */
548     private int[] mVerSym;
549 
550     /** Version Needed Table */
551     private VerNeed[] mVerNeedArr;
552 
553     /** Version Definition Table */
554     private VerDef[] mVerDefArr;
555 
556     /** Dynamic Table */
557     private List<DynamicEntry> mDynamicArr;
558 
559     /** Rodata offset */
560     private boolean mHasRodata;
561 
562     /** Rodata offset */
563     private long mRodataOffset;
564 
565     /** Rodata size */
566     private int mRodataSize;
567 
568     /** Rodata String List */
569     private List<String> mRoStrings;
570 
571     /** Rodata byte[] */
572     private byte[] mRoData;
573 
read(File file)574     public static ReadElf read(File file) throws IOException {
575         return new ReadElf(file);
576     }
577 
main(String[] args)578     public static void main(String[] args) throws IOException {
579         for (String arg : args) {
580             ReadElf elf = ReadElf.read(new File(arg));
581             elf.getDynamicSymbol("x");
582             elf.getSymbol("x");
583 
584             Symbol[] symArr;
585             System.out.println("===Symbol===");
586             symArr = elf.getSymArr();
587             for (int i = 0; i < symArr.length; i++) {
588                 System.out.println(String.format("%8x: %s", i, symArr[i].toString()));
589             }
590             System.out.println("===Dynamic Symbol===");
591             symArr = elf.getDynSymArr();
592             for (int i = 0; i < symArr.length; i++) {
593                 if (elf.mVerNeedEntryCnt > 0) {
594                     System.out.println(
595                             String.format(
596                                     "%8x: %s, %s, %s - %d",
597                                     i,
598                                     symArr[i].toString(),
599                                     symArr[i].getExternalLibName(),
600                                     symArr[i].getExternalLibFileName(),
601                                     symArr[i].getExternalLibVer()));
602                 } else {
603                     System.out.println(
604                             String.format(
605                                     "%8x: %s, %s - %d",
606                                     i,
607                                     symArr[i].toString(),
608                                     symArr[i].getVerDefLibName(),
609                                     symArr[i].getVerDefVersion()));
610                 }
611             }
612 
613             System.out.println("===Dynamic Dependencies===");
614             for (String DynDepEntry : elf.getDynamicDependencies()) {
615                 System.out.println(DynDepEntry);
616             }
617 
618             System.out.println("===Strings in Read Only(.rodata) section===");
619             for (String roStr : elf.getRoStrings()) {
620                 System.out.println(roStr);
621             }
622 
623             elf.close();
624         }
625     }
626 
isElf(File file)627     public static boolean isElf(File file) {
628         try {
629             if (file.length() < EI_NIDENT) {
630                 throw new IllegalArgumentException(
631                         "Too small to be an ELF file: " + file.getCanonicalPath());
632             }
633 
634             RandomAccessFile raFile = new RandomAccessFile(file, "r");
635             byte[] buffer = new byte[512];
636             raFile.seek(0);
637             raFile.readFully(buffer, 0, EI_NIDENT);
638             if (buffer[0] != ELFMAG[0]
639                     || buffer[1] != ELFMAG[1]
640                     || buffer[2] != ELFMAG[2]
641                     || buffer[3] != ELFMAG[3]) {
642                 throw new IllegalArgumentException("Invalid ELF file: " + file.getCanonicalPath());
643             }
644             raFile.close();
645             ;
646             return true;
647         } catch (Exception e) {
648             return false;
649         }
650     }
651 
getBits()652     public int getBits() {
653         if (mMachine == EM_386 || mMachine == EM_ARM || mMachine == EM_QDSP6) {
654             return 32;
655         } else if (mMachine == EM_AARCH64 || mMachine == EM_X86_64 || mMachine == EM_RISCV) {
656             return 64;
657         } else {
658             return -1;
659         }
660     }
661 
getArchitecture()662     public String getArchitecture() {
663         if (mMachine == EM_ARM || mMachine == EM_AARCH64) {
664             return ARCH_ARM;
665         } else if (mMachine == EM_386 || mMachine == EM_X86_64) {
666             return ARCH_X86;
667         } else if (mMachine == EM_RISCV) {
668             return ARCH_RISCV;
669         } else {
670             return ARCH_UNKNOWN;
671         }
672     }
673 
getSymbols()674     public Map<String, Symbol> getSymbols() throws IOException {
675         if (mSymbols == null) {
676             getSymbol("");
677         }
678         return mSymbols;
679     }
680 
getSymArr()681     public Symbol[] getSymArr() throws IOException {
682         if (mSymArr == null) {
683             getSymbol("");
684         }
685         return mSymArr;
686     }
687 
getDynamicSymbols()688     public Map<String, Symbol> getDynamicSymbols() throws IOException {
689         if (mDynamicSymbols == null) {
690             getDynamicSymbol("");
691         }
692         return mDynamicSymbols;
693     }
694 
getDynSymArr()695     public Symbol[] getDynSymArr() throws IOException {
696         if (mDynSymArr == null) {
697             getDynamicSymbol("");
698         }
699         return mDynSymArr;
700     }
701 
isDynamic()702     public boolean isDynamic() {
703         return mIsDynamic;
704     }
705 
getType()706     public int getType() {
707         return mType;
708     }
709 
isPIE()710     public boolean isPIE() {
711         return mIsPIE;
712     }
713 
ReadElf(File file)714     private ReadElf(File file) throws IOException {
715         mHasRodata = false;
716         mRoData = null;
717         mPath = file.getPath();
718         mFile = new RandomAccessFile(file, "r");
719 
720         if (mFile.length() < EI_NIDENT) {
721             throw new IllegalArgumentException("Too small to be an ELF file: " + file);
722         }
723 
724         readHeader();
725     }
726 
727     @Override
close()728     public void close() {
729         try {
730             mFile.close();
731         } catch (IOException ignored) {
732         }
733     }
734 
735     @Override
finalize()736     protected void finalize() throws Throwable {
737         try {
738             close();
739         } finally {
740             super.finalize();
741         }
742     }
743 
readHeader()744     private void readHeader() throws IOException {
745         mFile.seek(0);
746         mFile.readFully(mBuffer, 0, EI_NIDENT);
747 
748         if (mBuffer[0] != ELFMAG[0]
749                 || mBuffer[1] != ELFMAG[1]
750                 || mBuffer[2] != ELFMAG[2]
751                 || mBuffer[3] != ELFMAG[3]) {
752             throw new IllegalArgumentException("Invalid ELF file: " + mPath);
753         }
754 
755         int elfClass = mBuffer[EI_CLASS];
756         if (elfClass == ELFCLASS32) {
757             mAddrSize = 4;
758         } else if (elfClass == ELFCLASS64) {
759             mAddrSize = 8;
760         } else {
761             throw new IOException("Invalid ELF EI_CLASS: " + elfClass + ": " + mPath);
762         }
763 
764         mEndian = mBuffer[EI_DATA];
765         if (mEndian == ELFDATA2LSB) {
766         } else if (mEndian == ELFDATA2MSB) {
767             throw new IOException("Unsupported ELFDATA2MSB file: " + mPath);
768         } else {
769             throw new IOException("Invalid ELF EI_DATA: " + mEndian + ": " + mPath);
770         }
771 
772         mType = readHalf();
773 
774         int e_machine = readHalf();
775         if (e_machine != EM_386
776                 && e_machine != EM_X86_64
777                 && e_machine != EM_AARCH64
778                 && e_machine != EM_ARM
779                 && e_machine != EM_RISCV
780                 && e_machine != EM_QDSP6) {
781             throw new IOException("Invalid ELF e_machine: " + e_machine + ": " + mPath);
782         }
783 
784         // AbiTest relies on us rejecting any unsupported combinations.
785         if ((e_machine == EM_386 && elfClass != ELFCLASS32)
786                 || (e_machine == EM_X86_64 && elfClass != ELFCLASS64)
787                 || (e_machine == EM_AARCH64 && elfClass != ELFCLASS64)
788                 || (e_machine == EM_ARM && elfClass != ELFCLASS32)
789                 || (e_machine == EM_QDSP6 && elfClass != ELFCLASS32)) {
790             throw new IOException(
791                     "Invalid e_machine/EI_CLASS ELF combination: "
792                             + e_machine
793                             + "/"
794                             + elfClass
795                             + ": "
796                             + mPath);
797         }
798 
799         mMachine = e_machine;
800         long e_version = readWord();
801         if (e_version != EV_CURRENT) {
802             throw new IOException("Invalid e_version: " + e_version + ": " + mPath);
803         }
804 
805         long e_entry = readAddr();
806 
807         long ph_off = readOff();
808         long sh_off = readOff();
809 
810         long e_flags = readWord();
811         int e_ehsize = readHalf();
812         int e_phentsize = readHalf();
813         int e_phnum = readHalf();
814         int e_shentsize = readHalf();
815         int e_shnum = readHalf();
816         int e_shstrndx = readHalf();
817 
818         readSectionHeaders(sh_off, e_shnum, e_shentsize, e_shstrndx);
819         readProgramHeaders(ph_off, e_phnum, e_phentsize);
820     }
821 
readSectionHeaders(long sh_off, int e_shnum, int e_shentsize, int e_shstrndx)822     private void readSectionHeaders(long sh_off, int e_shnum, int e_shentsize, int e_shstrndx)
823             throws IOException {
824         // Read the Section Header String Table offset first.
825         {
826             mFile.seek(sh_off + e_shstrndx * e_shentsize);
827 
828             long sh_name = readWord();
829             long sh_type = readWord();
830             long sh_flags = readX(mAddrSize);
831             long sh_addr = readAddr();
832             long sh_offset = readOff();
833             long sh_size = readX(mAddrSize);
834             // ...
835 
836             if (sh_type == SHT_STRTAB) {
837                 mShStrTabOffset = sh_offset;
838                 mShStrTabSize = sh_size;
839             }
840         }
841 
842         for (int i = 0; i < e_shnum; ++i) {
843             // Don't bother to re-read the Section Header StrTab.
844             if (i == e_shstrndx) {
845                 continue;
846             }
847 
848             mFile.seek(sh_off + i * e_shentsize);
849 
850             long sh_name = readWord();
851             long sh_type = readWord();
852             long sh_flags = readX(mAddrSize);
853             long sh_addr = readAddr();
854             long sh_offset = readOff();
855             long sh_size = readX(mAddrSize);
856             long sh_link = readWord();
857             long sh_info = readWord();
858             long sh_addralign = readX(mAddrSize);
859             ;
860             long sh_entsize = readX(mAddrSize);
861             ;
862 
863             if (sh_type == SHT_SYMTAB || sh_type == SHT_DYNSYM) {
864                 final String symTabName = readShStrTabEntry(sh_name);
865                 if (".symtab".equals(symTabName)) {
866                     mSymTabOffset = sh_offset;
867                     mSymTabSize = sh_size;
868                     mSymEntCnt = (int) (sh_size / sh_entsize);
869                 } else if (".dynsym".equals(symTabName)) {
870                     mDynSymOffset = sh_offset;
871                     mDynSymSize = sh_size;
872                     mDynSymEntCnt = (int) (sh_size / sh_entsize);
873                 }
874                 System.out.println(
875                         String.format(
876                                 "%s, %d, %d, %d, %d, %d",
877                                 symTabName, sh_offset, sh_size, sh_link, sh_info, sh_entsize));
878             } else if (sh_type == SHT_STRTAB) {
879                 final String strTabName = readShStrTabEntry(sh_name);
880                 if (".strtab".equals(strTabName)) {
881                     mStrTabOffset = sh_offset;
882                     mStrTabSize = sh_size;
883                     System.out.println(
884                             String.format(
885                                     "%s, %d, %d, %d, %d",
886                                     strTabName, sh_offset, sh_size, sh_link, sh_info));
887                 } else if (".dynstr".equals(strTabName)) {
888                     mDynStrOffset = sh_offset;
889                     mDynStrSize = sh_size;
890                     System.out.println(
891                             String.format(
892                                     "%s, %d, %d, %d, %d",
893                                     strTabName, sh_offset, sh_size, sh_link, sh_info));
894                 }
895             } else if (sh_type == SHT_DYNAMIC) {
896                 mIsDynamic = true;
897                 final String strTabName = readShStrTabEntry(sh_name);
898                 mDynamicTabOffset = sh_offset;
899                 mDynamicTabSize = sh_size;
900                 System.out.println(
901                         String.format(
902                                 "%s, %d, %d, %d, %d",
903                                 strTabName, sh_offset, sh_size, sh_link, sh_info));
904             } else if (sh_type == SHT_GNU_VERSYM) {
905                 final String strTabName = readShStrTabEntry(sh_name);
906                 if (".gnu.version".equals(strTabName)) {
907                     mVerSymTabOffset = sh_offset;
908                     mVerSymTabSize = sh_size;
909                 }
910                 System.out.println(
911                         String.format(
912                                 "%s, %d, %d, %d, %d",
913                                 strTabName, sh_offset, sh_size, sh_link, sh_info));
914             } else if (sh_type == SHT_GNU_VERNEED) {
915                 final String strTabName = readShStrTabEntry(sh_name);
916                 if (".gnu.version_r".equals(strTabName)) {
917                     mVerNeedTabOffset = sh_offset;
918                     mVerNeedTabSize = sh_size;
919                     mVerNeedEntryCnt = (int) sh_info;
920                 }
921                 System.out.println(
922                         String.format(
923                                 "%s, %d, %d, %d, %d",
924                                 strTabName, sh_offset, sh_size, sh_link, sh_info));
925             } else if (sh_type == SHT_GNU_VERDEF) {
926                 final String strTabName = readShStrTabEntry(sh_name);
927                 if (".gnu.version_d".equals(strTabName)) {
928                     mVerDefTabOffset = sh_offset;
929                     mVerDefTabSize = sh_size;
930                     mVerDefEntryCnt = (int) sh_info;
931                 }
932                 System.out.println(
933                         String.format(
934                                 "%s, %d, %d, %d, %d",
935                                 strTabName, sh_offset, sh_size, sh_link, sh_info));
936             } else if (sh_type == SHT_PROGBITS) {
937                 final String strTabName = readShStrTabEntry(sh_name);
938                 if (".rodata".equals(strTabName)) {
939                     mHasRodata = true;
940                     mRodataOffset = sh_offset;
941                     mRodataSize = (int) sh_size;
942                 }
943                 System.out.println(
944                         String.format(
945                                 "%s, %d, %d, %d, %d",
946                                 strTabName, sh_offset, sh_size, sh_link, sh_info));
947             }
948         }
949     }
950 
readProgramHeaders(long ph_off, int e_phnum, int e_phentsize)951     private void readProgramHeaders(long ph_off, int e_phnum, int e_phentsize) throws IOException {
952         for (int i = 0; i < e_phnum; ++i) {
953             mFile.seek(ph_off + i * e_phentsize);
954 
955             long p_type = readWord();
956             if (p_type == PT_LOAD) {
957                 if (mAddrSize == 8) {
958                     // Only in Elf64_phdr; in Elf32_phdr p_flags is at the end.
959                     long p_flags = readWord();
960                 }
961                 long p_offset = readOff();
962                 long p_vaddr = readAddr();
963                 // ...
964 
965                 if (p_vaddr == 0) {
966                     mIsPIE = true;
967                 }
968             }
969         }
970     }
971 
readSymbolTable( Symbol[] symArr, boolean isDynSym, long symStrOffset, long symStrSize, long tableOffset, long tableSize)972     private HashMap<String, Symbol> readSymbolTable(
973             Symbol[] symArr,
974             boolean isDynSym,
975             long symStrOffset,
976             long symStrSize,
977             long tableOffset,
978             long tableSize)
979             throws IOException {
980         HashMap<String, Symbol> result = new HashMap<String, Symbol>();
981         mFile.seek(tableOffset);
982         int i = 0;
983         while (mFile.getFilePointer() < tableOffset + tableSize) {
984             long st_name = readWord();
985             int st_info;
986             int st_shndx;
987             long st_value;
988             long st_size;
989             int st_other;
990             if (mAddrSize == 8) {
991                 st_info = readByte();
992                 st_other = readByte();
993                 st_shndx = readHalf();
994                 st_value = readAddr();
995                 st_size = readX(mAddrSize);
996             } else {
997                 st_value = readAddr();
998                 st_size = readWord();
999                 st_info = readByte();
1000                 st_other = readByte();
1001                 st_shndx = readHalf();
1002             }
1003 
1004             String symName;
1005             if (st_name == 0) {
1006                 symName = "";
1007             } else {
1008                 symName = readStrTabEntry(symStrOffset, symStrSize, st_name);
1009             }
1010 
1011             Symbol sym = new Symbol(symName, st_info, st_shndx, st_value, st_size, st_other);
1012             if (!symName.equals("")) {
1013                 result.put(symName, sym);
1014             }
1015             if (isDynSym) {
1016                 if (mVerNeedEntryCnt > 0) {
1017                     if (sym.type == Symbol.STT_NOTYPE) {
1018                         sym.mVerNeed = mVerNeedArr[0];
1019                     } else {
1020                         sym.mVerNeed = getVerNeed(mVerSym[i]);
1021                     }
1022                 } else if (mVerDefEntryCnt > 0) {
1023                     sym.mVerDef = mVerDefArr[mVerSym[i]];
1024                 }
1025             }
1026             symArr[i] = sym;
1027             i++;
1028         }
1029         System.out.println(
1030                 String.format(
1031                         "Info readSymbolTable: %s, isDynSym %b, symbol# %d",
1032                         mPath, isDynSym, symArr.length));
1033         return result;
1034     }
1035 
readShStrTabEntry(long strOffset)1036     private String readShStrTabEntry(long strOffset) throws IOException {
1037         if (mShStrTabOffset == 0 || strOffset < 0 || strOffset >= mShStrTabSize) {
1038             return null;
1039         }
1040         return readString(mShStrTabOffset + strOffset);
1041     }
1042 
readStrTabEntry(long tableOffset, long tableSize, long strOffset)1043     private String readStrTabEntry(long tableOffset, long tableSize, long strOffset)
1044             throws IOException {
1045         if (tableOffset == 0 || strOffset < 0 || strOffset >= tableSize) {
1046             return null;
1047         }
1048         return readString(tableOffset + strOffset);
1049     }
1050 
readDynStrTabEntry(long strOffset)1051     private String readDynStrTabEntry(long strOffset) throws IOException {
1052         if (mDynStrOffset == 0 || strOffset < 0 || strOffset >= mDynStrSize) {
1053             return null;
1054         }
1055         return readString(mDynStrOffset + strOffset);
1056     }
1057 
getVerSym()1058     private int[] getVerSym() throws IOException {
1059         if (mVerSym == null) {
1060             mFile.seek(mVerSymTabOffset);
1061             int cnt = (int) mVerSymTabSize / 2;
1062             mVerSym = new int[cnt];
1063             for (int i = 0; i < cnt; i++) {
1064                 mVerSym[i] = readHalf();
1065                 //System.out.println(String.format("%d, %d", i, mVerSym[i]));
1066             }
1067         }
1068         return mVerSym;
1069     }
1070 
getVerNeed(int ndx)1071     public VerNeed getVerNeed(int ndx) throws IOException {
1072         // vna_other Contains version index unique for the file which is used in the version symbol table.
1073         if (ndx < 2) {
1074             return this.mVerNeedArr[ndx];
1075         }
1076 
1077         for (int i = 2; i < this.mVerNeedEntryCnt + 2; i++) {
1078             for (int j = 0; j < this.mVerNeedArr[i].vn_cnt; j++) {
1079                 if (this.mVerNeedArr[i].vn_vernaux[j].vna_other == ndx) {
1080                     return this.mVerNeedArr[i];
1081                 }
1082             }
1083         }
1084         System.out.println(String.format("no VerNeed found: %d", ndx));
1085         return null;
1086     }
1087 
getVerNeedArr()1088     private VerNeed[] getVerNeedArr() throws IOException {
1089         if (mVerNeedArr == null) {
1090             mVerNeedArr = new VerNeed[mVerNeedEntryCnt + 2];
1091 
1092             // SHT_GNU_versym 0: local
1093             mVerNeedArr[0] = new VerNeed("*local*", "*local*", 0);
1094             // HT_GNU_versym 1: global
1095             mVerNeedArr[1] = new VerNeed("*global*", "*global*", 1);
1096 
1097             long idx = mVerNeedTabOffset;
1098             for (int i = 2; i < mVerNeedEntryCnt + 2; i++) {
1099                 mFile.seek(idx);
1100                 mVerNeedArr[i] =
1101                         new VerNeed(readHalf(), readHalf(), readWord(), readWord(), readWord());
1102                 mVerNeedArr[i].vn_file_name = readDynStrTabEntry(mVerNeedArr[i].vn_file).toLowerCase();
1103 
1104                 mVerNeedArr[i].vn_vernaux = new VerNAux[mVerNeedArr[i].vn_cnt];
1105                 long idxAux = idx + mVerNeedArr[i].vn_aux;
1106                 for (int j = 0; j < mVerNeedArr[i].vn_cnt; j++) {
1107                     mFile.seek(idxAux);
1108                     mVerNeedArr[i].vn_vernaux[j] =
1109                             new VerNAux(readWord(), readHalf(), readHalf(), readWord(), readWord());
1110                     mVerNeedArr[i].vn_vernaux[j].vna_lib_name =
1111                             readDynStrTabEntry(mVerNeedArr[i].vn_vernaux[j].vna_name);
1112                     idxAux += mVerNeedArr[i].vn_vernaux[j].vna_next;
1113                 }
1114                 idx += mVerNeedArr[i].vn_next;
1115                 System.out.println(mVerNeedArr[i]);
1116             }
1117         }
1118 
1119         return mVerNeedArr;
1120     }
1121 
getVerDef()1122     private VerDef[] getVerDef() throws IOException {
1123         if (mVerDefArr == null) {
1124             mVerDefArr = new VerDef[mVerDefEntryCnt + 2];
1125 
1126             // SHT_GNU_versym 0: local
1127             mVerDefArr[0] = new VerDef("*local*");
1128             // HT_GNU_versym 1: global
1129             mVerDefArr[1] = new VerDef("*global*");
1130 
1131             long idx = mVerDefTabOffset;
1132             for (int i = 2; i < mVerDefEntryCnt + 2; i++) {
1133                 mFile.seek(idx);
1134                 mVerDefArr[i] =
1135                         new VerDef(
1136                                 readHalf(),
1137                                 readHalf(),
1138                                 readHalf(),
1139                                 readHalf(),
1140                                 readWord(),
1141                                 readWord(),
1142                                 readWord());
1143 
1144                 mVerDefArr[i].vd_verdaux = new VerDAux[mVerDefArr[i].vd_cnt];
1145                 long idxAux = idx + mVerDefArr[i].vd_aux;
1146                 for (int j = 0; j < mVerDefArr[i].vd_cnt; j++) {
1147                     mFile.seek(idxAux);
1148                     mVerDefArr[i].vd_verdaux[j] = new VerDAux(readWord(), readWord());
1149                     mVerDefArr[i].vd_verdaux[j].vda_lib_name =
1150                             readDynStrTabEntry(mVerDefArr[i].vd_verdaux[j].vda_name).toLowerCase();
1151                     idxAux += mVerDefArr[i].vd_verdaux[j].vda_next;
1152                 }
1153                 idx += mVerDefArr[i].vd_next;
1154                 System.out.println(mVerDefArr[i]);
1155             }
1156         }
1157         return mVerDefArr;
1158     }
1159 
readHalf()1160     private int readHalf() throws IOException {
1161         return (int) readX(2);
1162     }
1163 
readWord()1164     private long readWord() throws IOException {
1165         return readX(4);
1166     }
1167 
readOff()1168     private long readOff() throws IOException {
1169         return readX(mAddrSize);
1170     }
1171 
readAddr()1172     private long readAddr() throws IOException {
1173         return readX(mAddrSize);
1174     }
1175 
readX(int byteCount)1176     private long readX(int byteCount) throws IOException {
1177         mFile.readFully(mBuffer, 0, byteCount);
1178 
1179         int answer = 0;
1180         if (mEndian == ELFDATA2LSB) {
1181             for (int i = byteCount - 1; i >= 0; i--) {
1182                 answer = (answer << 8) | (mBuffer[i] & 0xff);
1183             }
1184         } else {
1185             final int N = byteCount - 1;
1186             for (int i = 0; i <= N; ++i) {
1187                 answer = (answer << 8) | (mBuffer[i] & 0xff);
1188             }
1189         }
1190 
1191         return answer;
1192     }
1193 
readString(long offset)1194     private String readString(long offset) throws IOException {
1195         long originalOffset = mFile.getFilePointer();
1196         mFile.seek(offset);
1197         mFile.readFully(mBuffer, 0, (int) Math.min(mBuffer.length, mFile.length() - offset));
1198         mFile.seek(originalOffset);
1199 
1200         for (int i = 0; i < mBuffer.length; ++i) {
1201             if (mBuffer[i] == 0) {
1202                 return new String(mBuffer, 0, i);
1203             }
1204         }
1205 
1206         return null;
1207     }
1208 
readByte()1209     private int readByte() throws IOException {
1210         return mFile.read() & 0xff;
1211     }
1212 
1213     /** Gets the symbol by name. */
1214     @CanIgnoreReturnValue
getSymbol(String name)1215     public Symbol getSymbol(String name) {
1216         if (mSymbols == null) {
1217             try {
1218                 mSymArr = new Symbol[mSymEntCnt];
1219                 mSymbols =
1220                         readSymbolTable(
1221                                 mSymArr,
1222                                 false,
1223                                 mStrTabOffset,
1224                                 mStrTabSize,
1225                                 mSymTabOffset,
1226                                 mSymTabSize);
1227             } catch (IOException e) {
1228                 return null;
1229             }
1230         }
1231         return mSymbols.get(name);
1232     }
1233 
1234     /** Gets a dynamic symbol by name. */
1235     @CanIgnoreReturnValue
getDynamicSymbol(String name)1236     public Symbol getDynamicSymbol(String name) throws IOException {
1237         if (mDynamicSymbols == null) {
1238             try {
1239                 int[] verSmyArr = this.getVerSym();
1240                 VerNeed[] verNeedArr = this.getVerNeedArr();
1241                 VerDef[] verDefArr = this.getVerDef();
1242                 mDynSymArr = new Symbol[mDynSymEntCnt];
1243                 mDynamicSymbols =
1244                         readSymbolTable(
1245                                 mDynSymArr,
1246                                 true,
1247                                 mDynStrOffset,
1248                                 mDynStrSize,
1249                                 mDynSymOffset,
1250                                 mDynSymSize);
1251             } catch (IOException e) {
1252                 return null;
1253             }
1254         }
1255         return mDynamicSymbols.get(name);
1256     }
1257 
1258     // Get Dynamic Linking Dependency List
getDynamicDependencies()1259     public List<String> getDynamicDependencies() throws IOException {
1260         List<String> result = new ArrayList<>();
1261         for (DynamicEntry entry : getDynamicList()) {
1262             if (entry.isNeeded()) {
1263                 result.add(readDynStr(entry.getValue()));
1264             }
1265         }
1266         return result;
1267     }
1268 
getDynamicList()1269     private List<DynamicEntry> getDynamicList() throws IOException {
1270         if (mDynamicArr == null) {
1271             int entryNo = 0;
1272             mDynamicArr = new ArrayList<>();
1273             mFile.seek(mDynamicTabOffset);
1274             System.out.println(
1275                     String.format(
1276                             "mDynamicTabOffset 0x%x, mDynamicTabSize %d",
1277                             mDynamicTabOffset, mDynamicTabSize));
1278             while (true) {
1279                 long tag = readX(mAddrSize);
1280                 long value = readX(mAddrSize);
1281                 // System.out.println(String.format("%d: 0x%x, %d", entryNo, tag, value));
1282                 mDynamicArr.add(new DynamicEntry(tag, value));
1283                 if (tag == 0) {
1284                     break;
1285                 }
1286                 entryNo++;
1287             }
1288         }
1289         return mDynamicArr;
1290     }
1291 
readDynStr(long strOffset)1292     private String readDynStr(long strOffset) throws IOException {
1293         int offset = (int) (strOffset & 0xFFFFFFFF);
1294         if (mDynStrOffset == 0 || offset < 0 || offset >= mDynStrSize) {
1295             System.err.println(
1296                     String.format(
1297                             "err mDynStrOffset: %d,  mDynStrSize: %d, offset: %d",
1298                             mDynStrOffset, mDynStrSize, offset));
1299             return String.format("%d", offset);
1300         }
1301         return readString(mDynStrOffset + offset);
1302     }
1303 
1304     /**
1305      * Gets a list of string from .rodata section
1306      *
1307      * @return a String list .rodata section
1308      */
getRoStrings()1309     public List<String> getRoStrings() throws IOException {
1310         if (mRoStrings == null) {
1311             mRoStrings = new ArrayList<>();
1312             byte[] byteArr = getRoData();
1313             if (byteArr != null) {
1314                 int strOffset = 0;
1315                 for (int i = 0; i < mRodataSize; i++) {
1316                     if (byteArr[i] == 0) {
1317                         // skip null string
1318                         if (i != strOffset) {
1319                             String str = new String(byteArr, strOffset, i - strOffset);
1320                             mRoStrings.add(str);
1321                         }
1322                         strOffset = i + 1;
1323                     }
1324                 }
1325             }
1326         }
1327         return mRoStrings;
1328     }
1329 
1330     /**
1331      * Gets .rodata section
1332      *
1333      * @return byte [] of .rodata or null if there is none
1334      */
getRoData()1335     public byte[] getRoData() throws IOException {
1336         if (mHasRodata && mRoData == null) {
1337             mRoData = new byte[mRodataSize];
1338             mFile.seek(mRodataOffset);
1339             mFile.readFully(mRoData);
1340         }
1341 
1342         return mRoData;
1343     }
1344 }
1345