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:
rmnull 2020-07-10 06:31:31 +05:30
parent 1b7c498d35
commit d607528a5b
No known key found for this signature in database
GPG Key ID: 35BAB82D31EFAD91
2 changed files with 84 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
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