patchelf 1.2.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/patchelf/cli.rb CHANGED
@@ -139,7 +139,7 @@ module PatchELF
139
139
  @options[:set][:soname] = soname
140
140
  end
141
141
 
142
- opts.on('--version', 'Show current gem\'s version.') {}
142
+ opts.on('--version', 'Show current gem\'s version.')
143
143
  end
144
144
  end
145
145
 
@@ -6,8 +6,10 @@ require 'elftools/exceptions'
6
6
  module PatchELF
7
7
  # Raised on an error during ELF modification.
8
8
  class PatchError < ELFTools::ELFError; end
9
+
9
10
  # Raised when Dynamic Tag is missing
10
11
  class MissingTagError < PatchError; end
12
+
11
13
  # Raised on missing Program Header(segment)
12
14
  class MissingSegmentError < PatchError; end
13
15
  end
@@ -3,9 +3,6 @@
3
3
  module PatchELF
4
4
  # Helper methods for internal usage.
5
5
  module Helper
6
- # The size of one page.
7
- PAGE_SIZE = 0x1000
8
-
9
6
  module_function
10
7
 
11
8
  # Color codes for pretty print.
@@ -16,6 +13,23 @@ module PatchELF
16
13
  error: "\e[38;5;196m" # heavy red
17
14
  }.freeze
18
15
 
16
+ # The size of one page.
17
+ def page_size(e_machine = nil)
18
+ # Different architectures have different minimum section alignments.
19
+ case e_machine
20
+ when ELFTools::Constants::EM_SPARC,
21
+ ELFTools::Constants::EM_MIPS,
22
+ ELFTools::Constants::EM_PPC,
23
+ ELFTools::Constants::EM_PPC64,
24
+ ELFTools::Constants::EM_AARCH64,
25
+ ELFTools::Constants::EM_TILEGX,
26
+ ELFTools::Constants::EM_LOONGARCH
27
+ 0x10000
28
+ else
29
+ 0x1000
30
+ end
31
+ end
32
+
19
33
  # For wrapping string with color codes for prettier inspect.
20
34
  # @param [String] str
21
35
  # Content to colorize.
@@ -48,7 +62,7 @@ module PatchELF
48
62
  # #=> 32
49
63
  # aligndown(0x10, 0x8)
50
64
  # #=> 16
51
- def aligndown(val, align = PAGE_SIZE)
65
+ def aligndown(val, align = page_size)
52
66
  val - (val & (align - 1))
53
67
  end
54
68
 
@@ -63,7 +77,7 @@ module PatchELF
63
77
  # #=> 64
64
78
  # alignup(0x10, 0x8)
65
79
  # #=> 16
66
- def alignup(val, align = PAGE_SIZE)
80
+ def alignup(val, align = page_size)
67
81
  (val & (align - 1)).zero? ? val : (aligndown(val, align) + align)
68
82
  end
69
83
  end
@@ -15,7 +15,7 @@ module PatchELF
15
15
  end
16
16
  end
17
17
 
18
- %i[info warn error].each do |sym|
18
+ %i[debug info warn error level=].each do |sym|
19
19
  define_method(sym) do |msg|
20
20
  @logger.__send__(sym, msg)
21
21
  nil
data/lib/patchelf/mm.rb CHANGED
@@ -115,7 +115,7 @@ module PatchELF
115
115
  # prefer backward than forward
116
116
  return extend_backward(loads[idx - 1]) if writable?(loads[idx - 1])
117
117
 
118
- # note: loads[idx].file_head has been changed in shift_attributes
118
+ # NOTE: loads[idx].file_head has been changed in shift_attributes
119
119
  extend_forward(loads[idx], @extend_size)
120
120
  end
121
121
 
@@ -161,7 +161,7 @@ module PatchELF
161
161
 
162
162
  seg.header.p_offset += extend_size
163
163
  # We have to change align of LOAD segment since ld.so checks it.
