Fix relocating MachO binary, when store projection changes (#46840)
* Remove "modify_object_macholib" According to documentation, this function is used when installing Mach-O binaries on linux. The implementation seems questionable at least, and the code seems to be never hit (Spack currently doesn't support installing Mach-O binaries on linux). * Fix relocation on macOS, when store projection changes --------- Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
This commit is contained in:
parent
d7643d4f88
commit
d70e9e131d
@ -205,23 +205,33 @@ def macho_find_paths(orig_rpaths, deps, idpath, old_layout_root, prefix_to_prefi
|
|||||||
paths_to_paths dictionary which maps all of the old paths to new paths
|
paths_to_paths dictionary which maps all of the old paths to new paths
|
||||||
"""
|
"""
|
||||||
paths_to_paths = dict()
|
paths_to_paths = dict()
|
||||||
|
# Sort from longest path to shortest, to ensure we try /foo/bar/baz before /foo/bar
|
||||||
|
prefix_iteration_order = sorted(prefix_to_prefix, key=len, reverse=True)
|
||||||
for orig_rpath in orig_rpaths:
|
for orig_rpath in orig_rpaths:
|
||||||
if orig_rpath.startswith(old_layout_root):
|
if orig_rpath.startswith(old_layout_root):
|
||||||
for old_prefix, new_prefix in prefix_to_prefix.items():
|
for old_prefix in prefix_iteration_order:
|
||||||
|
new_prefix = prefix_to_prefix[old_prefix]
|
||||||
if orig_rpath.startswith(old_prefix):
|
if orig_rpath.startswith(old_prefix):
|
||||||
new_rpath = re.sub(re.escape(old_prefix), new_prefix, orig_rpath)
|
new_rpath = re.sub(re.escape(old_prefix), new_prefix, orig_rpath)
|
||||||
paths_to_paths[orig_rpath] = new_rpath
|
paths_to_paths[orig_rpath] = new_rpath
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
paths_to_paths[orig_rpath] = orig_rpath
|
paths_to_paths[orig_rpath] = orig_rpath
|
||||||
|
|
||||||
if idpath:
|
if idpath:
|
||||||
for old_prefix, new_prefix in prefix_to_prefix.items():
|
for old_prefix in prefix_iteration_order:
|
||||||
|
new_prefix = prefix_to_prefix[old_prefix]
|
||||||
if idpath.startswith(old_prefix):
|
if idpath.startswith(old_prefix):
|
||||||
paths_to_paths[idpath] = re.sub(re.escape(old_prefix), new_prefix, idpath)
|
paths_to_paths[idpath] = re.sub(re.escape(old_prefix), new_prefix, idpath)
|
||||||
|
break
|
||||||
|
|
||||||
for dep in deps:
|
for dep in deps:
|
||||||
for old_prefix, new_prefix in prefix_to_prefix.items():
|
for old_prefix in prefix_iteration_order:
|
||||||
|
new_prefix = prefix_to_prefix[old_prefix]
|
||||||
if dep.startswith(old_prefix):
|
if dep.startswith(old_prefix):
|
||||||
paths_to_paths[dep] = re.sub(re.escape(old_prefix), new_prefix, dep)
|
paths_to_paths[dep] = re.sub(re.escape(old_prefix), new_prefix, dep)
|
||||||
|
break
|
||||||
|
|
||||||
if dep.startswith("@"):
|
if dep.startswith("@"):
|
||||||
paths_to_paths[dep] = dep
|
paths_to_paths[dep] = dep
|
||||||
|
|
||||||
@ -270,36 +280,6 @@ def modify_macho_object(cur_path, rpaths, deps, idpath, paths_to_paths):
|
|||||||
install_name_tool = executable.Executable("install_name_tool")
|
install_name_tool = executable.Executable("install_name_tool")
|
||||||
install_name_tool(*args)
|
install_name_tool(*args)
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def modify_object_macholib(cur_path, paths_to_paths):
|
|
||||||
"""
|
|
||||||
This function is used when install machO buildcaches on linux by
|
|
||||||
rewriting mach-o loader commands for dependency library paths of
|
|
||||||
mach-o binaries and the id path for mach-o libraries.
|
|
||||||
Rewritting of rpaths is handled by replace_prefix_bin.
|
|
||||||
Inputs
|
|
||||||
mach-o binary to be modified
|
|
||||||
dictionary mapping paths in old install layout to new install layout
|
|
||||||
"""
|
|
||||||
|
|
||||||
dll = macholib.MachO.MachO(cur_path)
|
|
||||||
dll.rewriteLoadCommands(paths_to_paths.get)
|
|
||||||
|
|
||||||
try:
|
|
||||||
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 macholib_get_paths(cur_path):
|
def macholib_get_paths(cur_path):
|
||||||
"""Get rpaths, dependent libraries, and library id of mach-o objects."""
|
"""Get rpaths, dependent libraries, and library id of mach-o objects."""
|
||||||
@ -415,10 +395,7 @@ def relocate_macho_binaries(
|
|||||||
# normalized paths
|
# normalized paths
|
||||||
rel_to_orig = macho_make_paths_normal(orig_path_name, rpaths, deps, idpath)
|
rel_to_orig = macho_make_paths_normal(orig_path_name, rpaths, deps, idpath)
|
||||||
# replace the relativized paths with normalized paths
|
# replace the relativized paths with normalized paths
|
||||||
if sys.platform == "darwin":
|
|
||||||
modify_macho_object(path_name, rpaths, deps, idpath, rel_to_orig)
|
modify_macho_object(path_name, rpaths, deps, idpath, rel_to_orig)
|
||||||
else:
|
|
||||||
modify_object_macholib(path_name, rel_to_orig)
|
|
||||||
# get the normalized paths in the mach-o binary
|
# get the normalized paths in the mach-o binary
|
||||||
rpaths, deps, idpath = macholib_get_paths(path_name)
|
rpaths, deps, idpath = macholib_get_paths(path_name)
|
||||||
# get the mapping of paths in old prefix to path in new prefix
|
# get the mapping of paths in old prefix to path in new prefix
|
||||||
@ -426,10 +403,7 @@ def relocate_macho_binaries(
|
|||||||
rpaths, deps, idpath, old_layout_root, prefix_to_prefix
|
rpaths, deps, idpath, old_layout_root, prefix_to_prefix
|
||||||
)
|
)
|
||||||
# replace the old paths with new paths
|
# replace the old paths with new paths
|
||||||
if sys.platform == "darwin":
|
|
||||||
modify_macho_object(path_name, rpaths, deps, idpath, paths_to_paths)
|
modify_macho_object(path_name, rpaths, deps, idpath, paths_to_paths)
|
||||||
else:
|
|
||||||
modify_object_macholib(path_name, paths_to_paths)
|
|
||||||
# get the new normalized path in the mach-o binary
|
# get the new normalized path in the mach-o binary
|
||||||
rpaths, deps, idpath = macholib_get_paths(path_name)
|
rpaths, deps, idpath = macholib_get_paths(path_name)
|
||||||
# get the mapping of paths to relative paths in the new prefix
|
# get the mapping of paths to relative paths in the new prefix
|
||||||
@ -437,10 +411,7 @@ def relocate_macho_binaries(
|
|||||||
path_name, new_layout_root, rpaths, deps, idpath
|
path_name, new_layout_root, rpaths, deps, idpath
|
||||||
)
|
)
|
||||||
# replace the new paths with relativized paths in the new prefix
|
# replace the new paths with relativized paths in the new prefix
|
||||||
if sys.platform == "darwin":
|
|
||||||
modify_macho_object(path_name, rpaths, deps, idpath, paths_to_paths)
|
modify_macho_object(path_name, rpaths, deps, idpath, paths_to_paths)
|
||||||
else:
|
|
||||||
modify_object_macholib(path_name, paths_to_paths)
|
|
||||||
else:
|
else:
|
||||||
# get the paths in the old prefix
|
# get the paths in the old prefix
|
||||||
rpaths, deps, idpath = macholib_get_paths(path_name)
|
rpaths, deps, idpath = macholib_get_paths(path_name)
|
||||||
@ -449,10 +420,7 @@ def relocate_macho_binaries(
|
|||||||
rpaths, deps, idpath, old_layout_root, prefix_to_prefix
|
rpaths, deps, idpath, old_layout_root, prefix_to_prefix
|
||||||
)
|
)
|
||||||
# replace the old paths with new paths
|
# replace the old paths with new paths
|
||||||
if sys.platform == "darwin":
|
|
||||||
modify_macho_object(path_name, rpaths, deps, idpath, paths_to_paths)
|
modify_macho_object(path_name, rpaths, deps, idpath, paths_to_paths)
|
||||||
else:
|
|
||||||
modify_object_macholib(path_name, paths_to_paths)
|
|
||||||
|
|
||||||
|
|
||||||
def _transform_rpaths(orig_rpaths, orig_root, new_prefixes):
|
def _transform_rpaths(orig_rpaths, orig_root, new_prefixes):
|
||||||
|
@ -549,3 +549,35 @@ def test_fetch_external_package_is_noop(default_mock_concretization, fetching_no
|
|||||||
spec.external_path = "/some/where"
|
spec.external_path = "/some/where"
|
||||||
assert spec.external
|
assert spec.external
|
||||||
spec.package.do_fetch()
|
spec.package.do_fetch()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"relocation_dict",
|
||||||
|
[
|
||||||
|
{"/foo/bar/baz": "/a/b/c", "/foo/bar": "/a/b"},
|
||||||
|
# Ensure correctness does not depend on the ordering of the dict
|
||||||
|
{"/foo/bar": "/a/b", "/foo/bar/baz": "/a/b/c"},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_macho_relocation_with_changing_projection(relocation_dict):
|
||||||
|
"""Tests that prefix relocation is computed correctly when the prefixes to be relocated
|
||||||
|
contain a directory and its subdirectories.
|
||||||
|
|
||||||
|
This happens when relocating to a new place AND changing the store projection. In that case we
|
||||||
|
might have a relocation dict like:
|
||||||
|
|
||||||
|
/foo/bar/baz/ -> /a/b/c
|
||||||
|
/foo/bar -> /a/b
|
||||||
|
|
||||||
|
What we need to check is that we don't end up in situations where we relocate to a mixture of
|
||||||
|
the two schemes, like /a/b/baz.
|
||||||
|
"""
|
||||||
|
original_rpath = "/foo/bar/baz/abcdef"
|
||||||
|
result = macho_find_paths(
|
||||||
|
[original_rpath],
|
||||||
|
deps=[],
|
||||||
|
idpath=None,
|
||||||
|
old_layout_root="/foo",
|
||||||
|
prefix_to_prefix=relocation_dict,
|
||||||
|
)
|
||||||
|
assert result[original_rpath] == "/a/b/c/abcdef"
|
||||||
|
Loading…
Reference in New Issue
Block a user