mirror of
git://git.yoctoproject.org/poky.git
synced 2025-07-19 21:09:03 +02:00

The ELF parser was assuming that the segment tables are in the first 4kb of the binary. Whilst this generally appears to be the case, there have been instances where the segment table is elsewhere (offset 2MB, in this sample I have). Solve this problem by mmap()ing the file instead. Also clean up the code a little whilst chasing the problem. (From OE-Core rev: a66660aa5bb709547ce0b65a4563e4217c3c3d9f) Signed-off-by: Ross Burton <ross.burton@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
172 lines
4.9 KiB
Python
172 lines
4.9 KiB
Python
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', True)
|
|
|
|
env = os.environ.copy()
|
|
env["LC_ALL"] = "C"
|
|
env["PATH"] = d.getVar('PATH', True)
|
|
|
|
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"
|
|
}[machine]
|
|
except:
|
|
return "Unknown (%s)" % repr(machine)
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
with ELFFile(sys.argv[1]) as elf:
|
|
elf.open()
|
|
print(elf.isDynamic())
|