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)


28
29
30
31
32
33
34
35
36
37
# 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 ? 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)
end

Instance Attribute Details

#elfObject (readonly)

Note:

This setting will be saved after #save being invoked.



16
17
18
# File 'lib/patchelf/patcher.rb', line 16

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)


81
82
83
84
# File 'lib/patchelf/patcher.rb', line 81

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.



44
45
46
# File 'lib/patchelf/patcher.rb', line 44

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)


54
55
56
57
58
# 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

#neededArray<String>

Get needed libraries.

Examples:

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

Returns:

  • (Array<String>)


66
67
68
# File 'lib/patchelf/patcher.rb', line 66

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>)


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

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)


90
91
92
93
# File 'lib/patchelf/patcher.rb', line 90

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.



103
104
105
106
# File 'lib/patchelf/patcher.rb', line 103

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

#rpathObject

Get rpath return [String?]



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

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)


152
153
154
# File 'lib/patchelf/patcher.rb', line 152

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

#runpathString?

Get runpath.

Returns:

  • (String?)


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

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)


162
163
164
# File 'lib/patchelf/patcher.rb', line 162

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.



179
180
181
182
183
184
185
186
187
188
189
190
191
192
# 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

#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.



118
119
120
# File 'lib/patchelf/patcher.rb', line 118

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)


128
129
130
131
132
# File 'lib/patchelf/patcher.rb', line 128

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)


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

def use_rpath!
  @rpath_sym = :rpath
  self
end