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