164
- seg.header.p_align = Helper::PAGE_SIZE if seg.is_a?(ELFTools::Segments::LoadSegment)
164
+ seg.header.p_align = Helper.page_size if seg.is_a?(ELFTools::Segments::LoadSegment)
165
165
  end
166
166
 
167
167
  @elf.header.e_shoff += extend_size if @elf.header.e_shoff >= threshold
@@ -30,7 +30,7 @@ module PatchELF
30
30
  @elf = ELFTools::ELFFile.new(File.open(filename))
31
31
  @set = {}
32
32
  @rpath_sym = :runpath
33
- @on_error = !logging ? :exception : on_error
33
+ @on_error = logging ? on_error : :exception
34
34
 
35
35
  on_error_syms = %i[exception log silent]
36
36
  raise ArgumentError, "on_error must be one of #{on_error_syms}" unless on_error_syms.include?(@on_error)
@@ -173,13 +173,20 @@ module PatchELF
173
173
  # Save the patched ELF as +out_file+.
174
174
  # @param [String?] out_file
175
175
  # If +out_file+ is +nil+, the original input file will be modified.
176
+ # @param [Boolean] patchelf_compatible
177
+ # When +patchelf_compatible+ is true, tries to produce same ELF as the one produced by NixOS/patchelf.
176
178
  # @return [void]
177
- def save(out_file = nil)
179
+ def save(out_file = nil, patchelf_compatible: false)
178
180
  # If nothing is modified, return directly.
179
181
  return if out_file.nil? && !dirty?
180
182
 
181
183
  out_file ||= @in_file
182
- saver = PatchELF::Saver.new(@in_file, out_file, @set)
184
+ saver = if patchelf_compatible
185
+ require 'patchelf/alt_saver'
186
+ PatchELF::AltSaver.new(@in_file, out_file, @set)
187
+ else
188
+ PatchELF::Saver.new(@in_file, out_file, @set)
189
+ end
183
190
 
184
191
  saver.save!
185
192
  end
@@ -53,8 +53,8 @@ module PatchELF
53
53
  def patch_interpreter
54
54
  return if @set[:interpreter].nil?
55
55
 
56
- new_interp = @set[:interpreter] + "\x00"
57
- old_interp = @elf.segment_by_type(:interp).interp_name + "\x00"
56
+ new_interp = "#{@set[:interpreter]}\x00"
57
+ old_interp = "#{@elf.segment_by_type(:interp).interp_name}\x00"
58
58
  return if old_interp == new_interp
59
59
 
60
60
  # These headers must be found here but not in the proc.
@@ -121,30 +121,27 @@ module PatchELF
121
121
  def patch_needed
122
122
  original_needs = dynamic.tags_by_type(:needed)
123
123
  @set[:needed].uniq!
124
+
125
+ original = original_needs.map(&:name)
126
+ replace = @set[:needed]
127
+
124
128
  # 3 sets:
125
129
  # 1. in original and in needs - remain unchanged
126
130
  # 2. in original but not in needs - remove
127
131
  # 3. not in original and in needs - append
128
- original_needs.each do |n|
129
- next if @set[:needed].include?(n.name)
130
-
131
- n.header.d_tag = IGNORE # temporarily mark
132
- end
133
-
134
- extra = @set[:needed] - original_needs.map(&:name)
135
- original_needs.each do |n|
136
- break if extra.empty?
137
- next if n.header.d_tag != IGNORE
132
+ append = replace - original
133
+ remove = original - replace
138
134
 
139
- n.header.d_tag = ELFTools::Constants::DT_NEEDED
140
- reg_str_table(extra.shift) { |idx| n.header.d_val = idx }
135
+ ignored_dyns = remove.each_with_object([]) do |name, ignored|
136
+ dyn = original_needs.find { |n| n.name == name }.header
137
+ dyn.d_tag = IGNORE
138
+ ignored << dyn
141
139
  end
142
- return if extra.empty?
143
140
 
