Class: PatchELF::Patcher

Inherits:
Object
  • Object
show all
Defined in:
lib/patchelf/patcher.rb

Overview

Class to handle all patching things.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filename, on_error: :log, logging: true) ⇒ Patcher

Instantiate a PatchELF::Patcher object.

Parameters:

  • filename (String)

    Filename of input ELF.

  • logging (Boolean) (defaults to: true)

    deprecated: use on_error instead

  • on_error (:log, :silent, :exception) (defaults to: :log)

    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
    

Raises:

  • (ArgumentError)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/patchelf/patcher.rb', line 30

def initialize(filename, on_error: :log, logging: true)
  @in_file = filename
  f = File.open(filename)
  @elf = ELFTools::ELFFile.new(f)
  @set = {}
  @rpath_sym = :runpath
  @on_error = logging ? on_error : :exception

  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)

  # Ensure file is closed when the {Patcher} object is garbage collected.
  ObjectSpace.define_finalizer(self, Helper.close_file_proc(f))
end

Instance Attribute Details

#elfObject (readonly)

Note:

This setting will be saved after #save being invoked.



18
19
20
# File 'lib/patchelf/patcher.rb', line 18

def elf
  @elf
end

Instance Method Details

#add_needed(need) ⇒ void

Note:

This setting will be saved after #save being invoked.

This method returns an undefined value.

Add the needed library.

Parameters:

  • need (String)


87
88
89
90
# File 'lib/patchelf/patcher.rb', line 87

def add_needed(need)
  @set[:needed] ||= needed_
  @set[:needed] << need
end

#interpreterString?

Returns Get interpreter’s name.

Examples:

PatchELF::Patcher.new('/bin/ls').interpreter
#=> "/lib64/ld-linux-x86-64.so.2"

Returns:

  • (String?)

    Get interpreter's name.



50
51
52
# File 'lib/patchelf/patcher.rb', line 50

def interpreter
  @set[:interpreter] || interpreter_
end

#interpreter=(interp) ⇒ Object

Note:

This setting will be saved after #save being invoked.

Set interpreter’s name.

If the input ELF has no existent interpreter, this method will show a warning and has no effect.

Parameters:

  • interp (String)


60
61
62
63
64
# File 'lib/patchelf/patcher.rb', line 60

def interpreter=(interp)
  return if interpreter_.nil? # will also show warning if there's no interp segment.

  @set[:interpreter] = interp
end

#neededArray<String>

Get needed libraries.

Examples:

patcher = PatchELF::Patcher.new('/bin/ls')
patcher.needed
#=> ["libselinux.so.1", "libc.so.6"]

Returns:

  • (Array<String>)


72
73
74
# File 'lib/patchelf/patcher.rb', line 72

def needed
  @set[:needed] || needed_
end

#needed=(needs) ⇒ Object

Note:

This setting will be saved after #save being invoked.

Set needed libraries.

Parameters:

  • needs (Array<String>)


79
80
81
# File 'lib/patchelf/patcher.rb', line 79

def needed=(needs)
  @set[:needed] = needs
end

#remove_needed(need) ⇒ void

Note:

This setting will be saved after #save being invoked.

This method returns an undefined value.

Remove the needed library.

Parameters:

  • need (String)


96
97
98
99
# File 'lib/patchelf/patcher.rb', line 96

def remove_needed(need)
  @set[:needed] ||= needed_
  @set[:needed].delete(need)
end

#replace_needed(src, tar) ⇒ void

Note:

This setting will be saved after #save being invoked.

This method returns an undefined value.

Replace needed library src with tar.

Parameters:

  • src (String)

    Library to be replaced.

  • tar (String)

    Library replace with.



109
110
111
112
# File 'lib/patchelf/patcher.rb', line 109

def replace_needed(src, tar)
  @set[:needed] ||= needed_
  @set[:needed].map! { |v| v == src ? tar : v }
end

#rpathObject

Get rpath return [String?]



148
149
150
# File 'lib/patchelf/patcher.rb', line 148

def rpath
  @set[:rpath] || runpath_(:rpath)
end

#rpath=(rpath) ⇒ Object

Note:

This setting will be saved after #save being invoked.

Set rpath

Modify / set DT_RPATH of the given ELF. similar to runpath= except DT_RPATH is modifed/created in DYNAMIC segment.

Parameters:

  • rpath (String)


158
159
160
# File 'lib/patchelf/patcher.rb', line 158

def rpath=(rpath)
  @set[:rpath] = rpath
end

#runpathString?

Get runpath.

Returns:

  • (String?)


142
143
144
# File 'lib/patchelf/patcher.rb', line 142

def runpath
  @set[@rpath_sym] || runpath_(@rpath_sym)
end

#runpath=(runpath) ⇒ Object

Note:

This setting will be saved after #save being invoked.

Set runpath.

If DT_RUNPATH is not presented in the input ELF, a new DT_RUNPATH attribute will be inserted into the DYNAMIC segment.

Parameters:

  • runpath (String)


168
169
170
# File 'lib/patchelf/patcher.rb', line 168

def runpath=(runpath)
  @set[@rpath_sym] = runpath
end

#save(out_file = nil, patchelf_compatible: false) ⇒ void

This method returns an undefined value.

Save the patched ELF as out_file.

Parameters:

  • out_file (String?) (defaults to: nil)

    If out_file is nil, the original input file will be modified.

  • patchelf_compatible (Boolean) (defaults to: false)

    When patchelf_compatible is true, tries to produce same ELF as the one produced by NixOS/patchelf.



185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/patchelf/patcher.rb', line 185

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

#sonameString?

Get the soname of a shared library.

Examples:

patcher = PatchELF::Patcher.new('/bin/ls')
patcher.soname
# [WARN] Entry DT_SONAME not found, not a shared library?
#=> nil
PatchELF::Patcher.new('/lib/x86_64-linux-gnu/libc.so.6').soname
#=> "libc.so.6"

Returns:

  • (String?)

    The name.



124
125
126
# File 'lib/patchelf/patcher.rb', line 124

def soname
  @set[:soname] || soname_
end

#soname=(name) ⇒ Object

Note:

This setting will be saved after #save being invoked.

Set soname.

If the input ELF is not a shared library with a soname, this method will show a warning and has no effect.

Parameters:

  • name (String)


134
135
136
137
138
# File 'lib/patchelf/patcher.rb', line 134

def soname=(name)
  return if soname_.nil?

  @set[:soname] = name
end

#use_rpath!self

Set all operations related to DT_RUNPATH to use DT_RPATH.

Returns:

  • (self)


174
175
176
177
# File 'lib/patchelf/patcher.rb', line 174

def use_rpath!
  @rpath_sym = :rpath
  self
end