xref: /OK3568_Linux_fs/yocto/poky/meta/lib/oe/qa.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#
2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only
3*4882a593Smuzhiyun#
4*4882a593Smuzhiyun
5*4882a593Smuzhiyunimport os, struct, mmap
6*4882a593Smuzhiyun
7*4882a593Smuzhiyunclass NotELFFileError(Exception):
8*4882a593Smuzhiyun    pass
9*4882a593Smuzhiyun
10*4882a593Smuzhiyunclass ELFFile:
11*4882a593Smuzhiyun    EI_NIDENT = 16
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun    EI_CLASS      = 4
14*4882a593Smuzhiyun    EI_DATA       = 5
15*4882a593Smuzhiyun    EI_VERSION    = 6
16*4882a593Smuzhiyun    EI_OSABI      = 7
17*4882a593Smuzhiyun    EI_ABIVERSION = 8
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun    E_MACHINE    = 0x12
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun    # possible values for EI_CLASS
22*4882a593Smuzhiyun    ELFCLASSNONE = 0
23*4882a593Smuzhiyun    ELFCLASS32   = 1
24*4882a593Smuzhiyun    ELFCLASS64   = 2
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun    # possible value for EI_VERSION
27*4882a593Smuzhiyun    EV_CURRENT   = 1
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun    # possible values for EI_DATA
30*4882a593Smuzhiyun    EI_DATA_NONE  = 0
31*4882a593Smuzhiyun    EI_DATA_LSB  = 1
32*4882a593Smuzhiyun    EI_DATA_MSB  = 2
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun    PT_INTERP = 3
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun    def my_assert(self, expectation, result):
37*4882a593Smuzhiyun        if not expectation == result:
38*4882a593Smuzhiyun            #print "'%x','%x' %s" % (ord(expectation), ord(result), self.name)
39*4882a593Smuzhiyun            raise NotELFFileError("%s is not an ELF" % self.name)
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun    def __init__(self, name):
42*4882a593Smuzhiyun        self.name = name
43*4882a593Smuzhiyun        self.objdump_output = {}
44*4882a593Smuzhiyun        self.data = None
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun    # Context Manager functions to close the mmap explicitly
47*4882a593Smuzhiyun    def __enter__(self):
48*4882a593Smuzhiyun        return self
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun    def __exit__(self, exc_type, exc_value, traceback):
51*4882a593Smuzhiyun        self.close()
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun    def close(self):
54*4882a593Smuzhiyun        if self.data:
55*4882a593Smuzhiyun            self.data.close()
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun    def open(self):
58*4882a593Smuzhiyun        with open(self.name, "rb") as f:
59*4882a593Smuzhiyun            try:
60*4882a593Smuzhiyun                self.data = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
61*4882a593Smuzhiyun            except ValueError:
62*4882a593Smuzhiyun                # This means the file is empty
63*4882a593Smuzhiyun                raise NotELFFileError("%s is empty" % self.name)
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun        # Check the file has the minimum number of ELF table entries
66*4882a593Smuzhiyun        if len(self.data) < ELFFile.EI_NIDENT + 4:
67*4882a593Smuzhiyun            raise NotELFFileError("%s is not an ELF" % self.name)
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun        # ELF header
70*4882a593Smuzhiyun        self.my_assert(self.data[0], 0x7f)
71*4882a593Smuzhiyun        self.my_assert(self.data[1], ord('E'))
72*4882a593Smuzhiyun        self.my_assert(self.data[2], ord('L'))
73*4882a593Smuzhiyun        self.my_assert(self.data[3], ord('F'))
74*4882a593Smuzhiyun        if self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS32:
75*4882a593Smuzhiyun            self.bits = 32
76*4882a593Smuzhiyun        elif self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS64:
77*4882a593Smuzhiyun            self.bits = 64
78*4882a593Smuzhiyun        else:
79*4882a593Smuzhiyun            # Not 32-bit or 64.. lets assert
80*4882a593Smuzhiyun            raise NotELFFileError("ELF but not 32 or 64 bit.")
81*4882a593Smuzhiyun        self.my_assert(self.data[ELFFile.EI_VERSION], ELFFile.EV_CURRENT)
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun        self.endian = self.data[ELFFile.EI_DATA]
84*4882a593Smuzhiyun        if self.endian not in (ELFFile.EI_DATA_LSB, ELFFile.EI_DATA_MSB):
85*4882a593Smuzhiyun            raise NotELFFileError("Unexpected EI_DATA %x" % self.endian)
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun    def osAbi(self):
88*4882a593Smuzhiyun        return self.data[ELFFile.EI_OSABI]
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun    def abiVersion(self):
91*4882a593Smuzhiyun        return self.data[ELFFile.EI_ABIVERSION]
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun    def abiSize(self):
94*4882a593Smuzhiyun        return self.bits
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun    def isLittleEndian(self):
97*4882a593Smuzhiyun        return self.endian == ELFFile.EI_DATA_LSB
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun    def isBigEndian(self):
100*4882a593Smuzhiyun        return self.endian == ELFFile.EI_DATA_MSB
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun    def getStructEndian(self):
103*4882a593Smuzhiyun        return {ELFFile.EI_DATA_LSB: "<",
104*4882a593Smuzhiyun                ELFFile.EI_DATA_MSB: ">"}[self.endian]
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun    def getShort(self, offset):
107*4882a593Smuzhiyun        return struct.unpack_from(self.getStructEndian() + "H", self.data, offset)[0]
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun    def getWord(self, offset):
110*4882a593Smuzhiyun        return struct.unpack_from(self.getStructEndian() + "i", self.data, offset)[0]
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun    def isDynamic(self):
113*4882a593Smuzhiyun        """
114*4882a593Smuzhiyun        Return True if there is a .interp segment (therefore dynamically
115*4882a593Smuzhiyun        linked), otherwise False (statically linked).
116*4882a593Smuzhiyun        """
117*4882a593Smuzhiyun        offset = self.getWord(self.bits == 32 and 0x1C or 0x20)
118*4882a593Smuzhiyun        size = self.getShort(self.bits == 32 and 0x2A or 0x36)
119*4882a593Smuzhiyun        count = self.getShort(self.bits == 32 and 0x2C or 0x38)
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun        for i in range(0, count):
122*4882a593Smuzhiyun            p_type = self.getWord(offset + i * size)
123*4882a593Smuzhiyun            if p_type == ELFFile.PT_INTERP:
124*4882a593Smuzhiyun                return True
125*4882a593Smuzhiyun        return False
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun    def machine(self):
128*4882a593Smuzhiyun        """
129*4882a593Smuzhiyun        We know the endian stored in self.endian and we
130*4882a593Smuzhiyun        know the position
131*4882a593Smuzhiyun        """
132*4882a593Smuzhiyun        return self.getShort(ELFFile.E_MACHINE)
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun    def set_objdump(self, cmd, output):
135*4882a593Smuzhiyun        self.objdump_output[cmd] = output
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun    def run_objdump(self, cmd, d):
138*4882a593Smuzhiyun        import bb.process
139*4882a593Smuzhiyun        import sys
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun        if cmd in self.objdump_output:
142*4882a593Smuzhiyun            return self.objdump_output[cmd]
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun        objdump = d.getVar('OBJDUMP')
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun        env = os.environ.copy()
147*4882a593Smuzhiyun        env["LC_ALL"] = "C"
148*4882a593Smuzhiyun        env["PATH"] = d.getVar('PATH')
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun        try:
151*4882a593Smuzhiyun            bb.note("%s %s %s" % (objdump, cmd, self.name))
152*4882a593Smuzhiyun            self.objdump_output[cmd] = bb.process.run([objdump, cmd, self.name], env=env, shell=False)[0]
153*4882a593Smuzhiyun            return self.objdump_output[cmd]
154*4882a593Smuzhiyun        except Exception as e:
155*4882a593Smuzhiyun            bb.note("%s %s %s failed: %s" % (objdump, cmd, self.name, e))
156*4882a593Smuzhiyun            return ""
157*4882a593Smuzhiyun
158*4882a593Smuzhiyundef elf_machine_to_string(machine):
159*4882a593Smuzhiyun    """
160*4882a593Smuzhiyun    Return the name of a given ELF e_machine field or the hex value as a string
161*4882a593Smuzhiyun    if it isn't recognised.
162*4882a593Smuzhiyun    """
163*4882a593Smuzhiyun    try:
164*4882a593Smuzhiyun        return {
165*4882a593Smuzhiyun            0x00: "Unset",
166*4882a593Smuzhiyun            0x02: "SPARC",
167*4882a593Smuzhiyun            0x03: "x86",
168*4882a593Smuzhiyun            0x08: "MIPS",
169*4882a593Smuzhiyun            0x14: "PowerPC",
170*4882a593Smuzhiyun            0x28: "ARM",
171*4882a593Smuzhiyun            0x2A: "SuperH",
172*4882a593Smuzhiyun            0x32: "IA-64",
173*4882a593Smuzhiyun            0x3E: "x86-64",
174*4882a593Smuzhiyun            0xB7: "AArch64",
175*4882a593Smuzhiyun            0xF7: "BPF"
176*4882a593Smuzhiyun        }[machine]
177*4882a593Smuzhiyun    except:
178*4882a593Smuzhiyun        return "Unknown (%s)" % repr(machine)
179*4882a593Smuzhiyun
180*4882a593Smuzhiyundef write_error(type, error, d):
181*4882a593Smuzhiyun    logfile = d.getVar('QA_LOGFILE')
182*4882a593Smuzhiyun    if logfile:
183*4882a593Smuzhiyun        p = d.getVar('P')
184*4882a593Smuzhiyun        with open(logfile, "a+") as f:
185*4882a593Smuzhiyun            f.write("%s: %s [%s]\n" % (p, error, type))
186*4882a593Smuzhiyun
187*4882a593Smuzhiyundef handle_error(error_class, error_msg, d):
188*4882a593Smuzhiyun    if error_class in (d.getVar("ERROR_QA") or "").split():
189*4882a593Smuzhiyun        write_error(error_class, error_msg, d)
190*4882a593Smuzhiyun        bb.error("QA Issue: %s [%s]" % (error_msg, error_class))
191*4882a593Smuzhiyun        d.setVar("QA_ERRORS_FOUND", "True")
192*4882a593Smuzhiyun        return False
193*4882a593Smuzhiyun    elif error_class in (d.getVar("WARN_QA") or "").split():
194*4882a593Smuzhiyun        write_error(error_class, error_msg, d)
195*4882a593Smuzhiyun        bb.warn("QA Issue: %s [%s]" % (error_msg, error_class))
196*4882a593Smuzhiyun    else:
197*4882a593Smuzhiyun        bb.note("QA Issue: %s [%s]" % (error_msg, error_class))
198*4882a593Smuzhiyun    return True
199*4882a593Smuzhiyun
200*4882a593Smuzhiyundef add_message(messages, section, new_msg):
201*4882a593Smuzhiyun    if section not in messages:
202*4882a593Smuzhiyun        messages[section] = new_msg
203*4882a593Smuzhiyun    else:
204*4882a593Smuzhiyun        messages[section] = messages[section] + "\n" + new_msg
205*4882a593Smuzhiyun
206*4882a593Smuzhiyundef exit_with_message_if_errors(message, d):
207*4882a593Smuzhiyun    qa_fatal_errors = bb.utils.to_boolean(d.getVar("QA_ERRORS_FOUND"), False)
208*4882a593Smuzhiyun    if qa_fatal_errors:
209*4882a593Smuzhiyun        bb.fatal(message)
210*4882a593Smuzhiyun
211*4882a593Smuzhiyundef exit_if_errors(d):
212*4882a593Smuzhiyun    exit_with_message_if_errors("Fatal QA errors were found, failing task.", d)
213*4882a593Smuzhiyun
214*4882a593Smuzhiyunif __name__ == "__main__":
215*4882a593Smuzhiyun    import sys
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun    with ELFFile(sys.argv[1]) as elf:
218*4882a593Smuzhiyun        elf.open()
219*4882a593Smuzhiyun        print(elf.isDynamic())
220