class PatchELF::Patcher
Class to handle all patching things.
Attributes
@!macro [new] note_apply
@note This setting will be saved after {#save} being invoked.
Public Class Methods
Instantiate a {Patcher} object. @param [String] filename
Filename of input ELF.
@param [Boolean] logging
*deprecated*: use +on_error+ instead
@param [:log, :silent, :exception] on_error
action when the desired segment/tag field isn't present :log = logs to stderr :exception = raise exception related to the error :silent = ignore the errors
# File lib/patchelf/patcher.rb, line 28 def initialize(filename, on_error: :log, logging: true) @in_file = filename @elf = ELFTools::ELFFile.new(File.open(filename)) @set = {} @rpath_sym = :runpath @on_error = !logging ? :exception : on_error on_error_syms = %i[exception log silent] raise ArgumentError, "on_error must be one of #{on_error_syms}" unless on_error_syms.include?(@on_error) end
Public Instance Methods
Add the needed library. @param [String] need @return [void] @macro note_apply
# File lib/patchelf/patcher.rb, line 81 def add_needed(need) @set[:needed] ||= needed_ @set[:needed] << need end
@return [String?]
Get interpreter's name.
@example
PatchELF::Patcher.new('/bin/ls').interpreter #=> "/lib64/ld-linux-x86-64.so.2"
# File lib/patchelf/patcher.rb, line 44 def interpreter @set[:interpreter] || interpreter_ end
Set interpreter's name.
If the input ELF has no existent interpreter, this method will show a warning and has no effect. @param [String] interp @macro note_apply
# File lib/patchelf/patcher.rb, line 54 def interpreter=(interp) return if interpreter_.nil? # will also show warning if there's no interp segment. @set[:interpreter] = interp end
Get needed libraries. @return [Array<String>] @example
patcher = PatchELF::Patcher.new('/bin/ls') patcher.needed #=> ["libselinux.so.1", "libc.so.6"]
# File lib/patchelf/patcher.rb, line 66 def needed @set[:needed] || needed_ end
Set needed libraries. @param [Array<String>] needs @macro note_apply
# File lib/patchelf/patcher.rb, line 73 def needed=(needs) @set[:needed] = needs end
Remove the needed library. @param [String] need @return [void] @macro note_apply
# File lib/patchelf/patcher.rb, line 90 def remove_needed(need) @set[:needed] ||= needed_ @set[:needed].delete(need) end
Replace needed library src
with tar
.
@param [String] src
Library to be replaced.
@param [String] tar
Library replace with.
@return [void] @macro note_apply
# File lib/patchelf/patcher.rb, line 103 def replace_needed(src, tar) @set[:needed] ||= needed_ @set[:needed].map! { |v| v == src ? tar : v } end
Get rpath return [String?]
# File lib/patchelf/patcher.rb, line 142 def rpath @set[:rpath] || runpath_(:rpath) end
Set rpath
Modify / set DT_RPATH of the given ELF. similar to runpath= except DT_RPATH is modifed/created in DYNAMIC segment. @param [String] rpath @macro note_apply
# File lib/patchelf/patcher.rb, line 152 def rpath=(rpath) @set[:rpath] = rpath end
Get runpath. @return [String?]
# File lib/patchelf/patcher.rb, line 136 def runpath @set[@rpath_sym] || runpath_(@rpath_sym) end
Set runpath.
If DT_RUNPATH is not presented in the input ELF, a new DT_RUNPATH attribute will be inserted into the DYNAMIC segment. @param [String] runpath @macro note_apply
# File lib/patchelf/patcher.rb, line 162 def runpath=(runpath) @set[@rpath_sym] = runpath end
Save the patched ELF as out_file
. @param [String?] out_file
If +out_file+ is +nil+, the original input file will be modified.
@param [Boolean] patchelf_compatible
When +patchelf_compatible+ is true, tries to produce same ELF as the one produced by NixOS/patchelf.
@return [void]
# File lib/patchelf/patcher.rb, line 179 def save(out_file = nil, patchelf_compatible: false) # If nothing is modified, return directly. return if out_file.nil? && !dirty? out_file ||= @in_file saver = if patchelf_compatible require 'patchelf/alt_saver' PatchELF::AltSaver.new(@in_file, out_file, @set) else PatchELF::Saver.new(@in_file, out_file, @set) end saver.save! end
Get the soname of a shared library. @return [String?] The name. @example
patcher = PatchELF::Patcher.new('/bin/ls') patcher.soname # [WARN] Entry DT_SONAME not found, not a shared library? #=> nil
@example
PatchELF::Patcher.new('/lib/x86_64-linux-gnu/libc.so.6').soname #=> "libc.so.6"
# File lib/patchelf/patcher.rb, line 118 def soname @set[:soname] || soname_ end
Set soname.
If the input ELF is not a shared library with a soname, this method will show a warning and has no effect. @param [String] name @macro note_apply
# File lib/patchelf/patcher.rb, line 128 def soname=(name) return if soname_.nil? @set[:soname] = name end
Set all operations related to DT_RUNPATH to use DT_RPATH. @return [self]
# File lib/patchelf/patcher.rb, line 168 def use_rpath! @rpath_sym = :rpath self end
Private Instance Methods
@return [Boolean]
# File lib/patchelf/patcher.rb, line 228 def dirty? @set.any? end
# File lib/patchelf/patcher.rb, line 242 def dynamic_or_log @elf.segment_by_type(:dynamic).tap do |s| if s.nil? log_or_raise 'DYNAMIC segment not found, might be a statically-linked ELF?', PatchELF::MissingSegmentError end end end
# File lib/patchelf/patcher.rb, line 202 def interpreter_ segment = @elf.segment_by_type(:interp) return log_or_raise 'No interpreter found.', PatchELF::MissingSegmentError if segment.nil? segment.interp_name end
# File lib/patchelf/patcher.rb, line 196 def log_or_raise(msg, exception = PatchELF::PatchError) raise exception, msg if @on_error == :exception PatchELF::Logger.warn(msg) if @on_error == :log end
@return [Array<String>]
# File lib/patchelf/patcher.rb, line 210 def needed_ segment = dynamic_or_log return if segment.nil? segment.tags_by_type(:needed).map(&:name) end
@return [String?]
# File lib/patchelf/patcher.rb, line 218 def runpath_(rpath_sym = :runpath) tag_name_or_log(rpath_sym, "Entry DT_#{rpath_sym.to_s.upcase} not found.") end
@return [String?]
# File lib/patchelf/patcher.rb, line 223 def soname_ tag_name_or_log(:soname, 'Entry DT_SONAME not found, not a shared library?') end
# File lib/patchelf/patcher.rb, line 232 def tag_name_or_log(type, log_msg) segment = dynamic_or_log return if segment.nil? tag = segment.tag_by_type(type) return log_or_raise log_msg, PatchELF::MissingTagError if tag.nil? tag.name end