123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- #
- # 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 = {}
- # Context Manager functions to close the mmap explicitly
- def __enter__(self):
- return self
- def __exit__(self, exc_type, exc_value, traceback):
- 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 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 {
- 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)
- if __name__ == "__main__":
- import sys
- with ELFFile(sys.argv[1]) as elf:
- elf.open()
- print(elf.isDynamic())
|