Merge pull request #7970 from rmNULL/patchelfrb-lookup-rpath
Introduce ELFShim#rpath. Refactor keg_relocate to use the same. Return nil on no DT_RPATH, DT_RUNPATH, DT_SONAME, PT_INTERP.
This commit is contained in:
commit
e7006beefb
@ -20,27 +20,21 @@ class Keg
|
|||||||
|
|
||||||
patchelf = DevelopmentTools.locate "patchelf"
|
patchelf = DevelopmentTools.locate "patchelf"
|
||||||
odie "Could not locate patchelf, please: brew install patchelf." if patchelf.nil?
|
odie "Could not locate patchelf, please: brew install patchelf." if patchelf.nil?
|
||||||
|
cmd = [patchelf]
|
||||||
|
|
||||||
cmd_rpath = [patchelf, "--print-rpath", file]
|
old_rpath = file.rpath
|
||||||
old_rpath = Utils.popen_read(*cmd_rpath, err: :out).strip
|
new_rpath = if old_rpath
|
||||||
|
rpath = old_rpath.split(":")
|
||||||
# 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)
|
|
||||||
|
|
||||||
unless $CHILD_STATUS.success?
|
|
||||||
raise ErrorDuringExecution.new(cmd_rpath, status: $CHILD_STATUS, output: [[:stderr, old_rpath]])
|
|
||||||
end
|
|
||||||
|
|
||||||
rpath = old_rpath
|
|
||||||
.split(":")
|
|
||||||
.map { |x| x.sub(old_prefix, new_prefix) }
|
.map { |x| x.sub(old_prefix, new_prefix) }
|
||||||
.select { |x| x.start_with?(new_prefix, "$ORIGIN") }
|
.select { |x| x.start_with?(new_prefix, "$ORIGIN") }
|
||||||
|
|
||||||
lib_path = "#{new_prefix}/lib"
|
lib_path = "#{new_prefix}/lib"
|
||||||
rpath << lib_path unless rpath.include? lib_path
|
rpath << lib_path unless rpath.include? lib_path
|
||||||
new_rpath = rpath.join(":")
|
new_rpath = rpath.join(":")
|
||||||
cmd = [patchelf, "--force-rpath", "--set-rpath", new_rpath]
|
cmd.push "--force-rpath", "--set-rpath", new_rpath
|
||||||
|
|
||||||
|
new_rpath
|
||||||
|
end
|
||||||
|
|
||||||
old_interpreter = file.interpreter
|
old_interpreter = file.interpreter
|
||||||
new_interpreter = if old_interpreter.nil?
|
new_interpreter = if old_interpreter.nil?
|
||||||
|
@ -1,7 +1,45 @@
|
|||||||
# frozen_string_literal: true
|
# 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
|
# @see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
|
||||||
module ELFShim
|
module ELFShim
|
||||||
|
using PatchELF if HOMEBREW_PATCHELF_RB
|
||||||
MAGIC_NUMBER_OFFSET = 0
|
MAGIC_NUMBER_OFFSET = 0
|
||||||
MAGIC_NUMBER_ASCII = "\x7fELF"
|
MAGIC_NUMBER_ASCII = "\x7fELF"
|
||||||
|
|
||||||
@ -68,16 +106,21 @@ module ELFShim
|
|||||||
elf_type == :executable
|
elf_type == :executable
|
||||||
end
|
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
|
def interpreter
|
||||||
return @interpreter if defined? @interpreter
|
return @interpreter if defined? @interpreter
|
||||||
|
|
||||||
@interpreter = if HOMEBREW_PATCHELF_RB
|
@interpreter = if HOMEBREW_PATCHELF_RB
|
||||||
begin
|
|
||||||
patchelf_patcher.interpreter
|
patchelf_patcher.interpreter
|
||||||
rescue PatchELF::PatchError => e
|
|
||||||
opoo e unless e.to_s.start_with? "No interpreter found"
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
elsif (patchelf = DevelopmentTools.locate "patchelf")
|
elsif (patchelf = DevelopmentTools.locate "patchelf")
|
||||||
interp = Utils.popen_read(patchelf, "--print-interpreter", to_s, err: :out).strip
|
interp = Utils.popen_read(patchelf, "--print-interpreter", to_s, err: :out).strip
|
||||||
$CHILD_STATUS.success? ? interp : nil
|
$CHILD_STATUS.success? ? interp : nil
|
||||||
@ -147,21 +190,7 @@ module ELFShim
|
|||||||
|
|
||||||
def needed_libraries_using_patchelf_rb(path)
|
def needed_libraries_using_patchelf_rb(path)
|
||||||
patcher = path.patchelf_patcher
|
patcher = path.patchelf_patcher
|
||||||
return [nil, []] unless patcher
|
[patcher.soname, patcher.needed]
|
||||||
|
|
||||||
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]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def needed_libraries_using_patchelf(path)
|
def needed_libraries_using_patchelf(path)
|
||||||
@ -198,14 +227,32 @@ module ELFShim
|
|||||||
end
|
end
|
||||||
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 unless rpath.blank?
|
||||||
|
end
|
||||||
|
|
||||||
def patchelf_patcher
|
def patchelf_patcher
|
||||||
return unless HOMEBREW_PATCHELF_RB
|
return unless HOMEBREW_PATCHELF_RB
|
||||||
|
|
||||||
@patchelf_patcher ||= begin
|
@patchelf_patcher ||= PatchELF::Patcher.new to_s, logging: false
|
||||||
Homebrew.install_bundler_gems!
|
|
||||||
require "patchelf"
|
|
||||||
PatchELF::Patcher.new to_s, logging: false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def metadata
|
def metadata
|
||||||
|
22
Library/Homebrew/test/os/linux/pathname_spec.rb
Normal file
22
Library/Homebrew/test/os/linux/pathname_spec.rb
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "extend/pathname"
|
||||||
|
|
||||||
|
describe Pathname, skip: HOMEBREW_PATCHELF_RB.blank? do
|
||||||
|
let(:elf_dir) { described_class.new "#{TEST_FIXTURE_DIR}/elf" }
|
||||||
|
let(:sho) { elf_dir/"libhello.so.0" }
|
||||||
|
let(:exec) { elf_dir/"hello" }
|
||||||
|
|
||||||
|
describe "#interpreter" do
|
||||||
|
it "returns interpreter" do
|
||||||
|
expect(exec.interpreter).to eq "/lib64/ld-linux-x86-64.so.2"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#rpath" do
|
||||||
|
it "returns nil when absent" do
|
||||||
|
expect(exec.rpath).to be_nil
|
||||||
|
expect(sho.rpath).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user