1+ /*
2+ * This file is part of AndroidIDE.
3+ *
4+ * AndroidIDE is free software: you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation, either version 3 of the License, or
7+ * (at your option) any later version.
8+ *
9+ * AndroidIDE is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ * GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License
15+ * along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
16+ */
17+
18+ package com.itsaky.androidide.plugins.util
19+
20+ import java.io.File
21+ import java.io.InputStream
22+
23+ /* *
24+ * Utility methods for working with ELF binaries.
25+ *
26+ * @author Akash Yadav
27+ * @see <a href="https://en.wikipedia.org/wiki/Executable_and_Linkable_Format">Executable and Linkable Format</a>
28+ */
29+ object ELFUtils {
30+
31+ /* *
32+ * The magic number of an ELF binary.
33+ */
34+ const val ELF_MAGIC_NUMBER : Int = 0x7F454C46
35+
36+ /* *
37+ * 4-byte magic number of an ELF binary.
38+ */
39+ val ELF_MAGIC_NUMBER_ARR = byteArrayOf(0x7F , 0x45 , 0x4C , 0x46 )
40+
41+ /* *
42+ * `EI_OSABI` of an ELF binary. This enumeration only contains ABIs that are supported by AndroidIDE.
43+ *
44+ * @property abiName The name of CPU ABI.
45+ * @property abi The number representing the ABI (`e_machine`).
46+ */
47+ enum class ElfAbi (val abiName : String , val abi : Byte ) {
48+ ARMV7 (" armeabi-v7a" , 0x28 ),
49+ AARCH64 (" arm64-v8a" , 0xB7 .toByte()),
50+ X86_64 (" x86_64" , 0x3E );
51+
52+ companion object {
53+ fun forName (name : String ) : ElfAbi ? = ElfAbi .values().firstOrNull { it.abiName == name }
54+ fun forAbi (abi : Byte ) : ElfAbi ? = ElfAbi .values().firstOrNull { it.abi == abi }
55+ }
56+ }
57+
58+ /* *
59+ * Check whether the data in given input stream corresponds to an ELF binary.
60+ * This checks if the magic number (first 4 bytes) in the byte array is `0x7F454C46` i.e. `0x7F E L F`.
61+ *
62+ * @return `true` if the file is an ELF binary, `false` otherwise.
63+ */
64+ fun isElf (bytes : ByteArray ) : Boolean {
65+ if (bytes.size < 4 ) {
66+ return false
67+ }
68+
69+ for (i in 0 .. 3 ) {
70+ if (bytes[i] != ELF_MAGIC_NUMBER_ARR [i]) {
71+ return false
72+ }
73+ }
74+
75+ return true
76+ }
77+
78+ /* *
79+ * Check whether the data in given input stream corresponds to an ELF binary.
80+ * This basically checks if the magic number in the input stream is `0x7F454C46` i.e. `0x7F E L F`.
81+ *
82+ * @return `true` if the file is an ELF binary, `false` otherwise.
83+ */
84+ fun isElf (inputStream : InputStream ) : Boolean {
85+ val bytes = ByteArray (4 )
86+ val read = inputStream.read(bytes)
87+ if (read != 4 ) {
88+ return false
89+ }
90+ return isElf(inputStream.readNBytes(4 ))
91+ }
92+
93+ /* *
94+ * Check whether the given file is an ELF binary.
95+ *
96+ * @return `true` if the file is an ELF binary, `false` otherwise.
97+ */
98+ fun isElf (file : File ): Boolean {
99+ return file.inputStream().use { isElf(it) }
100+ }
101+
102+ /* *
103+ * Get the [ElfAbi] for the given file.
104+ *
105+ * @return The [ElfAbi] for the file, or `null` if the file is not an ELF binary.
106+ */
107+ fun getElfAbi (file : File ) : ElfAbi ? {
108+ return file.inputStream().use { getElfAbi(it) }
109+ }
110+
111+ /* *
112+ * Get the [ElfAbi] from the given input stream.
113+ *
114+ * @return The [ElfAbi] for the input stream, or `null` if the input stream does not represent an ELF binary.
115+ */
116+ fun getElfAbi (inputStream : InputStream ) : ElfAbi ? {
117+ val header = ByteArray (20 )
118+ if (inputStream.read(header) < 20 ) {
119+ // incomplete data
120+ return null
121+ }
122+
123+ if (! isElf(header)) {
124+ // not an elf binary
125+ return null
126+ }
127+
128+ return ElfAbi .forAbi(header[18 ])
129+ }
130+ }
0 commit comments