From 54e6cbb34347a7f03844eae98aa7f856d6e5ac07 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Sun, 16 Dec 2018 20:32:50 -0500 Subject: [PATCH 01/11] mac/keg_relocate: Rewrite rpaths in install names --- Library/Homebrew/extend/os/mac/keg_relocate.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index 77c8a4ca31..4a7dd8de0a 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -87,6 +87,8 @@ class Keg "@loader_path/#{bad_name}" elsif file.mach_o_executable? && (lib + bad_name).exist? "#{lib}/#{bad_name}" + elsif bad_name.start_with? "@rpath" + bad_name.sub("@rpath", lib) elsif (abs_name = find_dylib(bad_name)) && abs_name.exist? abs_name.to_s else @@ -97,7 +99,7 @@ class Keg def each_install_name_for(file, &block) dylibs = file.dynamically_linked_libraries - dylibs.reject! { |fn| fn =~ /^@(loader_|executable_|r)path/ } + dylibs.reject! { |fn| fn =~ /^@(loader_|executable_)path/ } dylibs.each(&block) end From 618c5cd01a2a2e46c44e3ea9597653c9e5763764 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 19 Dec 2018 19:54:59 -0500 Subject: [PATCH 02/11] os/mac: Expose rpaths --- Library/Homebrew/os/mac/mach.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Library/Homebrew/os/mac/mach.rb b/Library/Homebrew/os/mac/mach.rb index 49e49cd702..341b946299 100644 --- a/Library/Homebrew/os/mac/mach.rb +++ b/Library/Homebrew/os/mac/mach.rb @@ -60,6 +60,10 @@ module MachOShim macho.dylib_id end + def rpaths + macho.rpaths + end + def archs mach_data.map { |m| m.fetch :arch }.extend(ArchitectureListExtension) end From 27d6cfb12e0a5cac753ab1b1157ebfb9d371f4f3 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 19 Dec 2018 19:55:21 -0500 Subject: [PATCH 03/11] [WIP] mac/keg_relocate: Emulate dylinker behavior Instead of assuming that #{lib} is the correct RPATH expansion, test all RPATHS and substitute the first one that works. --- Library/Homebrew/extend/os/mac/keg_relocate.rb | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index 4a7dd8de0a..c098ff2fdb 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -75,6 +75,20 @@ class Keg generic_fix_dynamic_linkage end + def expand_rpath(file, bad_name) + suffix = bad_name.sub(/^@rpath/, "") + + # short circuit: we expect lib to be usually correct, so we try it first + return (lib + suffix) if (lib + suffix).exist? + + file.rpaths.each do |rpath| + return (rpath + suffix) if (rpath + suffix).exist? + end + + opoo "Could not expand an RPATH in #{file}" + bad_name + 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. @@ -88,7 +102,7 @@ class Keg elsif file.mach_o_executable? && (lib + bad_name).exist? "#{lib}/#{bad_name}" elsif bad_name.start_with? "@rpath" - bad_name.sub("@rpath", lib) + expand_rpath bad_name elsif (abs_name = find_dylib(bad_name)) && abs_name.exist? abs_name.to_s else From a6bcbdb20d669520a595efd268c3b3fc6e57e0ba Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 19 Dec 2018 20:00:14 -0500 Subject: [PATCH 04/11] mac/keg_relocate: Prefer String#/, add ENV check --- Library/Homebrew/extend/os/mac/keg_relocate.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index c098ff2fdb..a68b7690fd 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -79,10 +79,10 @@ class Keg suffix = bad_name.sub(/^@rpath/, "") # short circuit: we expect lib to be usually correct, so we try it first - return (lib + suffix) if (lib + suffix).exist? + return (lib / suffix) if (lib / suffix).exist? file.rpaths.each do |rpath| - return (rpath + suffix) if (rpath + suffix).exist? + return (rpath / suffix) if (rpath / suffix).exist? end opoo "Could not expand an RPATH in #{file}" @@ -101,7 +101,7 @@ class Keg "@loader_path/#{bad_name}" elsif file.mach_o_executable? && (lib + bad_name).exist? "#{lib}/#{bad_name}" - elsif bad_name.start_with? "@rpath" + elsif bad_name.start_with? "@rpath" && ENV["HOMEBREW_RELOCATE_RPATHS"] expand_rpath bad_name elsif (abs_name = find_dylib(bad_name)) && abs_name.exist? abs_name.to_s From c4363e8c585be1a8bb26eb1d2070fa4752beee08 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 19 Dec 2018 20:12:28 -0500 Subject: [PATCH 05/11] mac/keg_relocate: Remove short-circuit, cleanup --- Library/Homebrew/extend/os/mac/keg_relocate.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index a68b7690fd..effb372b89 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -78,14 +78,11 @@ class Keg def expand_rpath(file, bad_name) suffix = bad_name.sub(/^@rpath/, "") - # short circuit: we expect lib to be usually correct, so we try it first - return (lib / suffix) if (lib / suffix).exist? - file.rpaths.each do |rpath| - return (rpath / suffix) if (rpath / suffix).exist? + return (rpath/suffix) if (rpath/suffix).exist? end - opoo "Could not expand an RPATH in #{file}" + opoo "Could not find library #{bad_name} for #{file}" bad_name end From d2dc4e559982774b8e7d1e3eb0cdf40c97180778 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 19 Dec 2018 20:23:02 -0500 Subject: [PATCH 06/11] mac/keg_relocate: Drop unnecessary parens --- Library/Homebrew/extend/os/mac/keg_relocate.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index effb372b89..0d856d82c2 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -79,7 +79,7 @@ class Keg suffix = bad_name.sub(/^@rpath/, "") file.rpaths.each do |rpath| - return (rpath/suffix) if (rpath/suffix).exist? + return rpath/suffix if (rpath/suffix).exist? end opoo "Could not find library #{bad_name} for #{file}" From 2e2dea6131e4a54eb1fdedd47bd4585a47c643e2 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 19 Dec 2018 20:26:35 -0500 Subject: [PATCH 07/11] mac/keg_relocate: Style nit --- Library/Homebrew/extend/os/mac/keg_relocate.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index 0d856d82c2..0ea4942b2d 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -98,7 +98,7 @@ class Keg "@loader_path/#{bad_name}" elsif file.mach_o_executable? && (lib + bad_name).exist? "#{lib}/#{bad_name}" - elsif bad_name.start_with? "@rpath" && ENV["HOMEBREW_RELOCATE_RPATHS"] + elsif bad_name.start_with?("@rpath") && ENV["HOMEBREW_RELOCATE_RPATHS"] expand_rpath bad_name elsif (abs_name = find_dylib(bad_name)) && abs_name.exist? abs_name.to_s From 54aca0d14fb523a3d97f2dc8141dac6048d9d7b2 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Thu, 27 Dec 2018 13:30:16 -0500 Subject: [PATCH 08/11] os/mac: Flush RPATHs from Mach-Os when possible --- Library/Homebrew/extend/os/mac/keg_relocate.rb | 9 +++++++++ Library/Homebrew/os/mac/mach.rb | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index 0ea4942b2d..3d0e318213 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -69,6 +69,15 @@ class Keg new_name = fixed_name(file, bad_name) change_install_name(bad_name, new_name, file) unless new_name == bad_name end + + # If none of the install names reference RPATH(s), then we can safely + # remove all RPATHs from the file. + if file.dynamically_linked_libraries.none? { |lib| lib.start_with?("@rpath") } + # Note: This could probably be made more efficient by reverse-sorting + # the RPATHs by offset and calling MachOFile#delete_command + # with repopulate: false. + file.rpaths.each { |r| file.delete_rpath(r) } + end end end diff --git a/Library/Homebrew/os/mac/mach.rb b/Library/Homebrew/os/mac/mach.rb index 341b946299..ad3e26ae16 100644 --- a/Library/Homebrew/os/mac/mach.rb +++ b/Library/Homebrew/os/mac/mach.rb @@ -64,6 +64,10 @@ module MachOShim macho.rpaths end + def delete_rpath(rpath) + macho.delete_rpath(rpath) + end + def archs mach_data.map { |m| m.fetch :arch }.extend(ArchitectureListExtension) end From a8c4136e9edb02e6b637b53357af7a921538c149 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Sun, 10 Mar 2019 21:05:51 -0400 Subject: [PATCH 09/11] os/mac: Use delegation for Mach-O methods --- Library/Homebrew/os/mac/mach.rb | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Library/Homebrew/os/mac/mach.rb b/Library/Homebrew/os/mac/mach.rb index ad3e26ae16..4e7c86e98f 100644 --- a/Library/Homebrew/os/mac/mach.rb +++ b/Library/Homebrew/os/mac/mach.rb @@ -2,6 +2,10 @@ require "macho" require "os/mac/architecture_list" module MachOShim + extend Forwardable + + delegate [:dylib_id, :rpaths, :delete_rpath] => :macho + # @private def macho @macho ||= begin @@ -56,18 +60,6 @@ module MachOShim lcs.map(&:name).map(&:to_s).uniq end - def dylib_id - macho.dylib_id - end - - def rpaths - macho.rpaths - end - - def delete_rpath(rpath) - macho.delete_rpath(rpath) - end - def archs mach_data.map { |m| m.fetch :arch }.extend(ArchitectureListExtension) end From 82dd5011e2e5644fc6d71e19f8354278fe9eeaab Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Sun, 10 Mar 2019 21:29:59 -0400 Subject: [PATCH 10/11] mac/keg_relocate: Delete loader_paths Fix the call to expand_rpath. --- Library/Homebrew/extend/os/mac/keg_relocate.rb | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index 3d0e318213..43a4717326 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -95,6 +95,14 @@ class Keg bad_name end + def expand_loader_path(file, bad_name) + suffix = bad_name.sub(/^@loader_path/, "") + + # Note: Weak dylibs are allowed to not exist on disk, so + # we don't check for existence & complain here. + file.parent/suffix + 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. @@ -104,11 +112,13 @@ class Keg elsif bad_name.start_with? CELLAR_PLACEHOLDER bad_name.sub(CELLAR_PLACEHOLDER, HOMEBREW_CELLAR) elsif (file.dylib? || file.mach_o_bundle?) && (file.parent + bad_name).exist? - "@loader_path/#{bad_name}" + file.parent/bad_name elsif file.mach_o_executable? && (lib + bad_name).exist? "#{lib}/#{bad_name}" - elsif bad_name.start_with?("@rpath") && ENV["HOMEBREW_RELOCATE_RPATHS"] - expand_rpath bad_name + elsif bad_name.start_with?("@rpath") && ENV["HOMEBREW_RELOCATE_METAVARS"] + expand_rpath file, bad_name + elsif bad_name.start_with?("@loader_path") && ENV["HOMEBREW_RELOCATE_METAVARS"] + expand_loader_path file, bad_name elsif (abs_name = find_dylib(bad_name)) && abs_name.exist? abs_name.to_s else @@ -119,7 +129,7 @@ class Keg def each_install_name_for(file, &block) dylibs = file.dynamically_linked_libraries - dylibs.reject! { |fn| fn =~ /^@(loader_|executable_)path/ } + dylibs.reject! { |fn| fn =~ /^@executable_path/ } dylibs.each(&block) end From 02216eedaf7fc359fb9e67753abafb569159b4c8 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Mon, 11 Mar 2019 20:58:04 -0400 Subject: [PATCH 11/11] mac/keg_relocate: Don't relocate loader_paths --- Library/Homebrew/extend/os/mac/keg_relocate.rb | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index 43a4717326..5139d2494a 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -95,14 +95,6 @@ class Keg bad_name end - def expand_loader_path(file, bad_name) - suffix = bad_name.sub(/^@loader_path/, "") - - # Note: Weak dylibs are allowed to not exist on disk, so - # we don't check for existence & complain here. - file.parent/suffix - 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. @@ -112,13 +104,11 @@ class Keg elsif bad_name.start_with? CELLAR_PLACEHOLDER bad_name.sub(CELLAR_PLACEHOLDER, HOMEBREW_CELLAR) elsif (file.dylib? || file.mach_o_bundle?) && (file.parent + bad_name).exist? - file.parent/bad_name + "@loader_path/#{bad_name}" elsif file.mach_o_executable? && (lib + bad_name).exist? "#{lib}/#{bad_name}" elsif bad_name.start_with?("@rpath") && ENV["HOMEBREW_RELOCATE_METAVARS"] expand_rpath file, bad_name - elsif bad_name.start_with?("@loader_path") && ENV["HOMEBREW_RELOCATE_METAVARS"] - expand_loader_path file, bad_name elsif (abs_name = find_dylib(bad_name)) && abs_name.exist? abs_name.to_s else @@ -129,7 +119,7 @@ class Keg def each_install_name_for(file, &block) dylibs = file.dynamically_linked_libraries - dylibs.reject! { |fn| fn =~ /^@executable_path/ } + dylibs.reject! { |fn| fn =~ /^@(loader|executable)_path/ } dylibs.each(&block) end