183 lines
5.5 KiB
Python
183 lines
5.5 KiB
Python
|
#!/usr/bin/env python
|
||
|
# -*- coding: utf-8 -*-
|
||
|
|
||
|
# Webcamoid, webcam capture application.
|
||
|
# Copyright (C) 2017 Gonzalo Exequiel Pedone
|
||
|
#
|
||
|
# Webcamoid is free software: you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published by
|
||
|
# the Free Software Foundation, either version 3 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# Webcamoid is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
|
||
|
#
|
||
|
# Web-Site: http://webcamoid.github.io/
|
||
|
|
||
|
import os
|
||
|
import struct
|
||
|
import sys
|
||
|
|
||
|
import tools.binary
|
||
|
|
||
|
|
||
|
class DeployToolsBinary(tools.binary.DeployToolsBinary):
|
||
|
def __init__(self):
|
||
|
super().__init__()
|
||
|
|
||
|
# 32 bits magic number.
|
||
|
self.MH_MAGIC = 0xfeedface # Native endian
|
||
|
self.MH_CIGAM = 0xcefaedfe # Reverse endian
|
||
|
|
||
|
# 64 bits magic number.
|
||
|
self.MH_MAGIC_64 = 0xfeedfacf # Native endian
|
||
|
self.MH_CIGAM_64 = 0xcffaedfe # Reverse endian
|
||
|
|
||
|
def isValid(self, path):
|
||
|
try:
|
||
|
with open(path, 'rb') as f:
|
||
|
# Read magic number.
|
||
|
magic = struct.unpack('I', f.read(4))[0]
|
||
|
|
||
|
if magic == self.MH_MAGIC \
|
||
|
or magic == self.MH_CIGAM \
|
||
|
or magic == self.MH_MAGIC_64 \
|
||
|
or magic == self.MH_CIGAM_64:
|
||
|
return True
|
||
|
except:
|
||
|
pass
|
||
|
|
||
|
return False
|
||
|
|
||
|
# https://github.com/aidansteele/osx-abi-macho-file-format-reference
|
||
|
def dump(self, binary):
|
||
|
# Commands definitions
|
||
|
LC_REQ_DYLD = 0x80000000
|
||
|
LC_LOAD_DYLIB = 0xc
|
||
|
LC_RPATH = 0x1c | LC_REQ_DYLD
|
||
|
LC_ID_DYLIB = 0xd
|
||
|
|
||
|
dylibImports = []
|
||
|
rpaths = []
|
||
|
dylibId = ''
|
||
|
|
||
|
with open(binary, 'rb') as f:
|
||
|
# Read magic number.
|
||
|
magic = struct.unpack('I', f.read(4))[0]
|
||
|
|
||
|
if magic == self.MH_MAGIC or magic == self.MH_CIGAM:
|
||
|
is32bits = True
|
||
|
elif magic == self.MH_MAGIC_64 or magic == self.MH_CIGAM_64:
|
||
|
is32bits = False
|
||
|
else:
|
||
|
return {}
|
||
|
|
||
|
# Read number of commands.
|
||
|
f.seek(12, os.SEEK_CUR)
|
||
|
ncmds = struct.unpack('I', f.read(4))[0]
|
||
|
|
||
|
# Move to load commands
|
||
|
f.seek(8 if is32bits else 12, os.SEEK_CUR)
|
||
|
|
||
|
for _ in range(ncmds):
|
||
|
# Read a load command and store it's position in the file.
|
||
|
loadCommandStart = f.tell()
|
||
|
loadCommand = struct.unpack('II', f.read(8))
|
||
|
|
||
|
# If the command list a library
|
||
|
if loadCommand[0] in [LC_LOAD_DYLIB, LC_RPATH, LC_ID_DYLIB]:
|
||
|
# Save the position of the next command.
|
||
|
nextCommand = f.tell() + loadCommand[1] - 8
|
||
|
|
||
|
# Move to the string
|
||
|
f.seek(loadCommandStart + struct.unpack('I', f.read(4))[0], os.SEEK_SET)
|
||
|
dylib = b''
|
||
|
|
||
|
# Read string until null character.
|
||
|
while True:
|
||
|
c = f.read(1)
|
||
|
|
||
|
if c == b'\x00':
|
||
|
break
|
||
|
|
||
|
dylib += c
|
||
|
s = dylib.decode(sys.getdefaultencoding())
|
||
|
|
||
|
if loadCommand[0] == LC_LOAD_DYLIB:
|
||
|
dylibImports.append(s)
|
||
|
elif loadCommand[0] == LC_RPATH:
|
||
|
rpaths.append(s)
|
||
|
elif loadCommand[0] == LC_ID_DYLIB:
|
||
|
dylibId = s
|
||
|
|
||
|
f.seek(nextCommand, os.SEEK_SET)
|
||
|
else:
|
||
|
f.seek(loadCommand[1] - 8, os.SEEK_CUR)
|
||
|
|
||
|
return {'imports': dylibImports, 'rpaths': rpaths, 'id': dylibId}
|
||
|
|
||
|
@staticmethod
|
||
|
def solveRefpath(path):
|
||
|
if not path.startswith('@'):
|
||
|
return path
|
||
|
|
||
|
searchPaths = []
|
||
|
|
||
|
if 'DYLD_LIBRARY_PATH' in os.environ:
|
||
|
searchPaths += os.environ['DYLD_LIBRARY_PATH'].split(':')
|
||
|
|
||
|
if 'DYLD_FRAMEWORK_PATH' in os.environ:
|
||
|
searchPaths += os.environ['DYLD_FRAMEWORK_PATH'].split(':')
|
||
|
|
||
|
if path.endswith('.dylib'):
|
||
|
dep = os.path.basename(path)
|
||
|
else:
|
||
|
i = path.rfind(os.sep, 0, path.rfind('.framework'))
|
||
|
dep = path[i + 1:]
|
||
|
|
||
|
for fpath in searchPaths:
|
||
|
realPath = os.path.join(fpath, dep)
|
||
|
|
||
|
if os.path.exists(realPath):
|
||
|
return realPath
|
||
|
|
||
|
return ''
|
||
|
|
||
|
def dependencies(self, binary):
|
||
|
machInfo = self.dump(binary)
|
||
|
|
||
|
if not machInfo:
|
||
|
return []
|
||
|
|
||
|
libs = []
|
||
|
|
||
|
for mach in machInfo['imports']:
|
||
|
mach = self.solveRefpath(mach)
|
||
|
|
||
|
if mach == '' or self.isExcluded(mach) or not os.path.exists(mach):
|
||
|
continue
|
||
|
|
||
|
dirName = os.path.dirname(mach)
|
||
|
dirName = os.path.realpath(dirName)
|
||
|
baseName = os.path.basename(mach)
|
||
|
libs.append(os.path.join(dirName, baseName))
|
||
|
|
||
|
return libs
|
||
|
|
||
|
def name(self, binary):
|
||
|
dep = os.path.basename(binary)
|
||
|
i = dep.find('.')
|
||
|
|
||
|
if i >= 0:
|
||
|
dep = dep[: dep.find('.')]
|
||
|
|
||
|
if 'Qt' in dep and not 'Qt5' in dep:
|
||
|
dep = dep.replace('Qt', 'Qt5')
|
||
|
|
||
|
return dep
|