elftools 2.0.0 → 2.1.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 +44 -0
- data/lib/elftools/dynamic/hash_table.rb +16 -0
- data/lib/elftools/dynamic/symbols.rb +45 -17
- data/lib/elftools/dynamic/versions.rb +87 -0
- data/lib/elftools/dynamic.rb +21 -4
- data/lib/elftools/elf_file.rb +1 -0
- data/lib/elftools/relative_relocations.rb +88 -0
- data/lib/elftools/relocation.rb +50 -0
- data/lib/elftools/sections/relative_relocation_section.rb +46 -0
- data/lib/elftools/sections/section.rb +31 -0
- data/lib/elftools/sections/sections.rb +8 -0
- data/lib/elftools/sections/sym_tab_section.rb +39 -7
- data/lib/elftools/sections/symbol.rb +91 -1
- 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 +109 -16
- 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
|
@@ -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
|
|
@@ -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,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'elftools/sections/section'
|
|
4
4
|
require 'elftools/sections/symbol'
|
|
5
|
+
require 'elftools/version_tables'
|
|
5
6
|
|
|
6
7
|
module ELFTools
|
|
7
8
|
module Sections
|
|
@@ -16,14 +17,15 @@ module ELFTools
|
|
|
16
17
|
# See {Section#initialize} for more information.
|
|
17
18
|
# @param [#pos=, #read] stream
|
|
18
19
|
# See {Section#initialize} for more information.
|
|
19
|
-
# @param [Proc]
|
|
20
|
-
# The method for fetching
|
|
21
|
-
# This lambda should be
|
|
20
|
+
# @param [Proc] sections
|
|
21
|
+
# The method for fetching the sections, which is where the names and
|
|
22
|
+
# the versions of these symbols are recorded. This lambda should be
|
|
23
|
+
# {ELFTools::ELFFile#sections}.
|
|
22
24
|
# @param [Integer] machine
|
|
23
25
|
# The machine of the ELF file, which decides what the fields of a
|
|
24
26
|
# symbol mean. This should be +e_machine+ of the ELF header.
|
|
25
|
-
def initialize(header, stream,
|
|
26
|
-
@
|
|
27
|
+
def initialize(header, stream, sections: nil, machine: nil, **_kwargs)
|
|
28
|
+
@sections = sections
|
|
27
29
|
@machine = machine
|
|
28
30
|
# For faster #symbol_by_name
|
|
29
31
|
super
|
|
@@ -91,7 +93,7 @@ module ELFTools
|
|
|
91
93
|
# Lazy loaded.
|
|
92
94
|
# @return [ELFTools::Sections::StrTabSection] The string table section.
|
|
93
95
|
def symstr
|
|
94
|
-
@symstr ||= @
|
|
96
|
+
@symstr ||= @sections.call[header.sh_link]
|
|
95
97
|
end
|
|
96
98
|
|
|
97
99
|
private
|
|
@@ -100,7 +102,37 @@ module ELFTools
|
|
|
100
102
|
stream.pos = header.sh_offset + n * header.sh_entsize
|
|
101
103
|
sym = Structs::ELF_sym[header.elf_class].new(endian: header.class.self_endian, offset: stream.pos)
|
|
102
104
|
sym.read(stream)
|
|
103
|
-
Symbol.new(sym, stream, symstr: method(:symstr), machine: @machine)
|
|
105
|
+
Symbol.new(sym, stream, symstr: method(:symstr), machine: @machine, version: -> { version_at(n) })
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# The version the +n+-th symbol binds to.
|
|
109
|
+
# @return [ELFTools::VersionTables::Version, nil]
|
|
110
|
+
# The version, +nil+ unless this is the table a file is loaded by and
|
|
111
|
+
# the file records versions at all.
|
|
112
|
+
def version_at(n)
|
|
113
|
+
return if versions.nil?
|
|
114
|
+
|
|
115
|
+
VersionTables.version(versions.version_at(n), versions_by_index)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# The section recording which version each of these symbols binds to,
|
|
119
|
+
# which is the one naming this table.
|
|
120
|
+
# @return [ELFTools::Sections::VersionSection, nil] The section.
|
|
121
|
+
def versions
|
|
122
|
+
return @versions if defined?(@versions)
|
|
123
|
+
|
|
124
|
+
@versions = @sections&.call&.find do |sec|
|
|
125
|
+
sec.is_a?(VersionSection) && @sections.call[sec.header.sh_link].equal?(self)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# The name each index the symbols record names.
|
|
130
|
+
# @return [Hash{Integer => String}] The names.
|
|
131
|
+
def versions_by_index
|
|
132
|
+
@versions_by_index ||= VersionTables.names(
|
|
133
|
+
@sections.call.grep(VersionNeedSection).flat_map(&:requirements),
|
|
134
|
+
@sections.call.grep(VersionDefinitionSection).flat_map(&:definitions)
|
|
135
|
+
)
|
|
104
136
|
end
|
|
105
137
|
end
|
|
106
138
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'elftools/constants'
|
|
4
|
+
require 'elftools/util'
|
|
4
5
|
|
|
5
6
|
module ELFTools
|
|
6
7
|
module Sections
|
|
@@ -19,11 +20,15 @@ module ELFTools
|
|
|
19
20
|
# access {Symbol#name}.
|
|
20
21
|
# @param [Integer] machine
|
|
21
22
|
# The machine of the ELF file, which a name of a value depends on.
|
|
22
|
-
|
|
23
|
+
# @param [Proc] version
|
|
24
|
+
# Call this to get the version this symbol binds to, which only the
|
|
25
|
+
# symbols a file is loaded by have.
|
|
26
|
+
def initialize(header, stream, symstr: nil, machine: nil, version: nil)
|
|
23
27
|
@header = header
|
|
24
28
|
@stream = stream
|
|
25
29
|
@symstr = symstr
|
|
26
30
|
@machine = machine
|
|
31
|
+
@version = version
|
|
27
32
|
end
|
|
28
33
|
|
|
29
34
|
# Return the symbol name.
|
|
@@ -32,6 +37,51 @@ module ELFTools
|
|
|
32
37
|
@name ||= @symstr.call.name_at(header.st_name)
|
|
33
38
|
end
|
|
34
39
|
|
|
40
|
+
# The version this symbol binds to.
|
|
41
|
+
#
|
|
42
|
+
# Only the symbols a file is loaded by have one, and only where the file
|
|
43
|
+
# records the versions at all. {#name} is left as the file records it,
|
|
44
|
+
# without the version appended.
|
|
45
|
+
# @return [String, nil] The name of the version.
|
|
46
|
+
# @example
|
|
47
|
+
# elf.dynamic.symbol_by_name('printf').version
|
|
48
|
+
# #=> 'GLIBC_2.2.5'
|
|
49
|
+
def version
|
|
50
|
+
binding_version&.name
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Whether {#version} is one the symbol asks for by name rather than the
|
|
54
|
+
# default one of its name.
|
|
55
|
+
# @return [Boolean] The answer.
|
|
56
|
+
def version_hidden?
|
|
57
|
+
binding_version&.hidden? || false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# What this symbol is worth, which for most of them is the address of
|
|
61
|
+
# what they name.
|
|
62
|
+
#
|
|
63
|
+
# A symbol of a file that is not loaded anywhere records an offset into
|
|
64
|
+
# the section holding it instead, and one the linker is still to place,
|
|
65
|
+
# which {ELFTools::Constants::SHN_COMMON} marks, records the alignment it
|
|
66
|
+
# needs. The ABI leaves the field to the kind of symbol for that reason,
|
|
67
|
+
# and this answers with what is recorded either way.
|
|
68
|
+
# @return [Integer] The value.
|
|
69
|
+
# @example
|
|
70
|
+
# elf.section_by_name('.symtab').symbol_by_name('main').value
|
|
71
|
+
# #=> 4196061 # 0x4006dd
|
|
72
|
+
def value
|
|
73
|
+
header.st_value.to_i
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# How many bytes what this symbol names takes.
|
|
77
|
+
# @return [Integer] The number, zero where the file records none.
|
|
78
|
+
# @example
|
|
79
|
+
# elf.section_by_name('.symtab').symbol_by_name('main').size
|
|
80
|
+
# #=> 142
|
|
81
|
+
def size
|
|
82
|
+
header.st_size.to_i
|
|
83
|
+
end
|
|
84
|
+
|
|
35
85
|
# What kind of entity this symbol refers to.
|
|
36
86
|
#
|
|
37
87
|
# The available types are listed in {ELFTools::Constants::STT}.
|
|
@@ -43,6 +93,15 @@ module ELFTools
|
|
|
43
93
|
header.st_info & 0xf
|
|
44
94
|
end
|
|
45
95
|
|
|
96
|
+
# Sets what kind of entity this symbol refers to.
|
|
97
|
+
# @param [Integer] type The type.
|
|
98
|
+
# @raise [ArgumentError] If the four bits recording it cannot hold it.
|
|
99
|
+
# @example
|
|
100
|
+
# symbol.type = ELFTools::Constants::STT_FUNC
|
|
101
|
+
def type=(type)
|
|
102
|
+
header.st_info = (bind << 4) | Util.fits!(type, 4, 'Symbol type')
|
|
103
|
+
end
|
|
104
|
+
|
|
46
105
|
# The name of {#type}.
|
|
47
106
|
#
|
|
48
107
|
# A machine names types of its own, so the name is only known when the
|
|
@@ -66,6 +125,15 @@ module ELFTools
|
|
|
66
125
|
header.st_info >> 4
|
|
67
126
|
end
|
|
68
127
|
|
|
128
|
+
# Sets how this symbol is linked against others with the same name.
|
|
129
|
+
# @param [Integer] bind The binding.
|
|
130
|
+
# @raise [ArgumentError] If the four bits recording it cannot hold it.
|
|
131
|
+
# @example
|
|
132
|
+
# symbol.bind = ELFTools::Constants::STB_WEAK
|
|
133
|
+
def bind=(bind)
|
|
134
|
+
header.st_info = (Util.fits!(bind, 4, 'Symbol binding') << 4) | type
|
|
135
|
+
end
|
|
136
|
+
|
|
69
137
|
# The name of {#bind}.
|
|
70
138
|
# @return [String] The name.
|
|
71
139
|
# @example
|
|
@@ -87,6 +155,19 @@ module ELFTools
|
|
|
87
155
|
header.st_other & 0x3
|
|
88
156
|
end
|
|
89
157
|
|
|
158
|
+
# Sets how this symbol is accessed once it becomes part of an executable
|
|
159
|
+
# or shared object.
|
|
160
|
+
#
|
|
161
|
+
# The rest of +st_other+ is left alone, which some machines record their
|
|
162
|
+
# own thing in.
|
|
163
|
+
# @param [Integer] visibility The visibility.
|
|
164
|
+
# @raise [ArgumentError] If the two bits recording it cannot hold it.
|
|
165
|
+
# @example
|
|
166
|
+
# symbol.visibility = ELFTools::Constants::STV_HIDDEN
|
|
167
|
+
def visibility=(visibility)
|
|
168
|
+
header.st_other = (header.st_other.to_i & 0xfc) | Util.fits!(visibility, 2, 'Symbol visibility')
|
|
169
|
+
end
|
|
170
|
+
|
|
90
171
|
# The name of {#visibility}.
|
|
91
172
|
# @return [String] The name.
|
|
92
173
|
# @example
|
|
@@ -96,6 +177,15 @@ module ELFTools
|
|
|
96
177
|
Constants::STV.mapping(@machine, visibility)
|
|
97
178
|
end
|
|
98
179
|
|
|
180
|
+
# The version this symbol binds to, whatever is asked of it.
|
|
181
|
+
# @return [ELFTools::Dynamic::Versions::Version, nil] The version.
|
|
182
|
+
def binding_version
|
|
183
|
+
return @binding_version if defined?(@binding_version)
|
|
184
|
+
|
|
185
|
+
@binding_version = @version&.call
|
|
186
|
+
end
|
|
187
|
+
private :binding_version
|
|
188
|
+
|
|
99
189
|
# The index of the section this symbol is defined in.
|
|
100
190
|
#
|
|
101
191
|
# Values in {ELFTools::Constants::SHN} have special meanings instead of
|
|
@@ -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
|
data/lib/elftools/structs.rb
CHANGED
|
@@ -18,15 +18,73 @@ module ELFTools
|
|
|
18
18
|
attr_accessor :elf_class # @return [Integer] 32 or 64.
|
|
19
19
|
attr_accessor :offset # @return [Integer] The file offset of this header.
|
|
20
20
|
|
|
21
|
-
#
|
|
22
|
-
#
|
|
21
|
+
# Reads the structure, remembering the bytes it was read from.
|
|
22
|
+
#
|
|
23
|
+
# They are taken back off the stream. A stream that cannot be seeked is
|
|
24
|
+
# serialized instead.
|
|
25
|
+
# @param [#pos=, #read] io The streaming object.
|
|
26
|
+
# @return [ELFTools::Structs::ELFStruct] Itself.
|
|
27
|
+
def read(io)
|
|
28
|
+
start = io.pos if io.respond_to?(:pos)
|
|
29
|
+
super.tap { @source = start.nil? ? to_binary_s : bytes_read(io, start) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Which bytes of this structure have been changed since it was read.
|
|
33
|
+
#
|
|
34
|
+
# Every field answers alike, however deeply it is nested, because what is
|
|
35
|
+
# compared is the bytes the structure occupies rather than the
|
|
36
|
+
# assignments that were made to it. A field assigned the value it
|
|
37
|
+
# already held leaves nothing behind.
|
|
38
|
+
# @return [Hash{Integer => String}]
|
|
39
|
+
# Where each run of changed bytes starts, as an offset into the
|
|
40
|
+
# structure, and the bytes it is to be replaced with.
|
|
41
|
+
# @example
|
|
42
|
+
# header.e_ident.ei_abiversion = 41
|
|
43
|
+
# header.patches
|
|
44
|
+
# #=> { 8 => "\x29" }
|
|
23
45
|
def patches
|
|
24
|
-
|
|
46
|
+
return {} if @source.nil?
|
|
47
|
+
|
|
48
|
+
changed_runs(@source, to_binary_s)
|
|
25
49
|
end
|
|
26
50
|
|
|
27
51
|
# BinData hash(Snapshot) that behaves like HashWithIndifferentAccess
|
|
28
52
|
alias to_h snapshot
|
|
29
53
|
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# The bytes a read has just taken from a stream, leaving it where the
|
|
57
|
+
# read left it.
|
|
58
|
+
# @param [#pos=, #read] io The streaming object.
|
|
59
|
+
# @param [Integer] start Where the read began.
|
|
60
|
+
# @return [String] The bytes.
|
|
61
|
+
def bytes_read(io, start)
|
|
62
|
+
here = io.pos
|
|
63
|
+
io.pos = start
|
|
64
|
+
io.read(here - start).tap { io.pos = here }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Where two strings of bytes differ, as the runs of bytes that differ.
|
|
68
|
+
#
|
|
69
|
+
# Only as far as +before+ reaches, so that a patch never covers more of
|
|
70
|
+
# the file than the structure it came from.
|
|
71
|
+
# @param [String] before The bytes as they were.
|
|
72
|
+
# @param [String] after The bytes as they are.
|
|
73
|
+
# @return [Hash{Integer => String}] Where each run starts, and its bytes.
|
|
74
|
+
def changed_runs(before, after)
|
|
75
|
+
runs = {}
|
|
76
|
+
start = nil
|
|
77
|
+
(0..before.bytesize).each do |i|
|
|
78
|
+
if i < before.bytesize && before.getbyte(i) != after.getbyte(i)
|
|
79
|
+
start ||= i
|
|
80
|
+
elsif start
|
|
81
|
+
runs[start] = after.byteslice(start, i - start)
|
|
82
|
+
start = nil
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
runs
|
|
86
|
+
end
|
|
87
|
+
|
|
30
88
|
class << self
|
|
31
89
|
# Hooks the constructor.
|
|
32
90
|
#
|
|
@@ -41,19 +99,7 @@ module ELFTools
|
|
|
41
99
|
def new(*args)
|
|
42
100
|
kwargs = args.last.is_a?(Hash) ? args.last : {}
|
|
43
101
|
offset = kwargs.delete(:offset)
|
|
44
|
-
super.tap
|
|
45
|
-
obj.offset = offset
|
|
46
|
-
obj.field_names.each do |f|
|
|
47
|
-
m = :"#{f}="
|
|
48
|
-
old_method = obj.singleton_method(m)
|
|
49
|
-
obj.singleton_class.send(:undef_method, m)
|
|
50
|
-
obj.define_singleton_method(m) do |val|
|
|
51
|
-
org = obj.send(f)
|
|
52
|
-
obj.patches[org.abs_offset] = ELFStruct.pack(val, org.num_bytes)
|
|
53
|
-
old_method.call(val)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
102
|
+
super.tap { |obj| obj.offset = offset }
|
|
57
103
|
end
|
|
58
104
|
|
|
59
105
|
# Gets the endianness of current class.
|
|
@@ -63,6 +109,10 @@ module ELFTools
|
|
|
63
109
|
end
|
|
64
110
|
|
|
65
111
|
# Packs an integer to string.
|
|
112
|
+
#
|
|
113
|
+
# @deprecated
|
|
114
|
+
# Nothing here packs a patch by hand anymore, see {ELFStruct#patches}.
|
|
115
|
+
# This is kept for anyone who called it and goes in the next major.
|
|
66
116
|
# @param [Integer] val
|
|
67
117
|
# @param [Integer] bytes
|
|
68
118
|
# @return [String]
|
|
@@ -200,6 +250,49 @@ module ELFTools
|
|
|
200
250
|
uint32 :shift2 # The second shift the bloom filter is built with
|
|
201
251
|
end
|
|
202
252
|
|
|
253
|
+
# An entry of the table of versions a file needs of another, which the
|
|
254
|
+
# +vn_next+ of the one before it points at.
|
|
255
|
+
class ELF_Verneed < ELFStruct
|
|
256
|
+
endian :big_and_little
|
|
257
|
+
uint16 :vn_version # Revision of this structure
|
|
258
|
+
uint16 :vn_cnt # How many versions of the file are needed
|
|
259
|
+
uint32 :vn_file # Name of the file, as an offset into the string table
|
|
260
|
+
uint32 :vn_aux # Where the versions start, as an offset from here
|
|
261
|
+
uint32 :vn_next # Where the next entry is, as an offset from here
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# A version an {ELF_Verneed} needs, which the +vna_next+ of the one before
|
|
265
|
+
# it points at.
|
|
266
|
+
class ELF_Vernaux < ELFStruct
|
|
267
|
+
endian :big_and_little
|
|
268
|
+
uint32 :vna_hash # Hash of the name
|
|
269
|
+
uint16 :vna_flags # Flags
|
|
270
|
+
uint16 :vna_other # The index the symbols name this version with
|
|
271
|
+
uint32 :vna_name # The name, as an offset into the string table
|
|
272
|
+
uint32 :vna_next # Where the next version is, as an offset from here
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# An entry of the table of versions a file defines, which the +vd_next+ of
|
|
276
|
+
# the one before it points at.
|
|
277
|
+
class ELF_Verdef < ELFStruct
|
|
278
|
+
endian :big_and_little
|
|
279
|
+
uint16 :vd_version # Revision of this structure
|
|
280
|
+
uint16 :vd_flags # Flags
|
|
281
|
+
uint16 :vd_ndx # The index the symbols name this version with
|
|
282
|
+
uint16 :vd_cnt # How many names follow, the version and its ancestors
|
|
283
|
+
uint32 :vd_hash # Hash of the name
|
|
284
|
+
uint32 :vd_aux # Where the names start, as an offset from here
|
|
285
|
+
uint32 :vd_next # Where the next entry is, as an offset from here
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# A name an {ELF_Verdef} records, its own or an ancestor's, which the
|
|
289
|
+
# +vda_next+ of the one before it points at.
|
|
290
|
+
class ELF_Verdaux < ELFStruct
|
|
291
|
+
endian :big_and_little
|
|
292
|
+
uint32 :vda_name # The name, as an offset into the string table
|
|
293
|
+
uint32 :vda_next # Where the next name is, as an offset from here
|
|
294
|
+
end
|
|
295
|
+
|
|
203
296
|
# Note header.
|
|
204
297
|
class ELF_Nhdr < ELFStruct
|
|
205
298
|
endian :big_and_little
|