patchelf 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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