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