patchelf 0.0.0 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e432d8ac7a3e75e8c9e80d433bc756e635d6b4d057b7e02ed952be113c7edbf5
4
- data.tar.gz: 463cee3d6e780346706752f2a537e67fde649db82b206a8d79f15e60dd4cb4ae
3
+ metadata.gz: 8560f68dba4f9e656585c38f31da63ae5f15320a03c1675adb13d27ffa2230b4
4
+ data.tar.gz: 2d19b129beb5df7bd713fc6f0f65bb942194bea201857bda499a18e4496c4fe2
5
5
  SHA512:
6
- metadata.gz: bebe4cca89632d3660045d7582c19cf356562d2cbd389cc910377e88ea293c4fb812bb8917e89670cad05224254497c361f608e7429dcfe2c09ed94d31834a9f
7
- data.tar.gz: 849a0a0b5b8c5bc1c1e52c4f65c9452917f58cf881bca89de4a4986df0556fd677724fe1b17466ff469eed8bcada2320d69169951d9ea0ac4aed1d9be6e4d5aa
6
+ metadata.gz: a2c443a08655333dc1b97ca62c1f6803bf6971565a9b40c80554aaf89a78b8e7548f0a854dd6703175e8be57154ca541ffe71f642a35ded2a734fbe1eae83b0e
7
+ data.tar.gz: 73707a964c3cc1a9d47efcb0baaa45d1fc8e42e2450acca899b5258ca2b9236229e9e560a5c9fa347bbd7c5a429644a66ceed5ac072cb7ac06db3b98088c1a8c
data/README.md CHANGED
@@ -12,19 +12,34 @@ Implements features of NixOS/patchelf in pure Ruby.
12
12
 
13
13
  ## Installation
14
14
 
15
- WIP.
15
+ Available on RubyGems.org!
16
+ ```
17
+ $ gem install patchelf
18
+ ```
16
19
 
17
20
  ## Usage
18
21
 
19
22
  ```
20
23
  $ patchelf.rb
21
24
  # Usage: patchelf.rb <commands> FILENAME [OUTPUT_FILE]
22
- # --pi, --print-interpreter Show interpreter's name.
23
- # --pn, --print-needed Show needed libraries specified in DT_NEEDED.
24
- # --ps, --print-soname Show soname specified in DT_SONAME.
25
- # --interp, --set-interpreter INTERP
25
+ # --print-interpreter, --pi Show interpreter's name.
26
+ # --print-needed, --pn Show needed libraries specified in DT_NEEDED.
27
+ # --print-runpath, --pr Show the path specified in DT_RUNPATH.
28
+ # --print-soname, --ps Show soname specified in DT_SONAME.
29
+ # --set-interpreter, --interp INTERP
26
30
  # Set interpreter's name.
27
- # --so, --set-soname SONAME Set name of a shared library.
31
+ # --set-needed, --needed LIB1,LIB2,LIB3
32
+ # Set needed libraries, this will remove all existent needed libraries.
33
+ # --add-needed LIB Append a new needed library.
34
+ # --remove-needed LIB Remove a needed library.
35
+ # --replace-needed LIB1,LIB2 Replace needed library LIB1 as LIB2.
36
+ # --set-runpath, --runpath PATH
37
+ # Set the path of runpath.
38
+ # --force-rpath According to the ld.so docs, DT_RPATH is obsolete,
39
+ # patchelf.rb will always try to get/set DT_RUNPATH first.
40
+ # Use this option to force every operations related to runpath (e.g. --runpath)
41
+ # to consider 'DT_RPATH' instead of 'DT_RUNPATH'.
42
+ # --set-soname, --so SONAME Set name of a shared library.
28
43
  # --version Show current gem's version.
29
44
 
30
45
  ```
@@ -32,8 +47,8 @@ $ patchelf.rb
32
47
  ### Display information
33
48
  ```
34
49
  $ patchelf.rb --print-interpreter --print-needed /bin/ls
35
- # Interpreter: /lib64/ld-linux-x86-64.so.2
36
- # Needed: libselinux.so.1 libc.so.6
50
+ # interpreter: /lib64/ld-linux-x86-64.so.2
51
+ # needed: libselinux.so.1 libc.so.6
37
52
 
38
53
  ```
