Complete Yocto mirror with license table for TQMa6UL (2038-compliance)
- 264 license table entries with exact download URLs (224/264 resolved) - Complete sources/ directory with all BitBake recipes - Build configuration: tqma6ul-multi-mba6ulx, spaetzle (musl) - Full traceability for Softwarefreigabeantrag - GCC 13.4.0, Linux 6.6.102, U-Boot 2023.04, musl 1.2.4 - License distribution: GPL-2.0 (24), MIT (23), GPL-2.0+ (18), BSD-3 (16)
This commit is contained in:
238
sources/poky/meta/lib/oe/qa.py
Normal file
238
sources/poky/meta/lib/oe/qa.py
Normal file
@@ -0,0 +1,238 @@
|
||||
#
|
||||
# Copyright OpenEmbedded Contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
|
||||
import os, struct, mmap
|
||||
|
||||
class NotELFFileError(Exception):
|
||||
pass
|
||||
|
||||
class ELFFile:
|
||||
EI_NIDENT = 16
|
||||
|
||||
EI_CLASS = 4
|
||||
EI_DATA = 5
|
||||
EI_VERSION = 6
|
||||
EI_OSABI = 7
|
||||
EI_ABIVERSION = 8
|
||||
|
||||
E_MACHINE = 0x12
|
||||
|
||||
# possible values for EI_CLASS
|
||||
ELFCLASSNONE = 0
|
||||
ELFCLASS32 = 1
|
||||
ELFCLASS64 = 2
|
||||
|
||||
# possible value for EI_VERSION
|
||||
EV_CURRENT = 1
|
||||
|
||||
# possible values for EI_DATA
|
||||
EI_DATA_NONE = 0
|
||||
EI_DATA_LSB = 1
|
||||
EI_DATA_MSB = 2
|
||||
|
||||
PT_INTERP = 3
|
||||
|
||||
def my_assert(self, expectation, result):
|
||||
if not expectation == result:
|
||||
#print "'%x','%x' %s" % (ord(expectation), ord(result), self.name)
|
||||
raise NotELFFileError("%s is not an ELF" % self.name)
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.objdump_output = {}
|
||||
self.data = None
|
||||
|
||||
# Context Manager functions to close the mmap explicitly
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
if self.data:
|
||||
self.data.close()
|
||||
|
||||
def open(self):
|
||||
with open(self.name, "rb") as f:
|
||||
try:
|
||||
self.data = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
|
||||
except ValueError:
|
||||
# This means the file is empty
|
||||
raise NotELFFileError("%s is empty" % self.name)
|
||||
|
||||
# Check the file has the minimum number of ELF table entries
|
||||
if len(self.data) < ELFFile.EI_NIDENT + 4:
|
||||
raise NotELFFileError("%s is not an ELF" % self.name)
|
||||
|
||||
# ELF header
|
||||
self.my_assert(self.data[0], 0x7f)
|
||||
self.my_assert(self.data[1], ord('E'))
|
||||
self.my_assert(self.data[2], ord('L'))
|
||||
self.my_assert(self.data[3], ord('F'))
|
||||
if self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS32:
|
||||
self.bits = 32
|
||||
elif self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS64:
|
||||
self.bits = 64
|
||||
else:
|
||||
# Not 32-bit or 64.. lets assert
|
||||
raise NotELFFileError("ELF but not 32 or 64 bit.")
|
||||
self.my_assert(self.data[ELFFile.EI_VERSION], ELFFile.EV_CURRENT)
|
||||
|
||||
self.endian = self.data[ELFFile.EI_DATA]
|
||||
if self.endian not in (ELFFile.EI_DATA_LSB, ELFFile.EI_DATA_MSB):
|
||||
raise NotELFFileError("Unexpected EI_DATA %x" % self.endian)
|
||||
|
||||
def osAbi(self):
|
||||
return self.data[ELFFile.EI_OSABI]
|
||||
|
||||
def abiVersion(self):
|
||||
return self.data[ELFFile.EI_ABIVERSION]
|
||||
|
||||
def abiSize(self):
|
||||
return self.bits
|
||||
|
||||
def isLittleEndian(self):
|
||||
return self.endian == ELFFile.EI_DATA_LSB
|
||||
|
||||
def isBigEndian(self):
|
||||
return self.endian == ELFFile.EI_DATA_MSB
|
||||
|
||||
def getStructEndian(self):
|
||||
return {ELFFile.EI_DATA_LSB: "<",
|
||||
ELFFile.EI_DATA_MSB: ">"}[self.endian]
|
||||
|
||||
def getShort(self, offset):
|
||||
return struct.unpack_from(self.getStructEndian() + "H", self.data, offset)[0]
|
||||
|
||||
def getWord(self, offset):
|
||||
return struct.unpack_from(self.getStructEndian() + "i", self.data, offset)[0]
|
||||
|
||||
def isDynamic(self):
|
||||
"""
|
||||
Return True if there is a .interp segment (therefore dynamically
|
||||
linked), otherwise False (statically linked).
|
||||
"""
|
||||
offset = self.getWord(self.bits == 32 and 0x1C or 0x20)
|
||||
size = self.getShort(self.bits == 32 and 0x2A or 0x36)
|
||||
count = self.getShort(self.bits == 32 and 0x2C or 0x38)
|
||||
|
||||
for i in range(0, count):
|
||||
p_type = self.getWord(offset + i * size)
|
||||
if p_type == ELFFile.PT_INTERP:
|
||||
return True
|
||||
return False
|
||||
|
||||
def machine(self):
|
||||
"""
|
||||
We know the endian stored in self.endian and we
|
||||
know the position
|
||||
"""
|
||||
return self.getShort(ELFFile.E_MACHINE)
|
||||
|
||||
def set_objdump(self, cmd, output):
|
||||
self.objdump_output[cmd] = output
|
||||
|
||||
def run_objdump(self, cmd, d):
|
||||
import bb.process
|
||||
import sys
|
||||
|
||||
if cmd in self.objdump_output:
|
||||
return self.objdump_output[cmd]
|
||||
|
||||
objdump = d.getVar('OBJDUMP')
|
||||
|
||||
env = os.environ.copy()
|
||||
env["LC_ALL"] = "C"
|
||||
env["PATH"] = d.getVar('PATH')
|
||||
|
||||
try:
|
||||
bb.note("%s %s %s" % (objdump, cmd, self.name))
|
||||
self.objdump_output[cmd] = bb.process.run([objdump, cmd, self.name], env=env, shell=False)[0]
|
||||
return self.objdump_output[cmd]
|
||||
except Exception as e:
|
||||
bb.note("%s %s %s failed: %s" % (objdump, cmd, self.name, e))
|
||||
return ""
|
||||
|
||||
def elf_machine_to_string(machine):
|
||||
"""
|
||||
Return the name of a given ELF e_machine field or the hex value as a string
|
||||
if it isn't recognised.
|
||||
"""
|
||||
try:
|
||||
return {
|
||||
0x00: "Unset",
|
||||
0x02: "SPARC",
|
||||
0x03: "x86",
|
||||
0x08: "MIPS",
|
||||
0x14: "PowerPC",
|
||||
0x28: "ARM",
|
||||
0x2A: "SuperH",
|
||||
0x32: "IA-64",
|
||||
0x3E: "x86-64",
|
||||
0xB7: "AArch64",
|
||||
0xF7: "BPF"
|
||||
}[machine]
|
||||
except:
|
||||
return "Unknown (%s)" % repr(machine)
|
||||
|
||||
def write_error(type, error, d):
|
||||
logfile = d.getVar('QA_LOGFILE')
|
||||
if logfile:
|
||||
p = d.getVar('P')
|
||||
with open(logfile, "a+") as f:
|
||||
f.write("%s: %s [%s]\n" % (p, error, type))
|
||||
|
||||
def handle_error(error_class, error_msg, d):
|
||||
if error_class in (d.getVar("ERROR_QA") or "").split():
|
||||
write_error(error_class, error_msg, d)
|
||||
bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
|
||||
d.setVar("QA_ERRORS_FOUND", "True")
|
||||
return False
|
||||
elif error_class in (d.getVar("WARN_QA") or "").split():
|
||||
write_error(error_class, error_msg, d)
|
||||
bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
|
||||
else:
|
||||
bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
|
||||
return True
|
||||
|
||||
def add_message(messages, section, new_msg):
|
||||
if section not in messages:
|
||||
messages[section] = new_msg
|
||||
else:
|
||||
messages[section] = messages[section] + "\n" + new_msg
|
||||
|
||||
def exit_with_message_if_errors(message, d):
|
||||
qa_fatal_errors = bb.utils.to_boolean(d.getVar("QA_ERRORS_FOUND"), False)
|
||||
if qa_fatal_errors:
|
||||
bb.fatal(message)
|
||||
|
||||
def exit_if_errors(d):
|
||||
exit_with_message_if_errors("Fatal QA errors were found, failing task.", d)
|
||||
|
||||
def check_upstream_status(fullpath):
|
||||
import re
|
||||
kinda_status_re = re.compile(r"^.*upstream.*status.*$", re.IGNORECASE | re.MULTILINE)
|
||||
strict_status_re = re.compile(r"^Upstream-Status: (Pending|Submitted|Denied|Inappropriate|Backport|Inactive-Upstream)( .+)?$", re.MULTILINE)
|
||||
guidelines = "https://docs.yoctoproject.org/contributor-guide/recipe-style-guide.html#patch-upstream-status"
|
||||
|
||||
with open(fullpath, encoding='utf-8', errors='ignore') as f:
|
||||
file_content = f.read()
|
||||
match_kinda = kinda_status_re.search(file_content)
|
||||
match_strict = strict_status_re.search(file_content)
|
||||
|
||||
if not match_strict:
|
||||
if match_kinda:
|
||||
return "Malformed Upstream-Status in patch\n%s\nPlease correct according to %s :\n%s" % (fullpath, guidelines, match_kinda.group(0))
|
||||
else:
|
||||
return "Missing Upstream-Status in patch\n%s\nPlease add according to %s ." % (fullpath, guidelines)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
with ELFFile(sys.argv[1]) as elf:
|
||||
elf.open()
|
||||
print(elf.isDynamic())
|
||||
Reference in New Issue
Block a user