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:
Shaun Jackman 2020-07-24 15:53:58 -07:00 committed by GitHub
commit e7006beefb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 106 additions and 43 deletions

View File

@ -20,27 +20,21 @@ 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
# 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(":")
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") }
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]
cmd.push "--force-rpath", "--set-rpath", new_rpath
new_rpath
end
old_interpreter = file.interpreter
new_interpreter = if old_interpreter.nil?

View File

@ -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
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 unless rpath.blank?
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

View 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