elftools 2.0.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/README.md +82 -0
- data/lib/elftools/constants.rb +53 -0
- data/lib/elftools/dynamic/hash_table.rb +16 -0
- data/lib/elftools/dynamic/symbols.rb +54 -19
- data/lib/elftools/dynamic/tag.rb +28 -6
- data/lib/elftools/dynamic/versions.rb +87 -0
- data/lib/elftools/dynamic.rb +33 -35
- data/lib/elftools/elf_file.rb +61 -11
- data/lib/elftools/relative_relocations.rb +87 -0
- data/lib/elftools/relocation.rb +72 -9
- data/lib/elftools/sections/relative_relocation_section.rb +46 -0
- data/lib/elftools/sections/relocation_section.rb +16 -5
- data/lib/elftools/sections/section.rb +31 -0
- data/lib/elftools/sections/sections.rb +8 -0
- data/lib/elftools/sections/sym_tab_section.rb +64 -10
- data/lib/elftools/sections/symbol.rb +112 -10
- data/lib/elftools/sections/version_definition_section.rb +40 -0
- data/lib/elftools/sections/version_need_section.rb +40 -0
- data/lib/elftools/sections/version_section.rb +44 -0
- data/lib/elftools/structs.rb +373 -17
- data/lib/elftools/util.rb +68 -11
- data/lib/elftools/version.rb +1 -1
- data/lib/elftools/version_tables.rb +155 -0
- metadata +9 -2
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)
|
|
@@ -413,6 +462,7 @@ module ELFTools
|
|
|
413
462
|
offset_from_vma: method(:offset_from_vma),
|
|
414
463
|
section_name_table: method(:section_name_table),
|
|
415
464
|
section_at: method(:section_at),
|
|
465
|
+
sections: method(:sections),
|
|
416
466
|
machine: header.e_machine.to_i)
|
|
417
467
|
end
|
|
418
468
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'elftools/constants'
|
|
4
|
+
require 'elftools/relocation'
|
|
5
|
+
require 'elftools/structs'
|
|
6
|
+
|
|
7
|
+
module ELFTools
|
|
8
|
+
# The relocations a file packs into a bitmap instead of recording one by one.
|
|
9
|
+
#
|
|
10
|
+
# Almost every relocation of a file that is loaded anywhere only adds the
|
|
11
|
+
# load bias to a word, which takes an entry recording an address, a type that
|
|
12
|
+
# is the same every time, and an addend that repeats what the word already
|
|
13
|
+
# holds. A file may pack them instead, as a run of addresses in ascending
|
|
14
|
+
# order, and spend a bit rather than an entry on each.
|
|
15
|
+
#
|
|
16
|
+
# An even entry is an address, and relocates the word there. An odd entry is
|
|
17
|
+
# a bitmap of the words following the last address, a set bit relocating one
|
|
18
|
+
# of them. Nothing records a type, because every relocation here is the one
|
|
19
|
+
# {ELFTools::Constants::R.relative} names.
|
|
20
|
+
class RelativeRelocations
|
|
21
|
+
# Instantiate a {ELFTools::RelativeRelocations} object.
|
|
22
|
+
# @param [#pos=, #read] stream Streaming object.
|
|
23
|
+
# @param [Range<Integer>] bytes The file offsets the table occupies.
|
|
24
|
+
# @param [Integer] elf_class 32 or 64, the width of an entry.
|
|
25
|
+
# @param [Symbol] endian +:little+ or +:big+.
|
|
26
|
+
# @param [Integer] machine
|
|
27
|
+
# The machine of the file, which decides what these relocations are of.
|
|
28
|
+
def initialize(stream, bytes, elf_class:, endian:, machine:)
|
|
29
|
+
@stream = stream
|
|
30
|
+
@bytes = bytes
|
|
31
|
+
@elf_class = elf_class
|
|
32
|
+
@endian = endian
|
|
33
|
+
@machine = machine
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# The relocations the table packs.
|
|
37
|
+
# @return [Array<ELFTools::Relocation>]
|
|
38
|
+
# The relocations, in the ascending order the table records them.
|
|
39
|
+
def to_a
|
|
40
|
+
type = Constants::R.relative(@machine)
|
|
41
|
+
addresses.map do |address, from|
|
|
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)
|
|
45
|
+
# Through the relocation, so that the type is laid out in +r_info+ the
|
|
46
|
+
# way the machine lays it out.
|
|
47
|
+
relocation.type = type if type
|
|
48
|
+
relocation
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# How many bytes an entry takes, which is the width of an address.
|
|
55
|
+
# @return [Integer] The number.
|
|
56
|
+
def width
|
|
57
|
+
@elf_class / 8
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Every address the table relocates, and the entry it was read from.
|
|
61
|
+
# @return [Array<Array(Integer, Integer)>] The addresses.
|
|
62
|
+
def addresses
|
|
63
|
+
found = []
|
|
64
|
+
# Where a bitmap counts from, which an address moves to just past itself.
|
|
65
|
+
here = 0
|
|
66
|
+
entries.each do |entry, from|
|
|
67
|
+
if entry.even?
|
|
68
|
+
found << [entry, from]
|
|
69
|
+
here = entry + width
|
|
70
|
+
else
|
|
71
|
+
# The lowest bit says the entry is a bitmap rather than an address.
|
|
72
|
+
(1...(width * 8)).each { |bit| found << [here + ((bit - 1) * width), from] if entry[bit] == 1 }
|
|
73
|
+
here += ((width * 8) - 1) * width
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
found
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# What the table records, and where each was read from.
|
|
80
|
+
# @return [Array<Array(Integer, Integer)>] The entries.
|
|
81
|
+
def entries
|
|
82
|
+
@stream.pos = @bytes.begin
|
|
83
|
+
format = "#{width == 8 ? 'Q' : 'L'}#{@endian == :big ? '>' : '<'}"
|
|
84
|
+
@stream.read(@bytes.size).to_s.unpack("#{format}*").each_with_index.map { |e, i| [e, @bytes.begin + (i * width)] }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
data/lib/elftools/relocation.rb
CHANGED
|
@@ -1,28 +1,41 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'elftools/constants'
|
|
4
|
+
require 'elftools/structs'
|
|
5
|
+
require 'elftools/util'
|
|
4
6
|
|
|
5
7
|
module ELFTools
|
|
6
8
|
# A relocation entry.
|
|
7
9
|
#
|
|
8
10
|
# Can be either a REL or RELA relocation.
|
|
9
11
|
class Relocation
|
|
10
|
-
attr_reader :header # @return [ELFTools::Structs::ELF_Rel, ELFTools::Structs::ELF_Rela] Rel(a) header.
|
|
11
12
|
attr_reader :stream # @return [#pos=, #read] Streaming object.
|
|
12
13
|
|
|
13
14
|
# Instantiate a {Relocation} object.
|
|
14
|
-
# @param [ELFTools::Structs::ELF_Rel, ELFTools::Structs::ELF_Rela] header
|
|
15
|
-
# 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.
|
|
16
19
|
# @param [#pos=, #read] stream The streaming object.
|
|
17
20
|
# @param [Integer] machine
|
|
18
21
|
# The machine of the ELF file, which decides what {#type} means and how
|
|
19
22
|
# {#header} records it.
|
|
20
23
|
def initialize(header, stream, machine: nil)
|
|
21
|
-
@
|
|
24
|
+
@fields = header.is_a?(Structs::Fields) ? header : Structs::Fields.of(header)
|
|
22
25
|
@stream = stream
|
|
23
26
|
@machine = machine
|
|
24
27
|
end
|
|
25
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
|
+
|
|
26
39
|
# Which symbol this relocation is against, as an index into the symbol
|
|
27
40
|
# table.
|
|
28
41
|
# @return [Integer] The symbol index.
|
|
@@ -37,6 +50,24 @@ module ELFTools
|
|
|
37
50
|
sym_and_type.last
|
|
38
51
|
end
|
|
39
52
|
|
|
53
|
+
# Sets which symbol this relocation is against.
|
|
54
|
+
# @param [Integer] index The symbol index.
|
|
55
|
+
# @raise [ArgumentError] If the bits recording it cannot hold it.
|
|
56
|
+
# @example
|
|
57
|
+
# relocation.symbol_index = 3
|
|
58
|
+
def symbol_index=(index)
|
|
59
|
+
@fields[:r_info] = info_of(Util.fits!(index, index_bits, 'Symbol index'), type)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Sets what this relocation does.
|
|
63
|
+
# @param [Integer] type The relocation type.
|
|
64
|
+
# @raise [ArgumentError] If the bits recording it cannot hold it.
|
|
65
|
+
# @example
|
|
66
|
+
# relocation.type = ELFTools::Constants::R::X86_64::R_X86_64_JUMP_SLOT
|
|
67
|
+
def type=(type)
|
|
68
|
+
@fields[:r_info] = info_of(symbol_index, Util.fits!(type, type_bits, 'Relocation type'))
|
|
69
|
+
end
|
|
70
|
+
|
|
40
71
|
# The name of {#type}.
|
|
41
72
|
#
|
|
42
73
|
# Every architecture numbers relocation types on its own, so the name is
|
|
@@ -51,6 +82,37 @@ module ELFTools
|
|
|
51
82
|
|
|
52
83
|
private
|
|
53
84
|
|
|
85
|
+
# The +r_info+ recording a symbol index and a type, laid out the way this
|
|
86
|
+
# file lays it out. The inverse of {#sym_and_type}.
|
|
87
|
+
# @return [Integer] The +r_info+.
|
|
88
|
+
def info_of(index, type)
|
|
89
|
+
return mips64_info_of(index, type) if mips64?
|
|
90
|
+
|
|
91
|
+
(index << mask_bit) | type
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# The +r_info+ as the 64-bit MIPS ABI records it, leaving the bytes the
|
|
95
|
+
# ABI keeps for itself as they were.
|
|
96
|
+
# @return [Integer] The +r_info+.
|
|
97
|
+
def mips64_info_of(index, type)
|
|
98
|
+
info = @fields[:r_info]
|
|
99
|
+
return (index << 32) | (info & 0xffff_ff00) | type if @fields.endian == :big
|
|
100
|
+
|
|
101
|
+
(info & 0x00ff_ffff_0000_0000) | (type << 56) | index
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# How many bits record a symbol index.
|
|
105
|
+
# @return [Integer] The number.
|
|
106
|
+
def index_bits
|
|
107
|
+
mips64? ? 32 : (@fields.elf_class - mask_bit)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# How many bits record a relocation type.
|
|
111
|
+
# @return [Integer] The number.
|
|
112
|
+
def type_bits
|
|
113
|
+
mips64? ? 8 : mask_bit
|
|
114
|
+
end
|
|
115
|
+
|
|
54
116
|
# What +r_info+ records, i.e. a symbol index and a relocation type. Most
|
|
55
117
|
# machines split the field in half between the two, one lays it out its
|
|
56
118
|
# own way.
|
|
@@ -58,14 +120,15 @@ module ELFTools
|
|
|
58
120
|
def sym_and_type
|
|
59
121
|
return mips64_sym_and_type if mips64?
|
|
60
122
|
|
|
61
|
-
|
|
123
|
+
info = @fields[:r_info]
|
|
124
|
+
[info >> mask_bit, info & ((1 << mask_bit) - 1)]
|
|
62
125
|
end
|
|
63
126
|
|
|
64
127
|
# Whether the file records relocations the way the 64-bit MIPS ABI does,
|
|
65
128
|
# which is the one layout that departs from halving +r_info+.
|
|
66
129
|
# @return [Boolean] The answer.
|
|
67
130
|
def mips64?
|
|
68
|
-
@machine == Constants::EM_MIPS &&
|
|
131
|
+
@machine == Constants::EM_MIPS && @fields.elf_class == 64
|
|
69
132
|
end
|
|
70
133
|
|
|
71
134
|
# Reads +r_info+ as the 64-bit MIPS ABI records it, i.e. a symbol index of
|
|
@@ -81,14 +144,14 @@ module ELFTools
|
|
|
81
144
|
# 0x0718050000000008 #=> [8, 7]
|
|
82
145
|
# @return [Array(Integer, Integer)] The symbol index and the type.
|
|
83
146
|
def mips64_sym_and_type
|
|
84
|
-
info =
|
|
85
|
-
return [info >> 32, info & 0xff] if
|
|
147
|
+
info = @fields[:r_info]
|
|
148
|
+
return [info >> 32, info & 0xff] if @fields.endian == :big
|
|
86
149
|
|
|
87
150
|
[info & 0xffff_ffff, info >> 56]
|
|
88
151
|
end
|
|
89
152
|
|
|
90
153
|
def mask_bit
|
|
91
|
-
|
|
154
|
+
@fields.elf_class == 32 ? 8 : 32
|
|
92
155
|
end
|
|
93
156
|
end
|
|
94
157
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'elftools/relative_relocations'
|
|
4
|
+
require 'elftools/sections/section'
|
|
5
|
+
|
|
6
|
+
module ELFTools
|
|
7
|
+
module Sections
|
|
8
|
+
# Class of the section packing the relocations that only add the load bias.
|
|
9
|
+
#
|
|
10
|
+
# This section is usually named .relr.dyn, and records the very relocations
|
|
11
|
+
# the +DT_RELR+ tag points at.
|
|
12
|
+
class RelativeRelocationSection < Section
|
|
13
|
+
# Instantiate a {RelativeRelocationSection} object.
|
|
14
|
+
# @param [ELFTools::Structs::ELF_Shdr] header
|
|
15
|
+
# See {Section#initialize} for more information.
|
|
16
|
+
# @param [#pos=, #read] stream
|
|
17
|
+
# See {Section#initialize} for more information.
|
|
18
|
+
# @param [Integer] machine
|
|
19
|
+
# The machine of the ELF file, which decides what these relocations
|
|
20
|
+
# are of. This should be +e_machine+ of the ELF header.
|
|
21
|
+
def initialize(header, stream, machine: nil, **_kwargs)
|
|
22
|
+
@machine = machine
|
|
23
|
+
super
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The relocations the section packs.
|
|
27
|
+
# @return [Array<ELFTools::Relocation>]
|
|
28
|
+
# The relocations, in the ascending order the section records them.
|
|
29
|
+
# @example
|
|
30
|
+
# section.relocations.map(&:type_name).uniq
|
|
31
|
+
# #=> ['R_X86_64_RELATIVE']
|
|
32
|
+
def relocations
|
|
33
|
+
@relocations ||= RelativeRelocations.new(
|
|
34
|
+
stream, header.sh_offset.to_i...(header.sh_offset.to_i + header.sh_size.to_i),
|
|
35
|
+
elf_class: header.elf_class, endian: header.class.self_endian, machine: @machine
|
|
36
|
+
).to_a
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# How many relocations the section packs.
|
|
40
|
+
# @return [Integer] The number.
|
|
41
|
+
def num_relocations
|
|
42
|
+
relocations.size
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
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
|
|
@@ -46,6 +46,37 @@ module ELFTools
|
|
|
46
46
|
stream.read(header.sh_size)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
# Is this section written to while the file runs?
|
|
50
|
+
# @return [Boolean] True or false.
|
|
51
|
+
# @example
|
|
52
|
+
# elf.section_by_name('.data').writable?
|
|
53
|
+
# #=> true
|
|
54
|
+
def writable?
|
|
55
|
+
header.sh_flags.allbits?(Constants::SHF_WRITE)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Is this section executed?
|
|
59
|
+
# @return [Boolean] True or false.
|
|
60
|
+
# @example
|
|
61
|
+
# elf.section_by_name('.text').executable?
|
|
62
|
+
# #=> true
|
|
63
|
+
def executable?
|
|
64
|
+
header.sh_flags.allbits?(Constants::SHF_EXECINSTR)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Does this section take memory while the file runs?
|
|
68
|
+
#
|
|
69
|
+
# The ones that do are what a file is loaded by, the rest being what is
|
|
70
|
+
# recorded about it, its symbol names and its debugging information
|
|
71
|
+
# among them.
|
|
72
|
+
# @return [Boolean] True or false.
|
|
73
|
+
# @example
|
|
74
|
+
# elf.section_by_name('.symtab').allocated?
|
|
75
|
+
# #=> false
|
|
76
|
+
def allocated?
|
|
77
|
+
header.sh_flags.allbits?(Constants::SHF_ALLOC)
|
|
78
|
+
end
|
|
79
|
+
|
|
49
80
|
# Is this a null section?
|
|
50
81
|
# @return [Boolean] No it's not.
|
|
51
82
|
def null?
|
|
@@ -7,9 +7,13 @@ require 'elftools/sections/section'
|
|
|
7
7
|
require 'elftools/sections/dynamic_section'
|
|
8
8
|
require 'elftools/sections/note_section'
|
|
9
9
|
require 'elftools/sections/null_section'
|
|
10
|
+
require 'elftools/sections/relative_relocation_section'
|
|
10
11
|
require 'elftools/sections/relocation_section'
|
|
11
12
|
require 'elftools/sections/str_tab_section'
|
|
12
13
|
require 'elftools/sections/sym_tab_section'
|
|
14
|
+
require 'elftools/sections/version_definition_section'
|
|
15
|
+
require 'elftools/sections/version_need_section'
|
|
16
|
+
require 'elftools/sections/version_section'
|
|
13
17
|
|
|
14
18
|
module ELFTools
|
|
15
19
|
# Defines different types of sections in this module.
|
|
@@ -27,6 +31,10 @@ module ELFTools
|
|
|
27
31
|
when Constants::SHT_NULL then NullSection
|
|
28
32
|
when Constants::SHT_NOTE then NoteSection
|
|
29
33
|
when Constants::SHT_RELA, Constants::SHT_REL then RelocationSection
|
|
34
|
+
when Constants::SHT_RELR then RelativeRelocationSection
|
|
35
|
+
when Constants::SHT_GNU_versym then VersionSection
|
|
36
|
+
when Constants::SHT_GNU_verneed then VersionNeedSection
|
|
37
|
+
when Constants::SHT_GNU_verdef then VersionDefinitionSection
|
|
30
38
|
when Constants::SHT_STRTAB then StrTabSection
|
|
31
39
|
when Constants::SHT_SYMTAB, Constants::SHT_DYNSYM then SymTabSection
|
|
32
40
|
else Section
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require 'elftools/sections/section'
|
|
4
4
|
require 'elftools/sections/symbol'
|
|
5
|
+
require 'elftools/structs'
|
|
6
|
+
require 'elftools/version_tables'
|
|
5
7
|
|
|
6
8
|
module ELFTools
|
|
7
9
|
module Sections
|
|
@@ -16,14 +18,15 @@ module ELFTools
|
|
|
16
18
|
# See {Section#initialize} for more information.
|
|
17
19
|
# @param [#pos=, #read] stream
|
|
18
20
|
# See {Section#initialize} for more information.
|
|
19
|
-
# @param [Proc]
|
|
20
|
-
# The method for fetching
|
|
21
|
-
# This lambda should be
|
|
21
|
+
# @param [Proc] sections
|
|
22
|
+
# The method for fetching the sections, which is where the names and
|
|
23
|
+
# the versions of these symbols are recorded. This lambda should be
|
|
24
|
+
# {ELFTools::ELFFile#sections}.
|
|
22
25
|
# @param [Integer] machine
|
|
23
26
|
# The machine of the ELF file, which decides what the fields of a
|
|
24
27
|
# symbol mean. This should be +e_machine+ of the ELF header.
|
|
25
|
-
def initialize(header, stream,
|
|
26
|
-
@
|
|
28
|
+
def initialize(header, stream, sections: nil, machine: nil, **_kwargs)
|
|
29
|
+
@sections = sections
|
|
27
30
|
@machine = machine
|
|
28
31
|
# For faster #symbol_by_name
|
|
29
32
|
super
|
|
@@ -91,16 +94,67 @@ module ELFTools
|
|
|
91
94
|
# Lazy loaded.
|
|
92
95
|
# @return [ELFTools::Sections::StrTabSection] The string table section.
|
|
93
96
|
def symstr
|
|
94
|
-
@symstr ||= @
|
|
97
|
+
@symstr ||= @sections.call[header.sh_link]
|
|
95
98
|
end
|
|
96
99
|
|
|
97
100
|
private
|
|
98
101
|
|
|
99
102
|
def create_symbol(n)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# The version the +n+-th symbol binds to.
|
|
131
|
+
# @return [ELFTools::VersionTables::Version, nil]
|
|
132
|
+
# The version, +nil+ unless this is the table a file is loaded by and
|
|
133
|
+
# the file records versions at all.
|
|
134
|
+
def version_at(n)
|
|
135
|
+
return if versions.nil?
|
|
136
|
+
|
|
137
|
+
VersionTables.version(versions.version_at(n), versions_by_index)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# The section recording which version each of these symbols binds to,
|
|
141
|
+
# which is the one naming this table.
|
|
142
|
+
# @return [ELFTools::Sections::VersionSection, nil] The section.
|
|
143
|
+
def versions
|
|
144
|
+
return @versions if defined?(@versions)
|
|
145
|
+
|
|
146
|
+
@versions = @sections&.call&.find do |sec|
|
|
147
|
+
sec.is_a?(VersionSection) && @sections.call[sec.header.sh_link].equal?(self)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# The name each index the symbols record names.
|
|
152
|
+
# @return [Hash{Integer => String}] The names.
|
|
153
|
+
def versions_by_index
|
|
154
|
+
@versions_by_index ||= VersionTables.names(
|
|
155
|
+
@sections.call.grep(VersionNeedSection).flat_map(&:requirements),
|
|
156
|
+
@sections.call.grep(VersionDefinitionSection).flat_map(&:definitions)
|
|
157
|
+
)
|
|
104
158
|
end
|
|
105
159
|
end
|
|
106
160
|
end
|