patchelf 0.0.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|