mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 18:31:55 -04:00
deploy-ng: new extensible blob format with PRC configurability
This commit is contained in:
parent
a7e00d0ee6
commit
d755de849c
@ -8,10 +8,12 @@ import marshal
|
|||||||
import imp
|
import imp
|
||||||
import platform
|
import platform
|
||||||
import struct
|
import struct
|
||||||
from io import StringIO, TextIOWrapper
|
from io import StringIO, BytesIO, TextIOWrapper
|
||||||
import distutils.sysconfig as sysconf
|
import distutils.sysconfig as sysconf
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
|
from . import pefile
|
||||||
|
|
||||||
# Temporary (?) try..except to protect against unbuilt p3extend_frozen.
|
# Temporary (?) try..except to protect against unbuilt p3extend_frozen.
|
||||||
try:
|
try:
|
||||||
import p3extend_frozen
|
import p3extend_frozen
|
||||||
@ -1589,7 +1591,7 @@ class Freezer:
|
|||||||
|
|
||||||
return target
|
return target
|
||||||
|
|
||||||
def generateRuntimeFromStub(self, basename, stub_file):
|
def generateRuntimeFromStub(self, basename, stub_file, fields={}):
|
||||||
# We must have a __main__ module to make an exe file.
|
# We must have a __main__ module to make an exe file.
|
||||||
if not self.__writingModule('__main__'):
|
if not self.__writingModule('__main__'):
|
||||||
message = "Can't generate an executable without a __main__ module."
|
message = "Can't generate an executable without a __main__ module."
|
||||||
@ -1602,15 +1604,18 @@ class Freezer:
|
|||||||
target = basename
|
target = basename
|
||||||
modext = '.so'
|
modext = '.so'
|
||||||
|
|
||||||
address_offset = 0
|
# First gather up the strings and code for all the module names, and
|
||||||
|
# put those in a string pool.
|
||||||
# First gather up the strings for all the module names.
|
pool = b""
|
||||||
blob = b""
|
|
||||||
strings = set()
|
strings = set()
|
||||||
|
|
||||||
for moduleName, mdef in self.getModuleDefs():
|
for moduleName, mdef in self.getModuleDefs():
|
||||||
strings.add(moduleName.encode('ascii'))
|
strings.add(moduleName.encode('ascii'))
|
||||||
|
|
||||||
|
for value in fields.values():
|
||||||
|
if value is not None:
|
||||||
|
strings.add(value.encode('utf-8'))
|
||||||
|
|
||||||
# Sort by length descending, allowing reuse of partial strings.
|
# Sort by length descending, allowing reuse of partial strings.
|
||||||
strings = sorted(strings, key=lambda str:-len(str))
|
strings = sorted(strings, key=lambda str:-len(str))
|
||||||
string_offsets = {}
|
string_offsets = {}
|
||||||
@ -1618,13 +1623,15 @@ class Freezer:
|
|||||||
for string in strings:
|
for string in strings:
|
||||||
# First check whether it's already in there; it could be part of
|
# First check whether it's already in there; it could be part of
|
||||||
# a longer string.
|
# a longer string.
|
||||||
offset = blob.find(string + b'\0')
|
offset = pool.find(string + b'\0')
|
||||||
if offset < 0:
|
if offset < 0:
|
||||||
offset = len(blob)
|
offset = len(pool)
|
||||||
blob += string + b'\0'
|
pool += string + b'\0'
|
||||||
string_offsets[string] = offset
|
string_offsets[string] = offset
|
||||||
|
|
||||||
# Generate export table.
|
# Now go through the modules and add them to the pool as well. These
|
||||||
|
# are not 0-terminated, but we later record their sizes and names in
|
||||||
|
# a table after the blob header.
|
||||||
moduleList = []
|
moduleList = []
|
||||||
|
|
||||||
for moduleName, mdef in self.getModuleDefs():
|
for moduleName, mdef in self.getModuleDefs():
|
||||||
@ -1635,9 +1642,9 @@ class Freezer:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# For whatever it's worth, align the code blocks.
|
# For whatever it's worth, align the code blocks.
|
||||||
if len(blob) & 3 != 0:
|
if len(pool) & 3 != 0:
|
||||||
pad = (4 - (len(blob) & 3))
|
pad = (4 - (len(pool) & 3))
|
||||||
blob += b'\0' * pad
|
pool += b'\0' * pad
|
||||||
|
|
||||||
assert not mdef.exclude
|
assert not mdef.exclude
|
||||||
# Allow importing this module.
|
# Allow importing this module.
|
||||||
@ -1649,8 +1656,8 @@ class Freezer:
|
|||||||
if getattr(module, "__path__", None):
|
if getattr(module, "__path__", None):
|
||||||
# Indicate package by negative size
|
# Indicate package by negative size
|
||||||
size = -size
|
size = -size
|
||||||
moduleList.append((moduleName, len(blob), size))
|
moduleList.append((moduleName, len(pool), size))
|
||||||
blob += code
|
pool += code
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# This is a module with no associated Python code. It is either
|
# This is a module with no associated Python code. It is either
|
||||||
@ -1668,55 +1675,327 @@ class Freezer:
|
|||||||
code = 'import sys;del sys.modules["%s"];import sys,os,imp;imp.load_dynamic("%s",os.path.join(os.path.dirname(sys.executable), "%s%s"))' % (moduleName, moduleName, moduleName, modext)
|
code = 'import sys;del sys.modules["%s"];import sys,os,imp;imp.load_dynamic("%s",os.path.join(os.path.dirname(sys.executable), "%s%s"))' % (moduleName, moduleName, moduleName, modext)
|
||||||
code = compile(code, moduleName, 'exec')
|
code = compile(code, moduleName, 'exec')
|
||||||
code = marshal.dumps(code)
|
code = marshal.dumps(code)
|
||||||
moduleList.append((moduleName, len(blob), len(code)))
|
moduleList.append((moduleName, len(pool), len(code)))
|
||||||
blob += code
|
pool += code
|
||||||
|
|
||||||
# Now compose the final blob.
|
# Determine the format of the header and module list entries depending
|
||||||
if '64' in self.platform:
|
# on the platform.
|
||||||
layout = '<QQixxxx'
|
num_pointers = 10
|
||||||
|
stub_data = bytearray(stub_file.read())
|
||||||
|
if self._is_executable_64bit(stub_data):
|
||||||
|
header_layout = '<QQHHHH8x%dQQ' % num_pointers
|
||||||
|
entry_layout = '<QQixxxx'
|
||||||
else:
|
else:
|
||||||
layout = '<IIi'
|
header_layout = '<QQHHHH8x%dII' % num_pointers
|
||||||
|
entry_layout = '<IIi'
|
||||||
|
|
||||||
blob2 = bytes()
|
# Calculate the size of the header and module table, so that we can
|
||||||
blobOffset = (len(moduleList) + 1) * struct.calcsize(layout)
|
# determine the proper offset for the string pointers.
|
||||||
blobOffset += address_offset
|
pool_offset = (len(moduleList) + 1) * struct.calcsize(entry_layout)
|
||||||
|
|
||||||
|
# The module table is the first thing in the blob.
|
||||||
|
blob = b""
|
||||||
for moduleName, offset, size in moduleList:
|
for moduleName, offset, size in moduleList:
|
||||||
encoded = moduleName.encode('ascii')
|
encoded = moduleName.encode('ascii')
|
||||||
stringOffset = blobOffset + string_offsets[encoded]
|
string_offset = pool_offset + string_offsets[encoded]
|
||||||
if size != 0:
|
if size != 0:
|
||||||
offset += blobOffset
|
offset += pool_offset
|
||||||
blob2 += struct.pack(layout, stringOffset, offset, size)
|
blob += struct.pack(entry_layout, string_offset, offset, size)
|
||||||
|
|
||||||
blob2 += struct.pack(layout, 0, 0, 0)
|
blob += struct.pack(entry_layout, 0, 0, 0)
|
||||||
assert len(blob2) + address_offset == blobOffset
|
|
||||||
|
|
||||||
blob = blob2 + blob
|
# Add the string pool.
|
||||||
del blob2
|
assert len(blob) == pool_offset
|
||||||
|
blob += pool
|
||||||
|
del pool
|
||||||
|
|
||||||
# Total blob length should be aligned to 8 bytes.
|
# Total blob length should be aligned to 8 bytes.
|
||||||
if len(blob) & 7 != 0:
|
if len(blob) & 7 != 0:
|
||||||
pad = (8 - (len(blob) & 7))
|
pad = (8 - (len(blob) & 7))
|
||||||
blob += b'\0' * pad
|
blob += b'\0' * pad
|
||||||
|
|
||||||
# Build from pre-built binary stub
|
if self.platform.startswith('win'):
|
||||||
with open(target, 'wb') as f:
|
# We don't use mmap on Windows.
|
||||||
f.write(stub_file.read())
|
blob_align = 32
|
||||||
offset = f.tell()
|
else:
|
||||||
|
# Align to page size, so that it can be mmapped.
|
||||||
|
blob_align = 4096
|
||||||
|
|
||||||
|
# Determine the blob offset, padding the stub if necessary.
|
||||||
|
blob_offset = len(stub_data)
|
||||||
|
if (blob_offset & (blob_align - 1)) != 0:
|
||||||
# Add padding to align to the page size, so it can be mmapped.
|
# Add padding to align to the page size, so it can be mmapped.
|
||||||
if offset % 4095 != 0:
|
pad = (blob_align - (blob_offset & (blob_align - 1)))
|
||||||
pad = (4096 - (offset & 4095))
|
stub_data += (b'\0' * pad)
|
||||||
f.write(b'\0' * pad)
|
blob_offset += pad
|
||||||
offset += pad
|
assert (blob_offset % blob_align) == 0
|
||||||
|
assert blob_offset == len(stub_data)
|
||||||
|
|
||||||
|
# Calculate the offsets for the variables. These are pointers,
|
||||||
|
# relative to the beginning of the blob.
|
||||||
|
field_offsets = {}
|
||||||
|
for key, value in fields.items():
|
||||||
|
if value is not None:
|
||||||
|
encoded = value.encode('utf-8')
|
||||||
|
field_offsets[key] = pool_offset + string_offsets[encoded]
|
||||||
|
|
||||||
|
# Compose the header we will be writing to the stub, to tell it where
|
||||||
|
# to find the module data blob, as well as other variables.
|
||||||
|
header = struct.pack(header_layout,
|
||||||
|
blob_offset,
|
||||||
|
len(blob),
|
||||||
|
1, # Version number
|
||||||
|
num_pointers, # Number of pointers that follow
|
||||||
|
0, # Codepage, not yet used
|
||||||
|
0, # Flags, not yet used
|
||||||
|
0, # Module table pointer, always 0 for now.
|
||||||
|
# The following variables need to be set before static init
|
||||||
|
# time. See configPageManager.cxx, where they are read.
|
||||||
|
field_offsets.get('prc_data', 0),
|
||||||
|
field_offsets.get('default_prc_dir', 0),
|
||||||
|
field_offsets.get('prc_dir_envvars', 0),
|
||||||
|
field_offsets.get('prc_path_envvars', 0),
|
||||||
|
field_offsets.get('prc_patterns', 0),
|
||||||
|
field_offsets.get('prc_encrypted_patterns', 0),
|
||||||
|
field_offsets.get('prc_encryption_key', 0),
|
||||||
|
field_offsets.get('prc_executable_patterns', 0),
|
||||||
|
field_offsets.get('prc_executable_args_envvar', 0),
|
||||||
|
0)
|
||||||
|
|
||||||
|
# Now, find the location of the 'blobinfo' symbol in the executable,
|
||||||
|
# to which we will write our header.
|
||||||
|
if not self._replace_symbol(stub_data, b'blobinfo', header):
|
||||||
|
# This must be a legacy deploy-stub, which requires the offset to
|
||||||
|
# be appended to the end.
|
||||||
|
blob += struct.pack('<Q', blob_offset)
|
||||||
|
|
||||||
|
with open(target, 'wb') as f:
|
||||||
|
f.write(stub_data)
|
||||||
|
assert f.tell() == blob_offset
|
||||||
f.write(blob)
|
f.write(blob)
|
||||||
# And tack on the offset of the blob to the end.
|
|
||||||
f.write(struct.pack('<Q', offset))
|
|
||||||
|
|
||||||
os.chmod(target, 0o755)
|
os.chmod(target, 0o755)
|
||||||
return target
|
return target
|
||||||
|
|
||||||
|
def _is_executable_64bit(self, data):
|
||||||
|
"""Returns true if this is a 64-bit executable."""
|
||||||
|
|
||||||
|
if data.startswith(b'MZ'):
|
||||||
|
# A Windows PE file.
|
||||||
|
offset, = struct.unpack_from('<I', data, 0x3c)
|
||||||
|
assert data[offset:offset+4] == b'PE\0\0'
|
||||||
|
|
||||||
|
magic, = struct.unpack_from('<H', data, offset + 24)
|
||||||
|
assert magic in (0x010b, 0x020b)
|
||||||
|
return magic == 0x020b
|
||||||
|
|
||||||
|
elif data.startswith(b"\177ELF"):
|
||||||
|
# A Linux/FreeBSD ELF executable.
|
||||||
|
elfclass = ord(data[4:5])
|
||||||
|
assert elfclass in (1, 2)
|
||||||
|
return elfclass == 2
|
||||||
|
|
||||||
|
elif data[:4] in (b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE'):
|
||||||
|
# 32-bit Mach-O file, as used on macOS.
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif data[:4] in (b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE'):
|
||||||
|
# 64-bit Mach-O file, as used on macOS.
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif data[:4] in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA',
|
||||||
|
b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA'):
|
||||||
|
# A universal file, containing one or more Mach-O files.
|
||||||
|
#TODO: how to handle universal stubs with more than one arch?
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _replace_symbol(self, data, symbol_name, replacement):
|
||||||
|
"""We store a custom section in the binary file containing a header
|
||||||
|
containing offsets to the binary data."""
|
||||||
|
|
||||||
|
if data.startswith(b'MZ'):
|
||||||
|
# A Windows PE file.
|
||||||
|
pe = pefile.PEFile()
|
||||||
|
pe.read(BytesIO(data))
|
||||||
|
addr = pe.get_export_address(symbol_name)
|
||||||
|
if addr is not None:
|
||||||
|
# We found it, return its offset in the file.
|
||||||
|
offset = pe.get_address_offset(addr)
|
||||||
|
if offset is not None:
|
||||||
|
data[offset:offset+len(replacement)] = replacement
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif data.startswith(b"\177ELF"):
|
||||||
|
return self._replace_symbol_elf(data, symbol_name, replacement)
|
||||||
|
|
||||||
|
elif data[:4] in (b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE',
|
||||||
|
b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE'):
|
||||||
|
off = self._find_symbol_macho(macho_data, symbol_name)
|
||||||
|
if off is not None:
|
||||||
|
data[off:off+len(replacement)] = replacement
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif data[:4] in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA'):
|
||||||
|
# Universal binary with 32-bit offsets.
|
||||||
|
return self._replace_symbol_fat(data, b'_' + symbol_name, replacement, False)
|
||||||
|
|
||||||
|
elif data[:4] in (b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA'):
|
||||||
|
# Universal binary with 64-bit offsets.
|
||||||
|
return self._replace_symbol_fat(data, b'_' + symbol_name, replacement, True)
|
||||||
|
|
||||||
|
# We don't know what kind of file this is.
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _replace_symbol_elf(self, elf_data, symbol_name, replacement):
|
||||||
|
""" The Linux/FreeBSD implementation of _replace_symbol. """
|
||||||
|
|
||||||
|
replaced = False
|
||||||
|
|
||||||
|
# Make sure we read in the correct endianness and integer size
|
||||||
|
endian = "<>"[ord(elf_data[5:6]) - 1]
|
||||||
|
is_64bit = ord(elf_data[4:5]) - 1 # 0 = 32-bits, 1 = 64-bits
|
||||||
|
header_struct = endian + ("HHIIIIIHHHHHH", "HHIQQQIHHHHHH")[is_64bit]
|
||||||
|
section_struct = endian + ("4xI4xIIII8xI", "4xI8xQQQI12xQ")[is_64bit]
|
||||||
|
symbol_struct = endian + ("IIIBBH", "IBBHQQ")[is_64bit]
|
||||||
|
|
||||||
|
header_size = struct.calcsize(header_struct)
|
||||||
|
type, machine, version, entry, phoff, shoff, flags, ehsize, phentsize, phnum, shentsize, shnum, shstrndx \
|
||||||
|
= struct.unpack_from(header_struct, elf_data, 16)
|
||||||
|
section_offsets = []
|
||||||
|
symbol_tables = []
|
||||||
|
string_tables = {}
|
||||||
|
|
||||||
|
# Seek to the section header table and find the symbol tables.
|
||||||
|
ptr = shoff
|
||||||
|
for i in range(shnum):
|
||||||
|
type, addr, offset, size, link, entsize = struct.unpack_from(section_struct, elf_data[ptr:ptr+shentsize])
|
||||||
|
ptr += shentsize
|
||||||
|
section_offsets.append(offset - addr)
|
||||||
|
if type == 0x0B and link != 0: # SHT_DYNSYM, links to string table
|
||||||
|
symbol_tables.append((offset, size, link, entsize))
|
||||||
|
string_tables[link] = None
|
||||||
|
|
||||||
|
# Read the relevant string tables.
|
||||||
|
for idx in list(string_tables.keys()):
|
||||||
|
ptr = shoff + idx * shentsize
|
||||||
|
type, addr, offset, size, link, entsize = struct.unpack_from(section_struct, elf_data[ptr:ptr+shentsize])
|
||||||
|
if type == 3:
|
||||||
|
string_tables[idx] = elf_data[offset:offset+size]
|
||||||
|
|
||||||
|
# Loop through to find the offset of the "blobinfo" symbol.
|
||||||
|
for offset, size, link, entsize in symbol_tables:
|
||||||
|
entries = size // entsize
|
||||||
|
for i in range(entries):
|
||||||
|
ptr = offset + i * entsize
|
||||||
|
fields = struct.unpack_from(symbol_struct, elf_data[ptr:ptr+entsize])
|
||||||
|
if is_64bit:
|
||||||
|
name, info, other, shndx, value, size = fields
|
||||||
|
else:
|
||||||
|
name, value, size, info, other, shndx = fields
|
||||||
|
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
|
||||||
|
name = string_tables[link][name : string_tables[link].find(b'\0', name)]
|
||||||
|
if name == symbol_name:
|
||||||
|
if shndx == 0: # SHN_UNDEF
|
||||||
|
continue
|
||||||
|
elif shndx >= 0xff00 and shndx <= 0xffff:
|
||||||
|
assert False
|
||||||
|
else:
|
||||||
|
# Got it. Make the replacement.
|
||||||
|
off = section_offsets[shndx] + value
|
||||||
|
elf_data[off:off+len(replacement)] = replacement
|
||||||
|
replaced = True
|
||||||
|
|
||||||
|
return replaced
|
||||||
|
|
||||||
|
def _find_symbol_macho(self, macho_data, symbol_name):
|
||||||
|
""" Returns the offset of the given symbol in the binary file. """
|
||||||
|
|
||||||
|
if macho_data[:4] in (b'\xCE\xFA\xED\xFE', b'\xCF\xFA\xED\xFE'):
|
||||||
|
endian = '<'
|
||||||
|
else:
|
||||||
|
endian = '>'
|
||||||
|
|
||||||
|
cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \
|
||||||
|
struct.unpack_from(endian + 'IIIIII', macho_data, 4)
|
||||||
|
|
||||||
|
is_64bit = (cputype & 0x1000000) != 0
|
||||||
|
segments = []
|
||||||
|
|
||||||
|
cmd_ptr = 28
|
||||||
|
nlist_struct = endian + 'IBBHI'
|
||||||
|
if is_64bit:
|
||||||
|
nlist_struct = endian + 'IBBHQ'
|
||||||
|
cmd_ptr += 4
|
||||||
|
nlist_size = struct.calcsize(nlist_struct)
|
||||||
|
|
||||||
|
for i in range(ncmds):
|
||||||
|
cmd, cmd_size = struct.unpack_from(endian + 'II', macho_data, cmd_ptr)
|
||||||
|
cmd_data = macho_data[cmd_ptr+8:cmd_ptr+cmd_size]
|
||||||
|
cmd_ptr += cmd_size
|
||||||
|
|
||||||
|
cmd &= ~0x80000000
|
||||||
|
|
||||||
|
if cmd == 0x01: # LC_SEGMENT
|
||||||
|
segname, vmaddr, vmsize, fileoff, filesize, maxprot, initprot, nsects, flags = \
|
||||||
|
struct.unpack_from(endian + '16sIIIIIIII', cmd_data)
|
||||||
|
segments.append((vmaddr, vmsize, fileoff))
|
||||||
|
|
||||||
|
elif cmd == 0x19: # LC_SEGMENT_64
|
||||||
|
segname, vmaddr, vmsize, fileoff, filesize, maxprot, initprot, nsects, flags = \
|
||||||
|
struct.unpack_from(endian + '16sQQQQIIII', cmd_data)
|
||||||
|
segments.append((vmaddr, vmsize, fileoff))
|
||||||
|
|
||||||
|
elif cmd == 0x2: # LC_SYMTAB
|
||||||
|
symoff, nsyms, stroff, strsize = \
|
||||||
|
struct.unpack_from(endian + 'IIII', cmd_data)
|
||||||
|
|
||||||
|
strings = macho_data[stroff:stroff+strsize]
|
||||||
|
|
||||||
|
for i in range(nsyms):
|
||||||
|
strx, type, sect, desc, value = struct.unpack_from(nlist_struct, macho_data, symoff)
|
||||||
|
symoff += nlist_size
|
||||||
|
name = strings[strx : strings.find(b'\0', strx)]
|
||||||
|
|
||||||
|
if name == symbol_name:
|
||||||
|
# Find out in which segment this is.
|
||||||
|
for vmaddr, vmsize, fileoff in segments:
|
||||||
|
# Is it defined in this segment?
|
||||||
|
rel = value - vmaddr
|
||||||
|
if rel >= 0 and rel < vmsize:
|
||||||
|
# Yes, so return the symbol offset.
|
||||||
|
return fileoff + rel
|
||||||
|
|
||||||
|
def _replace_symbol_fat(self, fat_data, symbol_name, replacement, is_64bit):
|
||||||
|
""" Implementation of _replace_symbol for universal binaries. """
|
||||||
|
num_fat, = struct.unpack_from('>I', fat_data, 4)
|
||||||
|
|
||||||
|
# After the header we get a table of executables in this fat file,
|
||||||
|
# each one with a corresponding offset into the file.
|
||||||
|
replaced = False
|
||||||
|
ptr = 8
|
||||||
|
for i in range(num_fat):
|
||||||
|
if is_64bit:
|
||||||
|
cputype, cpusubtype, offset, size, align = \
|
||||||
|
struct.unpack_from('>QQQQQ', fat_data, ptr)
|
||||||
|
ptr += 40
|
||||||
|
else:
|
||||||
|
cputype, cpusubtype, offset, size, align = \
|
||||||
|
struct.unpack_from('>IIIII', fat_data, ptr)
|
||||||
|
ptr += 20
|
||||||
|
|
||||||
|
macho_data = fat_data[offset:offset+size]
|
||||||
|
off = self._find_symbol_macho(macho_data, symbol_name)
|
||||||
|
if off is not None:
|
||||||
|
off += offset
|
||||||
|
fat_data[off:off+len(replacement)] = replacement
|
||||||
|
replaced = True
|
||||||
|
|
||||||
|
return replaced
|
||||||
|
|
||||||
def makeModuleDef(self, mangledName, code):
|
def makeModuleDef(self, mangledName, code):
|
||||||
result = ''
|
result = ''
|
||||||
result += 'static unsigned char %s[] = {' % (mangledName)
|
result += 'static unsigned char %s[] = {' % (mangledName)
|
||||||
|
@ -225,7 +225,17 @@ class build_apps(distutils.core.Command):
|
|||||||
stub_path = os.path.join(os.path.dirname(dtool_path), '..', 'bin', stub_name)
|
stub_path = os.path.join(os.path.dirname(dtool_path), '..', 'bin', stub_name)
|
||||||
stub_file = open(stub_path, 'rb')
|
stub_file = open(stub_path, 'rb')
|
||||||
|
|
||||||
freezer.generateRuntimeFromStub(os.path.join(builddir, appname), stub_file)
|
freezer.generateRuntimeFromStub(os.path.join(builddir, appname), stub_file, {
|
||||||
|
'prc_data': None,
|
||||||
|
'default_prc_dir': None,
|
||||||
|
'prc_dir_envvars': None,
|
||||||
|
'prc_path_envvars': None,
|
||||||
|
'prc_patterns': None,
|
||||||
|
'prc_encrypted_patterns': None,
|
||||||
|
'prc_encryption_key': None,
|
||||||
|
'prc_executable_patterns': None,
|
||||||
|
'prc_executable_args_envvar': None,
|
||||||
|
})
|
||||||
stub_file.close()
|
stub_file.close()
|
||||||
|
|
||||||
freezer_extras.update(freezer.extras)
|
freezer_extras.update(freezer.extras)
|
||||||
@ -492,16 +502,22 @@ class build_apps(distutils.core.Command):
|
|||||||
# Elf magic. Used on (among others) Linux and FreeBSD.
|
# Elf magic. Used on (among others) Linux and FreeBSD.
|
||||||
deps = self._read_dependencies_elf(fp, os.path.dirname(source_path), search_path)
|
deps = self._read_dependencies_elf(fp, os.path.dirname(source_path), search_path)
|
||||||
|
|
||||||
elif magic in (b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE',
|
elif magic in (b'\xCE\xFA\xED\xFE', b'\xCF\xFA\xED\xFE'):
|
||||||
b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE'):
|
|
||||||
# A Mach-O file, as used on macOS.
|
# A Mach-O file, as used on macOS.
|
||||||
deps = self._read_dependencies_macho(fp)
|
deps = self._read_dependencies_macho(fp, '<')
|
||||||
|
|
||||||
elif magic in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\bCA'):
|
elif magic in (b'\xFE\xED\xFA\xCE', b'\xFE\xED\xFA\xCF'):
|
||||||
|
deps = self._read_dependencies_macho(fp, '>')
|
||||||
|
|
||||||
|
elif magic in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA'):
|
||||||
# A fat file, containing multiple Mach-O binaries. In the future,
|
# A fat file, containing multiple Mach-O binaries. In the future,
|
||||||
# we may want to extract the one containing the architecture we
|
# we may want to extract the one containing the architecture we
|
||||||
# are building for.
|
# are building for.
|
||||||
deps = self._read_dependencies_fat(fp)
|
deps = self._read_dependencies_fat(fp, False)
|
||||||
|
|
||||||
|
elif magic in (b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA'):
|
||||||
|
# A 64-bit fat file.
|
||||||
|
deps = self._read_dependencies_fat(fp, True)
|
||||||
|
|
||||||
# If we discovered any dependencies, recursively add those.
|
# If we discovered any dependencies, recursively add those.
|
||||||
if deps:
|
if deps:
|
||||||
@ -573,23 +589,23 @@ class build_apps(distutils.core.Command):
|
|||||||
search_path += rpath
|
search_path += rpath
|
||||||
return needed
|
return needed
|
||||||
|
|
||||||
def _read_dependencies_macho(self, fp):
|
def _read_dependencies_macho(self, fp, endian):
|
||||||
""" Having read the first 4 bytes of the Mach-O file, fetches the
|
""" Having read the first 4 bytes of the Mach-O file, fetches the
|
||||||
dependent libraries and returns those as a list. """
|
dependent libraries and returns those as a list. """
|
||||||
|
|
||||||
cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \
|
cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \
|
||||||
struct.unpack('<IIIIII', fp.read(24))
|
struct.unpack(endian + 'IIIIII', fp.read(24))
|
||||||
|
|
||||||
is_64 = (cputype & 0x1000000) != 0
|
is_64bit = (cputype & 0x1000000) != 0
|
||||||
if is_64:
|
if is_64bit:
|
||||||
fp.read(4)
|
fp.read(4)
|
||||||
|
|
||||||
# After the header, we get a series of linker commands. We just
|
# After the header, we get a series of linker commands. We just
|
||||||
# iterate through them and gather up the LC_LOAD_DYLIB commands.
|
# iterate through them and gather up the LC_LOAD_DYLIB commands.
|
||||||
load_dylibs = []
|
load_dylibs = []
|
||||||
for i in range(ncmds):
|
for i in range(ncmds):
|
||||||
cmd, cmdsize = struct.unpack('<II', fp.read(8))
|
cmd, cmd_size = struct.unpack(endian + 'II', fp.read(8))
|
||||||
cmd_data = fp.read(cmdsize - 8)
|
cmd_data = fp.read(cmd_size - 8)
|
||||||
cmd &= ~0x80000000
|
cmd &= ~0x80000000
|
||||||
|
|
||||||
if cmd == 0x0c: # LC_LOAD_DYLIB
|
if cmd == 0x0c: # LC_LOAD_DYLIB
|
||||||
@ -600,20 +616,41 @@ class build_apps(distutils.core.Command):
|
|||||||
|
|
||||||
return load_dylibs
|
return load_dylibs
|
||||||
|
|
||||||
def _read_dependencies_fat(self, fp):
|
def _read_dependencies_fat(self, fp, is_64bit):
|
||||||
num_fat = struct.unpack('>I', fp.read(4))[0]
|
num_fat, = struct.unpack('>I', fp.read(4))
|
||||||
if num_fat == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
# After the header we get a table of executables in this fat file,
|
# After the header we get a table of executables in this fat file,
|
||||||
# each one with a corresponding offset into the file.
|
# each one with a corresponding offset into the file.
|
||||||
# We are just interested in the first one for now.
|
offsets = []
|
||||||
cputype, cpusubtype, offset, size, align = \
|
for i in range(num_fat):
|
||||||
struct.unpack('>IIIII', fp.read(20))
|
if is_64bit:
|
||||||
|
cputype, cpusubtype, offset, size, align = \
|
||||||
|
struct.unpack('>QQQQQ', fp.read(40))
|
||||||
|
else:
|
||||||
|
cputype, cpusubtype, offset, size, align = \
|
||||||
|
struct.unpack('>IIIII', fp.read(20))
|
||||||
|
offsets.append(offset)
|
||||||
|
|
||||||
# Add 4, since it expects we've already read the magic.
|
# Go through each of the binaries in the fat file.
|
||||||
fp.seek(offset + 4)
|
deps = []
|
||||||
return self._read_dependencies_macho(fp)
|
for offset in offsets:
|
||||||
|
# Add 4, since it expects we've already read the magic.
|
||||||
|
fp.seek(offset)
|
||||||
|
magic = fp.read(4)
|
||||||
|
|
||||||
|
if magic in (b'\xCE\xFA\xED\xFE', b'\xCF\xFA\xED\xFE'):
|
||||||
|
endian = '<'
|
||||||
|
elif magic in (b'\xFE\xED\xFA\xCE', b'\xFE\xED\xFA\xCF'):
|
||||||
|
endian = '>'
|
||||||
|
else:
|
||||||
|
# Not a Mach-O file we can read.
|
||||||
|
continue
|
||||||
|
|
||||||
|
for dep in self._read_dependencies_macho(fp, endian):
|
||||||
|
if dep not in deps:
|
||||||
|
deps.append(dep)
|
||||||
|
|
||||||
|
return deps
|
||||||
|
|
||||||
|
|
||||||
class bdist_apps(distutils.core.Command):
|
class bdist_apps(distutils.core.Command):
|
||||||
|
@ -19,6 +19,7 @@ if sys.version_info >= (3, 0):
|
|||||||
# Define some internally used structures.
|
# Define some internally used structures.
|
||||||
RVASize = namedtuple('RVASize', ('addr', 'size'))
|
RVASize = namedtuple('RVASize', ('addr', 'size'))
|
||||||
impdirtab = namedtuple('impdirtab', ('lookup', 'timdat', 'forward', 'name', 'impaddr'))
|
impdirtab = namedtuple('impdirtab', ('lookup', 'timdat', 'forward', 'name', 'impaddr'))
|
||||||
|
expdirtab = namedtuple('expdirtab', ('flags', 'timdat', 'majver', 'minver', 'name', 'ordinal_base', 'nentries', 'nnames', 'entries', 'names', 'ordinals'))
|
||||||
|
|
||||||
|
|
||||||
def _unpack_zstring(mem, offs=0):
|
def _unpack_zstring(mem, offs=0):
|
||||||
@ -571,10 +572,11 @@ class PEFile(object):
|
|||||||
|
|
||||||
# Read the sections into some kind of virtual memory.
|
# Read the sections into some kind of virtual memory.
|
||||||
self.vmem = bytearray(self.sections[-1].vaddr + self.sections[-1].size)
|
self.vmem = bytearray(self.sections[-1].vaddr + self.sections[-1].size)
|
||||||
|
memview = memoryview(self.vmem)
|
||||||
|
|
||||||
for section in self.sections:
|
for section in self.sections:
|
||||||
fp.seek(section.offset)
|
fp.seek(section.offset)
|
||||||
fp.readinto(memoryview(self.vmem)[section.vaddr:section.vaddr+section.size])
|
fp.readinto(memview[section.vaddr:section.vaddr+section.size])
|
||||||
|
|
||||||
# Read the import table.
|
# Read the import table.
|
||||||
start = self.imp_rva.addr
|
start = self.imp_rva.addr
|
||||||
@ -596,6 +598,43 @@ class PEFile(object):
|
|||||||
if self.res_rva.addr and self.res_rva.size:
|
if self.res_rva.addr and self.res_rva.size:
|
||||||
self.resources.unpack_from(self.vmem, self.res_rva.addr)
|
self.resources.unpack_from(self.vmem, self.res_rva.addr)
|
||||||
|
|
||||||
|
def get_export_address(self, symbol_name):
|
||||||
|
""" Finds the virtual address for a named export symbol. """
|
||||||
|
|
||||||
|
start = self.exp_rva.addr
|
||||||
|
expdir = expdirtab(*unpack('<IIHHIIIIIII', self.vmem[start:start+40]))
|
||||||
|
if expdir.nnames == 0 or expdir.ordinals == 0 or expdir.names == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
nptr = expdir.names
|
||||||
|
optr = expdir.ordinals
|
||||||
|
for i in range(expdir.nnames):
|
||||||
|
name_rva, = unpack('<I', self.vmem[nptr:nptr+4])
|
||||||
|
ordinal, = unpack('<H', self.vmem[optr:optr+2])
|
||||||
|
if name_rva != 0:
|
||||||
|
name = _unpack_zstring(self.vmem, name_rva)
|
||||||
|
if name == symbol_name:
|
||||||
|
assert ordinal >= 0 and ordinal < expdir.nentries
|
||||||
|
start = expdir.entries + 8 * ordinal
|
||||||
|
addr, = unpack('<I', self.vmem[start:start+4])
|
||||||
|
return addr
|
||||||
|
nptr += 4
|
||||||
|
optr += 2
|
||||||
|
|
||||||
|
def get_address_offset(self, addr):
|
||||||
|
""" Turns an address into a offset relative to the file beginning. """
|
||||||
|
|
||||||
|
section = self.get_address_section(addr)
|
||||||
|
if section is not None:
|
||||||
|
return (addr - section.vaddr) + section.offset
|
||||||
|
|
||||||
|
def get_address_section(self, addr):
|
||||||
|
""" Returns the section that this virtual address belongs to. """
|
||||||
|
|
||||||
|
for section in self.sections:
|
||||||
|
if addr >= section.vaddr and addr < section.vaddr + section.size:
|
||||||
|
return section
|
||||||
|
|
||||||
def add_icon(self, icon, ordinal=2):
|
def add_icon(self, icon, ordinal=2):
|
||||||
""" Adds an icon resource from the given Icon object. Requires
|
""" Adds an icon resource from the given Icon object. Requires
|
||||||
calling add_resource_section() afterwards. """
|
calling add_resource_section() afterwards. """
|
||||||
|
@ -37,6 +37,10 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
ConfigPageManager *ConfigPageManager::_global_ptr = NULL;
|
ConfigPageManager *ConfigPageManager::_global_ptr = NULL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,11 +94,44 @@ reload_implicit_pages() {
|
|||||||
}
|
}
|
||||||
_implicit_pages.clear();
|
_implicit_pages.clear();
|
||||||
|
|
||||||
|
// If we are running inside a deployed application, see if it exposes
|
||||||
|
// information about how the PRC data should be initialized.
|
||||||
|
struct BlobInfo {
|
||||||
|
uint64_t blob_offset;
|
||||||
|
uint64_t blob_size;
|
||||||
|
uint16_t version;
|
||||||
|
uint16_t num_pointers;
|
||||||
|
uint16_t codepage;
|
||||||
|
uint16_t flags;
|
||||||
|
uint64_t reserved;
|
||||||
|
const void *module_table;
|
||||||
|
const char *prc_data;
|
||||||
|
const char *default_prc_dir;
|
||||||
|
const char *prc_dir_envvars;
|
||||||
|
const char *prc_path_envvars;
|
||||||
|
const char *prc_patterns;
|
||||||
|
const char *prc_encrypted_patterns;
|
||||||
|
const char *prc_encryption_key;
|
||||||
|
const char *prc_executable_patterns;
|
||||||
|
const char *prc_executable_args_envvar;
|
||||||
|
};
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
const BlobInfo *blobinfo = (const BlobInfo *)GetProcAddress(GetModuleHandle(NULL), "blobinfo");
|
||||||
|
#else
|
||||||
|
const BlobInfo *blobinfo = (const BlobInfo *)dlsym(RTLD_SELF, "blobinfo");
|
||||||
|
#endif
|
||||||
|
if (blobinfo != nullptr && (blobinfo->version == 0 || blobinfo->num_pointers < 10)) {
|
||||||
|
blobinfo = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// PRC_PATTERNS lists one or more filename templates separated by spaces.
|
// PRC_PATTERNS lists one or more filename templates separated by spaces.
|
||||||
// Pull them out and store them in _prc_patterns.
|
// Pull them out and store them in _prc_patterns.
|
||||||
_prc_patterns.clear();
|
_prc_patterns.clear();
|
||||||
|
|
||||||
string prc_patterns = PRC_PATTERNS;
|
string prc_patterns = PRC_PATTERNS;
|
||||||
|
if (blobinfo != nullptr && blobinfo->prc_patterns != nullptr) {
|
||||||
|
prc_patterns = blobinfo->prc_patterns;
|
||||||
|
}
|
||||||
if (!prc_patterns.empty()) {
|
if (!prc_patterns.empty()) {
|
||||||
vector_string pat_list;
|
vector_string pat_list;
|
||||||
ConfigDeclaration::extract_words(prc_patterns, pat_list);
|
ConfigDeclaration::extract_words(prc_patterns, pat_list);
|
||||||
@ -114,6 +151,9 @@ reload_implicit_pages() {
|
|||||||
_prc_encrypted_patterns.clear();
|
_prc_encrypted_patterns.clear();
|
||||||
|
|
||||||
string prc_encrypted_patterns = PRC_ENCRYPTED_PATTERNS;
|
string prc_encrypted_patterns = PRC_ENCRYPTED_PATTERNS;
|
||||||
|
if (blobinfo != nullptr && blobinfo->prc_encrypted_patterns != nullptr) {
|
||||||
|
prc_encrypted_patterns = blobinfo->prc_encrypted_patterns;
|
||||||
|
}
|
||||||
if (!prc_encrypted_patterns.empty()) {
|
if (!prc_encrypted_patterns.empty()) {
|
||||||
vector_string pat_list;
|
vector_string pat_list;
|
||||||
ConfigDeclaration::extract_words(prc_encrypted_patterns, pat_list);
|
ConfigDeclaration::extract_words(prc_encrypted_patterns, pat_list);
|
||||||
@ -131,6 +171,9 @@ reload_implicit_pages() {
|
|||||||
_prc_executable_patterns.clear();
|
_prc_executable_patterns.clear();
|
||||||
|
|
||||||
string prc_executable_patterns = PRC_EXECUTABLE_PATTERNS;
|
string prc_executable_patterns = PRC_EXECUTABLE_PATTERNS;
|
||||||
|
if (blobinfo != nullptr && blobinfo->prc_executable_patterns != nullptr) {
|
||||||
|
prc_executable_patterns = blobinfo->prc_executable_patterns;
|
||||||
|
}
|
||||||
if (!prc_executable_patterns.empty()) {
|
if (!prc_executable_patterns.empty()) {
|
||||||
vector_string pat_list;
|
vector_string pat_list;
|
||||||
ConfigDeclaration::extract_words(prc_executable_patterns, pat_list);
|
ConfigDeclaration::extract_words(prc_executable_patterns, pat_list);
|
||||||
@ -151,6 +194,9 @@ reload_implicit_pages() {
|
|||||||
// spaces. Pull them out, and each of those contains the name of a single
|
// spaces. Pull them out, and each of those contains the name of a single
|
||||||
// directory to search. Add it to the search path.
|
// directory to search. Add it to the search path.
|
||||||
string prc_dir_envvars = PRC_DIR_ENVVARS;
|
string prc_dir_envvars = PRC_DIR_ENVVARS;
|
||||||
|
if (blobinfo != nullptr && blobinfo->prc_dir_envvars != nullptr) {
|
||||||
|
prc_dir_envvars = blobinfo->prc_dir_envvars;
|
||||||
|
}
|
||||||
if (!prc_dir_envvars.empty()) {
|
if (!prc_dir_envvars.empty()) {
|
||||||
vector_string prc_dir_envvar_list;
|
vector_string prc_dir_envvar_list;
|
||||||
ConfigDeclaration::extract_words(prc_dir_envvars, prc_dir_envvar_list);
|
ConfigDeclaration::extract_words(prc_dir_envvars, prc_dir_envvar_list);
|
||||||
@ -170,6 +216,9 @@ reload_implicit_pages() {
|
|||||||
// spaces. Pull them out, and then each one of those contains a list of
|
// spaces. Pull them out, and then each one of those contains a list of
|
||||||
// directories to search. Add each of those to the search path.
|
// directories to search. Add each of those to the search path.
|
||||||
string prc_path_envvars = PRC_PATH_ENVVARS;
|
string prc_path_envvars = PRC_PATH_ENVVARS;
|
||||||
|
if (blobinfo != nullptr && blobinfo->prc_path_envvars != nullptr) {
|
||||||
|
prc_path_envvars = blobinfo->prc_path_envvars;
|
||||||
|
}
|
||||||
if (!prc_path_envvars.empty()) {
|
if (!prc_path_envvars.empty()) {
|
||||||
vector_string prc_path_envvar_list;
|
vector_string prc_path_envvar_list;
|
||||||
ConfigDeclaration::extract_words(prc_path_envvars, prc_path_envvar_list);
|
ConfigDeclaration::extract_words(prc_path_envvars, prc_path_envvar_list);
|
||||||
@ -201,7 +250,7 @@ reload_implicit_pages() {
|
|||||||
* DEFAULT_PATHSEP.
|
* DEFAULT_PATHSEP.
|
||||||
*/
|
*/
|
||||||
string prc_path2_envvars = PRC_PATH2_ENVVARS;
|
string prc_path2_envvars = PRC_PATH2_ENVVARS;
|
||||||
if (!prc_path2_envvars.empty()) {
|
if (!prc_path2_envvars.empty() && blobinfo == nullptr) {
|
||||||
vector_string prc_path_envvar_list;
|
vector_string prc_path_envvar_list;
|
||||||
ConfigDeclaration::extract_words(prc_path2_envvars, prc_path_envvar_list);
|
ConfigDeclaration::extract_words(prc_path2_envvars, prc_path_envvar_list);
|
||||||
for (size_t i = 0; i < prc_path_envvar_list.size(); ++i) {
|
for (size_t i = 0; i < prc_path_envvar_list.size(); ++i) {
|
||||||
@ -225,6 +274,9 @@ reload_implicit_pages() {
|
|||||||
// If nothing's on the search path (PRC_DIR and PRC_PATH were not
|
// If nothing's on the search path (PRC_DIR and PRC_PATH were not
|
||||||
// defined), then use the DEFAULT_PRC_DIR.
|
// defined), then use the DEFAULT_PRC_DIR.
|
||||||
string default_prc_dir = DEFAULT_PRC_DIR;
|
string default_prc_dir = DEFAULT_PRC_DIR;
|
||||||
|
if (blobinfo != nullptr && blobinfo->default_prc_dir != nullptr) {
|
||||||
|
default_prc_dir = blobinfo->default_prc_dir;
|
||||||
|
}
|
||||||
if (!default_prc_dir.empty()) {
|
if (!default_prc_dir.empty()) {
|
||||||
// It's already from-os-specific by ppremake.
|
// It's already from-os-specific by ppremake.
|
||||||
Filename prc_dir_filename = default_prc_dir;
|
Filename prc_dir_filename = default_prc_dir;
|
||||||
@ -297,12 +349,24 @@ reload_implicit_pages() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
|
||||||
|
// If prc_data is predefined, we load it as an implicit page.
|
||||||
|
if (blobinfo != nullptr && blobinfo->prc_data != nullptr) {
|
||||||
|
ConfigPage *page = new ConfigPage("builtin", true, i);
|
||||||
|
++i;
|
||||||
|
_implicit_pages.push_back(page);
|
||||||
|
_pages_sorted = false;
|
||||||
|
|
||||||
|
istringstream in(blobinfo->prc_data);
|
||||||
|
page->read_prc(in);
|
||||||
|
}
|
||||||
|
|
||||||
// Now we have a list of filenames in order from most important to least
|
// Now we have a list of filenames in order from most important to least
|
||||||
// important. Walk through the list in reverse order to load their
|
// important. Walk through the list in reverse order to load their
|
||||||
// contents, because we want the first file in the list (the most important)
|
// contents, because we want the first file in the list (the most important)
|
||||||
// to be on the top of the stack.
|
// to be on the top of the stack.
|
||||||
ConfigFiles::reverse_iterator ci;
|
ConfigFiles::reverse_iterator ci;
|
||||||
int i = 1;
|
|
||||||
for (ci = config_files.rbegin(); ci != config_files.rend(); ++ci) {
|
for (ci = config_files.rbegin(); ci != config_files.rend(); ++ci) {
|
||||||
const ConfigFile &file = (*ci);
|
const ConfigFile &file = (*ci);
|
||||||
Filename filename = file._filename;
|
Filename filename = file._filename;
|
||||||
@ -313,6 +377,9 @@ reload_implicit_pages() {
|
|||||||
string command = filename.to_os_specific();
|
string command = filename.to_os_specific();
|
||||||
|
|
||||||
string envvar = PRC_EXECUTABLE_ARGS_ENVVAR;
|
string envvar = PRC_EXECUTABLE_ARGS_ENVVAR;
|
||||||
|
if (blobinfo != nullptr && blobinfo->prc_executable_args_envvar != nullptr) {
|
||||||
|
envvar = blobinfo->prc_executable_args_envvar;
|
||||||
|
}
|
||||||
if (!envvar.empty()) {
|
if (!envvar.empty()) {
|
||||||
string args = ExecutionEnvironment::get_environment_variable(envvar);
|
string args = ExecutionEnvironment::get_environment_variable(envvar);
|
||||||
if (!args.empty()) {
|
if (!args.empty()) {
|
||||||
@ -343,7 +410,11 @@ reload_implicit_pages() {
|
|||||||
_implicit_pages.push_back(page);
|
_implicit_pages.push_back(page);
|
||||||
_pages_sorted = false;
|
_pages_sorted = false;
|
||||||
|
|
||||||
page->read_encrypted_prc(in, PRC_ENCRYPTION_KEY);
|
if (blobinfo != nullptr && blobinfo->prc_encryption_key != nullptr) {
|
||||||
|
page->read_encrypted_prc(in, blobinfo->prc_encryption_key);
|
||||||
|
} else {
|
||||||
|
page->read_encrypted_prc(in, PRC_ENCRYPTION_KEY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ((file._file_flags & FF_read) != 0) {
|
} else if ((file._file_flags & FF_read) != 0) {
|
||||||
|
@ -6583,7 +6583,7 @@ if PkgSkip("PYTHON") == 0:
|
|||||||
if GetTarget() == 'windows':
|
if GetTarget() == 'windows':
|
||||||
TargetAdd('frozen_dllmain.obj', opts=OPTS, input='frozen_dllmain.c')
|
TargetAdd('frozen_dllmain.obj', opts=OPTS, input='frozen_dllmain.c')
|
||||||
|
|
||||||
if GetTarget() == 'linux':
|
if GetTarget() == 'linux' or GetTarget() == 'freebsd':
|
||||||
# Setup rpath so libs can be found in the same directory as the deployed game
|
# Setup rpath so libs can be found in the same directory as the deployed game
|
||||||
LibName('DEPLOYSTUB', "-Wl,-rpath,\$ORIGIN")
|
LibName('DEPLOYSTUB', "-Wl,-rpath,\$ORIGIN")
|
||||||
LibName('DEPLOYSTUB', "-Wl,-z,origin")
|
LibName('DEPLOYSTUB', "-Wl,-z,origin")
|
||||||
|
@ -22,6 +22,29 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Define an exposed symbol where we store the offset to the module data. */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
__declspec(dllexport)
|
||||||
|
#else
|
||||||
|
__attribute__((__visibility__("default"), used))
|
||||||
|
#endif
|
||||||
|
volatile struct {
|
||||||
|
uint64_t blob_offset;
|
||||||
|
uint64_t blob_size;
|
||||||
|
uint16_t version;
|
||||||
|
uint16_t num_pointers;
|
||||||
|
uint16_t codepage;
|
||||||
|
uint16_t flags;
|
||||||
|
uint64_t reserved;
|
||||||
|
|
||||||
|
// Leave room for future expansion. We only read pointer 0, but there are
|
||||||
|
// other pointers that are being read by configPageManager.cxx.
|
||||||
|
void *pointers[24];
|
||||||
|
|
||||||
|
// The reason we initialize it to -1 is because otherwise, smart linkers may
|
||||||
|
// end up putting it in the .bss section for zero-initialized data.
|
||||||
|
} blobinfo = {(uint64_t)-1};
|
||||||
|
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@ -221,15 +244,12 @@ error:
|
|||||||
return sts;
|
return sts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
#if defined(_WIN32) && PY_MAJOR_VERSION >= 3
|
* Maps the binary blob at the given memory address to memory, and returns the
|
||||||
int wmain(int argc, wchar_t *argv[]) {
|
* pointer to the beginning of it.
|
||||||
#else
|
*/
|
||||||
int main(int argc, char *argv[]) {
|
static void *map_blob(off_t offset, size_t size) {
|
||||||
#endif
|
void *blob;
|
||||||
struct _frozen *blob, *moddef;
|
|
||||||
uint64_t begin, end, size;
|
|
||||||
int retval;
|
|
||||||
FILE *runtime;
|
FILE *runtime;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -243,7 +263,7 @@ int main(int argc, char *argv[]) {
|
|||||||
mib[3] = getpid();
|
mib[3] = getpid();
|
||||||
if (sysctl(mib, 4, (void *)buffer, &bufsize, NULL, 0) == -1) {
|
if (sysctl(mib, 4, (void *)buffer, &bufsize, NULL, 0) == -1) {
|
||||||
perror("sysctl");
|
perror("sysctl");
|
||||||
return 1;
|
return NULL;
|
||||||
}
|
}
|
||||||
runtime = fopen(buffer, "rb");
|
runtime = fopen(buffer, "rb");
|
||||||
#else
|
#else
|
||||||
@ -251,46 +271,108 @@ int main(int argc, char *argv[]) {
|
|||||||
runtime = fopen(argv[0], "rb");
|
runtime = fopen(argv[0], "rb");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Get offsets
|
// Get offsets. In version 0, we read it from the end of the file.
|
||||||
fseek(runtime, -8, SEEK_END);
|
if (blobinfo.version == 0) {
|
||||||
end = ftell(runtime);
|
uint64_t end, begin;
|
||||||
fread(&begin, 8, 1, runtime);
|
fseek(runtime, -8, SEEK_END);
|
||||||
size = end - begin;
|
end = ftell(runtime);
|
||||||
|
fread(&begin, 8, 1, runtime);
|
||||||
|
|
||||||
|
offset = (off_t)begin;
|
||||||
|
size = (size_t)(end - begin);
|
||||||
|
}
|
||||||
|
|
||||||
// mmap the section indicated by the offset (or malloc/fread on windows)
|
// mmap the section indicated by the offset (or malloc/fread on windows)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
blob = (struct _frozen *)malloc(size);
|
blob = (void *)malloc(size);
|
||||||
assert(blob != NULL);
|
assert(blob != NULL);
|
||||||
fseek(runtime, (long)begin, SEEK_SET);
|
fseek(runtime, (long)offset, SEEK_SET);
|
||||||
fread(blob, size, 1, runtime);
|
fread(blob, size, 1, runtime);
|
||||||
#else
|
#else
|
||||||
blob = (struct _frozen *)mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(runtime), begin);
|
blob = (void *)mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(runtime), offset);
|
||||||
assert(blob != NULL);
|
assert(blob != MAP_FAILED);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
fclose(runtime);
|
fclose(runtime);
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
|
||||||
// Offset the pointers in the table using the base mmap address.
|
/**
|
||||||
moddef = blob;
|
* The inverse of map_blob.
|
||||||
while (moddef->name) {
|
*/
|
||||||
moddef->name = (char *)((uintptr_t)moddef->name + (uintptr_t)blob);
|
static void unmap_blob(void *blob) {
|
||||||
if (moddef->code != 0) {
|
if (blob) {
|
||||||
moddef->code = (unsigned char *)((uintptr_t)moddef->code + (uintptr_t)blob);
|
#ifdef _WIN32
|
||||||
|
free(blob);
|
||||||
|
#else
|
||||||
|
munmap(blob, blobinfo.blob_size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main entry point to deploy-stub.
|
||||||
|
*/
|
||||||
|
#if defined(_WIN32) && PY_MAJOR_VERSION >= 3
|
||||||
|
int wmain(int argc, wchar_t *argv[]) {
|
||||||
|
#else
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
#endif
|
||||||
|
int retval;
|
||||||
|
struct _frozen *moddef;
|
||||||
|
void *blob = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
printf("blob_offset: %d\n", (int)blobinfo.blob_offset);
|
||||||
|
printf("blob_size: %d\n", (int)blobinfo.blob_size);
|
||||||
|
printf("version: %d\n", (int)blobinfo.version);
|
||||||
|
printf("num_pointers: %d\n", (int)blobinfo.num_pointers);
|
||||||
|
printf("codepage: %d\n", (int)blobinfo.codepage);
|
||||||
|
printf("flags: %d\n", (int)blobinfo.flags);
|
||||||
|
printf("reserved: %d\n", (int)blobinfo.reserved);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// If we have a blob offset, we have to map the blob to memory.
|
||||||
|
if (blobinfo.version == 0 || blobinfo.blob_offset != 0) {
|
||||||
|
void *blob = map_blob((off_t)blobinfo.blob_offset, (size_t)blobinfo.blob_size);
|
||||||
|
assert(blob != NULL);
|
||||||
|
|
||||||
|
// Offset the pointers in the header using the base mmap address.
|
||||||
|
if (blobinfo.version > 0 && blobinfo.num_pointers > 0) {
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < blobinfo.num_pointers; ++i) {
|
||||||
|
if (i == 0 || blobinfo.pointers[i] != 0) {
|
||||||
|
blobinfo.pointers[i] = (void *)((uintptr_t)blobinfo.pointers[i] + (uintptr_t)blob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
blobinfo.pointers[0] = blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset the pointers in the module table using the base mmap address.
|
||||||
|
moddef = blobinfo.pointers[0];
|
||||||
|
while (moddef->name) {
|
||||||
|
moddef->name = (char *)((uintptr_t)moddef->name + (uintptr_t)blob);
|
||||||
|
if (moddef->code != 0) {
|
||||||
|
moddef->code = (unsigned char *)((uintptr_t)moddef->code + (uintptr_t)blob);
|
||||||
|
}
|
||||||
|
//printf("MOD: %s %p %d\n", moddef->name, (void*)moddef->code, moddef->size);
|
||||||
|
moddef++;
|
||||||
}
|
}
|
||||||
//printf("MOD: %s %p %d\n", moddef->name, (void*)moddef->code, moddef->size);
|
|
||||||
moddef++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (codepage != 0) {
|
||||||
|
SetConsoleCP(codepage);
|
||||||
|
SetConsoleOutputCP(codepage);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Run frozen application
|
// Run frozen application
|
||||||
PyImport_FrozenModules = blob;
|
PyImport_FrozenModules = blobinfo.pointers[0];
|
||||||
retval = Py_FrozenMain(argc, argv);
|
retval = Py_FrozenMain(argc, argv);
|
||||||
|
|
||||||
// Free resources
|
unmap_blob(blob);
|
||||||
#ifdef _WIN32
|
|
||||||
free(blob);
|
|
||||||
#else
|
|
||||||
munmap(blob, size);
|
|
||||||
#endif
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user