blob: 89acd3ead0ac678faa5ed976bc150faf4c503ff5 [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 Geissler7e0e3c02022-02-25 20:34:39 +000051 self.close()
52
53 def close(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -050054 if self.data:
55 self.data.close()
Brad Bishop37a0e4d2017-12-04 01:01:44 -050056
Patrick Williamsc124f4f2015-09-15 14:41:29 -050057 def open(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060058 with open(self.name, "rb") as f:
Brad Bishop37a0e4d2017-12-04 01:01:44 -050059 try:
60 self.data = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
61 except ValueError:
62 # This means the file is empty
63 raise NotELFFileError("%s is empty" % self.name)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050064
Brad Bishop37a0e4d2017-12-04 01:01:44 -050065 # Check the file has the minimum number of ELF table entries
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050066 if len(self.data) < ELFFile.EI_NIDENT + 4:
67 raise NotELFFileError("%s is not an ELF" % self.name)
68
Brad Bishop37a0e4d2017-12-04 01:01:44 -050069 # ELF header
Patrick Williamsc0f7c042017-02-23 20:41:17 -060070 self.my_assert(self.data[0], 0x7f)
71 self.my_assert(self.data[1], ord('E'))
72 self.my_assert(self.data[2], ord('L'))
73 self.my_assert(self.data[3], ord('F'))
Brad Bishop37a0e4d2017-12-04 01:01:44 -050074 if self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS32:
75 self.bits = 32
76 elif self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS64:
77 self.bits = 64
Patrick Williamsc124f4f2015-09-15 14:41:29 -050078 else:
Brad Bishop37a0e4d2017-12-04 01:01:44 -050079 # Not 32-bit or 64.. lets assert
80 raise NotELFFileError("ELF but not 32 or 64 bit.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -060081 self.my_assert(self.data[ELFFile.EI_VERSION], ELFFile.EV_CURRENT)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050082
Brad Bishop37a0e4d2017-12-04 01:01:44 -050083 self.endian = self.data[ELFFile.EI_DATA]
84 if self.endian not in (ELFFile.EI_DATA_LSB, ELFFile.EI_DATA_MSB):
85 raise NotELFFileError("Unexpected EI_DATA %x" % self.endian)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050086
87 def osAbi(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060088 return self.data[ELFFile.EI_OSABI]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050089
90 def abiVersion(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060091 return self.data[ELFFile.EI_ABIVERSION]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050092
93 def abiSize(self):
94 return self.bits
95
96 def isLittleEndian(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -050097 return self.endian == ELFFile.EI_DATA_LSB
Patrick Williamsc124f4f2015-09-15 14:41:29 -050098
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050099 def isBigEndian(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500100 return self.endian == ELFFile.EI_DATA_MSB
101
102 def getStructEndian(self):
103 return {ELFFile.EI_DATA_LSB: "<",
104 ELFFile.EI_DATA_MSB: ">"}[self.endian]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500105
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500106 def getShort(self, offset):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500107 return struct.unpack_from(self.getStructEndian() + "H", self.data, offset)[0]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500108
109 def getWord(self, offset):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500110 return struct.unpack_from(self.getStructEndian() + "i", self.data, offset)[0]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500111
112 def isDynamic(self):
113 """
114 Return True if there is a .interp segment (therefore dynamically
115 linked), otherwise False (statically linked).
116 """
117 offset = self.getWord(self.bits == 32 and 0x1C or 0x20)
118 size = self.getShort(self.bits == 32 and 0x2A or 0x36)
119 count = self.getShort(self.bits == 32 and 0x2C or 0x38)
120
121 for i in range(0, count):
122 p_type = self.getWord(offset + i * size)
123 if p_type == ELFFile.PT_INTERP:
124 return True
125 return False
126
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127 def machine(self):
128 """
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500129 We know the endian stored in self.endian and we
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500130 know the position
131 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500132 return self.getShort(ELFFile.E_MACHINE)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500133
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000134 def set_objdump(self, cmd, output):
135 self.objdump_output[cmd] = output
136
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500137 def run_objdump(self, cmd, d):
138 import bb.process
139 import sys
140
141 if cmd in self.objdump_output:
142 return self.objdump_output[cmd]
143
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500144 objdump = d.getVar('OBJDUMP')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500145
146 env = os.environ.copy()
147 env["LC_ALL"] = "C"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500148 env["PATH"] = d.getVar('PATH')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500149
150 try:
151 bb.note("%s %s %s" % (objdump, cmd, self.name))
152 self.objdump_output[cmd] = bb.process.run([objdump, cmd, self.name], env=env, shell=False)[0]
153 return self.objdump_output[cmd]
154 except Exception as e:
155 bb.note("%s %s %s failed: %s" % (objdump, cmd, self.name, e))
156 return ""
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500157
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600158def elf_machine_to_string(machine):
159 """
160 Return the name of a given ELF e_machine field or the hex value as a string
161 if it isn't recognised.
162 """
163 try:
164 return {
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600165 0x00: "Unset",
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600166 0x02: "SPARC",
167 0x03: "x86",
168 0x08: "MIPS",
169 0x14: "PowerPC",
170 0x28: "ARM",
171 0x2A: "SuperH",
172 0x32: "IA-64",
173 0x3E: "x86-64",
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800174 0xB7: "AArch64",
175 0xF7: "BPF"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600176 }[machine]
177 except:
178 return "Unknown (%s)" % repr(machine)
179
Andrew Geisslereff27472021-10-29 15:35:00 -0500180def write_error(type, error, d):
181 logfile = d.getVar('QA_LOGFILE')
182 if logfile:
183 p = d.getVar('P')
184 with open(logfile, "a+") as f:
185 f.write("%s: %s [%s]\n" % (p, error, type))
186
187def handle_error(error_class, error_msg, d):
188 if error_class in (d.getVar("ERROR_QA") or "").split():
189 write_error(error_class, error_msg, d)
190 bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
191 d.setVar("QA_ERRORS_FOUND", "True")
192 return False
193 elif error_class in (d.getVar("WARN_QA") or "").split():
194 write_error(error_class, error_msg, d)
195 bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
196 else:
197 bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
198 return True
199
200def add_message(messages, section, new_msg):
201 if section not in messages:
202 messages[section] = new_msg
203 else:
204 messages[section] = messages[section] + "\n" + new_msg
205
206def exit_with_message_if_errors(message, d):
207 qa_fatal_errors = bb.utils.to_boolean(d.getVar("QA_ERRORS_FOUND"), False)
208 if qa_fatal_errors:
209 bb.fatal(message)
210
211def exit_if_errors(d):
212 exit_with_message_if_errors("Fatal QA errors were found, failing task.", d)
213
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500214if __name__ == "__main__":
215 import sys
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500216
217 with ELFFile(sys.argv[1]) as elf:
218 elf.open()
219 print(elf.isDynamic())