39
54
 
@@ -47,11 +62,54 @@ $ file ls.patch
47
62
 
48
63
  ```
49
64
 
65
+ ### Modify dependency libraries
66
+
67
+ #### Add
68
+ ```
69
+ $ patchelf.rb --add-needed libnew.so /bin/ls ls.patch
70
+ ```
71
+
72
+ #### Remove
73
+ ```
74
+ $ patchelf.rb --remove-needed libc.so.6 /bin/ls ls.patch
75
+ ```
76
+
77
+ #### Replace
78
+ ```
79
+ $ patchelf.rb --replace-needed libc.so.6,libcnew.so.6 /bin/ls ls.patch
80
+
81
+ $ readelf -d ls.patch | grep NEEDED
82
+ # 0x0000000000000001 (NEEDED) Shared library: [libselinux.so.1]
83
+ # 0x0000000000000001 (NEEDED) Shared library: [libcnew.so.6]
84
+
85
+ ```
86
+
87
+ #### Set directly
88
+ ```
89
+ $ patchelf.rb --needed a.so,b.so,c.so /bin/ls ls.patch
90
+
91
+ $ readelf -d ls.patch | grep NEEDED
92
+ # 0x0000000000000001 (NEEDED) Shared library: [a.so]
93
+ # 0x0000000000000001 (NEEDED) Shared library: [b.so]
94
+ # 0x0000000000000001 (NEEDED) Shared library: [c.so]
95
+
96
+ ```
97
+
98
+ ### Set RUNPATH of an executable
99
+ ```
100
+ $ patchelf.rb --runpath . /bin/ls ls.patch
101
+
102
+ $ readelf -d ls.patch | grep RUNPATH
103
+ # 0x000000000000001d (RUNPATH) Library runpath: [.]
104
+
105
+ ```
106
+
50
107
  ### Change SONAME of a shared library
51
108
  ```
52
- $ patchelf.rb --soname libnewname.so.217 /lib/x86_64-linux-gnu/libc.so.6 ./libc.patched
109
+ $ patchelf.rb --so libc.so.217 /lib/x86_64-linux-gnu/libc.so.6 libc.patch
53
110
 
54
- $ readelf -d libc.patched | grep SONAME
111
+ $ readelf -d libc.patch | grep SONAME
112
+ # 0x000000000000000e (SONAME) Library soname: [libc.so.217]
55
113
 
