Read dependency libraries from the ELF file itself rather than using ldd. Also, remove any RPATH/RUNPATH entry if the .dynamic section contains one
This commit is contained in:
parent
2e0329302b
commit
f5834b67c5
|
|
@ -12,6 +12,7 @@ import string
|
|||
import types
|
||||
import getpass
|
||||
import platform
|
||||
import struct
|
||||
from direct.p3d.FileSpec import FileSpec
|
||||
from direct.p3d.SeqValue import SeqValue
|
||||
from direct.showbase import Loader
|
||||
|
|
@ -1062,7 +1063,7 @@ class Packager:
|
|||
|
||||
# Copy it every time, because the source file might
|
||||
# have changed since last time we ran.
|
||||
assert file.filename.exists(), "File doesn't exist: %s" % ffilename
|
||||
assert file.filename.exists(), "File doesn't exist: %s" % file.filename
|
||||
tmpfile = Filename.temporary('', "p3d_" + file.filename.getBasename())
|
||||
file.filename.copyTo(tmpfile)
|
||||
file.filename = tmpfile
|
||||
|
|
@ -1178,6 +1179,120 @@ class Packager:
|
|||
|
||||
return filenames
|
||||
|
||||
def __readAndStripELF(self, file):
|
||||
""" Reads the indicated ELF binary, and returns a list with
|
||||
dependencies. If it contains data that should be stripped,
|
||||
it writes the stripped library to a temporary file. Returns
|
||||
None if the file failed to read (e.g. not an ELF file). """
|
||||
|
||||
# Read the first 16 bytes, which identify the ELF file.
|
||||
elf = open(file.filename.toOsSpecific(), 'rb')
|
||||
try:
|
||||
ident = elf.read(16)
|
||||
except IOError:
|
||||
elf.close()
|
||||
return None
|
||||
|
||||
if not ident.startswith("\177ELF"):
|
||||
# Not an elf. Beware of orcs.
|
||||
return None
|
||||
|
||||
# Make sure we read in the correct endianness and integer size
|
||||
byteOrder = "<>"[ord(ident[5]) - 1]
|
||||
elfClass = ord(ident[4]) - 1 # 32-bits, 64-bits
|
||||
headerStruct = byteOrder + ("HHIIIIIHHHHHH", "HHIQQQIHHHHHH")[elfClass]
|
||||
sectionStruct = byteOrder + ("4xI8xIII8xI", "4xI16xQQI12xQ")[elfClass]
|
||||
dynamicStruct = byteOrder + ("iI", "qQ")[elfClass]
|
||||
|
||||
type, machine, version, entry, phoff, shoff, flags, ehsize, phentsize, phnum, shentsize, shnum, shstrndx \
|
||||
= struct.unpack(headerStruct, elf.read(struct.calcsize(headerStruct)))
|
||||
dynamicSections = []
|
||||
stringTables = {}
|
||||
|
||||
# Seek to the section header table and find the .dynamic section.
|
||||
elf.seek(shoff)
|
||||
for i in range(shnum):
|
||||
type, offset, size, link, entsize = struct.unpack_from(sectionStruct, elf.read(shentsize))
|
||||
if type == 6 and link != 0: # DYNAMIC type, links to string table
|
||||
dynamicSections.append((offset, size, link, entsize))
|
||||
stringTables[link] = None
|
||||
|
||||
# Read the relevant string tables.
|
||||
for idx in stringTables.keys():
|
||||
elf.seek(shoff + idx * shentsize)
|
||||
type, offset, size, link, entsize = struct.unpack_from(sectionStruct, elf.read(shentsize))
|
||||
if type != 3: continue
|
||||
elf.seek(offset)
|
||||
stringTables[idx] = elf.read(size)
|
||||
|
||||
# Loop through the dynamic sections and rewrite it if it has an rpath/runpath.
|
||||
rewriteSections = []
|
||||
filenames = []
|
||||
rpath = []
|
||||
for offset, size, link, entsize in dynamicSections:
|
||||
elf.seek(offset)
|
||||
data = elf.read(entsize)
|
||||
tag, val = struct.unpack_from(dynamicStruct, data)
|
||||
newSectionData = ""
|
||||
startReplace = None
|
||||
pad = 0
|
||||
|
||||
# Read tags until we find a NULL tag.
|
||||
while tag != 0:
|
||||
if tag == 1: # A NEEDED entry. Read it from the string table.
|
||||
filenames.append(stringTables[link][val : stringTables[link].find('\0', val)])
|
||||
|
||||
elif tag == 15 or tag == 29:
|
||||
rpath += stringTables[link][val : stringTables[link].find('\0', val)].split(':')
|
||||
# An RPATH or RUNPATH entry.
|
||||
if not startReplace:
|
||||
startReplace = elf.tell() - entsize
|
||||
if startReplace:
|
||||
pad += entsize
|
||||
|
||||
elif startReplace is not None:
|
||||
newSectionData += data
|
||||
|
||||
data = elf.read(entsize)
|
||||
tag, val = struct.unpack_from(dynamicStruct, data)
|
||||
|
||||
if startReplace is not None:
|
||||
newSectionData += data + ("\0" * pad)
|
||||
rewriteSections.append((startReplace, newSectionData))
|
||||
elf.close()
|
||||
|
||||
# No rpaths/runpaths found, so nothing to do any more.
|
||||
if len(rewriteSections) == 0:
|
||||
return filenames
|
||||
|
||||
# Attempt to resolve any of the directly
|
||||
# dependent filenames along the RPATH.
|
||||
for f in range(len(filenames)):
|
||||
filename = filenames[f]
|
||||
for rdir in rpath:
|
||||
if os.path.isfile(os.path.join(rdir, filename)):
|
||||
filenames[f] = os.path.join(rdir, filename)
|
||||
break
|
||||
|
||||
if not file.deleteTemp:
|
||||
# Copy the file to a temporary location because we
|
||||
# don't want to modify the original (there's a big
|
||||
# chance that we break it).
|
||||
|
||||
tmpfile = Filename.temporary('', "p3d_" + file.filename.getBasename())
|
||||
file.filename.copyTo(tmpfile)
|
||||
file.filename = tmpfile
|
||||
file.deleteTemp = True
|
||||
|
||||
# Open the temporary file and rewrite the dynamic sections.
|
||||
elf = open(file.filename.toOsSpecific(), 'r+b')
|
||||
for offset, data in rewriteSections:
|
||||
elf.seek(offset)
|
||||
elf.write(data)
|
||||
elf.write("\0" * pad)
|
||||
elf.close()
|
||||
return filenames
|
||||
|
||||
def __addImplicitDependenciesPosix(self):
|
||||
""" Walks through the list of files, looking for so's
|
||||
and executables that might include implicit dependencies
|
||||
|
|
@ -1195,19 +1310,24 @@ class Packager:
|
|||
# Skip this file.
|
||||
continue
|
||||
|
||||
tempFile = Filename.temporary('', 'p3d_', '.txt')
|
||||
command = 'ldd "%s" >"%s"' % (
|
||||
file.filename.toOsSpecific(),
|
||||
tempFile.toOsSpecific())
|
||||
try:
|
||||
os.system(command)
|
||||
except:
|
||||
pass
|
||||
filenames = None
|
||||
# Check if this is an ELF binary.
|
||||
filenames = self.__readAndStripELF(file)
|
||||
|
||||
# If that failed, perhaps ldd will help us.
|
||||
if filenames is None:
|
||||
tempFile = Filename.temporary('', 'p3d_', '.txt')
|
||||
command = 'ldd "%s" >"%s"' % (
|
||||
file.filename.toOsSpecific(),
|
||||
tempFile.toOsSpecific())
|
||||
try:
|
||||
os.system(command)
|
||||
except:
|
||||
pass
|
||||
|
||||
if tempFile.exists():
|
||||
filenames = self.__parseDependenciesPosix(tempFile)
|
||||
tempFile.unlink()
|
||||
|
||||
if tempFile.exists():
|
||||
filenames = self.__parseDependenciesPosix(tempFile)
|
||||
tempFile.unlink()
|
||||
if filenames is None:
|
||||
self.notify.warning("Unable to determine dependencies from %s" % (file.filename))
|
||||
continue
|
||||
|
|
|
|||
Loading…
Reference in New Issue