introduce ELFShim#rpath, refactor keg_relocate to use the same.
refines PatchELF #runpath, #rpath #soname #interpreter to return nil. let Brew hard exits on PatchELF::PatchError. Co-authored-by: Shaun Jackman <sjackman@gmail.com> Co-authored-by: Mike McQuaid <mike@mikemcquaid.com>
This commit is contained in:
		
							parent
							
								
									1b7c498d35
								
							
						
					
					
						commit
						d607528a5b
					
				@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user