56
114
  ```
57
115
 
@@ -60,11 +118,11 @@ $ readelf -d libc.patched | grep SONAME
60
118
  require 'patchelf'
61
119
 
62
120
  patcher = PatchELF::Patcher.new('/bin/ls')
63
- patcher.get(:interpreter)
121
+ patcher.interpreter
64
122
  #=> "/lib64/ld-linux-x86-64.so.2"
65
123
 
66
124
  patcher.interpreter = '/lib/AAAA.so.2'
67
- patcher.get(:interpreter)
125
+ patcher.interpreter
68
126
  #=> "/lib/AAAA.so.2"
69
127
 
70
128
  patcher.save('ls.patch')
@@ -76,4 +134,4 @@ patcher.save('ls.patch')
76
134
 
77
135
  ## Environment
78
136
 
79
- patchelf.rb is implemented in pure Ruby, so it should work in all environments include Linux, maxOS, and Windows!
137
+ patchelf.rb is implemented in pure Ruby, so it should work in all environments include Linux, macOS, and Windows!
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'patchelf/cli'
4
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Main module of patchelf.
2
4
  #
3
5
  # @author david942j
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
4
 
3
5
  require 'patchelf/patcher'
@@ -25,30 +27,50 @@ module PatchELF
25
27
  def work(argv)
26
28
  @options = {
27
29
  set: {},
28
- print: []
30
+ print: [],
31
+ needed: []
29
32
  }
30
33
  return $stdout.puts "PatchELF Version #{PatchELF::VERSION}" if argv.include?('--version')
31
34
  return $stdout.puts option_parser unless parse(argv)
32
35
 
33
36
  # Now the options are (hopefully) valid, let's process the ELF file.
34
- patcher = PatchELF::Patcher.new(@options[:in_file])
35
- # TODO: Handle ELFTools::ELFError
37
+ begin
38
+ @patcher = PatchELF::Patcher.new(@options[:in_file])
39
+ rescue ELFTools::ELFError, Errno::ENOENT => e
40
+ return PatchELF::Logger.error(e.message)
41
+ end
42
+ patcher.use_rpath! if @options[:force_rpath]
43
+ readonly
44
+ patch_requests
45
+ patcher.save(@options[:out_file])
46
+ end
47
+
48
+ private
49
+
50
+ def patcher
51
+ @patcher
52
+ end
53
+
54
+ def readonly
36
55
  @options[:print].uniq.each do |s|
37
- content = patcher.get(s)
56
+ content = patcher.__send__(s)
38
57
  next if content.nil?
39
58
 
40
- $stdout.puts "#{s.to_s.capitalize}: #{Array(content).join(' ')}"
59
+ s = :rpath if @options[:force_rpath] && s == :runpath
60
+ $stdout.puts "#{s}: #{Array(content).join(' ')}"
41
61
  end
62
+ end
42
63
 
64
+ def patch_requests
43
65
  @options[:set].each do |sym, val|
44
66
  patcher.__send__("#{sym}=".to_sym, val)
45
67
  end
46
68
 
47
- patcher.save(@options[:out_file])
69
+ @options[:needed].each do |type, val|
70
+ patcher.__send__("#{type}_needed".to_sym, *val)
71
+ end
48
72
  end
49
73
 
50
- private
51
-
52
74
  def parse(argv)
53
75
  remain = option_parser.permute(argv)
54
76
  return false if remain.first.nil?
@@ -62,23 +84,58 @@ module PatchELF
62
84
  @option_parser ||= OptionParser.new do |opts|
63
85
  opts.banner = USAGE
64
86
 
65
- opts.on('--pi', '--print-interpreter', 'Show interpreter\'s name.') do
87
+ opts.on('--print-interpreter', '--pi', 'Show interpreter\'s name.') do
66
88
  @options[:print] << :interpreter
67
89
  end
68
90
 
69
- opts.on('--pn', '--print-needed', 'Show needed libraries specified in DT_NEEDED.') do
91
+ opts.on('--print-needed', '--pn', 'Show needed libraries specified in DT_NEEDED.') do
70
92
  @options[:print] << :needed
71
93
  end
72
94
 
73
- opts.on('--ps', '--print-soname', 'Show soname specified in DT_SONAME.') do
95
+ opts.on('--print-runpath', '--pr', 'Show the path specified in DT_RUNPATH.') do
96
+ @options[:print] << :runpath
97
+ end
98
+
99
+ opts.on('--print-soname', '--ps', 'Show soname specified in DT_SONAME.') do
74
100
  @options[:print] << :soname
75
101
  end
76
102
 
77
- opts.on('--interp INTERP', '--set-interpreter INTERP', 'Set interpreter\'s name.') do |interp|
103
+ opts.on('--set-interpreter INTERP', '--interp INTERP', 'Set interpreter\'s name.') do |interp|
78
104
  @options[:set][:interpreter] = interp
79
105
  end
80
106
 
81
- opts.on('--so SONAME', '--set-soname SONAME', 'Set name of a shared library.') do |soname|
107
+ opts.on('--set-needed LIB1,LIB2,LIB3', '--needed LIB1,LIB2,LIB3', Array,
108
+ 'Set needed libraries, this will remove all existent needed libraries.') do |needs|
109
+ @options[:set][:needed] = needs
110
+ end
111
+
112
+ opts.on('--add-needed LIB', 'Append a new needed library.') do |lib|
113
+ @options[:needed] << [:add, lib]
114
+ end
115
+
116
+ opts.on('--remove-needed LIB', 'Remove a needed library.') do |lib|
117
+ @options[:needed] << [:remove, lib]
118
+ end
119
+
120
+ opts.on('--replace-needed LIB1,LIB2', Array, 'Replace needed library LIB1 as LIB2.') do |libs|
121
+ @options[:needed] << [:replace, libs]
122
+ end
123
+
124
+ opts.on('--set-runpath PATH', '--runpath PATH', 'Set the path of runpath.') do |path|
125
+ @options[:set][:runpath] = path
126
+ end
127
+
128
+ opts.on(
129
+ '--force-rpath',
130
+ 'According to the ld.so docs, DT_RPATH is obsolete,',
131
+ "#{SCRIPT_NAME} will always try to get/set DT_RUNPATH first.",
132
+ 'Use this option to force every operations related to runpath (e.g. --runpath)',
133
+ 'to consider \'DT_RPATH\' instead of \'DT_RUNPATH\'.'
134
+ ) do
135
+ @options[:force_rpath] = true
136
+ end
137
+
138
+ opts.on('--set-soname SONAME', '--so SONAME', 'Set name of a shared library.') do |soname|
82
139
  @options[:set][:soname] = soname
83
140
  end
84
141
 
@@ -0,0 +1,13 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ require 'elftools/exceptions'
5
+
6
+ module PatchELF
7
+ # Raised on an error during ELF modification.
8
+ class PatchError < ELFTools::ELFError; end
9
+ # Raised when Dynamic Tag is missing
10
+ class MissingTagError < PatchError; end
11
+ # Raised on missing Program Header(segment)
12
+ class MissingSegmentError < PatchError; end
13
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PatchELF
2
4
  # Helper methods for internal usage.
3
5
  module Helper
@@ -40,11 +42,11 @@ module PatchELF
40
42
  # @return [Integer]
41
43
  # Aligned result.
42
44
  # @example
43
- # Helper.aligndown(0x1234)
45
+ # aligndown(0x1234)
44
46
  # #=> 4096
45
- # Helper.aligndown(0x33, 0x20)
47
+ # aligndown(0x33, 0x20)
46
48
  # #=> 32
47
- # Helper.aligndown(0x10, 0x8)
49
+ # aligndown(0x10, 0x8)
48
50
  # #=> 16
49
51
  def aligndown(val, align = PAGE_SIZE)
50
52
  val - (val & (align - 1))
@@ -55,11 +57,11 @@ module PatchELF
55
57
  # @return [Integer]
56
58
  # Aligned result.
57
59
  # @example
58
- # Helper.alignup(0x1234)
60
+ # alignup(0x1234)
59
61
  # #=> 8192
60
- # Helper.alignup(0x33, 0x20)
62
+ # alignup(0x33, 0x20)
61
63
  # #=> 64
62
- # Helper.alignup(0x10, 0x8)
64
+ # alignup(0x10, 0x8)
63
65
  # #=> 16
64
66
  def alignup(val, align = PAGE_SIZE)
65
67
  (val & (align - 1)).zero? ? val : (aligndown(val, align) + align)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
 
3
5
  require 'patchelf/helper'
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'patchelf/helper'
2
- require 'patchelf/interval'
3
4
 
4
5
  module PatchELF
5
6
  # Memory management, provides malloc/free to allocate LOAD segments.
7
+ # @private
6
8
  class MM
7
- attr_reader :extend_size # @return [Integer]
8
- attr_reader :threshold # @return [Integer]
9
+ attr_reader :extend_size # @return [Integer] The size extended.
10
+ attr_reader :threshold # @return [Integer] Where the file start to be extended.
9
11
 
10
12
  # Instantiate a {MM} object.
11
13
  # @param [ELFTools::ELFFile] elf
@@ -21,9 +23,10 @@ module PatchELF
21
23
  # @yieldreturn [void]
22
24
  # One can only do the following things in the block:
23
25
  # 1. Set ELF headers' attributes (with ELFTools)
24
- # 2. Invoke {Patcher#inline_patch}
26
+ # 2. Invoke {Saver#inline_patch}
25
27
  def malloc(size, &block)
26
- # TODO: check size > 0
28
+ raise ArgumentError, 'malloc\'s size most be positive.' if size <= 0
29
+
27
30
  @request << [size, block]
28
31
  end
29
32
 
@@ -32,39 +35,21 @@ module PatchELF
32
35
  def dispatch!
33
36
  return if @request.empty?
34
37
 
35
- request_size = @request.map(&:first).inject(0, :+)
36
- # TODO: raise exception if no LOAD exists.
37
-
38
- # We're going to expand the first LOAD segment.
39
- # Sometimes there's a 'gap' between the first and the second LOAD segment,
40
- # in this case we only need to expand the first LOAD segment and remain all other things unchanged.
41
- if gap_useful?(request_size)
42
- invoke_callbacks
43
- grow_first_load(request_size)
44
- elsif extendable?(request_size)
45
- # After extended we should have large enough 'gap'.
46
-
47
- # | 1 | | 2 |
48
- # | 1 | | 2 |
49
- #=>
50
- # | 1 | | 2 |
51
- # | 1 | | 2 |
52
- # This is really dangerous..
53
- # We have to check all p_offset / sh_offset
54
- # 1. Use ELFTools to patch all headers
55
- # 2. Mark the extended size, inline_patch will behave different after this.
56
- # 3. Invoke block.call, which might copy tables and (not-allow-to-patch) strings into the gap
57
-
58
- @threshold = load_segments[1].file_head
59
- # 1.file_tail + request_size <= 2.file_head + 0x1000x
60
- @extend_size = PatchELF::Helper.alignup(request_size - gap_between_load.size)
61
- shift_attributes
62
-
63
- invoke_callbacks
64
- grow_first_load(request_size)
65
- # else
66
- # This can happen in 32bit
67
- end
38
+ @request_size = @request.map(&:first).inject(0, :+)
39
+ # The malloc-ed area must be 'rw-' since the dynamic table will be modified during runtime.
40
+ # Find all LOADs and calculate their f-gaps and m-gaps.
41
+ # We prefer f-gap since it doesn't need move the whole binaries.
42
+ # 1. Find if any f-gap has enough size, and one of the LOAD next to it is 'rw-'.
43
+ # - expand (forwardlly), only need to change the attribute of LOAD.
44
+ # 2. Do 1. again but consider m-gaps instead.
45
+ # - expand (forwardlly), need to modify all section headers.
46
+ # 3. We have to create a new LOAD, now we need to expand the first LOAD for putting new segment header.
47
+
48
+ # First of all we check if there're less than two LOADs.
49
+ abnormal_elf('No LOAD segment found, not an executable.') if load_segments.empty?
50
+ # TODO: Handle only one LOAD. (be careful if memsz > filesz)
51
+
52
+ fgap_method || mgap_method || new_load_method
68
53
  end
69
54
 
70
55
  # Query if extended.
@@ -73,7 +58,11 @@ module PatchELF
73
58
  defined?(@threshold)
74
59
  end
75
60
 
61
+ # Get correct offset after the extension.
62
+ #
63
+ # @param [Integer] off
76
64
  # @return [Integer]
65
+ # Shifted offset.
77
66
  def extended_offset(off)
78
67
  return off unless defined?(@threshold)
79
68
  return off if off < @threshold
@@ -83,37 +72,75 @@ module PatchELF
83
72
 
84
73
  private
85
74
 
86
- def gap_useful?(need_size)
87
- # Two conditions:
88
- # 1. gap is large enough
89
- gap = gap_between_load
90
- return false if gap.size < need_size
75
+ def fgap_method
76
+ idx = find_gap { |prv, nxt| nxt.file_head - prv.file_tail }
77
+ return false if idx.nil?
78
+
79
+ loads = load_segments
80
+ # prefer extend backwardly
81
+ return extend_backward(loads[idx - 1]) if writable?(loads[idx - 1])
82
+
83
+ extend_forward(loads[idx])
84
+ end
91
85
 
92
- # XXX: Do we really need this..?
93
- # If gap is enough but not all zeros, we will fail on extension..
94
- # 2. gap is all zeroes.
95
- # @elf.stream.pos = gap.head
96
- # return false unless @elf.stream.read(gap.size).bytes.inject(0, :+).zero?
86
+ def extend_backward(seg, size = @request_size)
87
+ invoke_callbacks(seg, seg.file_tail)
88
+ seg.header.p_filesz += size
89
+ seg.header.p_memsz += size
90
+ true
91
+ end
97
92
 
93
+ def extend_forward(seg, size = @request_size)
94
+ seg.header.p_offset -= size
95
+ seg.header.p_vaddr -= size
96
+ seg.header.p_filesz += size
97
+ seg.header.p_memsz += size
98
+ invoke_callbacks(seg, seg.file_head)
98
99
  true
99
100
  end
100
101
 
101
- # @return [PatchELF::Interval]
102
- def gap_between_load
103
- # We need this cache since the second LOAD might be changed
104
- return @gap_between_load if defined?(@gap_between_load)
102
+ def mgap_method
103
+ # | 1 | | 2 |
104
+ # | 1 | | 2 |
105
+ #=>
106
+ # | 1 | | 2 |
107
+ # | 1 | | 2 |
108
+ idx = find_gap(check_sz: false) { |prv, nxt| PatchELF::Helper.aligndown(nxt.mem_head) - prv.mem_tail }
109
+ return false if idx.nil?
110
+
111
+ loads = load_segments
112
+ @threshold = loads[idx].file_head
113
+ @extend_size = PatchELF::Helper.alignup(@request_size)
114
+ shift_attributes
115
+ # prefer backward than forward
116
+ return extend_backward(loads[idx - 1]) if writable?(loads[idx - 1])
117
+
118
+ # note: loads[idx].file_head has been changed in shift_attributes
119
+ extend_forward(loads[idx], @extend_size)
120
+ end
121
+
122
+ def find_gap(check_sz: true)
123
+ loads = load_segments
124
+ loads.each_with_index do |l, i|
125
+ next if i.zero?
126
+ next unless writable?(l) || writable?(loads[i - 1])
105
127
 
106
- loads = load_segments.map do |seg|
107
- PatchELF::Interval.new(seg.file_head, seg.size)
128
+ sz = yield(loads[i - 1], l)
129
+ abnormal_elf('LOAD segments are out of order.') if check_sz && sz.negative?
130
+ next unless sz >= @request_size
131
+
132
+ return i
108
133
  end
109
- # TODO: raise if loads.min != loads.first
134
+ nil
135
+ end
136
+
137
+ # TODO
138
+ def new_load_method
139
+ raise NotImplementedError
140
+ end
110
141
 
111
- loads.sort!
112
- # Only one LOAD, the gap has infinity size!
113
- size = if loads.size == 1 then Float::INFINITY
114
- else loads[1].head - loads.first.tail
115
- end
116
- @gap_between_load = PatchELF::Interval.new(loads.first.tail, size)
142
+ def writable?(seg)
143
+ seg.readable? && seg.writable?
117
144
  end
118
145
 
119
146
  # For all attributes >= threshold, += offset
@@ -123,14 +150,18 @@ module PatchELF
123
150
  # all
124
151
  # Segments:
125
152
  # all
126
- # XXX: will be buggy if one day the number of segments might be changed.
153
+ # XXX: will be buggy if someday the number of segments can be changed.
127
154
 
128
155
  # Bottom-up
129
156
  @elf.each_sections do |sec|
130
157
  sec.header.sh_offset += extend_size if sec.header.sh_offset >= threshold
131
158
  end
132
159
  @elf.each_segments do |seg|
133
- seg.header.p_offset += extend_size if seg.header.p_offset >= threshold
160
+ next unless seg.header.p_offset >= threshold
161
+
162
+ seg.header.p_offset += extend_size
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)
134
165
  end
135
166
 
136
167
  @elf.header.e_shoff += extend_size if @elf.header.e_shoff >= threshold
@@ -140,28 +171,16 @@ module PatchELF
140
171
  @elf.segments_by_type(:load)
141
172
  end
142
173
 
143
- def extendable?(request_size)
144
- loads = load_segments
145
- # We can assume loads.size >= 2 because
146
- # 0: has raised an exception before
147
- # 1: the gap must be used, nobody cares extendable size.
148
- # Calcluate the max size of the first LOAD segment can be.
149
- PatchELF::Helper.aligndown(loads[1].mem_head) - loads.first.mem_tail >= request_size
150
- end
151
-
152
- def invoke_callbacks
153
- seg = load_segments.first
154
- cur = gap_between_load.head
174
+ def invoke_callbacks(seg, start)
175
+ cur = start
155
176
  @request.each do |sz, block|
156
177
  block.call(cur, seg.offset_to_vma(cur))
157
178
  cur += sz
158
179
  end
159
180
  end
160
181
 
161
- def grow_first_load(size)
162
- seg = load_segments.first
163
- seg.header.p_filesz += size
164
- seg.header.p_memsz += size
182
+ def abnormal_elf(msg)
183
+ raise ArgumentError, msg
165
184
  end
166
185
  end
167
186
  end