blob: 21066c4dc3b3cfceace83cc7858a35e123c05caf [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
2# SPDX-License-Identifier: GPL-2.0-only
3#
4
Brad Bishop37a0e4d2017-12-04 01:01:44 -05005import os, struct, mmap
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05006
7class NotELFFileError(Exception):
8 pass
9
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010class ELFFile:
11 EI_NIDENT = 16
12
13 EI_CLASS = 4
14 EI_DATA = 5
15 EI_VERSION = 6
16 EI_OSABI = 7
17 EI_ABIVERSION = 8
18
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050019 E_MACHINE = 0x12
20
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021 # possible values for EI_CLASS
22 ELFCLASSNONE = 0
23 ELFCLASS32 = 1
24 ELFCLASS64 = 2
25
26 # possible value for EI_VERSION
27 EV_CURRENT = 1
28
29 # possible values for EI_DATA
Brad Bishop37a0e4d2017-12-04 01:01:44 -050030 EI_DATA_NONE = 0
31 EI_DATA_LSB = 1
32 EI_DATA_MSB = 2
Patrick Williamsc124f4f2015-09-15 14:41:29 -050033
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050034 PT_INTERP = 3
35
Patrick Williamsc124f4f2015-09-15 14:41:29 -050036 def my_assert(self, expectation, result):
37 if not expectation == result:
38 #print "'%x','%x' %s" % (ord(expectation), ord(result), self.name)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050039 raise NotELFFileError("%s is not an ELF" % self.name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050040
Brad Bishop37a0e4d2017-12-04 01:01:44 -050041 def __init__(self, name):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050042 self.name = name
Patrick Williamsc124f4f2015-09-15 14:41:29 -050043 self.objdump_output = {}
44
Brad Bishop37a0e4d2017-12-04 01:01:44 -050045 # Context Manager functions to close the mmap explicitly
46 def __enter__(self):
47 return self
48
49 def __exit__(self, exc_type, exc_value, traceback):
50 self.data.close()
51
Patrick Williamsc124f4f2015-09-15 14:41:29 -050052 def open(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060053 with open(self.name, "rb") as f:
Brad Bishop37a0e4d2017-12-04 01:01:44 -050054 try:
55 self.data = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
56 except ValueError:
57 # This means the file is empty
58 raise NotELFFileError("%s is empty" % self.name)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050059
Brad Bishop37a0e4d2017-12-04 01:01:44 -050060 # Check the file has the minimum number of ELF table entries
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050061 if len(self.data) < ELFFile.EI_NIDENT + 4:
62 raise NotELFFileError("%s is not an ELF" % self.name)
63
Brad Bishop37a0e4d2017-12-04 01:01:44 -050064 # ELF header
Patrick Williamsc0f7c042017-02-23 20:41:17 -060065 self.my_assert(self.data[0], 0x7f)
66 self.my_assert(self.data[1], ord('E'))
67 self.my_assert(self.data[2], ord('L'))
68 self.my_assert(self.data[3], ord('F'))
Brad Bishop37a0e4d2017-12-04 01:01:44 -050069 if self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS32:
70 self.bits = 32
71 elif self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS64:
72 self.bits = 64
Patrick Williamsc124f4f2015-09-15 14:41:29 -050073 else:
Brad Bishop37a0e4d2017-12-04 01:01:44 -050074 # Not 32-bit or 64.. lets assert
75 raise NotELFFileError("ELF but not 32 or 64 bit.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -060076 self.my_assert(self.data[ELFFile.EI_VERSION], ELFFile.EV_CURRENT)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050077
Brad Bishop37a0e4d2017-12-04 01:01:44 -050078 self.endian = self.data[ELFFile.EI_DATA]
79 if self.endian not in (ELFFile.EI_DATA_LSB, ELFFile.EI_DATA_MSB):
80 raise NotELFFileError("Unexpected EI_DATA %x" % self.endian)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050081
82 def osAbi(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060083 return self.data[ELFFile.EI_OSABI]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050084
85 def abiVersion(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060086 return self.data[ELFFile.EI_ABIVERSION]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050087
88 def abiSize(self):
89 return self.bits
90
91 def isLittleEndian(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -050092 return self.endian == ELFFile.EI_DATA_LSB
Patrick Williamsc124f4f2015-09-15 14:41:29 -050093
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050094 def isBigEndian(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -050095 return self.endian == ELFFile.EI_DATA_MSB
96
97 def getStructEndian(self):
98 return {ELFFile.EI_DATA_LSB: "<",
99 ELFFile.EI_DATA_MSB: ">"}[self.endian]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500100
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500101 def getShort(self, offset):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500102 return struct.unpack_from(self.getStructEndian() + "H", self.data, offset)[0]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500103
104 def getWord(self, offset):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500105 return struct.unpack_from(self.getStructEndian() + "i", self.data, offset)[0]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500106
107 def isDynamic(self):
108 """
109 Return True if there is a .interp segment (therefore dynamically
110 linked), otherwise False (statically linked).
111 """
112 offset = self.getWord(self.bits == 32 and 0x1C or 0x20)
113 size = self.getShort(self.bits == 32 and 0x2A or 0x36)
114 count = self.getShort(self.bits == 32 and 0x2C or 0x38)
115
116 for i in range(0, count):
117 p_type = self.getWord(offset + i * size)
118 if p_type == ELFFile.PT_INTERP:
119 return True
120 return False
121
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500122 def machine(self):
123 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500124 We know the endian stored in self.endian and we
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500125 know the position
126 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500127 return self.getShort(ELFFile.E_MACHINE)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500128
129 def run_objdump(self, cmd, d):
130 import bb.process
131 import sys
132
133 if cmd in self.objdump_output:
134 return self.objdump_output[cmd]
135
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500136 objdump = d.getVar('OBJDUMP')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500137
138 env = os.environ.copy()
139 env["LC_ALL"] = "C"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500140 env["PATH"] = d.getVar('PATH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500141
142 try:
143 bb.note("%s %s %s" % (objdump, cmd, self.name))
144 self.objdump_output[cmd] = bb.process.run([objdump, cmd, self.name], env=env, shell=False)[0]
145 return self.objdump_output[cmd]
146 except Exception as e:
147 bb.note("%s %s %s failed: %s" % (objdump, cmd, self.name, e))
148 return ""
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500149
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600150def elf_machine_to_string(machine):
151 """
152 Return the name of a given ELF e_machine field or the hex value as a string
153 if it isn't recognised.
154 """
155 try:
156 return {
157 0x02: "SPARC",
158 0x03: "x86",
159 0x08: "MIPS",
160 0x14: "PowerPC",
161 0x28: "ARM",
162 0x2A: "SuperH",
163 0x32: "IA-64",
164 0x3E: "x86-64",
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800165 0xB7: "AArch64",
166 0xF7: "BPF"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600167 }[machine]
168 except:
169 return "Unknown (%s)" % repr(machine)
170
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500171if __name__ == "__main__":
172 import sys
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500173
174 with ELFFile(sys.argv[1]) as elf:
175 elf.open()
176 print(elf.isDynamic())