New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
building: macOS: use @rpath to rewrite binaries' dependency paths #7664
building: macOS: use @rpath to rewrite binaries' dependency paths #7664
Conversation
bbf38a8
to
79fe2a0
Compare
When rewriting the dylib identifier and paths to linked non-system shared libraries in collected binaries, use `@rpath` instead of directly using `@loader_path`. E.g., for a binary collected as `mypackage/lib/libsomething.dylib`, instead of rewriting the identifier and paths to linked dylibs as `@loader_lib/../../<something>`, rewrite as `@rpath/<something>` and set rpath to `@loader_path/../..`. This ensures that the dylib identifier of a shared library (now `@rpath/libsomething.dylib`) always matches the name/path used by another collected binary to refer to this shared library (the dependency path now also being `@rpath/libsomething.dylib`), even if the shared library and referring binary are located at different directory depth w.r.t. the top-level application directory. Under the old approach that directly uses `@loader_path`, the dylib identifier of shared library might end up being `@loader_path/../../libsomething.dylib`, but the library path in the binary located one directory higher would end up being `@loader_path/../libsomething.dylib`. Mismatches in dylib identifiers are typically not a problem, because the linked dylib is actually found at the specified location. However, some packages do not place shared libraries in "correct" location, and work around this by "preloading" the shared libraries from another location, for example via `ctypes`. In this case, for a binary to recognize an already-loaded library, the referring name and the target dylib's identifier must match. And in PyInstaller's case, the only way to achieve that is to have identifiers (and referring paths) normalized to `@rpath/<name>`, and store the actual relative path to the top-level application directory in the rpath. Using `@rpath`, however, means that we now need to ensure that our rpath is the only one set in the binary; i.e., we need to remove all other rpaths, and add our target rpath if necessary. To do so, we need to use the external macOS-provided `install_name_tool` utility, which we now use for all header modifications: modification of dylib identifier, modification of dependend dylib paths, and removal/addition of rpaths.
79fe2a0
to
67eb981
Compare
The dylib identifier mismatch came up while I was looking into pyinstaller/pyinstaller-hooks-contrib#591. The
but there is actually no rpath set in this binary. So the The only reason the package actually works is that it pre-loads the So in the frozen application, even if we collect the offending shared library via The same sort of problem has been observed with |
It would have been nice if there had been an option to force the old-style behaviour. There's proprietary libraries like ODA out there that have additional runtime modules with a non-dylib extension (.tx*) and the actual dylibs rely specific
|
Can you provide a specific example of the paths and identity of such dylibs and plugins, and what the requirement is (i.e., does it require I'd really prefer not to go back to the old behavior (or having to support both); for libraries with special requirements such as the ones you mention and which we cannot test against, it might make more sense to undo the changes using the |
I can understand not wanting to revert your changes as they probably improve the majority of use-cases on macOS. (I'll also so see if I can provide a reasonably useable example but I suppose this is a very niece case) |
…d commands Extend the new `install_name_tool`-based library path rewriting (introduced in pyinstaller#7664) to the following list of load commands: `LC_LOAD_DYLIB`, `LC_LOAD_UPWARD_DYLIB`, `LC_LOAD_WEAK_DYLIB`, `LC_PREBOUND_DYLIB`, and `LC_REEXPORT_DYLIB`. This should match the behavior of the old `macholib`-based path rewriting.
…d commands (#7704) Extend the new `install_name_tool`-based library path rewriting (introduced in #7664) to the following list of load commands: `LC_LOAD_DYLIB`, `LC_LOAD_UPWARD_DYLIB`, `LC_LOAD_WEAK_DYLIB`, `LC_PREBOUND_DYLIB`, and `LC_REEXPORT_DYLIB`. This should match the behavior of the old `macholib`-based path rewriting. --------- Co-authored-by: Rok Mandeljc <rok.mandeljc@gmail.com>
When rewriting the dylib identifier and paths to linked non-system shared libraries in collected binaries, use
@rpath
instead of directly using@loader_path
.E.g., for a binary collected as
mypackage/lib/libsomething.dylib
, instead of rewriting the identifier and paths to linked dylibs as@loader_lib/../../<something>
, rewrite as@rpath/<something>
and set rpath to@loader_path/../..
.This ensures that the dylib identifier of a shared library (now
@rpath/libsomething.dylib
) always matches the name/path used by another collected binary to refer to this shared library (the dependency path now also being@rpath/libsomething.dylib
), even if the shared library and referring binary are located at different directory depth w.r.t. the top-level application directory. Under the old approach that directly uses@loader_path
, the dylib identifier of shared library might end up being@loader_path/../../libsomething.dylib
, but the library path in the binary located one directory higher would end up being@loader_path/../libsomething.dylib
.Mismatches in dylib identifiers are typically not a problem, because the linked dylib is actually found at the specified location. However, some packages do not place shared libraries in "correct" location, and work around this by "preloading" the shared libraries from another location, for example via
ctypes
. In this case, for a binary to recognize an already-loaded library, the referring name and the target dylib's identifier must match. And in PyInstaller's case, the only way to achieve that is to have identifiers (and referring paths) normalized to@rpath/<name>
, and store the actual relative path to the top-level application directory in the rpath.Using
@rpath
, however, means that we now need to ensure that our rpath is the only one set in the binary; i.e., we need to remove all other rpaths, and add our target rpath if necessary. To do so, we need to use the external macOS-providedinstall_name_tool
utility, which we now use for all header modifications: modification of dylib identifier, modification of dependend dylib paths, and removal/addition of rpaths.