blob: efab7e856454c5953ddc8892cb3fa7c022fdc01a [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 = {}
Andrew Geissler82c905d2020-04-13 13:39:40 -050044 self.data = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -050045
Brad Bishop37a0e4d2017-12-04 01:01:44 -050046 # Context Manager functions to close the mmap explicitly
47 def __enter__(self):
48 return self
49
50 def __exit__(self, exc_type, exc_value, traceback):
Andrew Geissler82c905d2020-04-13 13:39:40 -050051 if self.data:
52 self.data.close()
Brad Bishop37a0e4d2017-12-04 01:01:44 -050053
Patrick Williamsc124f4f2015-09-15 14:41:29 -050054 def open(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060055 with open(self.name, "rb") as f:
Brad Bishop37a0e4d2017-12-04 01:01:44 -050056 try:
57 self.data = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
58 except ValueError:
59 # This means the file is empty
60 raise NotELFFileError("%s is empty" % self.name)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050061
Brad Bishop37a0e4d2017-12-04 01:01:44 -050062 # Check the file has the minimum number of ELF table entries
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050063 if len(self.data) < ELFFile.EI_NIDENT + 4:
64 raise NotELFFileError("%s is not an ELF" % self.name)
65
Brad Bishop37a0e4d2017-12-04 01:01:44 -050066 # ELF header
Patrick Williamsc0f7c042017-02-23 20:41:17 -060067 self.my_assert(self.data[0], 0x7f)
68 self.my_assert(self.data[1], ord('E'))
69 self.my_assert(self.data[2], ord('L'))
70 self.my_assert(self.data[3], ord('F'))
Brad Bishop37a0e4d2017-12-04 01:01:44 -050071 if self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS32:
72 self.bits = 32
73 elif self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS64:
74 self.bits = 64
Patrick Williamsc124f4f2015-09-15 14:41:29 -050075 else:
Brad Bishop37a0e4d2017-12-04 01:01:44 -050076 # Not 32-bit or 64.. lets assert
77 raise NotELFFileError("ELF but not 32 or 64 bit.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -060078 self.my_assert(self.data[ELFFile.EI_VERSION], ELFFile.EV_CURRENT)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050079
Brad Bishop37a0e4d2017-12-04 01:01:44 -050080 self.endian = self.data[ELFFile.EI_DATA]
81 if self.endian not in (ELFFile.EI_DATA_LSB, ELFFile.EI_DATA_MSB):
82 raise NotELFFileError("Unexpected EI_DATA %x" % self.endian)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050083
84 def osAbi(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060085 return self.data[ELFFile.EI_OSABI]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050086
87 def abiVersion(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060088 return self.data[ELFFile.EI_ABIVERSION]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050089
90 def abiSize(self):
91 return self.bits
92
93 def isLittleEndian(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -050094 return self.endian == ELFFile.EI_DATA_LSB
Patrick Williamsc124f4f2015-09-15 14:41:29 -050095
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050096 def isBigEndian(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -050097 return self.endian == ELFFile.EI_DATA_MSB
98
99 def getStructEndian(self):
100 return {ELFFile.EI_DATA_LSB: "<",
101 ELFFile.EI_DATA_MSB: ">"}[self.endian]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500103 def getShort(self, offset):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500104 return struct.unpack_from(self.getStructEndian() + "H", self.data, offset)[0]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500105
106 def getWord(self, offset):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500107 return struct.unpack_from(self.getStructEndian() + "i", self.data, offset)[0]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500108
109 def isDynamic(self):
110 """
111 Return True if there is a .interp segment (therefore dynamically
112 linked), otherwise False (statically linked).
113 """
114 offset = self.getWord(self.bits == 32 and 0x1C or 0x20)
115 size = self.getShort(self.bits == 32 and 0x2A or 0x36)
116 count = self.getShort(self.bits == 32 and 0x2C or 0x38)
117
118 for i in range(0, count):
119 p_type = self.getWord(offset + i * size)
120 if p_type == ELFFile.PT_INTERP:
121 return True
122 return False
123
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500124 def machine(self):
125 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500126 We know the endian stored in self.endian and we
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127 know the position
128 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500129 return self.getShort(ELFFile.E_MACHINE)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500130
131 def run_objdump(self, cmd, d):
132 import bb.process
133 import sys
134
135 if cmd in self.objdump_output:
136 return self.objdump_output[cmd]
137
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500138 objdump = d.getVar('OBJDUMP')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139
140 env = os.environ.copy()
141 env["LC_ALL"] = "C"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500142 env["PATH"] = d.getVar('PATH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500143
144 try:
145 bb.note("%s %s %s" % (objdump, cmd, self.name))
146 self.objdump_output[cmd] = bb.process.run([objdump, cmd, self.name], env=env, shell=False)[0]
147 return self.objdump_output[cmd]
148 except Exception as e:
149 bb.note("%s %s %s failed: %s" % (objdump, cmd, self.name, e))
150 return ""
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500151
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600152def elf_machine_to_string(machine):
153 """
154 Return the name of a given ELF e_machine field or the hex value as a string
155 if it isn't recognised.
156 """
157 try:
158 return {
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600159 0x00: "Unset",
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600160 0x02: "SPARC",
161 0x03: "x86",
162 0x08: "MIPS",
163 0x14: "PowerPC",
164 0x28: "ARM",
165 0x2A: "SuperH",
166 0x32: "IA-64",
167 0x3E: "x86-64",
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800168 0xB7: "AArch64",
169 0xF7: "BPF"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600170 }[machine]
171 except:
172 return "Unknown (%s)" % repr(machine)
173
Andrew Geisslereff27472021-10-29 15:35:00 -0500174def write_error(type, error, d):
175 logfile = d.getVar('QA_LOGFILE')
176 if logfile:
177 p = d.getVar('P')
178 with open(logfile, "a+") as f:
179 f.write("%s: %s [%s]\n" % (p, error, type))
180
181def handle_error(error_class, error_msg, d):
182 if error_class in (d.getVar("ERROR_QA") or "").split():
183 write_error(error_class, error_msg, d)
184 bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
185 d.setVar("QA_ERRORS_FOUND", "True")
186 return False
187 elif error_class in (d.getVar("WARN_QA") or "").split():
188 write_error(error_class, error_msg, d)
189 bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
190 else:
191 bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
192 return True
193
194def add_message(messages, section, new_msg):
195 if section not in messages:
196 messages[section] = new_msg
197 else:
198 messages[section] = messages[section] + "\n" + new_msg
199
200def exit_with_message_if_errors(message, d):
201 qa_fatal_errors = bb.utils.to_boolean(d.getVar("QA_ERRORS_FOUND"), False)
202 if qa_fatal_errors:
203 bb.fatal(message)
204
205def exit_if_errors(d):
206 exit_with_message_if_errors("Fatal QA errors were found, failing task.", d)
207
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500208if __name__ == "__main__":
209 import sys
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500210
211 with ELFFile(sys.argv[1]) as elf:
212 elf.open()
213 print(elf.isDynamic())