1import os, struct, mmap 2 3class NotELFFileError(Exception): 4 pass 5 6class ELFFile: 7 EI_NIDENT = 16 8 9 EI_CLASS = 4 10 EI_DATA = 5 11 EI_VERSION = 6 12 EI_OSABI = 7 13 EI_ABIVERSION = 8 14 15 E_MACHINE = 0x12 16 17 # possible values for EI_CLASS 18 ELFCLASSNONE = 0 19 ELFCLASS32 = 1 20 ELFCLASS64 = 2 21 22 # possible value for EI_VERSION 23 EV_CURRENT = 1 24 25 # possible values for EI_DATA 26 EI_DATA_NONE = 0 27 EI_DATA_LSB = 1 28 EI_DATA_MSB = 2 29 30 PT_INTERP = 3 31 32 def my_assert(self, expectation, result): 33 if not expectation == result: 34 #print "'%x','%x' %s" % (ord(expectation), ord(result), self.name) 35 raise NotELFFileError("%s is not an ELF" % self.name) 36 37 def __init__(self, name): 38 self.name = name 39 self.objdump_output = {} 40 41 # Context Manager functions to close the mmap explicitly 42 def __enter__(self): 43 return self 44 45 def __exit__(self, exc_type, exc_value, traceback): 46 self.data.close() 47 48 def open(self): 49 with open(self.name, "rb") as f: 50 try: 51 self.data = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) 52 except ValueError: 53 # This means the file is empty 54 raise NotELFFileError("%s is empty" % self.name) 55 56 # Check the file has the minimum number of ELF table entries 57 if len(self.data) < ELFFile.EI_NIDENT + 4: 58 raise NotELFFileError("%s is not an ELF" % self.name) 59 60 # ELF header 61 self.my_assert(self.data[0], 0x7f) 62 self.my_assert(self.data[1], ord('E')) 63 self.my_assert(self.data[2], ord('L')) 64 self.my_assert(self.data[3], ord('F')) 65 if self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS32: 66 self.bits = 32 67 elif self.data[ELFFile.EI_CLASS] == ELFFile.ELFCLASS64: 68 self.bits = 64 69 else: 70 # Not 32-bit or 64.. lets assert 71 raise NotELFFileError("ELF but not 32 or 64 bit.") 72 self.my_assert(self.data[ELFFile.EI_VERSION], ELFFile.EV_CURRENT) 73 74 self.endian = self.data[ELFFile.EI_DATA] 75 if self.endian not in (ELFFile.EI_DATA_LSB, ELFFile.EI_DATA_MSB): 76 raise NotELFFileError("Unexpected EI_DATA %x" % self.endian) 77 78 def osAbi(self): 79 return self.data[ELFFile.EI_OSABI] 80 81 def abiVersion(self): 82 return self.data[ELFFile.EI_ABIVERSION] 83 84 def abiSize(self): 85 return self.bits 86 87 def isLittleEndian(self): 88 return self.endian == ELFFile.EI_DATA_LSB 89 90 def isBigEndian(self): 91 return self.endian == ELFFile.EI_DATA_MSB 92 93 def getStructEndian(self): 94 return {ELFFile.EI_DATA_LSB: "<", 95 ELFFile.EI_DATA_MSB: ">"}[self.endian] 96 97 def getShort(self, offset): 98 return struct.unpack_from(self.getStructEndian() + "H", self.data, offset)[0] 99 100 def getWord(self, offset): 101 return struct.unpack_from(self.getStructEndian() + "i", self.data, offset)[0] 102 103 def isDynamic(self): 104 """ 105 Return True if there is a .interp segment (therefore dynamically 106 linked), otherwise False (statically linked). 107 """ 108 offset = self.getWord(self.bits == 32 and 0x1C or 0x20) 109 size = self.getShort(self.bits == 32 and 0x2A or 0x36) 110 count = self.getShort(self.bits == 32 and 0x2C or 0x38) 111 112 for i in range(0, count): 113 p_type = self.getWord(offset + i * size) 114 if p_type == ELFFile.PT_INTERP: 115 return True 116 return False 117 118 def machine(self): 119 """ 120 We know the endian stored in self.endian and we 121 know the position 122 """ 123 return self.getShort(ELFFile.E_MACHINE) 124 125 def run_objdump(self, cmd, d): 126 import bb.process 127 import sys 128 129 if cmd in self.objdump_output: 130 return self.objdump_output[cmd] 131 132 objdump = d.getVar('OBJDUMP') 133 134 env = os.environ.copy() 135 env["LC_ALL"] = "C" 136 env["PATH"] = d.getVar('PATH') 137 138 try: 139 bb.note("%s %s %s" % (objdump, cmd, self.name)) 140 self.objdump_output[cmd] = bb.process.run([objdump, cmd, self.name], env=env, shell=False)[0] 141 return self.objdump_output[cmd] 142 except Exception as e: 143 bb.note("%s %s %s failed: %s" % (objdump, cmd, self.name, e)) 144 return "" 145 146def elf_machine_to_string(machine): 147 """ 148 Return the name of a given ELF e_machine field or the hex value as a string 149 if it isn't recognised. 150 """ 151 try: 152 return { 153 0x02: "SPARC", 154 0x03: "x86", 155 0x08: "MIPS", 156 0x14: "PowerPC", 157 0x28: "ARM", 158 0x2A: "SuperH", 159 0x32: "IA-64", 160 0x3E: "x86-64", 161 0xB7: "AArch64" 162 }[machine] 163 except: 164 return "Unknown (%s)" % repr(machine) 165 166if __name__ == "__main__": 167 import sys 168 169 with ELFFile(sys.argv[1]) as elf: 170 elf.open() 171 print(elf.isDynamic()) 172