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
|
@@ -1,17 +1,20 @@
|
|
|
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
|
module Sections
|
|
7
9
|
# Class of symbol.
|
|
8
10
|
class Symbol
|
|
9
|
-
attr_reader :header # @return [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] Section header.
|
|
10
11
|
attr_reader :stream # @return [#pos=, #read] Streaming object.
|
|
11
12
|
|
|
12
13
|
# Instantiate a {ELFTools::Sections::Symbol} object.
|
|
13
|
-
# @param [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] header
|
|
14
|
-
# 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.
|
|
15
18
|
# @param [#pos=, #read] stream The streaming object.
|
|
16
19
|
# @param [ELFTools::Sections::StrTabSection, Proc] symstr
|
|
17
20
|
# The symbol string section.
|
|
@@ -19,17 +22,76 @@ module ELFTools
|
|
|
19
22
|
# access {Symbol#name}.
|
|
20
23
|
# @param [Integer] machine
|
|
21
24
|
# The machine of the ELF file, which a name of a value depends on.
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
# @param [Proc] version
|
|
26
|
+
# Call this to get the version this symbol binds to, which only the
|
|
27
|
+
# symbols a file is loaded by have.
|
|
28
|
+
def initialize(header, stream, symstr: nil, machine: nil, version: nil)
|
|
29
|
+
@fields = header.is_a?(Structs::Fields) ? header : Structs::Fields.of(header)
|
|
24
30
|
@stream = stream
|
|
25
31
|
@symstr = symstr
|
|
26
32
|
@machine = machine
|
|
33
|
+
@version = version
|
|
34
|
+
end
|
|
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
|
|
27
44
|
end
|
|
28
45
|
|
|
29
46
|
# Return the symbol name.
|
|
30
47
|
# @return [String] The name.
|
|
31
48
|
def name
|
|
32
|
-
@name ||= @symstr.call.name_at(
|
|
49
|
+
@name ||= @symstr.call.name_at(@fields[:st_name])
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# The version this symbol binds to.
|
|
53
|
+
#
|
|
54
|
+
# Only the symbols a file is loaded by have one, and only where the file
|
|
55
|
+
# records the versions at all. {#name} is left as the file records it,
|
|
56
|
+
# without the version appended.
|
|
57
|
+
# @return [String, nil] The name of the version.
|
|
58
|
+
# @example
|
|
59
|
+
# elf.dynamic.symbol_by_name('printf').version
|
|
60
|
+
# #=> 'GLIBC_2.2.5'
|
|
61
|
+
def version
|
|
62
|
+
binding_version&.name
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Whether {#version} is one the symbol asks for by name rather than the
|
|
66
|
+
# default one of its name.
|
|
67
|
+
# @return [Boolean] The answer.
|
|
68
|
+
def version_hidden?
|
|
69
|
+
binding_version&.hidden? || false
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# What this symbol is worth, which for most of them is the address of
|
|
73
|
+
# what they name.
|
|
74
|
+
#
|
|
75
|
+
# A symbol of a file that is not loaded anywhere records an offset into
|
|
76
|
+
# the section holding it instead, and one the linker is still to place,
|
|
77
|
+
# which {ELFTools::Constants::SHN_COMMON} marks, records the alignment it
|
|
78
|
+
# needs. The ABI leaves the field to the kind of symbol for that reason,
|
|
79
|
+
# and this answers with what is recorded either way.
|
|
80
|
+
# @return [Integer] The value.
|
|
81
|
+
# @example
|
|
82
|
+
# elf.section_by_name('.symtab').symbol_by_name('main').value
|
|
83
|
+
# #=> 4196061 # 0x4006dd
|
|
84
|
+
def value
|
|
85
|
+
@fields[:st_value]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# How many bytes what this symbol names takes.
|
|
89
|
+
# @return [Integer] The number, zero where the file records none.
|
|
90
|
+
# @example
|
|
91
|
+
# elf.section_by_name('.symtab').symbol_by_name('main').size
|
|
92
|
+
# #=> 142
|
|
93
|
+
def size
|
|
94
|
+
@fields[:st_size]
|
|
33
95
|
end
|
|
34
96
|
|
|
35
97
|
# What kind of entity this symbol refers to.
|
|
@@ -40,7 +102,16 @@ module ELFTools
|
|
|
40
102
|
# symbol.type == ELFTools::Constants::STT_FUNC
|
|
41
103
|
# #=> true
|
|
42
104
|
def type
|
|
43
|
-
|
|
105
|
+
@fields[:st_info] & 0xf
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Sets what kind of entity this symbol refers to.
|
|
109
|
+
# @param [Integer] type The type.
|
|
110
|
+
# @raise [ArgumentError] If the four bits recording it cannot hold it.
|
|
111
|
+
# @example
|
|
112
|
+
# symbol.type = ELFTools::Constants::STT_FUNC
|
|
113
|
+
def type=(type)
|
|
114
|
+
header.st_info = (bind << 4) | Util.fits!(type, 4, 'Symbol type')
|
|
44
115
|
end
|
|
45
116
|
|
|
46
117
|
# The name of {#type}.
|
|
@@ -63,7 +134,16 @@ module ELFTools
|
|
|
63
134
|
# symbol.bind == ELFTools::Constants::STB_GLOBAL
|
|
64
135
|
# #=> true
|
|
65
136
|
def bind
|
|
66
|
-
|
|
137
|
+
@fields[:st_info] >> 4
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Sets how this symbol is linked against others with the same name.
|
|
141
|
+
# @param [Integer] bind The binding.
|
|
142
|
+
# @raise [ArgumentError] If the four bits recording it cannot hold it.
|
|
143
|
+
# @example
|
|
144
|
+
# symbol.bind = ELFTools::Constants::STB_WEAK
|
|
145
|
+
def bind=(bind)
|
|
146
|
+
header.st_info = (Util.fits!(bind, 4, 'Symbol binding') << 4) | type
|
|
67
147
|
end
|
|
68
148
|
|
|
69
149
|
# The name of {#bind}.
|
|
@@ -84,7 +164,20 @@ module ELFTools
|
|
|
84
164
|
# symbol.visibility == ELFTools::Constants::STV_HIDDEN
|
|
85
165
|
# #=> true
|
|
86
166
|
def visibility
|
|
87
|
-
|
|
167
|
+
@fields[:st_other] & 0x3
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Sets how this symbol is accessed once it becomes part of an executable
|
|
171
|
+
# or shared object.
|
|
172
|
+
#
|
|
173
|
+
# The rest of +st_other+ is left alone, which some machines record their
|
|
174
|
+
# own thing in.
|
|
175
|
+
# @param [Integer] visibility The visibility.
|
|
176
|
+
# @raise [ArgumentError] If the two bits recording it cannot hold it.
|
|
177
|
+
# @example
|
|
178
|
+
# symbol.visibility = ELFTools::Constants::STV_HIDDEN
|
|
179
|
+
def visibility=(visibility)
|
|
180
|
+
header.st_other = (header.st_other.to_i & 0xfc) | Util.fits!(visibility, 2, 'Symbol visibility')
|
|
88
181
|
end
|
|
89
182
|
|
|
90
183
|
# The name of {#visibility}.
|
|
@@ -96,6 +189,15 @@ module ELFTools
|
|
|
96
189
|
Constants::STV.mapping(@machine, visibility)
|
|
97
190
|
end
|
|
98
191
|
|
|
192
|
+
# The version this symbol binds to, whatever is asked of it.
|
|
193
|
+
# @return [ELFTools::Dynamic::Versions::Version, nil] The version.
|
|
194
|
+
def binding_version
|
|
195
|
+
return @binding_version if defined?(@binding_version)
|
|
196
|
+
|
|
197
|
+
@binding_version = @version&.call
|
|
198
|
+
end
|
|
199
|
+
private :binding_version
|
|
200
|
+
|
|
99
201
|
# The index of the section this symbol is defined in.
|
|
100
202
|
#
|
|
101
203
|
# Values in {ELFTools::Constants::SHN} have special meanings instead of
|
|
@@ -105,7 +207,7 @@ module ELFTools
|
|
|
105
207
|
# symbol.section_index == ELFTools::Constants::SHN_UNDEF
|
|
106
208
|
# #=> true # the symbol is undefined and to be resolved at runtime
|
|
107
209
|
def section_index
|
|
108
|
-
|
|
210
|
+
@fields[:st_shndx]
|
|
109
211
|
end
|
|
110
212
|
end
|
|
111
213
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'elftools/sections/section'
|
|
4
|
+
require 'elftools/version_tables'
|
|
5
|
+
|
|
6
|
+
module ELFTools
|
|
7
|
+
module Sections
|
|
8
|
+
# Class of the section recording the versions a file defines for what it
|
|
9
|
+
# exports.
|
|
10
|
+
#
|
|
11
|
+
# This section is usually named .gnu.version_d, and records the very
|
|
12
|
+
# versions the +DT_VERDEF+ tag points at.
|
|
13
|
+
class VersionDefinitionSection < Section
|
|
14
|
+
# Instantiate a {VersionDefinitionSection} object.
|
|
15
|
+
# @param [ELFTools::Structs::ELF_Shdr] header
|
|
16
|
+
# See {Section#initialize} for more information.
|
|
17
|
+
# @param [#pos=, #read] stream
|
|
18
|
+
# See {Section#initialize} for more information.
|
|
19
|
+
# @param [Proc] section_at
|
|
20
|
+
# The method for fetching other sections by index, which is where the
|
|
21
|
+
# names are recorded.
|
|
22
|
+
def initialize(header, stream, section_at: nil, **_kwargs)
|
|
23
|
+
@section_at = section_at
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# The versions this file defines for what it exports.
|
|
28
|
+
# @return [Array<ELFTools::VersionTables::Definition>]
|
|
29
|
+
# The definitions, in the order the section records them.
|
|
30
|
+
# @example
|
|
31
|
+
# section.definitions.map(&:name).first(3)
|
|
32
|
+
# #=> ['libc.so.6', 'GLIBC_2.2.5', 'GLIBC_2.2.6']
|
|
33
|
+
def definitions
|
|
34
|
+
@definitions ||= VersionTables.new(stream, @section_at.call(header.sh_link),
|
|
35
|
+
endian: header.class.self_endian)
|
|
36
|
+
.definitions(header.sh_offset.to_i, header.sh_info.to_i)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'elftools/sections/section'
|
|
4
|
+
require 'elftools/version_tables'
|
|
5
|
+
|
|
6
|
+
module ELFTools
|
|
7
|
+
module Sections
|
|
8
|
+
# Class of the section recording the versions a file needs of the files it
|
|
9
|
+
# is loaded with.
|
|
10
|
+
#
|
|
11
|
+
# This section is usually named .gnu.version_r, and records the very
|
|
12
|
+
# versions the +DT_VERNEED+ tag points at.
|
|
13
|
+
class VersionNeedSection < Section
|
|
14
|
+
# Instantiate a {VersionNeedSection} object.
|
|
15
|
+
# @param [ELFTools::Structs::ELF_Shdr] header
|
|
16
|
+
# See {Section#initialize} for more information.
|
|
17
|
+
# @param [#pos=, #read] stream
|
|
18
|
+
# See {Section#initialize} for more information.
|
|
19
|
+
# @param [Proc] section_at
|
|
20
|
+
# The method for fetching other sections by index, which is where the
|
|
21
|
+
# names are recorded.
|
|
22
|
+
def initialize(header, stream, section_at: nil, **_kwargs)
|
|
23
|
+
@section_at = section_at
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# The versions this file needs of the files it is loaded with.
|
|
28
|
+
# @return [Array<ELFTools::VersionTables::Requirement>]
|
|
29
|
+
# The requirements, in the order the section records them.
|
|
30
|
+
# @example
|
|
31
|
+
# section.requirements.map { |need| [need.file, need.versions.map(&:name)] }
|
|
32
|
+
# #=> [['libc.so.6', ['GLIBC_2.4', 'GLIBC_2.2.5']]]
|
|
33
|
+
def requirements
|
|
34
|
+
@requirements ||= VersionTables.new(stream, @section_at.call(header.sh_link),
|
|
35
|
+
endian: header.class.self_endian)
|
|
36
|
+
.requirements(header.sh_offset.to_i, header.sh_info.to_i)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'elftools/sections/section'
|
|
4
|
+
|
|
5
|
+
module ELFTools
|
|
6
|
+
module Sections
|
|
7
|
+
# Class of the section recording which version each symbol binds to.
|
|
8
|
+
#
|
|
9
|
+
# This section is usually named .gnu.version, and holds an index per symbol
|
|
10
|
+
# of the table its +sh_link+ names, the very indices the +DT_VERSYM+ tag
|
|
11
|
+
# points at.
|
|
12
|
+
class VersionSection < Section
|
|
13
|
+
# How many symbols the section records a version for.
|
|
14
|
+
# @return [Integer] The number.
|
|
15
|
+
def num_versions
|
|
16
|
+
header.sh_size.to_i / entry_size
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# What the +n+-th symbol records as its version, which is an index into
|
|
20
|
+
# the versions a file needs or defines, with the highest bit marking a
|
|
21
|
+
# version the symbol asks for by name rather than the default one.
|
|
22
|
+
# @param [Integer] n The symbol index.
|
|
23
|
+
# @return [Integer, nil] The index, +nil+ if the section records none for it.
|
|
24
|
+
# @example
|
|
25
|
+
# section.version_at(1)
|
|
26
|
+
# #=> 2
|
|
27
|
+
def version_at(n)
|
|
28
|
+
return if n.negative? || n >= num_versions
|
|
29
|
+
|
|
30
|
+
stream.pos = header.sh_offset.to_i + (n * entry_size)
|
|
31
|
+
stream.read(entry_size).unpack1(header.class.self_endian == :big ? 'S>' : 'S<')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
# What an entry takes, which the section records and the format fixes at
|
|
37
|
+
# two bytes either way.
|
|
38
|
+
# @return [Integer] The number of bytes.
|
|
39
|
+
def entry_size
|
|
40
|
+
2
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|