From 4cfe70ce5093e030a964b7612e3709dd68d05cda Mon Sep 17 00:00:00 2001 From: Gabriel Gerlero Date: Thu, 6 Jul 2023 12:36:15 -0300 Subject: [PATCH 1/2] mac/keg_relocate: use relative install names --- .../Homebrew/extend/os/mac/keg_relocate.rb | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index 24435b0b76..83888fb51d 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -45,20 +45,24 @@ class Keg change_dylib_id(dylib_id_for(file), file) if file.dylib? each_linkage_for(file, :dynamically_linked_libraries) do |bad_name| - # Don't fix absolute paths unless they are rooted in the build directory - next if bad_name.start_with?("/") && - !rooted_in_build_directory?(bad_name) - - new_name = fixed_name(file, bad_name) - change_install_name(bad_name, new_name, file) if new_name != bad_name + # Don't fix absolute paths unless they are rooted in the build directory. + new_name = if bad_name.start_with?("/") && !rooted_in_build_directory?(bad_name) + bad_name + else + fixed_name(file, bad_name) + end + loader_name = loader_name_for(file, new_name) + change_install_name(bad_name, loader_name, file) if loader_name != bad_name end each_linkage_for(file, :rpaths) do |bad_name| # Strip duplicate rpaths and rpaths rooted in the build directory. - next if !rooted_in_build_directory?(bad_name) && - (file.rpaths.count(bad_name) == 1) - - delete_rpath(bad_name, file) + if rooted_in_build_directory?(bad_name) || (file.rpaths.count(bad_name) > 1) + delete_rpath(bad_name, file) + else + loader_name = loader_name_for(file, bad_name) + change_rpath(bad_name, loader_name, file) if loader_name != bad_name + end end end end @@ -66,6 +70,15 @@ class Keg generic_fix_dynamic_linkage end + def loader_name_for(file, target) + # Use @loader_path-relative install names for other Homebrew-installed binaries. + if ENV["HOMEBREW_RELOCATABLE_INSTALL_NAMES"] && target.start_with?(HOMEBREW_PREFIX) + "@loader_path/#{Pathname(target).relative_path_from(file.dirname)}" + else + target + end + end + # If file is a dylib or bundle itself, look for the dylib named by # bad_name relative to the lib directory, so that we can skip the more # expensive recursive search if possible. From 15a0c7fd7df2cae55e3b702a04e7bbf1ad401f27 Mon Sep 17 00:00:00 2001 From: Gabriel Gerlero Date: Tue, 20 Jun 2023 16:42:13 -0300 Subject: [PATCH 2/2] linkage_checker: resolve some variable install names on macOS --- .../Homebrew/extend/os/mac/keg_relocate.rb | 2 +- Library/Homebrew/linkage_checker.rb | 12 ++++++-- Library/Homebrew/os/mac/mach.rb | 28 +++++++++++++++++-- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index 83888fb51d..c36e0ffb7e 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -103,7 +103,7 @@ class Keg def each_linkage_for(file, linkage_type, &block) links = file.method(linkage_type) - .call + .call(resolve_variable_references: false) .grep_v(/^@(loader_|executable_|r)path/) links.each(&block) end diff --git a/Library/Homebrew/linkage_checker.rb b/Library/Homebrew/linkage_checker.rb index 17c4542b3c..d7646826f3 100644 --- a/Library/Homebrew/linkage_checker.rb +++ b/Library/Homebrew/linkage_checker.rb @@ -31,6 +31,7 @@ class LinkageChecker @unwanted_system_dylibs = [] @version_conflict_deps = [] @files_missing_rpaths = [] + @executable_path_dylibs = [] check_dylibs(rebuild_cache: rebuild_cache) end @@ -39,13 +40,14 @@ class LinkageChecker display_items "System libraries", @system_dylibs display_items "Homebrew libraries", @brewed_dylibs display_items "Indirect dependencies with linkage", @indirect_deps - display_items "Variable-referenced libraries", @variable_dylibs + display_items "@rpath-referenced libraries", @variable_dylibs display_items "Missing libraries", @broken_dylibs display_items "Broken dependencies", @broken_deps display_items "Undeclared dependencies with linkage", @undeclared_deps display_items "Dependencies with no linkage", @unnecessary_deps display_items "Unwanted system libraries", @unwanted_system_dylibs display_items "Files with missing rpath", @files_missing_rpaths + display_items "@executable_path references in libraries", @executable_path_dylibs end alias generic_display_normal_output display_normal_output private :generic_display_normal_output @@ -74,6 +76,7 @@ class LinkageChecker display_items "Undeclared dependencies with linkage", @undeclared_deps, puts_output: puts_output display_items "Files with missing rpath", @files_missing_rpaths, puts_output: puts_output + display_items "@executable_path references in libraries", @executable_path_dylibs, puts_output: puts_output end alias generic_display_test_output display_test_output private :generic_display_test_output @@ -85,7 +88,7 @@ class LinkageChecker issues = [@broken_deps, unexpected_broken_dylibs] if test issues += [@unwanted_system_dylibs, @version_conflict_deps, unexpected_present_dylibs] - issues += [@undeclared_deps, @files_missing_rpaths] if strict + issues += [@undeclared_deps, @files_missing_rpaths, @executable_path_dylibs] if strict end issues.any?(&:present?) end @@ -182,9 +185,12 @@ class LinkageChecker checked_dylibs << dylib - if dylib.start_with? "@" + if dylib.start_with? "@rpath" @variable_dylibs << dylib next + elsif dylib.start_with?("@executable_path") && !Pathname(file).binary_executable? + @executable_path_dylibs << dylib + next end begin diff --git a/Library/Homebrew/os/mac/mach.rb b/Library/Homebrew/os/mac/mach.rb index b421103de7..300b61ab47 100644 --- a/Library/Homebrew/os/mac/mach.rb +++ b/Library/Homebrew/os/mac/mach.rb @@ -9,7 +9,7 @@ require "macho" module MachOShim extend Forwardable - delegate [:dylib_id, :rpaths] => :macho + delegate [:dylib_id] => :macho def macho @macho ||= MachO.open(to_s) @@ -79,10 +79,32 @@ module MachOShim macho.write! end - def dynamically_linked_libraries(except: :none) + def dynamically_linked_libraries(except: :none, resolve_variable_references: true) lcs = macho.dylib_load_commands.reject { |lc| lc.type == except } - lcs.map(&:name).map(&:to_s).uniq + names = lcs.map(&:name).map(&:to_s).uniq + + names.map! { |name| resolve_variable_name(name) } if resolve_variable_references + + names + end + + def rpaths(resolve_variable_references: true) + names = macho.rpaths.uniq + + names.map! { |name| resolve_variable_name(name) } if resolve_variable_references + + names + end + + def resolve_variable_name(name) + if name.start_with? "@loader_path" + Pathname(name.sub("@loader_path", dirname)).cleanpath.to_s + elsif name.start_with?("@executable_path") && binary_executable? + Pathname(name.sub("@executable_path", dirname)).cleanpath.to_s + else + name + end end def archs