def locate(self, filename, loader=None): if not isinstance(filename, (str, unicode)): raise TypeError("%r is not a string" % (filename,)) if filename.startswith('@loader_path/') and loader is not None: fn = self.trans_table.get((loader.filename, filename)) if fn is None: loader_path = loader.loader_path try: fn = dyld_find( filename, env=self.env, executable_path=self.executable_path, loader_path=loader_path) self.trans_table[(loader.filename, filename)] = fn except ValueError: return None else: fn = self.trans_table.get(filename) if fn is None: try: fn = dyld_find( filename, env=self.env, executable_path=self.executable_path) self.trans_table[filename] = fn except ValueError: return None return fn
def locate(self, filename, loader=None): assert isinstance(filename, (str, unicode)) if filename.startswith('@loader_path/') and loader is not None: fn = self.trans_table.get((loader.filename, filename)) if fn is None: try: fn = dyld_find(filename, env=self.env, executable_path=self.executable_path, #----------------------------------------------- loader_path=loader.filename) # was loader=loader.filename #----------------------------------------------- self.trans_table[(loader.filename, filename)] = fn except ValueError: return None else: fn = self.trans_table.get(filename) if fn is None: try: fn = dyld_find(filename, env=self.env, executable_path=self.executable_path) self.trans_table[filename] = fn except ValueError: return None return fn
def locate(self, filename, loader=None): if not isinstance(filename, (str, unicode)): raise TypeError("%r is not a string" % (filename, )) if filename.startswith("@loader_path/") and loader is not None: fn = self.trans_table.get((loader.filename, filename)) if fn is None: loader_path = loader.loader_path try: fn = dyld_find( filename, env=self.env, executable_path=self.executable_path, loader_path=loader_path, ) self.trans_table[(loader.filename, filename)] = fn except ValueError: return None else: fn = self.trans_table.get(filename) if fn is None: try: fn = dyld_find(filename, env=self.env, executable_path=self.executable_path) self.trans_table[filename] = fn except ValueError: return None return fn
def testBasic(self): self.assertEqual(dyld.dyld_find("libSystem.dylib"), "/usr/lib/libSystem.dylib") self.assertEqual( dyld.dyld_find("System.framework/System"), "/System/Library/Frameworks/System.framework/System", )
def locate(self, filename): fn = self.trans_table.get(filename) if fn is None: try: fn = dyld_find(filename, env=self.env, executable_path=self.executable_path) self.trans_table[filename] = fn except ValueError: return None return fn
def locate(self, filename): assert isinstance(filename, (str, unicode)) fn = self.trans_table.get(filename) if fn is None: try: fn = dyld_find(filename, env=self.env, executable_path=self.executable_path) self.trans_table[filename] = fn except ValueError: return None return fn
def test_dyld_find(self): result = dyld.dyld_find('/usr/lib/libSystem.dylib') self.assertEqual(result, '/usr/lib/libSystem.dylib') self.assertIsInstance(result, str) # bytes on 2.x, unicode on 3.x result = dyld.dyld_find(b'/usr/lib/libSystem.dylib'.decode('ascii')) self.assertEqual(result, '/usr/lib/libSystem.dylib') self.assertIsInstance(result, str) # bytes on 2.x, unicode on 3.x patcher = DyldPatcher() try: patcher.log_calls('dyld_image_suffix_search') patcher.log_calls('dyld_override_search') patcher.log_calls('dyld_executable_path_search') patcher.log_calls('dyld_default_search') result = dyld.dyld_find('/usr/lib/libSystem.dylib') self.assertEqual(patcher.calls[:-1], [ ('dyld_override_search', ('/usr/lib/libSystem.dylib', None), {}), ('dyld_executable_path_search', ('/usr/lib/libSystem.dylib', None), {}), ('dyld_default_search', ('/usr/lib/libSystem.dylib', None), {}), ]) self.assertEqual(patcher.calls[-1][0], 'dyld_image_suffix_search') patcher.clear_calls() result = dyld.dyld_find('/usr/lib/libSystem.dylib', env=None) self.assertEqual(patcher.calls[:-1], [ ('dyld_override_search', ('/usr/lib/libSystem.dylib', None), {}), ('dyld_executable_path_search', ('/usr/lib/libSystem.dylib', None), {}), ('dyld_default_search', ('/usr/lib/libSystem.dylib', None), {}), ]) self.assertEqual(patcher.calls[-1][0], 'dyld_image_suffix_search') patcher.clear_calls() result = dyld.dyld_find('/usr/lib/libSystem.dylib', env={}) self.assertEqual(patcher.calls[:-1], [ ('dyld_override_search', ('/usr/lib/libSystem.dylib', {}), {}), ('dyld_executable_path_search', ('/usr/lib/libSystem.dylib', None), {}), ('dyld_default_search', ('/usr/lib/libSystem.dylib', {}), {}), ]) self.assertEqual(patcher.calls[-1][0], 'dyld_image_suffix_search') patcher.clear_calls() result = dyld.dyld_find('/usr/lib/libSystem.dylib', executable_path="/opt/py2app/bin", env={}) self.assertEqual(patcher.calls[:-1], [ ('dyld_override_search', ('/usr/lib/libSystem.dylib', {}), {}), ('dyld_executable_path_search', ('/usr/lib/libSystem.dylib', "/opt/py2app/bin"), {}), ('dyld_default_search', ('/usr/lib/libSystem.dylib', {}), {}), ]) self.assertEqual(patcher.calls[-1][0], 'dyld_image_suffix_search') patcher.clear_calls() finally: patcher.cleanup()
def _getImports_macholib(pth): """ Find the binary dependencies of PTH. This implementation is for Mac OS X and uses library macholib. """ from macholib.MachO import MachO from macholib.mach_o import LC_RPATH from macholib.dyld import dyld_find from macholib.util import in_system_path rslt = set() seen = set() # Libraries read from binary headers. ## Walk through mach binary headers. m = MachO(pth) for header in m.headers: for idx, name, lib in header.walkRelocatables(): # Sometimes some libraries are present multiple times. if lib not in seen: seen.add(lib) # Walk through mach binary headers and look for LC_RPATH. # macholib can't handle @rpath. LC_RPATH has to be read # from the MachO header. # TODO Do we need to remove LC_RPATH from MachO load commands? # Will it cause any harm to leave them untouched? # Removing LC_RPATH should be implemented when getting # files from the bincache if it is necessary. run_paths = set() for header in m.headers: for command in header.commands: # A command is a tupple like: # (<macholib.mach_o.load_command object at 0x>, # <macholib.mach_o.rpath_command object at 0x>, # '../lib\x00\x00') cmd_type = command[0].cmd if cmd_type == LC_RPATH: rpath = command[2].decode('utf-8') # Remove trailing '\x00' characters. # e.g. '../lib\x00\x00' rpath = rpath.rstrip('\x00') # Replace the @executable_path and @loader_path keywords # with the actual path to the binary. executable_path = os.path.dirname(pth) rpath = re.sub('^@(executable_path|loader_path|rpath)(/|$)', executable_path + r'\2', rpath) # Make rpath absolute. According to Apple doc LC_RPATH # is always relative to the binary location. rpath = os.path.normpath(os.path.join(executable_path, rpath)) run_paths.update([rpath]) else: # Frameworks that have this structure Name.framework/Versions/N/Name # need to to search at the same level as the framework dir. # This is specifically needed so that the QtWebEngine dependencies # can be found. if '.framework' in pth: run_paths.update(['../../../']) # for distributions like Anaconda, all of the dylibs are stored in the lib directory # of the Python distribution, not alongside of the .so's in each module's subdirectory. run_paths.add(os.path.join(base_prefix, 'lib')) ## Try to find files in file system. # In cases with @loader_path or @executable_path # try to look in the same directory as the checked binary is. # This seems to work in most cases. exec_path = os.path.abspath(os.path.dirname(pth)) for lib in seen: # Suppose that @rpath is not used for system libraries and # using macholib can be avoided. # macholib can't handle @rpath. if lib.startswith('@rpath'): lib = lib.replace('@rpath', '.') # Make path relative. final_lib = None # Absolute path to existing lib on disk. # Try multiple locations. for run_path in run_paths: # @rpath may contain relative value. Use exec_path as # base path. if not os.path.isabs(run_path): run_path = os.path.join(exec_path, run_path) # Stop looking for lib when found in first location. if os.path.exists(os.path.join(run_path, lib)): final_lib = os.path.abspath(os.path.join(run_path, lib)) rslt.add(final_lib) break # Log error if no existing file found. if not final_lib: logger.error('Can not find path %s (needed by %s)', lib, pth) # Macholib has to be used to get absolute path to libraries. else: # macholib can't handle @loader_path. It has to be # handled the same way as @executable_path. # It is also replaced by 'exec_path'. if lib.startswith('@loader_path'): lib = lib.replace('@loader_path', '@executable_path') try: lib = dyld_find(lib, executable_path=exec_path) rslt.add(lib) except ValueError: # Starting with Big Sur, system libraries are hidden. And # we do not collect system libraries on any macOS version # anyway, so suppress the corresponding error messages. if not in_system_path(lib): logger.error('Can not find path %s (needed by %s)', lib, pth) return rslt
def _getImports_macholib(pth): """ Find the binary dependencies of PTH. This implementation is for Mac OS X and uses library macholib. """ from macholib.MachO import MachO from macholib.mach_o import LC_RPATH from macholib.dyld import dyld_find rslt = set() seen = set() # Libraries read from binary headers. ## Walk through mach binary headers. m = MachO(pth) for header in m.headers: for idx, name, lib in header.walkRelocatables(): # Sometimes some libraries are present multiple times. if lib not in seen: seen.add(lib) # Walk through mach binary headers and look for LC_RPATH. # macholib can't handle @rpath. LC_RPATH has to be read # from the MachO header. # TODO Do we need to remove LC_RPATH from MachO load commands? # Will it cause any harm to leave them untouched? # Removing LC_RPATH should be implemented when getting # files from the bincache if it is necessary. run_paths = set() for header in m.headers: for command in header.commands: # A command is a tupple like: # (<macholib.mach_o.load_command object at 0x>, # <macholib.mach_o.rpath_command object at 0x>, # '../lib\x00\x00') cmd_type = command[0].cmd if cmd_type == LC_RPATH: rpath = command[2].decode('utf-8') # Remove trailing '\x00' characters. # e.g. '../lib\x00\x00' rpath = rpath.rstrip('\x00') # Replace the @executable_path and @loader_path keywords # with the actual path to the binary. executable_path = os.path.dirname(pth) rpath = re.sub('^@(executable_path|loader_path|rpath)/', executable_path + '/', rpath) # Make rpath absolute. According to Apple doc LC_RPATH # is always relative to the binary location. rpath = os.path.normpath(os.path.join(executable_path, rpath)) run_paths.update([rpath]) else: # Frameworks that have this structure Name.framework/Versions/N/Name # need to to search at the same level as the framework dir. # This is specifically needed so that the QtWebEngine dependencies # can be found. if '.framework' in pth: run_paths.update(['../../../']) # for distributions like Anaconda, all of the dylibs are stored in the lib directory # of the Python distribution, not alongside of the .so's in each module's subdirectory. run_paths.add(os.path.join(base_prefix, 'lib')) ## Try to find files in file system. # In cases with @loader_path or @executable_path # try to look in the same directory as the checked binary is. # This seems to work in most cases. exec_path = os.path.abspath(os.path.dirname(pth)) for lib in seen: # Suppose that @rpath is not used for system libraries and # using macholib can be avoided. # macholib can't handle @rpath. if lib.startswith('@rpath'): lib = lib.replace('@rpath', '.') # Make path relative. final_lib = None # Absolute path to existing lib on disk. # Try multiple locations. for run_path in run_paths: # @rpath may contain relative value. Use exec_path as # base path. if not os.path.isabs(run_path): run_path = os.path.join(exec_path, run_path) # Stop looking for lib when found in first location. if os.path.exists(os.path.join(run_path, lib)): final_lib = os.path.abspath(os.path.join(run_path, lib)) rslt.add(final_lib) break # Log error if no existing file found. if not final_lib: logger.error('Can not find path %s (needed by %s)', lib, pth) # Macholib has to be used to get absolute path to libraries. else: # macholib can't handle @loader_path. It has to be # handled the same way as @executable_path. # It is also replaced by 'exec_path'. if lib.startswith('@loader_path'): lib = lib.replace('@loader_path', '@executable_path') try: lib = dyld_find(lib, executable_path=exec_path) rslt.add(lib) except ValueError: logger.error('Can not find path %s (needed by %s)', lib, pth) return rslt
def testBasic(self): self.assertEqual(dyld.dyld_find('libSystem.dylib'), '/usr/lib/libSystem.dylib') self.assertEqual(dyld.dyld_find('System.framework/System'), '/System/Library/Frameworks/System.framework/System')
def test_dyld_find(self): result = dyld.dyld_find("/usr/lib/libSystem.dylib") self.assertEqual(result, "/usr/lib/libSystem.dylib") self.assertIsInstance(result, str) # bytes on 2.x, unicode on 3.x result = dyld.dyld_find(b"/usr/lib/libSystem.dylib".decode("ascii")) self.assertEqual(result, "/usr/lib/libSystem.dylib") self.assertIsInstance(result, str) # bytes on 2.x, unicode on 3.x patcher = DyldPatcher() try: patcher.log_calls("dyld_image_suffix_search") patcher.log_calls("dyld_override_search") patcher.log_calls("dyld_executable_path_search") patcher.log_calls("dyld_default_search") result = dyld.dyld_find("/usr/lib/libSystem.dylib") self.assertEqual( patcher.calls[:-1], [ ("dyld_override_search", ("/usr/lib/libSystem.dylib", None), {}), ( "dyld_executable_path_search", ("/usr/lib/libSystem.dylib", None), {}, ), ("dyld_default_search", ("/usr/lib/libSystem.dylib", None), {}), ], ) self.assertEqual(patcher.calls[-1][0], "dyld_image_suffix_search") patcher.clear_calls() result = dyld.dyld_find("/usr/lib/libSystem.dylib", env=None) self.assertEqual( patcher.calls[:-1], [ ("dyld_override_search", ("/usr/lib/libSystem.dylib", None), {}), ( "dyld_executable_path_search", ("/usr/lib/libSystem.dylib", None), {}, ), ("dyld_default_search", ("/usr/lib/libSystem.dylib", None), {}), ], ) self.assertEqual(patcher.calls[-1][0], "dyld_image_suffix_search") patcher.clear_calls() result = dyld.dyld_find("/usr/lib/libSystem.dylib", env={}) self.assertEqual( patcher.calls[:-1], [ ("dyld_override_search", ("/usr/lib/libSystem.dylib", {}), {}), ( "dyld_executable_path_search", ("/usr/lib/libSystem.dylib", None), {}, ), ("dyld_default_search", ("/usr/lib/libSystem.dylib", {}), {}), ], ) self.assertEqual(patcher.calls[-1][0], "dyld_image_suffix_search") patcher.clear_calls() result = dyld.dyld_find("/usr/lib/libSystem.dylib", executable_path="/opt/py2app/bin", env={}) self.assertEqual( patcher.calls[:-1], [ ("dyld_override_search", ("/usr/lib/libSystem.dylib", {}), {}), ( "dyld_executable_path_search", ("/usr/lib/libSystem.dylib", "/opt/py2app/bin"), {}, ), ("dyld_default_search", ("/usr/lib/libSystem.dylib", {}), {}), ], ) self.assertEqual(patcher.calls[-1][0], "dyld_image_suffix_search") patcher.clear_calls() finally: patcher.cleanup()