Relocate mach-o binaries using macholib on linux. (#12946)

Changes deps and rpaths for bins and libs, changes id for libs.
This commit is contained in:
Patrick Gartung 2019-09-26 13:04:58 -05:00 committed by GitHub
parent 950338aa92
commit 7dae058f91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 74 additions and 95 deletions

View File

@ -573,20 +573,19 @@ def extract_tarball(spec, filename, allow_root=False, unsigned=False,
# so the pathname should be the same now that the directory layout
# is confirmed
workdir = os.path.join(tmpdir, os.path.basename(spec.prefix))
install_tree(workdir, spec.prefix, symlinks=True)
# cleanup
os.remove(tarfile_path)
os.remove(specfile_path)
try:
relocate_package(workdir, spec, allow_root)
relocate_package(spec.prefix, spec, allow_root)
except Exception as e:
shutil.rmtree(workdir)
shutil.rmtree(spec.prefix)
tty.die(e)
# Delay creating spec.prefix until verification is complete
# and any relocation has been done.
else:
install_tree(workdir, spec.prefix, symlinks=True)
finally:
shutil.rmtree(tmpdir)

View File

@ -41,20 +41,20 @@ def __init__(self, file_path, old_len, new_len):
(file_path, old_len, new_len))
class MissingMachotoolsException(spack.error.SpackError):
class MissingMacholibException(spack.error.SpackError):
"""
Raised when the size of the file changes after binary path substitution.
"""
def __init__(self, error):
super(MissingMachotoolsException, self).__init__(
super(MissingMacholibException, self).__init__(
"%s\n"
"Python package machotools needs to be avaiable to list\n"
"Python package macholib needs to be avaiable to list\n"
"and modify a mach-o binary's rpaths, deps and id.\n"
"Use virtualenv with pip install machotools or\n"
"use spack to install the py-machotools package\n"
"spack install py-machotools\n"
"spack activate py-machotools\n"
"Use virtualenv with pip install macholib or\n"
"use spack to install the py-macholib package\n"
"spack install py-macholib\n"
"spack activate py-macholib\n"
"spack load python\n"
% error)
@ -275,55 +275,50 @@ def modify_macho_object(cur_path, rpaths, deps, idpath,
return
def modify_object_machotools(cur_path, rpaths, deps, idpath,
new_rpaths, new_deps, new_idpath):
def modify_object_macholib(cur_path, old_dir, new_dir):
"""
Modify MachO binary path_name by replacing old_dir with new_dir
or the relative path to spack install root.
The old install dir in LC_ID_DYLIB is replaced with the new install dir
using py-machotools
using py-macholib
The old install dir in LC_LOAD_DYLIB is replaced with the new install dir
using py-machotools
using py-macholib
The old install dir in LC_RPATH is replaced with the new install dir using
using py-machotools
using py-macholib
"""
if cur_path.endswith('.o'):
return
try:
import machotools
from macholib.MachO import MachO
from macholib.mach_o import LC_RPATH
except ImportError as e:
raise MissingMachotoolsException(e)
rewriter = machotools.rewriter_factory(cur_path)
if machotools.detect.is_dylib(cur_path):
if not new_idpath == idpath:
rewriter.install_name = new_idpath
for orig, new in zip(deps, new_deps):
if not orig == new:
rewriter.change_dependency(orig, new)
rewriter.commit()
return
raise MissingMacholibException(e)
def match_func(cpath):
return str(cpath).replace(old_dir, new_dir)
dll = MachO(cur_path)
dll.rewriteLoadCommands(match_func)
for header in dll.headers:
for (idx, (lc, cmd, data)) in enumerate(header.commands):
if lc.cmd == LC_RPATH:
rpath = data
rpath = rpath.strip('\x00')
new_rpath = match_func(rpath)
header.rewriteDataForCommand(idx, new_rpath)
def machotools_get_paths(path_name):
"""
Examines the output of otool -l path_name for these three fields:
LC_ID_DYLIB, LC_LOAD_DYLIB, LC_RPATH and parses out the rpaths,
dependiencies and library id.
Returns these values.
"""
try:
import machotools
except ImportError as e:
raise MissingMachotoolsException(e)
idpath = None
rpaths = list()
deps = list()
rewriter = machotools.rewriter_factory(path_name)
if machotools.detect.is_dylib(path_name):
idpath = rewriter.install_name
rpaths = rewriter.rpaths
deps = rewriter.dependencies
return rpaths, deps, idpath
f = open(dll.filename, 'rb+')
for header in dll.headers:
f.seek(0)
dll.write(f)
f.seek(0, 2)
f.flush()
f.close()
except Exception:
pass
return
def strings_contains_installroot(path_name, root_dir):
@ -405,48 +400,41 @@ def relocate_macho_binaries(path_names, old_dir, new_dir, allow_root):
"""
placeholder = set_placeholder(old_dir)
for path_name in path_names:
deps = set()
idpath = None
if platform.system().lower() == 'darwin':
if path_name.endswith('.o'):
continue
else:
rpaths, deps, idpath = macho_get_paths(path_name)
else:
rpaths, deps, idpath = machotools_get_paths(path_name)
# one pass to replace placeholder
(n_rpaths,
n_deps,
n_idpath) = macho_replace_paths(placeholder,
new_dir,
rpaths,
deps,
idpath)
# another pass to replace old_dir
(new_rpaths,
new_deps,
new_idpath) = macho_replace_paths(old_dir,
new_dir,
n_rpaths,
n_deps,
n_idpath)
if path_name.endswith('.o'):
continue
if new_dir == old_dir:
continue
if platform.system().lower() == 'darwin':
rpaths, deps, idpath = macho_get_paths(path_name)
# one pass to replace placeholder
(n_rpaths,
n_deps,
n_idpath) = macho_replace_paths(placeholder,
new_dir,
rpaths,
deps,
idpath)
# another pass to replace old_dir
(new_rpaths,
new_deps,
new_idpath) = macho_replace_paths(old_dir,
new_dir,
n_rpaths,
n_deps,
n_idpath)
modify_macho_object(path_name,
rpaths, deps, idpath,
new_rpaths, new_deps, new_idpath)
else:
modify_object_machotools(path_name,
rpaths, deps, idpath,
new_rpaths, new_deps, new_idpath)
if not new_dir == old_dir:
if len(new_dir) <= len(old_dir):
replace_prefix_bin(path_name, old_dir, new_dir)
else:
tty.warn('Cannot do a binary string replacement'
' with padding for %s'
' because %s is longer than %s' %
(path_name, new_dir, old_dir))
modify_object_macholib(path_name, placeholder, new_dir)
modify_object_macholib(path_name, old_dir, new_dir)
if len(new_dir) <= len(old_dir):
replace_prefix_bin(path_name, old_dir, new_dir)
else:
tty.warn('Cannot do a binary string replacement'
' with padding for %s'
' because %s is longer than %s' %
(path_name, new_dir, old_dir))
def relocate_elf_binaries(path_names, old_dir, new_dir, allow_root):
@ -498,21 +486,13 @@ def make_macho_binaries_relative(cur_path_names, orig_path_names, old_dir,
idpath = None
if platform.system().lower() == 'darwin':
(rpaths, deps, idpath) = macho_get_paths(cur_path)
else:
(rpaths, deps, idpath) = machotools_get_paths(cur_path)
(new_rpaths,
new_deps,
new_idpath) = macho_make_paths_relative(orig_path, old_dir,
rpaths, deps, idpath)
if platform.system().lower() == 'darwin':
(new_rpaths,
new_deps,
new_idpath) = macho_make_paths_relative(orig_path, old_dir,
rpaths, deps, idpath)
modify_macho_object(cur_path,
rpaths, deps, idpath,
new_rpaths, new_deps, new_idpath)
else:
modify_object_machotools(cur_path,
rpaths, deps, idpath,
new_rpaths, new_deps, new_idpath)
if (not allow_root and
not file_is_relocatable(cur_path)):
raise InstallRootStringException(cur_path, old_dir)