144
- # no spaces, need append
145
- extra.each do |name|
146
- tag = lazy_dyn(:needed)
147
- reg_str_table(name) { |idx| tag.d_val = idx }
141
+ append.zip(ignored_dyns) do |name, ignored_dyn|
142
+ dyn = ignored_dyn || lazy_dyn(:needed)
143
+ dyn.d_tag = ELFTools::Constants::DT_NEEDED
144
+ reg_str_table(name) { |idx| dyn.d_val = idx }
148
145
  end
149
146
  end
150
147
 
@@ -187,7 +184,7 @@ module PatchELF
187
184
  need_size = strtab_string.size + @strtab_extend_requests.reduce(0) { |sum, (str, _)| sum + str.size + 1 }
188
185
  dynstr = section_header('.dynstr')
189
186
  @mm.malloc(need_size) do |off, vaddr|
190
- new_str = strtab_string + @strtab_extend_requests.map(&:first).join("\x00") + "\x00"
187
+ new_str = "#{strtab_string}#{@strtab_extend_requests.map(&:first).join("\x00")}\x00"
191
188
  inline_patch(off, new_str)
192
189
  cur = strtab_string.size
193
190
  @strtab_extend_requests.each do |str, block|
@@ -209,7 +206,7 @@ module PatchELF
209
206
  # @yieldparam [Integer] idx
210
207
  # @yieldreturn [void]
211
208
  def reg_str_table(str, &block)
212
- idx = strtab_string.index(str + "\x00")
209
+ idx = strtab_string.index("#{str}\x00")
213
210
  # Request string is already exist
214
211
  return yield idx if idx
215
212
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  module PatchELF
4
4
  # Current gem version.
5
- VERSION = '1.2.0'.freeze
5
+ VERSION = '1.4.0'.freeze
6
6
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: patchelf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - david942j
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-29 00:00:00.000000000 Z
11
+ date: 2022-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: elftools
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.1'
19
+ version: '1.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.1'
26
+ version: '1.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,28 +44,28 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.8'
47
+ version: '3'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.8'
54
+ version: '3'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rubocop
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.62'
61
+ version: '1'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0.62'
68
+ version: '1'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: simplecov
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -73,6 +73,9 @@ dependencies:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0.17'
76
+ - - "<"
77
+ - !ruby/object:Gem::Version
78
+ version: '0.18'
76
79
  type: :development
77
80
  prerelease: false
78
81
  version_requirements: !ruby/object:Gem::Requirement
@@ -80,6 +83,9 @@ dependencies:
80
83
  - - "~>"
81
84
  - !ruby/object:Gem::Version
82
85
  version: '0.17'
86
+ - - "<"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.18'
83
89
  - !ruby/object:Gem::Dependency
84
90
  name: tty-platform
85
91
  requirement: !ruby/object:Gem::Requirement
@@ -121,6 +127,7 @@ files:
121
127
  - README.md
122
128
  - bin/patchelf.rb
123
129
  - lib/patchelf.rb
130
+ - lib/patchelf/alt_saver.rb
124
131
  - lib/patchelf/cli.rb
125
132
  - lib/patchelf/exceptions.rb
126
133
  - lib/patchelf/helper.rb
@@ -132,7 +139,8 @@ files:
132
139
  homepage: https://github.com/david942j/patchelf.rb
133
140
  licenses:
134
141
  - MIT
135
- metadata: {}
142
+ metadata:
143
+ rubygems_mfa_required: 'true'
136
144
  post_install_message:
137
145
  rdoc_options: []
138
146
  require_paths:
@@ -141,14 +149,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
141
149
  requirements:
142
150
  - - ">="
143
151
  - !ruby/object:Gem::Version
144
- version: '2.3'
152
+ version: '2.6'
145
153
  required_rubygems_version: !ruby/object:Gem::Requirement
146
154
  requirements:
147
155
  - - ">="
148
156
  - !ruby/object:Gem::Version
149
157
  version: '0'
150
158
  requirements: []
151
- rubygems_version: 3.0.3
159
+ rubygems_version: 3.1.6
152
160
  signing_key:
153
161
  specification_version: 4
154
162
  summary: patchelf