Merge pull request #12940 from danielnachun/patch_prefix2

Binary patching of build prefixes
This commit is contained in:
Daniel Nachun 2022-03-21 10:44:22 -07:00 committed by GitHub
commit 058be73b72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 3 deletions

View File

@ -1228,6 +1228,14 @@ class FormulaInstaller
keg = Keg.new(formula.prefix) keg = Keg.new(formula.prefix)
skip_linkage = formula.bottle_specification.skip_relocation? skip_linkage = formula.bottle_specification.skip_relocation?
keg.replace_placeholders_with_locations tab.changed_files, skip_linkage: skip_linkage 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 end
sig { params(output: T.nilable(String)).void } sig { params(output: T.nilable(String)).void }

View File

@ -529,6 +529,8 @@ class Keg
elf_files elf_files
end end
def codesign_patched_binary(_binary_file); end
private private
def resolve_any_conflicts(dst, dry_run: false, verbose: false, overwrite: false) def resolve_any_conflicts(dst, dry_run: false, verbose: false, overwrite: false)

View File

@ -165,6 +165,45 @@ class Keg
changed_files changed_files
end 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 = {}) def detect_cxx_stdlibs(_options = {})
[] []
end end

View File

@ -473,6 +473,8 @@ class Bottle
end end
class BottleSpecification class BottleSpecification
RELOCATABLE_CELLARS = [:any, :any_skip_relocation].freeze
extend T::Sig extend T::Sig
attr_rw :rebuild attr_rw :rebuild
@ -518,12 +520,15 @@ class BottleSpecification
def compatible_locations?(tag: Utils::Bottles.tag) def compatible_locations?(tag: Utils::Bottles.tag)
cellar = tag_to_cellar(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 prefix = Pathname(cellar).parent.to_s
compatible_cellar = cellar == HOMEBREW_CELLAR.to_s cellar_relocatable = cellar.size >= HOMEBREW_CELLAR.to_s.size && ENV["HOMEBREW_RELOCATE_BUILD_PREFIX"]
compatible_prefix = prefix == HOMEBREW_PREFIX.to_s 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 compatible_cellar && compatible_prefix
end end

View 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