diff --git a/Library/Homebrew/extend/os/linux/keg_relocate.rb b/Library/Homebrew/extend/os/linux/keg_relocate.rb index a707898c03..2866373b91 100644 --- a/Library/Homebrew/extend/os/linux/keg_relocate.rb +++ b/Library/Homebrew/extend/os/linux/keg_relocate.rb @@ -20,28 +20,22 @@ class Keg patchelf = DevelopmentTools.locate "patchelf" odie "Could not locate patchelf, please: brew install patchelf." if patchelf.nil? + cmd = [patchelf] - cmd_rpath = [patchelf, "--print-rpath", file] - old_rpath = Utils.popen_read(*cmd_rpath, err: :out).strip + old_rpath = file.rpath + new_rpath = if old_rpath + rpath = old_rpath.split(":") + .map { |x| x.sub(old_prefix, new_prefix) } + .select { |x| x.start_with?(new_prefix, "$ORIGIN") } - # patchelf requires that the ELF file have a .dynstr section. - # Skip ELF files that do not have a .dynstr section. - return if ["cannot find section .dynstr", "strange: no string table"].include?(old_rpath) + lib_path = "#{new_prefix}/lib" + rpath << lib_path unless rpath.include? lib_path + new_rpath = rpath.join(":") + cmd.push "--force-rpath", "--set-rpath", new_rpath - unless $CHILD_STATUS.success? - raise ErrorDuringExecution.new(cmd_rpath, status: $CHILD_STATUS, output: [[:stderr, old_rpath]]) + new_rpath end - rpath = old_rpath - .split(":") - .map { |x| x.sub(old_prefix, new_prefix) } - .select { |x| x.start_with?(new_prefix, "$ORIGIN") } - - lib_path = "#{new_prefix}/lib" - rpath << lib_path unless rpath.include? lib_path - new_rpath = rpath.join(":") - cmd = [patchelf, "--force-rpath", "--set-rpath", new_rpath] - old_interpreter = file.interpreter new_interpreter = if old_interpreter.nil? nil diff --git a/Library/Homebrew/os/linux/elf.rb b/Library/Homebrew/os/linux/elf.rb index 2730d1da41..bf6c692eaa 100644 --- a/Library/Homebrew/os/linux/elf.rb +++ b/Library/Homebrew/os/linux/elf.rb @@ -1,7 +1,45 @@ # frozen_string_literal: true +if HOMEBREW_PATCHELF_RB + require "utils/gems" + Homebrew.install_bundler_gems! + require "patchelf" + + module PatchELF + refine Patcher do + # patchelf.rb throws exception when the requested entry is missing in the ELF file. + # We prefer an API that returns nil. + + def rpath + super + rescue PatchELF::MissingTagError + nil + end + + def runpath + super + rescue PatchELF::MissingTagError + nil + end + + def soname + super + rescue PatchELF::MissingTagError + nil + end + + def interpreter + super + rescue PatchELF::MissingSegmentError + nil + end + end + end +end + # @see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header module ELFShim + using PatchELF if HOMEBREW_PATCHELF_RB MAGIC_NUMBER_OFFSET = 0 MAGIC_NUMBER_ASCII = "\x7fELF" @@ -68,16 +106,21 @@ module ELFShim elf_type == :executable end + def rpath + return @rpath if defined? @rpath + + @rpath = if HOMEBREW_PATCHELF_RB + rpath_using_patchelf_rb + else + rpath_using_patchelf + end + end + def interpreter return @interpreter if defined? @interpreter @interpreter = if HOMEBREW_PATCHELF_RB - begin - patchelf_patcher.interpreter - rescue PatchELF::PatchError => e - opoo e unless e.to_s.start_with? "No interpreter found" - nil - end + patchelf_patcher.interpreter elsif (patchelf = DevelopmentTools.locate "patchelf") interp = Utils.popen_read(patchelf, "--print-interpreter", to_s, err: :out).strip $CHILD_STATUS.success? ? interp : nil @@ -147,21 +190,7 @@ module ELFShim def needed_libraries_using_patchelf_rb(path) patcher = path.patchelf_patcher - return [nil, []] unless patcher - - soname = begin - patcher.soname - rescue PatchELF::PatchError => e - opoo e unless e.to_s.start_with? "Entry DT_SONAME not found, not a shared library?" - nil - end - needed = begin - patcher.needed - rescue PatchELF::PatchError => e - opoo e - [] - end - [soname, needed] + [patcher.soname, patcher.needed] end def needed_libraries_using_patchelf(path) @@ -198,14 +227,32 @@ module ELFShim end end + def rpath_using_patchelf_rb + patchelf_patcher.runpath || patchelf_patcher.rpath + end + + def rpath_using_patchelf + patchelf = DevelopmentTools.locate "patchelf" + odie "Could not locate patchelf, please: brew install patchelf." if patchelf.nil? + + cmd_rpath = [patchelf, "--print-rpath", to_s] + rpath = Utils.popen_read(*cmd_rpath, err: :out).strip + + # patchelf requires that the ELF file have a .dynstr section. + # Skip ELF files that do not have a .dynstr section. + return if ["cannot find section .dynstr", "strange: no string table"].include?(rpath) + + unless $CHILD_STATUS.success? + raise ErrorDuringExecution.new(cmd_rpath, status: $CHILD_STATUS, output: [[:stderr, rpath]]) + end + + rpath + end + def patchelf_patcher return unless HOMEBREW_PATCHELF_RB - @patchelf_patcher ||= begin - Homebrew.install_bundler_gems! - require "patchelf" - PatchELF::Patcher.new to_s, logging: false - end + @patchelf_patcher ||= PatchELF::Patcher.new to_s, logging: false end def metadata