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 +4 -4
- data/README.md +71 -13
- data/bin/patchelf.rb +1 -0
- data/lib/patchelf.rb +2 -0
- data/lib/patchelf/cli.rb +70 -13
- data/lib/patchelf/exceptions.rb +13 -0
- data/lib/patchelf/helper.rb +8 -6
- data/lib/patchelf/logger.rb +2 -0
- data/lib/patchelf/mm.rb +98 -79
- data/lib/patchelf/patcher.rb +143 -138
- data/lib/patchelf/saver.rb +285 -0
- data/lib/patchelf/version.rb +3 -1
- metadata +10 -10
- data/lib/patchelf/interval.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8560f68dba4f9e656585c38f31da63ae5f15320a03c1675adb13d27ffa2230b4
|
4
|
+
data.tar.gz: 2d19b129beb5df7bd713fc6f0f65bb942194bea201857bda499a18e4496c4fe2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
# --
|
23
|
-
# --
|
24
|
-
# --
|
25
|
-
# --
|
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
|
-
# --
|
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
|
-
#
|
36
|
-
#
|
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 --
|
109
|
+
$ patchelf.rb --so libc.so.217 /lib/x86_64-linux-gnu/libc.so.6 libc.patch
|
53
110
|
|
54
|
-
$ readelf -d libc.
|
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.
|
121
|
+
patcher.interpreter
|
64
122
|
#=> "/lib64/ld-linux-x86-64.so.2"
|
65
123
|
|
66
124
|
patcher.interpreter = '/lib/AAAA.so.2'
|
67
|
-
patcher.
|
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,
|
137
|
+
patchelf.rb is implemented in pure Ruby, so it should work in all environments include Linux, macOS, and Windows!
|
data/bin/patchelf.rb
CHANGED
data/lib/patchelf.rb
CHANGED
data/lib/patchelf/cli.rb
CHANGED
@@ -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
|
-
|
35
|
-
|
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.
|
56
|
+
content = patcher.__send__(s)
|
38
57
|
next if content.nil?
|
39
58
|
|
40
|
-
|
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
|
-
|
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('--
|
87
|
+
opts.on('--print-interpreter', '--pi', 'Show interpreter\'s name.') do
|
66
88
|
@options[:print] << :interpreter
|
67
89
|
end
|
68
90
|
|
69
|
-
opts.on('--
|
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('--
|
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('--
|
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('--
|
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
|
data/lib/patchelf/helper.rb
CHANGED
@@ -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
|
-
#
|
45
|
+
# aligndown(0x1234)
|
44
46
|
# #=> 4096
|
45
|
-
#
|
47
|
+
# aligndown(0x33, 0x20)
|
46
48
|
# #=> 32
|
47
|
-
#
|
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
|
-
#
|
60
|
+
# alignup(0x1234)
|
59
61
|
# #=> 8192
|
60
|
-
#
|
62
|
+
# alignup(0x33, 0x20)
|
61
63
|
# #=> 64
|
62
|
-
#
|
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)
|
data/lib/patchelf/logger.rb
CHANGED
data/lib/patchelf/mm.rb
CHANGED
@@ -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 {
|
26
|
+
# 2. Invoke {Saver#inline_patch}
|
25
27
|
def malloc(size, &block)
|
26
|
-
|
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
|
-
#
|
37
|
-
|
38
|
-
# We'
|
39
|
-
#
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
102
|
-
|
103
|
-
#
|
104
|
-
|
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
|
-
|
107
|
-
|
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
|
-
|
134
|
+
nil
|
135
|
+
end
|
136
|
+
|
137
|
+
# TODO
|
138
|
+
def new_load_method
|
139
|
+
raise NotImplementedError
|
140
|
+
end
|
110
141
|
|
111
|
-
|
112
|
-
|
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
|
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
|
-
|
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
|
144
|
-
|
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
|
162
|
-
|
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
|