blob: f8ae3c743ff5736a486db17c6346716852b31a0a [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
Patrick Williams92b42cb2022-09-03 06:53:57 -05002# Copyright OpenEmbedded Contributors
3#
Brad Bishopc342db32019-05-15 21:57:59 -04004# SPDX-License-Identifier: GPL-2.0-only
5#
6
Brad Bishop37a0e4d2017-12-04 01:01:44 -05007import os, struct, mmap
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05008
9class NotELFFileError(Exception):
10 pass
11
Patrick Williamsc124f4f2015-09-15 14:41:29 -050012class ELFFile:
13 EI_NIDENT = 16
14
15 EI_CLASS = 4
16 EI_DATA = 5
17 EI_VERSION = 6
18 EI_OSABI = 7
19 EI_ABIVERSION = 8
20
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050021 E_MACHINE = 0x12
22
Patrick Williamsc124f4f2015-09-15 14:41:29 -050023 # possible values for EI_CLASS
24 ELFCLASSNONE = 0
25 ELFCLASS32 = 1
26 ELFCLASS64 = 2
27
28 # possible value for EI_VERSION
29 EV_CURRENT = 1
30
31 # possible values for EI_DATA
Brad Bishop37a0e4d2017-12-04 01:01:44 -050032 EI_DATA_NONE = 0
33 EI_DATA_LSB = 1
34 EI_DATA_MSB = 2
Patrick Williamsc124f4f2015-09-15 14:41:29 -050035
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050036 PT_INTERP = 3
37
Patrick Williamsc124f4f2015-09-15 14:41:29 -050038 def my_assert(self, expectation, result):
39 if not expectation == result:
40 #print "'%x','%x' %s" % (ord(expectation), ord(result), self.name)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050041 raise NotELFFileError("%s is not an ELF" % self.name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050042
Brad Bishop37a0e4d2017-12-04 01:01:44 -050043 def __init__(self, name):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050044 self.name = name
Patrick Williamsc124f4f2015-09-15 14:41:29 -050045 self.objdump_output = {}
Andrew Geissler82c905d2020-04-13 13:39:40 -050046 self.data = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -050047
Brad Bishop37a0e4d2017-12-04 01:01:44 -050048 # Context Manager functions to close the mmap explicitly
49 def __enter__(self):
50 return self
51
52 def __exit__(self, exc_type, exc_value, traceback):
Andrew Geissler7e0e3c02022-02-25 20:34:39 +000053 self.close()
54
55 def close(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -050056 if self.data:
57 self.data.close()
Brad Bishop37a0e4d2017-12-04 01:01:44 -050058
Patrick Williamsc124f4f2015-09-15 14:41:29 -050059 def open(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060060 with open(self.name, "rb") as f:
Brad Bishop37a0e4d2017-12-04 01:01:44 -050061 try:
62 self.data = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
63 except ValueError:
64 # This means the file is empty
65 raise NotELFFileError("%s is empty" % self.name)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050066
Brad Bishop37a0e4d2017-12-04 01:01:44 -050067 # Check the file has the minimum number of ELF table entries
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050068 if len(self.data) < ELFFile.EI_NIDENT + 4:
69 raise NotELFFileError("%s is not an ELF" % self.name)
70
Brad Bishop37a0e4d2017-12-04 01:01:44 -050071 # ELF header
Patrick Williamsc0f7c042017-02-23 20:41:17 -060072 self.my_assert(self.data[0], 0x7f)
73 self.my_assert(self.data[1], ord('E'))
74 self.my_assert(self.data[2], ord('L'))
75 self.my_assert(self.data[3], ord('F'))
Brad Bishop37a0e4d2017-12-04 01:01:44 -050076 if self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS32:
77 self.bits = 32
78 elif self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS64:
79 self.bits = 64
Patrick Williamsc124f4f2015-09-15 14:41:29 -050080 else:
Brad Bishop37a0e4d2017-12-04 01:01:44 -050081 # Not 32-bit or 64.. lets assert
82 raise NotELFFileError("ELF but not 32 or 64 bit.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -060083 self.my_assert(self.data[ELFFile.EI_VERSION], ELFFile.EV_CURRENT)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050084
Brad Bishop37a0e4d2017-12-04 01:01:44 -050085 self.endian = self.data[ELFFile.EI_DATA]
86 if self.endian not in (ELFFile.EI_DATA_LSB, ELFFile.EI_DATA_MSB):
87 raise NotELFFileError("Unexpected EI_DATA %x" % self.endian)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050088
89 def osAbi(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060090 return self.data[ELFFile.EI_OSABI]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050091
92 def abiVersion(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060093 return self.data[ELFFile.EI_ABIVERSION]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050094
95 def abiSize(self):
96 return self.bits
97
98 def isLittleEndian(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -050099 return self.endian == ELFFile.EI_DATA_LSB
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500100
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500101 def isBigEndian(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500102 return self.endian == ELFFile.EI_DATA_MSB
103
104 def getStructEndian(self):
105 return {ELFFile.EI_DATA_LSB: "<",
106 ELFFile.EI_DATA_MSB: ">"}[self.endian]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500107
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500108 def getShort(self, offset):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500109 return struct.unpack_from(self.getStructEndian() + "H", self.data, offset)[0]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500110
111 def getWord(self, offset):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500112 return struct.unpack_from(self.getStructEndian() + "i", self.data, offset)[0]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500113
114 def isDynamic(self):
115 """
116 Return True if there is a .interp segment (therefore dynamically
117 linked), otherwise False (statically linked).
118 """
119 offset = self.getWord(self.bits == 32 and 0x1C or 0x20)
120 size = self.getShort(self.bits == 32 and 0x2A or 0x36)
121 count = self.getShort(self.bits == 32 and 0x2C or 0x38)
122
123 for i in range(0, count):
124 p_type = self.getWord(offset + i * size)
125 if p_type == ELFFile.PT_INTERP:
126 return True
127 return False
128
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500129 def machine(self):
130 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500131 We know the endian stored in self.endian and we
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500132 know the position
133 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500134 return self.getShort(ELFFile.E_MACHINE)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500135
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000136 def set_objdump(self, cmd, output):
137 self.objdump_output[cmd] = output
138
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139 def run_objdump(self, cmd, d):
140 import bb.process
141 import sys
142
143 if cmd in self.objdump_output:
144 return self.objdump_output[cmd]
145
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500146 objdump = d.getVar('OBJDUMP')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147
148 env = os.environ.copy()
149 env["LC_ALL"] = "C"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500150 env["PATH"] = d.getVar('PATH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500151
152 try:
153 bb.note("%s %s %s" % (objdump, cmd, self.name))
154 self.objdump_output[cmd] = bb.process.run([objdump, cmd, self.name], env=env, shell=False)[0]
155 return self.objdump_output[cmd]
156 except Exception as e:
157 bb.note("%s %s %s failed: %s" % (objdump, cmd, self.name, e))
158 return ""
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500159
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600160def elf_machine_to_string(machine):
161 """
162 Return the name of a given ELF e_machine field or the hex value as a string
163 if it isn't recognised.
164 """
165 try:
166 return {
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600167 0x00: "Unset",
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600168 0x02: "SPARC",
169 0x03: "x86",
170 0x08: "MIPS",
171 0x14: "PowerPC",
172 0x28: "ARM",
173 0x2A: "SuperH",
174 0x32: "IA-64",
175 0x3E: "x86-64",
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800176 0xB7: "AArch64",
177 0xF7: "BPF"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600178 }[machine]
179 except:
180 return "Unknown (%s)" % repr(machine)
181
Andrew Geisslereff27472021-10-29 15:35:00 -0500182def write_error(type, error, d):
183 logfile = d.getVar('QA_LOGFILE')
184 if logfile:
185 p = d.getVar('P')
186 with open(logfile, "a+") as f:
187 f.write("%s: %s [%s]\n" % (p, error, type))
188
189def handle_error(error_class, error_msg, d):
190 if error_class in (d.getVar("ERROR_QA") or "").split():
191 write_error(error_class, error_msg, d)
192 bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
193 d.setVar("QA_ERRORS_FOUND", "True")
194 return False
195 elif error_class in (d.getVar("WARN_QA") or "").split():
196 write_error(error_class, error_msg, d)
197 bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
198 else:
199 bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
200 return True
201
202def add_message(messages, section, new_msg):
203 if section not in messages:
204 messages[section] = new_msg
205 else:
206 messages[section] = messages[section] + "\n" + new_msg
207
208def exit_with_message_if_errors(message, d):
209 qa_fatal_errors = bb.utils.to_boolean(d.getVar("QA_ERRORS_FOUND"), False)
210 if qa_fatal_errors:
211 bb.fatal(message)
212
213def exit_if_errors(d):
214 exit_with_message_if_errors("Fatal QA errors were found, failing task.", d)
215
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600216def check_upstream_status(fullpath):
217 import re
218 kinda_status_re = re.compile(r"^.*upstream.*status.*$", re.IGNORECASE | re.MULTILINE)
Andrew Geissler220dafd2023-10-04 10:18:08 -0500219 strict_status_re = re.compile(r"^Upstream-Status: (Pending|Submitted|Denied|Inappropriate|Backport|Inactive-Upstream)( .+)?$", re.MULTILINE)
220 guidelines = "https://docs.yoctoproject.org/contributor-guide/recipe-style-guide.html#patch-upstream-status"
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600221
222 with open(fullpath, encoding='utf-8', errors='ignore') as f:
223 file_content = f.read()
224 match_kinda = kinda_status_re.search(file_content)
225 match_strict = strict_status_re.search(file_content)
226
227 if not match_strict:
228 if match_kinda:
229 return "Malformed Upstream-Status in patch\n%s\nPlease correct according to %s :\n%s" % (fullpath, guidelines, match_kinda.group(0))
230 else:
231 return "Missing Upstream-Status in patch\n%s\nPlease add according to %s ." % (fullpath, guidelines)
232
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500233if __name__ == "__main__":
234 import sys
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500235
236 with ELFFile(sys.argv[1]) as elf:
237 elf.open()
238 print(elf.isDynamic())