elftools 2.1.0 → 2.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/lib/elftools/constants.rb +9 -0
- data/lib/elftools/dynamic/symbols.rb +13 -6
- data/lib/elftools/dynamic/tag.rb +28 -6
- data/lib/elftools/dynamic.rb +12 -31
- data/lib/elftools/elf_file.rb +60 -11
- data/lib/elftools/relative_relocations.rb +3 -4
- data/lib/elftools/relocation.rb +27 -14
- data/lib/elftools/sections/relocation_section.rb +16 -5
- data/lib/elftools/sections/sym_tab_section.rb +26 -4
- data/lib/elftools/sections/symbol.rb +23 -11
- data/lib/elftools/structs.rb +264 -1
- data/lib/elftools/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cbbd11ec3b95d867ab34551c14f1a0a53c3c5e2ebb539d226b16378873a0f74a
|
|
4
|
+
data.tar.gz: 05301ad23c8d366858d57f58cccf4a3274608a036de07e629e85075b4e1babd5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2e96723b2a63f7f6692cc6777bd64bd3b5e3aae7321a09da3a9098e26a4a95162bc892a2933088bc6fef89f14cb72269434e71c32bc2551f5b252105b09d6aa8
|
|
7
|
+
data.tar.gz: b177302e11ea50108f582dc4f70651136dcc5a15e2363856f2cfc453fbd2979eb202520a40468b4349af12e0dc1c84960b4972548c360bf902729e3acb98f456
|
data/lib/elftools/constants.rb
CHANGED
|
@@ -466,6 +466,15 @@ module ELFTools
|
|
|
466
466
|
end
|
|
467
467
|
include SHN
|
|
468
468
|
|
|
469
|
+
# The escape value the ELF header records where it cannot hold a count of
|
|
470
|
+
# the program headers, which is then recorded in the first section header.
|
|
471
|
+
module PN
|
|
472
|
+
extend Naming
|
|
473
|
+
|
|
474
|
+
PN_XNUM = 0xffff # the number of program headers is too large for the header to hold
|
|
475
|
+
end
|
|
476
|
+
include PN
|
|
477
|
+
|
|
469
478
|
# Section flag mask types, records in +sh_flag+.
|
|
470
479
|
module SHF
|
|
471
480
|
SHF_WRITE = (1 << 0) # Writable
|
|
@@ -29,11 +29,11 @@ module ELFTools
|
|
|
29
29
|
return if n.negative?
|
|
30
30
|
|
|
31
31
|
@symbol_at_map ||= {}
|
|
32
|
-
@symbol_at_map[n] ||=
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
@symbol_at_map[n] ||= Sections::Symbol.new(
|
|
33
|
+
Structs::Fields.new(Structs::ELF_sym[header.elf_class], stream, sym_offset + (n * sym_entsize),
|
|
34
|
+
elf_class: header.elf_class, endian: endian),
|
|
35
|
+
stream, symstr: string_table_reader, machine: @machine, version: -> { version_at(n) }
|
|
36
|
+
)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
# How many symbols the tags reach.
|
|
@@ -154,7 +154,14 @@ module ELFTools
|
|
|
154
154
|
# has no way of disagreeing with.
|
|
155
155
|
# @return [Integer] The number.
|
|
156
156
|
def sym_entsize
|
|
157
|
-
@sym_entsize ||=
|
|
157
|
+
@sym_entsize ||= Structs::ELF_sym[header.elf_class].num_bytes(elf_class: header.elf_class, endian: endian)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# What reads the table these symbols are named in, kept so that reading
|
|
161
|
+
# a table of them does not make one for every symbol.
|
|
162
|
+
# @return [Method] The method.
|
|
163
|
+
def string_table_reader
|
|
164
|
+
@string_table_reader ||= method(:string_table)
|
|
158
165
|
end
|
|
159
166
|
|
|
160
167
|
# Get the +DT_SYMTAB+'s +d_val+ offset related to file.
|
data/lib/elftools/dynamic/tag.rb
CHANGED
|
@@ -1,25 +1,47 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'elftools/constants'
|
|
4
|
+
require 'elftools/structs'
|
|
4
5
|
|
|
5
6
|
module ELFTools
|
|
6
7
|
module Dynamic
|
|
7
8
|
# A tag class.
|
|
8
9
|
class Tag
|
|
9
|
-
attr_reader :header # @return [ELFTools::Structs::ELF_Dyn] The dynamic tag header.
|
|
10
10
|
attr_reader :stream # @return [#pos=, #read] Streaming object.
|
|
11
11
|
|
|
12
12
|
# Instantiate a {ELFTools::Dynamic::Tag} object.
|
|
13
|
-
# @param [ELF_Dyn] header
|
|
13
|
+
# @param [ELFTools::Structs::ELF_Dyn, ELFTools::Structs::Fields] header
|
|
14
|
+
# The dynamic tag header, or what the file records in one.
|
|
15
|
+
# {ELFTools::Structs::Fields} answers what the tag records until
|
|
16
|
+
# something asks for {#header} itself, which builds the structure then.
|
|
14
17
|
# @param [#pos=, #read] stream Streaming object.
|
|
15
18
|
# @param [ELFTools::Dynamic::StringTable] strtab
|
|
16
19
|
# The string table the names of tags are recorded in.
|
|
17
20
|
def initialize(header, stream, strtab)
|
|
18
|
-
@
|
|
21
|
+
@fields = header.is_a?(Structs::Fields) ? header : Structs::Fields.of(header)
|
|
19
22
|
@stream = stream
|
|
20
23
|
@strtab = strtab
|
|
21
24
|
end
|
|
22
25
|
|
|
26
|
+
# The structure the file records this tag in.
|
|
27
|
+
#
|
|
28
|
+
# One is built here for a tag read without it, which is what assigning to
|
|
29
|
+
# a field of one needs and what {ELFTools::ELFFile#patches} reports the
|
|
30
|
+
# changes of.
|
|
31
|
+
# @return [ELFTools::Structs::ELF_Dyn] The structure.
|
|
32
|
+
def header
|
|
33
|
+
@fields.struct
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# What kind of tag this is, which {ELFTools::Constants::DT} names.
|
|
37
|
+
# @return [Integer] The type.
|
|
38
|
+
# @example
|
|
39
|
+
# dynamic.tag_by_type(:needed).type == ELFTools::Constants::DT_NEEDED
|
|
40
|
+
# #=> true
|
|
41
|
+
def type
|
|
42
|
+
@fields[:d_tag]
|
|
43
|
+
end
|
|
44
|
+
|
|
23
45
|
# Some dynamic have name.
|
|
24
46
|
TYPE_WITH_NAME = [Constants::DT_NEEDED,
|
|
25
47
|
Constants::DT_SONAME,
|
|
@@ -40,7 +62,7 @@ module ELFTools
|
|
|
40
62
|
# dynamic.tag_by_type(:needed).value
|
|
41
63
|
# #=> 'libc.so.6'
|
|
42
64
|
def value
|
|
43
|
-
name ||
|
|
65
|
+
name || @fields[:d_val]
|
|
44
66
|
end
|
|
45
67
|
|
|
46
68
|
# Is this tag has a name?
|
|
@@ -48,7 +70,7 @@ module ELFTools
|
|
|
48
70
|
# The criteria here is if this tag's type is in {TYPE_WITH_NAME}.
|
|
49
71
|
# @return [Boolean] Is this tag has a name.
|
|
50
72
|
def name?
|
|
51
|
-
TYPE_WITH_NAME.include?(
|
|
73
|
+
TYPE_WITH_NAME.include?(type)
|
|
52
74
|
end
|
|
53
75
|
|
|
54
76
|
# Return the name of this tag.
|
|
@@ -59,7 +81,7 @@ module ELFTools
|
|
|
59
81
|
def name
|
|
60
82
|
return nil unless name?
|
|
61
83
|
|
|
62
|
-
@strtab.name_at(
|
|
84
|
+
@strtab.name_at(@fields[:d_val])
|
|
63
85
|
end
|
|
64
86
|
end
|
|
65
87
|
end
|
data/lib/elftools/dynamic.rb
CHANGED
|
@@ -38,7 +38,7 @@ module ELFTools
|
|
|
38
38
|
0.step do |i|
|
|
39
39
|
tag = tag_at(i).tap(&block)
|
|
40
40
|
arr << tag
|
|
41
|
-
break if tag.
|
|
41
|
+
break if tag.type == ELFTools::Constants::DT_NULL
|
|
42
42
|
end
|
|
43
43
|
arr
|
|
44
44
|
end
|
|
@@ -79,7 +79,7 @@ module ELFTools
|
|
|
79
79
|
# #=> #<ELFTools::Dynamic::Tag:0x0055d3d2d91b28 @header={:d_tag=>3, :d_val=>6295552}>
|
|
80
80
|
def tag_by_type(type)
|
|
81
81
|
type = Util.to_constant(Constants::DT, type)
|
|
82
|
-
each_tag.find { |tag| tag.
|
|
82
|
+
each_tag.find { |tag| tag.type == type }
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
# Get tags of specific type.
|
|
@@ -91,7 +91,7 @@ module ELFTools
|
|
|
91
91
|
# @see #tag_by_type
|
|
92
92
|
def tags_by_type(type)
|
|
93
93
|
type = Util.to_constant(Constants::DT, type)
|
|
94
|
-
each_tag.select { |tag| tag.
|
|
94
|
+
each_tag.select { |tag| tag.type == type }
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
# Get the +n+-th tag.
|
|
@@ -112,11 +112,10 @@ module ELFTools
|
|
|
112
112
|
@tag_at_map ||= {}
|
|
113
113
|
return @tag_at_map[n] if @tag_at_map[n]
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
@tag_at_map[n] = Tag.new(dyn.read(stream), stream, string_table)
|
|
115
|
+
elf_class = header.elf_class
|
|
116
|
+
offset = tag_start + (n * Structs::ELF_Dyn.num_bytes(elf_class: elf_class, endian: endian))
|
|
117
|
+
fields = Structs::Fields.new(Structs::ELF_Dyn, stream, offset, elf_class: elf_class, endian: endian)
|
|
118
|
+
@tag_at_map[n] = Tag.new(fields, stream, string_table)
|
|
120
119
|
end
|
|
121
120
|
|
|
122
121
|
# The relocations the tags point at.
|
|
@@ -176,32 +175,14 @@ module ELFTools
|
|
|
176
175
|
# An entry takes what its structure takes. DT_RELAENT and DT_RELENT
|
|
177
176
|
# record the same number, which a file has no way of disagreeing with
|
|
178
177
|
# and every file here agrees with.
|
|
179
|
-
|
|
178
|
+
elf_class = header.elf_class
|
|
179
|
+
entsize = klass.num_bytes(elf_class: elf_class, endian: endian)
|
|
180
180
|
Array.new(size.header.d_val.to_i / entsize) do |i|
|
|
181
|
-
|
|
181
|
+
fields = Structs::Fields.new(klass, stream, offset + (i * entsize), elf_class: elf_class, endian: endian)
|
|
182
|
+
Relocation.new(fields, stream, machine: @machine)
|
|
182
183
|
end
|
|
183
184
|
end
|
|
184
185
|
|
|
185
|
-
# A structure of the endianness and the class the file records it in.
|
|
186
|
-
# @param [Class] klass The structure class.
|
|
187
|
-
# @return [ELFTools::Structs::ELFStruct] The structure, before it is read.
|
|
188
|
-
def struct(klass)
|
|
189
|
-
struct = klass.new(endian:)
|
|
190
|
-
struct.elf_class = header.elf_class
|
|
191
|
-
struct
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
# Reads a structure the file records at a file offset.
|
|
195
|
-
# @param [Class] klass The structure class.
|
|
196
|
-
# @param [Integer] offset The file offset.
|
|
197
|
-
# @return [ELFTools::Structs::ELFStruct] The structure.
|
|
198
|
-
def read_struct(klass, offset)
|
|
199
|
-
struct = struct(klass)
|
|
200
|
-
struct.offset = offset
|
|
201
|
-
stream.pos = offset
|
|
202
|
-
struct.read(stream)
|
|
203
|
-
end
|
|
204
|
-
|
|
205
186
|
# The file offset the address a tag records points at.
|
|
206
187
|
# @param [ELFTools::Dynamic::Tag] tag The tag.
|
|
207
188
|
# @return [Integer] The file offset.
|
|
@@ -210,7 +191,7 @@ module ELFTools
|
|
|
210
191
|
def offset_of(tag)
|
|
211
192
|
vma = tag.header.d_val.to_i
|
|
212
193
|
@offset_from_vma.call(vma) ||
|
|
213
|
-
raise(ELFError, format('Invalid %s address 0x%x', Constants::DT.mapping(@machine, tag.
|
|
194
|
+
raise(ELFError, format('Invalid %s address 0x%x', Constants::DT.mapping(@machine, tag.type), vma))
|
|
214
195
|
end
|
|
215
196
|
|
|
216
197
|
# The names the tags and the symbols point at.
|
data/lib/elftools/elf_file.rb
CHANGED
|
@@ -105,12 +105,19 @@ module ELFTools
|
|
|
105
105
|
#========= method about sections
|
|
106
106
|
|
|
107
107
|
# Number of sections in this file.
|
|
108
|
+
#
|
|
109
|
+
# A file with more sections than the ELF header can count records a zero
|
|
110
|
+
# there and states the number in the first section header instead, which a
|
|
111
|
+
# file with no section headers at all records as well.
|
|
108
112
|
# @return [Integer] The desired number.
|
|
109
113
|
# @example
|
|
110
114
|
# elf.num_sections
|
|
111
115
|
# #=> 29
|
|
112
116
|
def num_sections
|
|
113
|
-
header.e_shnum
|
|
117
|
+
count = header.e_shnum.to_i
|
|
118
|
+
return count unless count.zero? && !header.e_shoff.to_i.zero?
|
|
119
|
+
|
|
120
|
+
first_section_header.sh_size.to_i
|
|
114
121
|
end
|
|
115
122
|
|
|
116
123
|
# Acquire the section named as +name+.
|
|
@@ -197,15 +204,25 @@ module ELFTools
|
|
|
197
204
|
# elf.section_name_table.name
|
|
198
205
|
# #=> '.shstrtab'
|
|
199
206
|
def section_name_table
|
|
200
|
-
|
|
207
|
+
index = header.e_shstrndx.to_i
|
|
208
|
+
# An index too large for the ELF header is stated in the first section
|
|
209
|
+
# header instead.
|
|
210
|
+
index = first_section_header.sh_link.to_i if index == Constants::SHN_XINDEX
|
|
211
|
+
section_at(index)
|
|
201
212
|
end
|
|
202
213
|
|
|
203
214
|
#========= method about segments
|
|
204
215
|
|
|
205
216
|
# Number of segments in this file.
|
|
217
|
+
#
|
|
218
|
+
# A file with more segments than the ELF header can count states the number
|
|
219
|
+
# in the first section header instead, as it does for the sections.
|
|
206
220
|
# @return [Integer] The desited number.
|
|
207
221
|
def num_segments
|
|
208
|
-
header.e_phnum
|
|
222
|
+
count = header.e_phnum.to_i
|
|
223
|
+
return count unless count == Constants::PN_XNUM
|
|
224
|
+
|
|
225
|
+
first_section_header.sh_info.to_i
|
|
209
226
|
end
|
|
210
227
|
|
|
211
228
|
# Iterate all segments.
|
|
@@ -373,15 +390,35 @@ module ELFTools
|
|
|
373
390
|
|
|
374
391
|
# bad idea..
|
|
375
392
|
def loaded_headers
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
393
|
+
# By identity, so that a graph that reaches the same thing by many paths,
|
|
394
|
+
# or leads round in circles, is walked once rather than over and over.
|
|
395
|
+
headers_in(self, {}.compare_by_identity).flatten
|
|
396
|
+
end
|
|
379
397
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
398
|
+
# The structures reachable from an object, whatever holds them.
|
|
399
|
+
# @param [Object] obj The object.
|
|
400
|
+
# @param [Hash] seen What has been reached already.
|
|
401
|
+
# @return [Array] The structures, nested.
|
|
402
|
+
def headers_in(obj, seen)
|
|
403
|
+
# A class is not one of the things a file records.
|
|
404
|
+
return [] if seen[obj] || obj.is_a?(Module)
|
|
405
|
+
|
|
406
|
+
seen[obj] = true
|
|
407
|
+
return obj if obj.is_a?(::ELFTools::Structs::ELFStruct)
|
|
408
|
+
|
|
409
|
+
held_by(obj).map { |held| headers_in(held, seen) }
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# What an object holds, which a walk of it goes on to.
|
|
413
|
+
# @param [Object] obj The object.
|
|
414
|
+
# @return [Enumerable] What it holds.
|
|
415
|
+
def held_by(obj)
|
|
416
|
+
return obj if obj.is_a?(Array)
|
|
417
|
+
# A hash is held by its values, which is where the tags and the symbols
|
|
418
|
+
# read through the tags are remembered.
|
|
419
|
+
return obj.each_value if obj.is_a?(Hash)
|
|
420
|
+
|
|
421
|
+
obj.instance_variables.map { |name| obj.instance_variable_get(name) }
|
|
385
422
|
end
|
|
386
423
|
|
|
387
424
|
def identify
|
|
@@ -404,6 +441,18 @@ module ELFTools
|
|
|
404
441
|
raise ELFDataError, format('Invalid EI_DATA "\x%02x"', ei_data) if endian.nil?
|
|
405
442
|
end
|
|
406
443
|
|
|
444
|
+
# The first section header, which is where a file too large for the counts
|
|
445
|
+
# the ELF header records states them.
|
|
446
|
+
# @return [ELFTools::Structs::ELF_Shdr] The header.
|
|
447
|
+
def first_section_header
|
|
448
|
+
@first_section_header ||= begin
|
|
449
|
+
stream.pos = header.e_shoff.to_i
|
|
450
|
+
shdr = Structs::ELF_Shdr.new(endian:, offset: stream.pos)
|
|
451
|
+
shdr.elf_class = elf_class
|
|
452
|
+
shdr.read(stream)
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
407
456
|
def create_section(n)
|
|
408
457
|
stream.pos = header.e_shoff + n * header.e_shentsize
|
|
409
458
|
shdr = Structs::ELF_Shdr.new(endian:, offset: stream.pos)
|
|
@@ -39,10 +39,9 @@ module ELFTools
|
|
|
39
39
|
def to_a
|
|
40
40
|
type = Constants::R.relative(@machine)
|
|
41
41
|
addresses.map do |address, from|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
relocation = Relocation.new(rel, @stream, machine: @machine)
|
|
42
|
+
fields = Structs::Fields.from(Structs::ELF_Rel, { r_offset: address, r_info: 0 },
|
|
43
|
+
elf_class: @elf_class, endian: @endian, offset: from)
|
|
44
|
+
relocation = Relocation.new(fields, @stream, machine: @machine)
|
|
46
45
|
# Through the relocation, so that the type is laid out in +r_info+ the
|
|
47
46
|
# way the machine lays it out.
|
|
48
47
|
relocation.type = type if type
|
data/lib/elftools/relocation.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'elftools/constants'
|
|
4
|
+
require 'elftools/structs'
|
|
4
5
|
require 'elftools/util'
|
|
5
6
|
|
|
6
7
|
module ELFTools
|
|
@@ -8,22 +9,33 @@ module ELFTools
|
|
|
8
9
|
#
|
|
9
10
|
# Can be either a REL or RELA relocation.
|
|
10
11
|
class Relocation
|
|
11
|
-
attr_reader :header # @return [ELFTools::Structs::ELF_Rel, ELFTools::Structs::ELF_Rela] Rel(a) header.
|
|
12
12
|
attr_reader :stream # @return [#pos=, #read] Streaming object.
|
|
13
13
|
|
|
14
14
|
# Instantiate a {Relocation} object.
|
|
15
|
-
# @param [ELFTools::Structs::ELF_Rel, ELFTools::Structs::ELF_Rela] header
|
|
16
|
-
# The relocation header.
|
|
15
|
+
# @param [ELFTools::Structs::ELF_Rel, ELFTools::Structs::ELF_Rela, ELFTools::Structs::Fields] header
|
|
16
|
+
# The relocation header, or what the file records in one.
|
|
17
|
+
# {ELFTools::Structs::Fields} answers what the relocation records until
|
|
18
|
+
# something asks for {#header} itself, which builds the structure then.
|
|
17
19
|
# @param [#pos=, #read] stream The streaming object.
|
|
18
20
|
# @param [Integer] machine
|
|
19
21
|
# The machine of the ELF file, which decides what {#type} means and how
|
|
20
22
|
# {#header} records it.
|
|
21
23
|
def initialize(header, stream, machine: nil)
|
|
22
|
-
@
|
|
24
|
+
@fields = header.is_a?(Structs::Fields) ? header : Structs::Fields.of(header)
|
|
23
25
|
@stream = stream
|
|
24
26
|
@machine = machine
|
|
25
27
|
end
|
|
26
28
|
|
|
29
|
+
# The structure the file records this relocation in.
|
|
30
|
+
#
|
|
31
|
+
# One is built here for a relocation read without it, which is what
|
|
32
|
+
# assigning to a field of one needs and what {ELFTools::ELFFile#patches}
|
|
33
|
+
# reports the changes of.
|
|
34
|
+
# @return [ELFTools::Structs::ELF_Rel, ELFTools::Structs::ELF_Rela] The structure.
|
|
35
|
+
def header
|
|
36
|
+
@fields.struct
|
|
37
|
+
end
|
|
38
|
+
|
|
27
39
|
# Which symbol this relocation is against, as an index into the symbol
|
|
28
40
|
# table.
|
|
29
41
|
# @return [Integer] The symbol index.
|
|
@@ -44,7 +56,7 @@ module ELFTools
|
|
|
44
56
|
# @example
|
|
45
57
|
# relocation.symbol_index = 3
|
|
46
58
|
def symbol_index=(index)
|
|
47
|
-
|
|
59
|
+
@fields[:r_info] = info_of(Util.fits!(index, index_bits, 'Symbol index'), type)
|
|
48
60
|
end
|
|
49
61
|
|
|
50
62
|
# Sets what this relocation does.
|
|
@@ -53,7 +65,7 @@ module ELFTools
|
|
|
53
65
|
# @example
|
|
54
66
|
# relocation.type = ELFTools::Constants::R::X86_64::R_X86_64_JUMP_SLOT
|
|
55
67
|
def type=(type)
|
|
56
|
-
|
|
68
|
+
@fields[:r_info] = info_of(symbol_index, Util.fits!(type, type_bits, 'Relocation type'))
|
|
57
69
|
end
|
|
58
70
|
|
|
59
71
|
# The name of {#type}.
|
|
@@ -83,8 +95,8 @@ module ELFTools
|
|
|
83
95
|
# ABI keeps for itself as they were.
|
|
84
96
|
# @return [Integer] The +r_info+.
|
|
85
97
|
def mips64_info_of(index, type)
|
|
86
|
-
info =
|
|
87
|
-
return (index << 32) | (info & 0xffff_ff00) | type if
|
|
98
|
+
info = @fields[:r_info]
|
|
99
|
+
return (index << 32) | (info & 0xffff_ff00) | type if @fields.endian == :big
|
|
88
100
|
|
|
89
101
|
(info & 0x00ff_ffff_0000_0000) | (type << 56) | index
|
|
90
102
|
end
|
|
@@ -92,7 +104,7 @@ module ELFTools
|
|
|
92
104
|
# How many bits record a symbol index.
|
|
93
105
|
# @return [Integer] The number.
|
|
94
106
|
def index_bits
|
|
95
|
-
mips64? ? 32 : (
|
|
107
|
+
mips64? ? 32 : (@fields.elf_class - mask_bit)
|
|
96
108
|
end
|
|
97
109
|
|
|
98
110
|
# How many bits record a relocation type.
|
|
@@ -108,14 +120,15 @@ module ELFTools
|
|
|
108
120
|
def sym_and_type
|
|
109
121
|
return mips64_sym_and_type if mips64?
|
|
110
122
|
|
|
111
|
-
|
|
123
|
+
info = @fields[:r_info]
|
|
124
|
+
[info >> mask_bit, info & ((1 << mask_bit) - 1)]
|
|
112
125
|
end
|
|
113
126
|
|
|
114
127
|
# Whether the file records relocations the way the 64-bit MIPS ABI does,
|
|
115
128
|
# which is the one layout that departs from halving +r_info+.
|
|
116
129
|
# @return [Boolean] The answer.
|
|
117
130
|
def mips64?
|
|
118
|
-
@machine == Constants::EM_MIPS &&
|
|
131
|
+
@machine == Constants::EM_MIPS && @fields.elf_class == 64
|
|
119
132
|
end
|
|
120
133
|
|
|
121
134
|
# Reads +r_info+ as the 64-bit MIPS ABI records it, i.e. a symbol index of
|
|
@@ -131,14 +144,14 @@ module ELFTools
|
|
|
131
144
|
# 0x0718050000000008 #=> [8, 7]
|
|
132
145
|
# @return [Array(Integer, Integer)] The symbol index and the type.
|
|
133
146
|
def mips64_sym_and_type
|
|
134
|
-
info =
|
|
135
|
-
return [info >> 32, info & 0xff] if
|
|
147
|
+
info = @fields[:r_info]
|
|
148
|
+
return [info >> 32, info & 0xff] if @fields.endian == :big
|
|
136
149
|
|
|
137
150
|
[info & 0xffff_ffff, info >> 56]
|
|
138
151
|
end
|
|
139
152
|
|
|
140
153
|
def mask_bit
|
|
141
|
-
|
|
154
|
+
@fields.elf_class == 32 ? 8 : 32
|
|
142
155
|
end
|
|
143
156
|
end
|
|
144
157
|
end
|
|
@@ -78,12 +78,23 @@ module ELFTools
|
|
|
78
78
|
private
|
|
79
79
|
|
|
80
80
|
def create_relocation(n)
|
|
81
|
-
stream.pos = header.sh_offset + n * header.sh_entsize
|
|
82
81
|
klass = rela? ? Structs::ELF_Rela : Structs::ELF_Rel
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
fields = Structs::Fields.new(klass, stream, table_offset + (n * entsize),
|
|
83
|
+
elf_class: header.elf_class, endian: header.class.self_endian)
|
|
84
|
+
Relocation.new(fields, stream, machine: @machine)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Where this table starts in the file, asked of the header once rather
|
|
88
|
+
# than once for every relocation read.
|
|
89
|
+
# @return [Integer] The file offset.
|
|
90
|
+
def table_offset
|
|
91
|
+
@table_offset ||= header.sh_offset.to_i
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# How many bytes this table spaces its entries by.
|
|
95
|
+
# @return [Integer] The number.
|
|
96
|
+
def entsize
|
|
97
|
+
@entsize ||= header.sh_entsize.to_i
|
|
87
98
|
end
|
|
88
99
|
end
|
|
89
100
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'elftools/sections/section'
|
|
4
4
|
require 'elftools/sections/symbol'
|
|
5
|
+
require 'elftools/structs'
|
|
5
6
|
require 'elftools/version_tables'
|
|
6
7
|
|
|
7
8
|
module ELFTools
|
|
@@ -99,10 +100,31 @@ module ELFTools
|
|
|
99
100
|
private
|
|
100
101
|
|
|
101
102
|
def create_symbol(n)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
Symbol.new(
|
|
104
|
+
Structs::Fields.new(Structs::ELF_sym[header.elf_class], stream, table_offset + (n * entsize),
|
|
105
|
+
elf_class: header.elf_class, endian: header.class.self_endian),
|
|
106
|
+
stream, symstr: symstr_reader, machine: @machine, version: -> { version_at(n) }
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Where this table starts in the file, asked of the header once rather
|
|
111
|
+
# than once for every symbol read.
|
|
112
|
+
# @return [Integer] The file offset.
|
|
113
|
+
def table_offset
|
|
114
|
+
@table_offset ||= header.sh_offset.to_i
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# How many bytes this table spaces its entries by.
|
|
118
|
+
# @return [Integer] The number.
|
|
119
|
+
def entsize
|
|
120
|
+
@entsize ||= header.sh_entsize.to_i
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# What reads the section these symbols are named in, kept so that
|
|
124
|
+
# reading the table does not make one for every symbol.
|
|
125
|
+
# @return [Method] The method.
|
|
126
|
+
def symstr_reader
|
|
127
|
+
@symstr_reader ||= method(:symstr)
|
|
106
128
|
end
|
|
107
129
|
|
|
108
130
|
# The version the +n+-th symbol binds to.
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'elftools/constants'
|
|
4
|
+
require 'elftools/structs'
|
|
4
5
|
require 'elftools/util'
|
|
5
6
|
|
|
6
7
|
module ELFTools
|
|
7
8
|
module Sections
|
|
8
9
|
# Class of symbol.
|
|
9
10
|
class Symbol
|
|
10
|
-
attr_reader :header # @return [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] Section header.
|
|
11
11
|
attr_reader :stream # @return [#pos=, #read] Streaming object.
|
|
12
12
|
|
|
13
13
|
# Instantiate a {ELFTools::Sections::Symbol} object.
|
|
14
|
-
# @param [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] header
|
|
15
|
-
# The symbol header.
|
|
14
|
+
# @param [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym, ELFTools::Structs::Fields] header
|
|
15
|
+
# The symbol header, or what the file records in one. {ELFTools::Structs::Fields}
|
|
16
|
+
# answers what the symbol records until something asks for {#header}
|
|
17
|
+
# itself, which builds the structure then.
|
|
16
18
|
# @param [#pos=, #read] stream The streaming object.
|
|
17
19
|
# @param [ELFTools::Sections::StrTabSection, Proc] symstr
|
|
18
20
|
# The symbol string section.
|
|
@@ -24,17 +26,27 @@ module ELFTools
|
|
|
24
26
|
# Call this to get the version this symbol binds to, which only the
|
|
25
27
|
# symbols a file is loaded by have.
|
|
26
28
|
def initialize(header, stream, symstr: nil, machine: nil, version: nil)
|
|
27
|
-
@
|
|
29
|
+
@fields = header.is_a?(Structs::Fields) ? header : Structs::Fields.of(header)
|
|
28
30
|
@stream = stream
|
|
29
31
|
@symstr = symstr
|
|
30
32
|
@machine = machine
|
|
31
33
|
@version = version
|
|
32
34
|
end
|
|
33
35
|
|
|
36
|
+
# The structure the file records this symbol in.
|
|
37
|
+
#
|
|
38
|
+
# One is built here for a symbol read without it, which is what assigning
|
|
39
|
+
# to a field of a symbol needs and what {ELFTools::ELFFile#patches}
|
|
40
|
+
# reports the changes of.
|
|
41
|
+
# @return [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] The structure.
|
|
42
|
+
def header
|
|
43
|
+
@fields.struct
|
|
44
|
+
end
|
|
45
|
+
|
|
34
46
|
# Return the symbol name.
|
|
35
47
|
# @return [String] The name.
|
|
36
48
|
def name
|
|
37
|
-
@name ||= @symstr.call.name_at(
|
|
49
|
+
@name ||= @symstr.call.name_at(@fields[:st_name])
|
|
38
50
|
end
|
|
39
51
|
|
|
40
52
|
# The version this symbol binds to.
|
|
@@ -70,7 +82,7 @@ module ELFTools
|
|
|
70
82
|
# elf.section_by_name('.symtab').symbol_by_name('main').value
|
|
71
83
|
# #=> 4196061 # 0x4006dd
|
|
72
84
|
def value
|
|
73
|
-
|
|
85
|
+
@fields[:st_value]
|
|
74
86
|
end
|
|
75
87
|
|
|
76
88
|
# How many bytes what this symbol names takes.
|
|
@@ -79,7 +91,7 @@ module ELFTools
|
|
|
79
91
|
# elf.section_by_name('.symtab').symbol_by_name('main').size
|
|
80
92
|
# #=> 142
|
|
81
93
|
def size
|
|
82
|
-
|
|
94
|
+
@fields[:st_size]
|
|
83
95
|
end
|
|
84
96
|
|
|
85
97
|
# What kind of entity this symbol refers to.
|
|
@@ -90,7 +102,7 @@ module ELFTools
|
|
|
90
102
|
# symbol.type == ELFTools::Constants::STT_FUNC
|
|
91
103
|
# #=> true
|
|
92
104
|
def type
|
|
93
|
-
|
|
105
|
+
@fields[:st_info] & 0xf
|
|
94
106
|
end
|
|
95
107
|
|
|
96
108
|
# Sets what kind of entity this symbol refers to.
|
|
@@ -122,7 +134,7 @@ module ELFTools
|
|
|
122
134
|
# symbol.bind == ELFTools::Constants::STB_GLOBAL
|
|
123
135
|
# #=> true
|
|
124
136
|
def bind
|
|
125
|
-
|
|
137
|
+
@fields[:st_info] >> 4
|
|
126
138
|
end
|
|
127
139
|
|
|
128
140
|
# Sets how this symbol is linked against others with the same name.
|
|
@@ -152,7 +164,7 @@ module ELFTools
|
|
|
152
164
|
# symbol.visibility == ELFTools::Constants::STV_HIDDEN
|
|
153
165
|
# #=> true
|
|
154
166
|
def visibility
|
|
155
|
-
|
|
167
|
+
@fields[:st_other] & 0x3
|
|
156
168
|
end
|
|
157
169
|
|
|
158
170
|
# Sets how this symbol is accessed once it becomes part of an executable
|
|
@@ -195,7 +207,7 @@ module ELFTools
|
|
|
195
207
|
# symbol.section_index == ELFTools::Constants::SHN_UNDEF
|
|
196
208
|
# #=> true # the symbol is undefined and to be resolved at runtime
|
|
197
209
|
def section_index
|
|
198
|
-
|
|
210
|
+
@fields[:st_shndx]
|
|
199
211
|
end
|
|
200
212
|
end
|
|
201
213
|
end
|
data/lib/elftools/structs.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require 'bindata'
|
|
4
4
|
|
|
5
|
+
require 'elftools/exceptions'
|
|
6
|
+
|
|
5
7
|
module ELFTools
|
|
6
8
|
# Define ELF related structures in this module.
|
|
7
9
|
#
|
|
@@ -15,6 +17,23 @@ module ELFTools
|
|
|
15
17
|
{ selection: :elf_class, choices: { 32 => :"#{t}32", 64 => :"#{t}64" }, copy_on_change: true }
|
|
16
18
|
end
|
|
17
19
|
|
|
20
|
+
# How an integer of each width is packed, for +String#unpack+, whether it
|
|
21
|
+
# records a sign or not.
|
|
22
|
+
UNPACK_TEMPLATES = {
|
|
23
|
+
little: {
|
|
24
|
+
false => { 1 => 'C', 2 => 'v', 4 => 'V', 8 => 'Q<' },
|
|
25
|
+
true => { 1 => 'c', 2 => 's<', 4 => 'l<', 8 => 'q<' }
|
|
26
|
+
},
|
|
27
|
+
big: {
|
|
28
|
+
false => { 1 => 'C', 2 => 'n', 4 => 'N', 8 => 'Q>' },
|
|
29
|
+
true => { 1 => 'c', 2 => 's>', 4 => 'l>', 8 => 'q>' }
|
|
30
|
+
}
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
# Bytes of set bits to read a prototype from, longer than any structure
|
|
34
|
+
# here, so that reading one never runs short of them.
|
|
35
|
+
SET_BITS = ("\xff" * 256).b.freeze
|
|
36
|
+
|
|
18
37
|
attr_accessor :elf_class # @return [Integer] 32 or 64.
|
|
19
38
|
attr_accessor :offset # @return [Integer] The file offset of this header.
|
|
20
39
|
|
|
@@ -103,9 +122,49 @@ module ELFTools
|
|
|
103
122
|
end
|
|
104
123
|
|
|
105
124
|
# Gets the endianness of current class.
|
|
125
|
+
#
|
|
126
|
+
# A class is of one endianness for as long as it exists, and asking
|
|
127
|
+
# bindata what it is named costs more than remembering the answer.
|
|
106
128
|
# @return [:little, :big] The endianness.
|
|
107
129
|
def self_endian
|
|
108
|
-
bindata_name[-2..] == 'be' ? :big : :little
|
|
130
|
+
@self_endian ||= bindata_name[-2..] == 'be' ? :big : :little
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# What the fields of a structure record, read straight from its bytes.
|
|
134
|
+
#
|
|
135
|
+
# Reading a table of structures costs a structure for every entry of
|
|
136
|
+
# it otherwise, which is most of what reading the table costs. Nothing
|
|
137
|
+
# is remembered of the bytes, so a caller that means to assign to a
|
|
138
|
+
# field wants a structure instead.
|
|
139
|
+
# @param [String] bytes The bytes a structure is recorded in.
|
|
140
|
+
# @param [Integer] elf_class 32 or 64, which decides how wide the fields recording an address are.
|
|
141
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
142
|
+
# @return [Hash{Symbol => Integer}] Each field, and what it records.
|
|
143
|
+
# @raise [ELFTools::ELFError] If this is not a structure of integers.
|
|
144
|
+
# @example
|
|
145
|
+
# ELF64_sym.unpack_fields(bytes, elf_class: 64, endian: :little)
|
|
146
|
+
# #=> { st_name: 1, st_info: 18, st_other: 0, st_shndx: 15, st_value: 4198864, st_size: 101 }
|
|
147
|
+
def unpack_fields(bytes, elf_class:, endian:)
|
|
148
|
+
values = bytes.unpack(unpack_template(elf_class, endian))
|
|
149
|
+
fields = {}
|
|
150
|
+
# Paired by hand rather than zipped, which would make an array for
|
|
151
|
+
# every field of every structure read.
|
|
152
|
+
field_names(elf_class, endian).each_with_index { |name, i| fields[name] = values[i] }
|
|
153
|
+
fields
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# How many bytes a structure of this kind takes.
|
|
157
|
+
# @param [Integer] elf_class 32 or 64, which decides how wide the fields recording an address are.
|
|
158
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
159
|
+
# @return [Integer] The number.
|
|
160
|
+
# @example
|
|
161
|
+
# ELF64_sym.num_bytes(elf_class: 64, endian: :little)
|
|
162
|
+
# #=> 24
|
|
163
|
+
def num_bytes(elf_class:, endian:)
|
|
164
|
+
@num_bytes ||= {}
|
|
165
|
+
# Nested rather than keyed by the pair, which would make an array of
|
|
166
|
+
# it for every structure read.
|
|
167
|
+
(@num_bytes[elf_class] ||= {})[endian] ||= prototype(elf_class, endian).num_bytes
|
|
109
168
|
end
|
|
110
169
|
|
|
111
170
|
# Packs an integer to string.
|
|
@@ -128,6 +187,210 @@ module ELFTools
|
|
|
128
187
|
out = out.pack('C*')
|
|
129
188
|
self_endian == :little ? out : out.reverse
|
|
130
189
|
end
|
|
190
|
+
|
|
191
|
+
private
|
|
192
|
+
|
|
193
|
+
# A structure of this kind with every bit of it set, which the layout is
|
|
194
|
+
# read off: how wide each field is, and whether it records a sign.
|
|
195
|
+
#
|
|
196
|
+
# Every structure of a kind and a class is laid out alike, so one is
|
|
197
|
+
# built for the kind rather than for each question asked about it.
|
|
198
|
+
# @param [Integer] elf_class 32 or 64, which decides how wide the fields recording an address are.
|
|
199
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
200
|
+
# @return [ELFTools::Structs::ELFStruct] The structure.
|
|
201
|
+
def prototype(elf_class, endian)
|
|
202
|
+
@prototypes ||= {}
|
|
203
|
+
(@prototypes[elf_class] ||= {})[endian] ||= begin
|
|
204
|
+
struct = new(endian: endian)
|
|
205
|
+
struct.elf_class = elf_class
|
|
206
|
+
struct.read(SET_BITS)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# What the fields of this structure are named, in the order they are
|
|
211
|
+
# recorded.
|
|
212
|
+
# @param [Integer] elf_class 32 or 64, which decides how wide the fields recording an address are.
|
|
213
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
214
|
+
# @return [Array<Symbol>] The names.
|
|
215
|
+
def field_names(elf_class, endian)
|
|
216
|
+
@field_names ||= {}
|
|
217
|
+
(@field_names[elf_class] ||= {})[endian] ||= prototype(elf_class, endian).field_names
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# How the fields of this structure are packed, as a template for
|
|
221
|
+
# +String#unpack+.
|
|
222
|
+
# @param [Integer] elf_class 32 or 64, which decides how wide the fields recording an address are.
|
|
223
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
224
|
+
# @return [String] The template.
|
|
225
|
+
def unpack_template(elf_class, endian)
|
|
226
|
+
@unpack_templates ||= {}
|
|
227
|
+
(@unpack_templates[elf_class] ||= {})[endian] ||=
|
|
228
|
+
prototype(elf_class, endian).each_pair.map { |_, field| field_template(field, endian) }.join
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# How one field is packed, which is its width and whether what it read
|
|
232
|
+
# from a field of set bits came back negative.
|
|
233
|
+
# @param [BinData::Base] field The field, read from bits that are all set.
|
|
234
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
235
|
+
# @return [String] The template.
|
|
236
|
+
# @raise [ELFTools::ELFError] If the field is not an integer of 1, 2, 4, or 8 bytes.
|
|
237
|
+
def field_template(field, endian)
|
|
238
|
+
width = field.num_bytes
|
|
239
|
+
template = UNPACK_TEMPLATES.fetch(endian)
|
|
240
|
+
raise ELFError, format('%s is not a structure of integers', name) unless template[false].key?(width)
|
|
241
|
+
|
|
242
|
+
template.fetch(field.to_i.negative?)[width]
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# What a structure the file records at an offset holds, read without
|
|
248
|
+
# building the structure.
|
|
249
|
+
#
|
|
250
|
+
# Reading a table of structures costs a structure for every entry of it
|
|
251
|
+
# otherwise, and setting up the fields of one is most of what it costs.
|
|
252
|
+
# {#struct} builds one for whatever wants the structure itself, which
|
|
253
|
+
# is what assigning to a field takes, and what is assigned is answered
|
|
254
|
+
# with from then on.
|
|
255
|
+
class Fields
|
|
256
|
+
attr_reader :elf_class # @return [Integer] 32 or 64.
|
|
257
|
+
attr_reader :endian # @return [:little, :big] The endianness the file records it in.
|
|
258
|
+
|
|
259
|
+
# What a structure already built holds, for a caller that has one.
|
|
260
|
+
# @param [ELFTools::Structs::ELFStruct] struct The structure.
|
|
261
|
+
# @return [ELFTools::Structs::Fields] The fields.
|
|
262
|
+
def self.of(struct)
|
|
263
|
+
allocate.tap { |fields| fields.send(:built_from, struct) }
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Fields nothing in the file records, for what a file states some other
|
|
267
|
+
# way than by recording a structure of it.
|
|
268
|
+
#
|
|
269
|
+
# There is nothing to read and nothing to patch, so a structure is only
|
|
270
|
+
# built if something asks for one, and is built from these.
|
|
271
|
+
# @param [Class] klass The structure class.
|
|
272
|
+
# @param [Hash{Symbol => Integer}] fields What each field is to record.
|
|
273
|
+
# @param [Integer] elf_class 32 or 64.
|
|
274
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
275
|
+
# @param [Integer] offset Where in the file this came from.
|
|
276
|
+
# @return [ELFTools::Structs::Fields] The fields.
|
|
277
|
+
# @example
|
|
278
|
+
# Fields.from(ELF_Rel, { r_offset: 0x1000 }, elf_class: 64, endian: :little, offset: 0x40)
|
|
279
|
+
def self.from(klass, fields, elf_class:, endian:, offset:)
|
|
280
|
+
allocate.tap { |made| made.send(:made_of, klass, fields, elf_class, endian, offset) }
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# @param [Class] klass The structure class.
|
|
284
|
+
# @param [#pos=, #read] stream The streaming object.
|
|
285
|
+
# @param [Integer] offset The file offset the structure is recorded at.
|
|
286
|
+
# @param [Integer] elf_class 32 or 64.
|
|
287
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
288
|
+
# @raise [EOFError] If the file does not reach that far.
|
|
289
|
+
def initialize(klass, stream, offset, elf_class:, endian:)
|
|
290
|
+
@klass = klass
|
|
291
|
+
@stream = stream
|
|
292
|
+
@offset = offset
|
|
293
|
+
@elf_class = elf_class
|
|
294
|
+
@endian = endian
|
|
295
|
+
@fields = unpack
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# What one field of the structure records.
|
|
299
|
+
#
|
|
300
|
+
# The structure answers once there is one, so that a field assigned to
|
|
301
|
+
# reads back as it was assigned.
|
|
302
|
+
# @param [Symbol] name The name of the field.
|
|
303
|
+
# @return [Integer] The value.
|
|
304
|
+
# @example
|
|
305
|
+
# fields[:st_value]
|
|
306
|
+
# #=> 4198864
|
|
307
|
+
def [](name)
|
|
308
|
+
return @struct[name].to_i if @struct
|
|
309
|
+
|
|
310
|
+
@fields[name]
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# Assigns to one field.
|
|
314
|
+
#
|
|
315
|
+
# The structure takes the assignment wherever the file records one, so
|
|
316
|
+
# that {ELFTools::ELFFile#patches} reports it. Fields the file records
|
|
317
|
+
# no structure of take it themselves, there being nothing to patch.
|
|
318
|
+
# @param [Symbol] name The name of the field.
|
|
319
|
+
# @param [Integer] value What it is to record.
|
|
320
|
+
# @return [void]
|
|
321
|
+
def []=(name, value)
|
|
322
|
+
if @struct.nil? && @stream.nil?
|
|
323
|
+
@fields[name] = value
|
|
324
|
+
else
|
|
325
|
+
struct[name] = value
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# The structure itself, read from where the file records it, or built
|
|
330
|
+
# from these fields where it records none.
|
|
331
|
+
#
|
|
332
|
+
# The same one however often it is asked for, so that assigning to a
|
|
333
|
+
# field of it is not forgotten.
|
|
334
|
+
# @return [ELFTools::Structs::ELFStruct] The structure.
|
|
335
|
+
def struct
|
|
336
|
+
@struct ||= @stream.nil? ? build : read
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
private
|
|
340
|
+
|
|
341
|
+
# Takes a structure that is already built as what these fields hold.
|
|
342
|
+
# @param [ELFTools::Structs::ELFStruct] struct The structure.
|
|
343
|
+
# @return [void]
|
|
344
|
+
def built_from(struct)
|
|
345
|
+
@struct = struct
|
|
346
|
+
@elf_class = struct.elf_class
|
|
347
|
+
@endian = struct.class.self_endian
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Takes fields nothing in the file records a structure of.
|
|
351
|
+
# @return [void]
|
|
352
|
+
def made_of(klass, fields, elf_class, endian, offset)
|
|
353
|
+
@klass = klass
|
|
354
|
+
@fields = fields
|
|
355
|
+
@elf_class = elf_class
|
|
356
|
+
@endian = endian
|
|
357
|
+
@offset = offset
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# The structure the file records, read from where it records it.
|
|
361
|
+
# @return [ELFTools::Structs::ELFStruct] The structure.
|
|
362
|
+
def read
|
|
363
|
+
struct = new_struct
|
|
364
|
+
@stream.pos = @offset
|
|
365
|
+
struct.read(@stream)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# A structure holding what these fields hold, for fields the file
|
|
369
|
+
# records no structure of.
|
|
370
|
+
# @return [ELFTools::Structs::ELFStruct] The structure.
|
|
371
|
+
def build
|
|
372
|
+
new_struct.tap { |struct| @fields.each { |name, value| struct[name] = value } }
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# A structure of this kind, of the class and the order of the file, and
|
|
376
|
+
# of where in it this came from.
|
|
377
|
+
# @return [ELFTools::Structs::ELFStruct] The structure.
|
|
378
|
+
def new_struct
|
|
379
|
+
struct = @klass.new(endian: @endian, offset: @offset)
|
|
380
|
+
struct.elf_class = @elf_class
|
|
381
|
+
struct
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# What the fields record, read from the bytes recording them.
|
|
385
|
+
# @return [Hash{Symbol => Integer}] Each field, and what it records.
|
|
386
|
+
# @raise [EOFError] If the file does not reach that far, as reading the structure itself does.
|
|
387
|
+
def unpack
|
|
388
|
+
num_bytes = @klass.num_bytes(elf_class: @elf_class, endian: @endian)
|
|
389
|
+
@stream.pos = @offset
|
|
390
|
+
bytes = @stream.read(num_bytes)
|
|
391
|
+
raise EOFError, 'End of file reached' if bytes.nil? || bytes.bytesize < num_bytes
|
|
392
|
+
|
|
393
|
+
@klass.unpack_fields(bytes, elf_class: @elf_class, endian: @endian)
|
|
131
394
|
end
|
|
132
395
|
end
|
|
133
396
|
|
data/lib/elftools/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: elftools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- david942j
|
|
@@ -259,7 +259,7 @@ licenses:
|
|
|
259
259
|
- MIT
|
|
260
260
|
metadata:
|
|
261
261
|
rubygems_mfa_required: 'true'
|
|
262
|
-
changelog_uri: https://github.com/david942j/rbelftools/blob/v2.
|
|
262
|
+
changelog_uri: https://github.com/david942j/rbelftools/blob/v2.2.0/CHANGELOG.md
|
|
263
263
|
rdoc_options: []
|
|
264
264
|
require_paths:
|
|
265
265
|
- lib
|