Merge pull request #12940 from danielnachun/patch_prefix2
Binary patching of build prefixes
This commit is contained in:
		
						commit
						058be73b72
					
				@ -1228,6 +1228,14 @@ class FormulaInstaller
 | 
			
		||||
    keg = Keg.new(formula.prefix)
 | 
			
		||||
    skip_linkage = formula.bottle_specification.skip_relocation?
 | 
			
		||||
    keg.replace_placeholders_with_locations tab.changed_files, skip_linkage: skip_linkage
 | 
			
		||||
 | 
			
		||||
    cellar = formula.bottle_specification.tag_to_cellar(Utils::Bottles.tag)
 | 
			
		||||
    return if [:any, :any_skip_relocation].include?(cellar)
 | 
			
		||||
 | 
			
		||||
    prefix = Pathname(cellar).parent.to_s
 | 
			
		||||
    return if cellar == HOMEBREW_CELLAR.to_s && prefix == HOMEBREW_PREFIX.to_s
 | 
			
		||||
 | 
			
		||||
    keg.relocate_build_prefix(keg, prefix, HOMEBREW_PREFIX)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { params(output: T.nilable(String)).void }
 | 
			
		||||
 | 
			
		||||
@ -529,6 +529,8 @@ class Keg
 | 
			
		||||
    elf_files
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def codesign_patched_binary(_binary_file); end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def resolve_any_conflicts(dst, dry_run: false, verbose: false, overwrite: false)
 | 
			
		||||
 | 
			
		||||
@ -165,6 +165,45 @@ class Keg
 | 
			
		||||
    changed_files
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def relocate_build_prefix(keg, old_prefix, new_prefix)
 | 
			
		||||
    each_unique_file_matching(old_prefix) do |file|
 | 
			
		||||
      # Skip files which are not binary, as they do not need null padding.
 | 
			
		||||
      next unless keg.binary_file?(file)
 | 
			
		||||
 | 
			
		||||
      # Skip sharballs, which appear to break if patched.
 | 
			
		||||
      next if file.text_executable?
 | 
			
		||||
 | 
			
		||||
      # Split binary by null characters into array and substitute new prefix for old prefix.
 | 
			
		||||
      # Null padding is added if the new string is too short.
 | 
			
		||||
      file.ensure_writable do
 | 
			
		||||
        binary = File.binread file
 | 
			
		||||
        odebug "Replacing build prefix in: #{file}"
 | 
			
		||||
        binary_strings = binary.split(/#{NULL_BYTE}/o, -1)
 | 
			
		||||
        match_indices = binary_strings.each_index.select { |i| binary_strings[i].include?(old_prefix) }
 | 
			
		||||
 | 
			
		||||
        # Only perform substitution on strings which match prefix regex.
 | 
			
		||||
        match_indices.each do |i|
 | 
			
		||||
          s = binary_strings[i]
 | 
			
		||||
          binary_strings[i] = s.gsub(old_prefix, new_prefix)
 | 
			
		||||
                               .ljust(s.size, NULL_BYTE)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # Rejoin strings by null bytes.
 | 
			
		||||
        patched_binary = binary_strings.join(NULL_BYTE)
 | 
			
		||||
        if patched_binary.size != binary.size
 | 
			
		||||
          raise <<~EOS
 | 
			
		||||
            Patching failed!  Original and patched binary sizes do not match.
 | 
			
		||||
            Original size: #{binary.size}
 | 
			
		||||
            Patched size: #{patched_binary.size}
 | 
			
		||||
          EOS
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        file.atomic_write patched_binary
 | 
			
		||||
      end
 | 
			
		||||
      codesign_patched_binary(file)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def detect_cxx_stdlibs(_options = {})
 | 
			
		||||
    []
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -473,6 +473,8 @@ class Bottle
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class BottleSpecification
 | 
			
		||||
  RELOCATABLE_CELLARS = [:any, :any_skip_relocation].freeze
 | 
			
		||||
 | 
			
		||||
  extend T::Sig
 | 
			
		||||
 | 
			
		||||
  attr_rw :rebuild
 | 
			
		||||
@ -518,12 +520,15 @@ class BottleSpecification
 | 
			
		||||
  def compatible_locations?(tag: Utils::Bottles.tag)
 | 
			
		||||
    cellar = tag_to_cellar(tag)
 | 
			
		||||
 | 
			
		||||
    return true if [:any, :any_skip_relocation].include?(cellar)
 | 
			
		||||
    return true if RELOCATABLE_CELLARS.include?(cellar)
 | 
			
		||||
 | 
			
		||||
    prefix = Pathname(cellar).parent.to_s
 | 
			
		||||
 | 
			
		||||
    compatible_cellar = cellar == HOMEBREW_CELLAR.to_s
 | 
			
		||||
    compatible_prefix = prefix == HOMEBREW_PREFIX.to_s
 | 
			
		||||
    cellar_relocatable = cellar.size >= HOMEBREW_CELLAR.to_s.size && ENV["HOMEBREW_RELOCATE_BUILD_PREFIX"]
 | 
			
		||||
    prefix_relocatable = prefix.size >= HOMEBREW_PREFIX.to_s.size && ENV["HOMEBREW_RELOCATE_BUILD_PREFIX"]
 | 
			
		||||
 | 
			
		||||
    compatible_cellar = cellar == HOMEBREW_CELLAR.to_s || cellar_relocatable
 | 
			
		||||
    compatible_prefix = prefix == HOMEBREW_PREFIX.to_s || prefix_relocatable
 | 
			
		||||
 | 
			
		||||
    compatible_cellar && compatible_prefix
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								Library/Homebrew/test/keg_relocate/binary_relocation_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								Library/Homebrew/test/keg_relocate/binary_relocation_spec.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
# typed: false
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "keg_relocate"
 | 
			
		||||
 | 
			
		||||
describe Keg do
 | 
			
		||||
  subject(:keg) { described_class.new(HOMEBREW_CELLAR/"foo/1.0.0") }
 | 
			
		||||
 | 
			
		||||
  let(:dir) { HOMEBREW_CELLAR/"foo/1.0.0" }
 | 
			
		||||
  let(:newdir) { HOMEBREW_CELLAR/"foo" }
 | 
			
		||||
  let(:binary_file) { dir/"file.bin" }
 | 
			
		||||
 | 
			
		||||
  before do
 | 
			
		||||
    dir.mkpath
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def setup_binary_file
 | 
			
		||||
    binary_file.atomic_write <<~EOS
 | 
			
		||||
      \x00#{dir}\x00
 | 
			
		||||
    EOS
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "#relocate_build_prefix" do
 | 
			
		||||
    specify "replace prefix in binary files" do
 | 
			
		||||
      setup_binary_file
 | 
			
		||||
 | 
			
		||||
      keg.relocate_build_prefix(keg, dir, newdir)
 | 
			
		||||
 | 
			
		||||
      old_prefix_matches = Set.new
 | 
			
		||||
      keg.each_unique_file_matching(dir) do |file|
 | 
			
		||||
        old_prefix_matches << file
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      expect(old_prefix_matches.size).to eq 0
 | 
			
		||||
 | 
			
		||||
      new_prefix_matches = Set.new
 | 
			
		||||
      keg.each_unique_file_matching(newdir) do |file|
 | 
			
		||||
        new_prefix_matches << file
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      expect(new_prefix_matches.size).to eq 1
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user