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:
		| @@ -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 |     # so the pathname should be the same now that the directory layout | ||||||
|     # is confirmed |     # is confirmed | ||||||
|     workdir = os.path.join(tmpdir, os.path.basename(spec.prefix)) |     workdir = os.path.join(tmpdir, os.path.basename(spec.prefix)) | ||||||
|  |     install_tree(workdir, spec.prefix, symlinks=True) | ||||||
|  |  | ||||||
|     # cleanup |     # cleanup | ||||||
|     os.remove(tarfile_path) |     os.remove(tarfile_path) | ||||||
|     os.remove(specfile_path) |     os.remove(specfile_path) | ||||||
|  |  | ||||||
|     try: |     try: | ||||||
|         relocate_package(workdir, spec, allow_root) |         relocate_package(spec.prefix, spec, allow_root) | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
|         shutil.rmtree(workdir) |         shutil.rmtree(spec.prefix) | ||||||
|         tty.die(e) |         tty.die(e) | ||||||
|     # Delay creating spec.prefix until verification is complete |     # Delay creating spec.prefix until verification is complete | ||||||
|     # and any relocation has been done. |     # and any relocation has been done. | ||||||
|     else: |  | ||||||
|         install_tree(workdir, spec.prefix, symlinks=True) |  | ||||||
|     finally: |     finally: | ||||||
|         shutil.rmtree(tmpdir) |         shutil.rmtree(tmpdir) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -41,20 +41,20 @@ def __init__(self, file_path, old_len, new_len): | |||||||
|             (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. |     Raised when the size of the file changes after binary path substitution. | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     def __init__(self, error): |     def __init__(self, error): | ||||||
|         super(MissingMachotoolsException, self).__init__( |         super(MissingMacholibException, self).__init__( | ||||||
|             "%s\n" |             "%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" |             "and modify a mach-o binary's rpaths, deps and id.\n" | ||||||
|             "Use virtualenv with pip install machotools or\n" |             "Use virtualenv with pip install macholib or\n" | ||||||
|             "use spack to install the py-machotools package\n" |             "use spack to install the py-macholib package\n" | ||||||
|             "spack install py-machotools\n" |             "spack install py-macholib\n" | ||||||
|             "spack activate py-machotools\n" |             "spack activate py-macholib\n" | ||||||
|             "spack load python\n" |             "spack load python\n" | ||||||
|             % error) |             % error) | ||||||
|  |  | ||||||
| @@ -275,55 +275,50 @@ def modify_macho_object(cur_path, rpaths, deps, idpath, | |||||||
|     return |     return | ||||||
|  |  | ||||||
|  |  | ||||||
| def modify_object_machotools(cur_path, rpaths, deps, idpath, | def modify_object_macholib(cur_path, old_dir, new_dir): | ||||||
|                              new_rpaths, new_deps, new_idpath): |  | ||||||
|     """ |     """ | ||||||
|     Modify MachO binary path_name by replacing old_dir with new_dir |     Modify MachO binary path_name by replacing old_dir with new_dir | ||||||
|     or the relative path to spack install root. |     or the relative path to spack install root. | ||||||
|     The old install dir in LC_ID_DYLIB is replaced with the new install dir |     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 |     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 |     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'): |     if cur_path.endswith('.o'): | ||||||
|         return |         return | ||||||
|     try: |     try: | ||||||
|         import machotools |         from macholib.MachO import MachO | ||||||
|  |         from macholib.mach_o import LC_RPATH | ||||||
|     except ImportError as e: |     except ImportError as e: | ||||||
|         raise MissingMachotoolsException(e) |         raise MissingMacholibException(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 |  | ||||||
|  |  | ||||||
|  |     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: |     try: | ||||||
|         import machotools |         f = open(dll.filename, 'rb+') | ||||||
|     except ImportError as e: |         for header in dll.headers: | ||||||
|         raise MissingMachotoolsException(e) |             f.seek(0) | ||||||
|     idpath = None |             dll.write(f) | ||||||
|     rpaths = list() |         f.seek(0, 2) | ||||||
|     deps = list() |         f.flush() | ||||||
|     rewriter = machotools.rewriter_factory(path_name) |         f.close() | ||||||
|     if machotools.detect.is_dylib(path_name): |     except Exception: | ||||||
|         idpath = rewriter.install_name |         pass | ||||||
|     rpaths = rewriter.rpaths |  | ||||||
|     deps = rewriter.dependencies |     return | ||||||
|     return rpaths, deps, idpath |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def strings_contains_installroot(path_name, root_dir): | def strings_contains_installroot(path_name, root_dir): | ||||||
| @@ -405,15 +400,12 @@ def relocate_macho_binaries(path_names, old_dir, new_dir, allow_root): | |||||||
|     """ |     """ | ||||||
|     placeholder = set_placeholder(old_dir) |     placeholder = set_placeholder(old_dir) | ||||||
|     for path_name in path_names: |     for path_name in path_names: | ||||||
|         deps = set() |  | ||||||
|         idpath = None |  | ||||||
|         if platform.system().lower() == 'darwin': |  | ||||||
|         if path_name.endswith('.o'): |         if path_name.endswith('.o'): | ||||||
|             continue |             continue | ||||||
|             else: |         if new_dir == old_dir: | ||||||
|  |             continue | ||||||
|  |         if platform.system().lower() == 'darwin': | ||||||
|             rpaths, deps, idpath = macho_get_paths(path_name) |             rpaths, deps, idpath = macho_get_paths(path_name) | ||||||
|         else: |  | ||||||
|             rpaths, deps, idpath = machotools_get_paths(path_name) |  | ||||||
|             # one pass to replace placeholder |             # one pass to replace placeholder | ||||||
|             (n_rpaths, |             (n_rpaths, | ||||||
|              n_deps, |              n_deps, | ||||||
| @@ -430,16 +422,12 @@ def relocate_macho_binaries(path_names, old_dir, new_dir, allow_root): | |||||||
|                                                n_rpaths, |                                                n_rpaths, | ||||||
|                                                n_deps, |                                                n_deps, | ||||||
|                                                n_idpath) |                                                n_idpath) | ||||||
|         if platform.system().lower() == 'darwin': |  | ||||||
|             modify_macho_object(path_name, |             modify_macho_object(path_name, | ||||||
|                                 rpaths, deps, idpath, |                                 rpaths, deps, idpath, | ||||||
|                                 new_rpaths, new_deps, new_idpath) |                                 new_rpaths, new_deps, new_idpath) | ||||||
|         else: |         else: | ||||||
|             modify_object_machotools(path_name, |             modify_object_macholib(path_name, placeholder, new_dir) | ||||||
|                                      rpaths, deps, idpath, |             modify_object_macholib(path_name, old_dir, new_dir) | ||||||
|                                      new_rpaths, new_deps, new_idpath) |  | ||||||
|  |  | ||||||
|         if not new_dir == old_dir: |  | ||||||
|         if len(new_dir) <= len(old_dir): |         if len(new_dir) <= len(old_dir): | ||||||
|             replace_prefix_bin(path_name, old_dir, new_dir) |             replace_prefix_bin(path_name, old_dir, new_dir) | ||||||
|         else: |         else: | ||||||
| @@ -498,21 +486,13 @@ def make_macho_binaries_relative(cur_path_names, orig_path_names, old_dir, | |||||||
|         idpath = None |         idpath = None | ||||||
|         if platform.system().lower() == 'darwin': |         if platform.system().lower() == 'darwin': | ||||||
|             (rpaths, deps, idpath) = macho_get_paths(cur_path) |             (rpaths, deps, idpath) = macho_get_paths(cur_path) | ||||||
|         else: |  | ||||||
|             (rpaths, deps, idpath) = machotools_get_paths(cur_path) |  | ||||||
|             (new_rpaths, |             (new_rpaths, | ||||||
|              new_deps, |              new_deps, | ||||||
|              new_idpath) = macho_make_paths_relative(orig_path, old_dir, |              new_idpath) = macho_make_paths_relative(orig_path, old_dir, | ||||||
|                                                      rpaths, deps, idpath) |                                                      rpaths, deps, idpath) | ||||||
|         if platform.system().lower() == 'darwin': |  | ||||||
|             modify_macho_object(cur_path, |             modify_macho_object(cur_path, | ||||||
|                                 rpaths, deps, idpath, |                                 rpaths, deps, idpath, | ||||||
|                                 new_rpaths, new_deps, new_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 |         if (not allow_root and | ||||||
|                 not file_is_relocatable(cur_path)): |                 not file_is_relocatable(cur_path)): | ||||||
|             raise InstallRootStringException(cur_path, old_dir) |             raise InstallRootStringException(cur_path, old_dir) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Patrick Gartung
					Patrick Gartung