elftools 1.3.1 → 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 +193 -7
- data/lib/elftools/constants/machine.rb +248 -0
- data/lib/elftools/constants/machine_names.rb +246 -0
- data/lib/elftools/constants/relocation/aarch64.rb +213 -0
- data/lib/elftools/constants/relocation/alpha.rb +47 -0
- data/lib/elftools/constants/relocation/amdgpu.rb +28 -0
- data/lib/elftools/constants/relocation/arm.rb +150 -0
- data/lib/elftools/constants/relocation/avr.rb +51 -0
- data/lib/elftools/constants/relocation/bfin.rb +72 -0
- data/lib/elftools/constants/relocation/bpf.rb +21 -0
- data/lib/elftools/constants/relocation/cr16.rb +46 -0
- data/lib/elftools/constants/relocation/cris.rb +46 -0
- data/lib/elftools/constants/relocation/crx.rb +35 -0
- data/lib/elftools/constants/relocation/csky.rb +79 -0
- data/lib/elftools/constants/relocation/d10v.rb +23 -0
- data/lib/elftools/constants/relocation/d30v.rb +27 -0
- data/lib/elftools/constants/relocation/dlx.rb +24 -0
- data/lib/elftools/constants/relocation/epiphany.rb +28 -0
- data/lib/elftools/constants/relocation/fr30.rb +27 -0
- data/lib/elftools/constants/relocation/frv.rb +57 -0
- data/lib/elftools/constants/relocation/ft32.rb +27 -0
- data/lib/elftools/constants/relocation/h8.rb +69 -0
- data/lib/elftools/constants/relocation/hppa.rb +135 -0
- data/lib/elftools/constants/relocation/i370.rb +26 -0
- data/lib/elftools/constants/relocation/i386.rb +59 -0
- data/lib/elftools/constants/relocation/i860.rb +48 -0
- data/lib/elftools/constants/relocation/i960.rb +22 -0
- data/lib/elftools/constants/relocation/ia64.rb +117 -0
- data/lib/elftools/constants/relocation/ip2k.rb +29 -0
- data/lib/elftools/constants/relocation/iq2000.rb +27 -0
- data/lib/elftools/constants/relocation/kvx.rb +97 -0
- data/lib/elftools/constants/relocation/lm32.rb +32 -0
- data/lib/elftools/constants/relocation/loongarch.rb +144 -0
- data/lib/elftools/constants/relocation/m32c.rb +27 -0
- data/lib/elftools/constants/relocation/m32r.rb +57 -0
- data/lib/elftools/constants/relocation/m68hc11.rb +35 -0
- data/lib/elftools/constants/relocation/m68k.rb +57 -0
- data/lib/elftools/constants/relocation/mcore.rb +27 -0
- data/lib/elftools/constants/relocation/mep.rb +36 -0
- data/lib/elftools/constants/relocation/metag.rb +63 -0
- data/lib/elftools/constants/relocation/microblaze.rb +48 -0
- data/lib/elftools/constants/relocation/mips.rb +125 -0
- data/lib/elftools/constants/relocation/mmix.rb +51 -0
- data/lib/elftools/constants/relocation/mn10200.rb +22 -0
- data/lib/elftools/constants/relocation/mn10300.rb +49 -0
- data/lib/elftools/constants/relocation/moxie.rb +17 -0
- data/lib/elftools/constants/relocation/msp430.rb +27 -0
- data/lib/elftools/constants/relocation/msp430x.rb +37 -0
- data/lib/elftools/constants/relocation/mt.rb +21 -0
- data/lib/elftools/constants/relocation/nds32.rb +167 -0
- data/lib/elftools/constants/relocation/nfp.rb +41 -0
- data/lib/elftools/constants/relocation/nfp3200.rb +46 -0
- data/lib/elftools/constants/relocation/nios2.rb +74 -0
- data/lib/elftools/constants/relocation/or1k.rb +69 -0
- data/lib/elftools/constants/relocation/pj.rb +24 -0
- data/lib/elftools/constants/relocation/ppc.rb +131 -0
- data/lib/elftools/constants/relocation/ppc64.rb +177 -0
- data/lib/elftools/constants/relocation/pru.rb +31 -0
- data/lib/elftools/constants/relocation/riscv.rb +74 -0
- data/lib/elftools/constants/relocation/rl78.rb +70 -0
- data/lib/elftools/constants/relocation/rx.rb +81 -0
- data/lib/elftools/constants/relocation/s12z.rb +22 -0
- data/lib/elftools/constants/relocation/s390.rb +82 -0
- data/lib/elftools/constants/relocation/score.rb +36 -0
- data/lib/elftools/constants/relocation/sh.rb +124 -0
- data/lib/elftools/constants/relocation/sparc.rb +108 -0
- data/lib/elftools/constants/relocation/spu.rb +32 -0
- data/lib/elftools/constants/relocation/tic6x.rb +48 -0
- data/lib/elftools/constants/relocation/tilegx.rb +134 -0
- data/lib/elftools/constants/relocation/tilepro.rb +105 -0
- data/lib/elftools/constants/relocation/v800.rb +101 -0
- data/lib/elftools/constants/relocation/v850.rb +66 -0
- data/lib/elftools/constants/relocation/vax.rb +29 -0
- data/lib/elftools/constants/relocation/visium.rb +30 -0
- data/lib/elftools/constants/relocation/wasm32.rb +16 -0
- data/lib/elftools/constants/relocation/x86_64.rb +68 -0
- data/lib/elftools/constants/relocation/xgate.rb +34 -0
- data/lib/elftools/constants/relocation/xstormy16.rb +29 -0
- data/lib/elftools/constants/relocation/xtensa.rb +75 -0
- data/lib/elftools/constants/relocation/z80.rb +28 -0
- data/lib/elftools/constants/relocation.rb +181 -0
- data/lib/elftools/constants.rb +168 -231
- data/lib/elftools/dynamic/hash_table.rb +232 -0
- data/lib/elftools/dynamic/string_table.rb +30 -0
- data/lib/elftools/dynamic/symbols.rb +174 -0
- data/lib/elftools/dynamic/tag.rb +66 -0
- data/lib/elftools/dynamic/versions.rb +87 -0
- data/lib/elftools/dynamic.rb +119 -62
- data/lib/elftools/elf_file.rb +74 -22
- data/lib/elftools/lazy_array.rb +42 -4
- data/lib/elftools/note.rb +6 -3
- data/lib/elftools/relative_relocations.rb +88 -0
- data/lib/elftools/relocation.rb +144 -0
- data/lib/elftools/sections/dynamic_section.rb +13 -0
- data/lib/elftools/sections/relative_relocation_section.rb +46 -0
- data/lib/elftools/sections/relocation_section.rb +24 -43
- data/lib/elftools/sections/section.rb +35 -4
- data/lib/elftools/sections/sections.rb +8 -0
- data/lib/elftools/sections/sym_tab_section.rb +47 -35
- data/lib/elftools/sections/symbol.rb +202 -0
- 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/segments/dynamic_segment.rb +1 -0
- data/lib/elftools/segments/load_segment.rb +33 -5
- data/lib/elftools/segments/segment.rb +8 -4
- data/lib/elftools/structs.rb +132 -18
- data/lib/elftools/util.rb +69 -12
- data/lib/elftools/version.rb +1 -1
- data/lib/elftools/version_tables.rb +155 -0
- metadata +108 -11
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'elftools/constants'
|
|
4
|
+
require 'elftools/util'
|
|
5
|
+
|
|
6
|
+
module ELFTools
|
|
7
|
+
module Sections
|
|
8
|
+
# Class of symbol.
|
|
9
|
+
class Symbol
|
|
10
|
+
attr_reader :header # @return [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] Section header.
|
|
11
|
+
attr_reader :stream # @return [#pos=, #read] Streaming object.
|
|
12
|
+
|
|
13
|
+
# Instantiate a {ELFTools::Sections::Symbol} object.
|
|
14
|
+
# @param [ELFTools::Structs::ELF32_sym, ELFTools::Structs::ELF64_sym] header
|
|
15
|
+
# The symbol header.
|
|
16
|
+
# @param [#pos=, #read] stream The streaming object.
|
|
17
|
+
# @param [ELFTools::Sections::StrTabSection, Proc] symstr
|
|
18
|
+
# The symbol string section.
|
|
19
|
+
# If +Proc+ is given, it will be called at the first time
|
|
20
|
+
# access {Symbol#name}.
|
|
21
|
+
# @param [Integer] machine
|
|
22
|
+
# The machine of the ELF file, which a name of a value depends on.
|
|
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)
|
|
27
|
+
@header = header
|
|
28
|
+
@stream = stream
|
|
29
|
+
@symstr = symstr
|
|
30
|
+
@machine = machine
|
|
31
|
+
@version = version
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Return the symbol name.
|
|
35
|
+
# @return [String] The name.
|
|
36
|
+
def name
|
|
37
|
+
@name ||= @symstr.call.name_at(header.st_name)
|
|
38
|
+
end
|
|
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
|
+
|
|
85
|
+
# What kind of entity this symbol refers to.
|
|
86
|
+
#
|
|
87
|
+
# The available types are listed in {ELFTools::Constants::STT}.
|
|
88
|
+
# @return [Integer] The type.
|
|
89
|
+
# @example
|
|
90
|
+
# symbol.type == ELFTools::Constants::STT_FUNC
|
|
91
|
+
# #=> true
|
|
92
|
+
def type
|
|
93
|
+
header.st_info & 0xf
|
|
94
|
+
end
|
|
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
|
+
|
|
105
|
+
# The name of {#type}.
|
|
106
|
+
#
|
|
107
|
+
# A machine names types of its own, so the name is only known when the
|
|
108
|
+
# machine of the file is.
|
|
109
|
+
# @return [String] The name.
|
|
110
|
+
# @example
|
|
111
|
+
# symbol.type_name
|
|
112
|
+
# #=> 'STT_FUNC'
|
|
113
|
+
def type_name
|
|
114
|
+
Constants::STT.mapping(@machine, type)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# How this symbol is linked against others with the same name.
|
|
118
|
+
#
|
|
119
|
+
# The available bindings are listed in {ELFTools::Constants::STB}.
|
|
120
|
+
# @return [Integer] The binding.
|
|
121
|
+
# @example
|
|
122
|
+
# symbol.bind == ELFTools::Constants::STB_GLOBAL
|
|
123
|
+
# #=> true
|
|
124
|
+
def bind
|
|
125
|
+
header.st_info >> 4
|
|
126
|
+
end
|
|
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
|
+
|
|
137
|
+
# The name of {#bind}.
|
|
138
|
+
# @return [String] The name.
|
|
139
|
+
# @example
|
|
140
|
+
# symbol.bind_name
|
|
141
|
+
# #=> 'STB_GLOBAL'
|
|
142
|
+
def bind_name
|
|
143
|
+
Constants::STB.mapping(@machine, bind)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# How this symbol is accessed once it becomes part of an executable or
|
|
147
|
+
# shared object.
|
|
148
|
+
#
|
|
149
|
+
# The available visibilities are listed in {ELFTools::Constants::STV}.
|
|
150
|
+
# @return [Integer] The visibility.
|
|
151
|
+
# @example
|
|
152
|
+
# symbol.visibility == ELFTools::Constants::STV_HIDDEN
|
|
153
|
+
# #=> true
|
|
154
|
+
def visibility
|
|
155
|
+
header.st_other & 0x3
|
|
156
|
+
end
|
|
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
|
+
|
|
171
|
+
# The name of {#visibility}.
|
|
172
|
+
# @return [String] The name.
|
|
173
|
+
# @example
|
|
174
|
+
# symbol.visibility_name
|
|
175
|
+
# #=> 'STV_DEFAULT'
|
|
176
|
+
def visibility_name
|
|
177
|
+
Constants::STV.mapping(@machine, visibility)
|
|
178
|
+
end
|
|
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
|
+
|
|
189
|
+
# The index of the section this symbol is defined in.
|
|
190
|
+
#
|
|
191
|
+
# Values in {ELFTools::Constants::SHN} have special meanings instead of
|
|
192
|
+
# being an index.
|
|
193
|
+
# @return [Integer] The section index.
|
|
194
|
+
# @example
|
|
195
|
+
# symbol.section_index == ELFTools::Constants::SHN_UNDEF
|
|
196
|
+
# #=> true # the symbol is undefined and to be resolved at runtime
|
|
197
|
+
def section_index
|
|
198
|
+
header.st_shndx.to_i
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
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
|
|
@@ -49,6 +49,29 @@ module ELFTools
|
|
|
49
49
|
mem_head + mem_size
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
# Returns the lowest virtual address backed by this segment's file content.
|
|
53
|
+
#
|
|
54
|
+
# A segment is mapped from an aligned boundary, so the addresses preceding
|
|
55
|
+
# {#mem_head} within the same alignment unit are backed by this segment as
|
|
56
|
+
# well. The bound is clamped to the address of the first byte of the file,
|
|
57
|
+
# so that a malformed ELF, whose +p_offset+ and +p_vaddr+ are not congruent
|
|
58
|
+
# modulo +p_align+, cannot be converted into a negative file offset.
|
|
59
|
+
# @return [Integer]
|
|
60
|
+
# The vma.
|
|
61
|
+
def mapped_head
|
|
62
|
+
[header.p_vaddr.to_i & -header.p_align.to_i, mem_head - file_head].max
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns the end virtual address backed by this segment's file content.
|
|
66
|
+
#
|
|
67
|
+
# Addresses in between this and {#mem_tail} occupy memory but have no
|
|
68
|
+
# content in file, therefore cannot be converted into a file offset.
|
|
69
|
+
# @return [Integer]
|
|
70
|
+
# The vma.
|
|
71
|
+
def mapped_tail
|
|
72
|
+
mem_head + size
|
|
73
|
+
end
|
|
74
|
+
|
|
52
75
|
# Query if the given file offset located in this segment.
|
|
53
76
|
# @param [Integer] offset
|
|
54
77
|
# File offset.
|
|
@@ -56,27 +79,32 @@ module ELFTools
|
|
|
56
79
|
# Size.
|
|
57
80
|
# @return [Boolean]
|
|
58
81
|
def offset_in?(offset, size = 0)
|
|
59
|
-
file_head <= offset && offset + size
|
|
82
|
+
file_head <= offset && offset + size <= file_tail
|
|
60
83
|
end
|
|
61
84
|
|
|
62
85
|
# Convert file offset into virtual memory address.
|
|
86
|
+
#
|
|
87
|
+
# The conversion is a shift by a constant, so it holds no matter +p_offset+
|
|
88
|
+
# is aligned with +p_vaddr+ or not.
|
|
63
89
|
# @param [Integer] offset
|
|
64
90
|
# File offset.
|
|
65
91
|
# @return [Integer]
|
|
66
92
|
def offset_to_vma(offset)
|
|
67
|
-
# XXX: What if file_head is not aligned with p_vaddr (which is invalid according to ELF spec)?
|
|
68
93
|
offset - file_head + header.p_vaddr
|
|
69
94
|
end
|
|
70
95
|
|
|
71
|
-
# Query if the given virtual memory address
|
|
96
|
+
# Query if the given virtual memory address is backed by this segment's
|
|
97
|
+
# file content.
|
|
98
|
+
#
|
|
99
|
+
# Only addresses in between {#mapped_head} and {#mapped_tail} can be
|
|
100
|
+
# converted into a file offset, see {#vma_to_offset}.
|
|
72
101
|
# @param [Integer] vma
|
|
73
102
|
# Virtual memory address.
|
|
74
103
|
# @param [Integer] size
|
|
75
104
|
# Size.
|
|
76
105
|
# @return [Boolean]
|
|
77
106
|
def vma_in?(vma, size = 0)
|
|
78
|
-
vma >=
|
|
79
|
-
vma + size <= mem_tail
|
|
107
|
+
vma >= mapped_head && vma + size <= mapped_tail
|
|
80
108
|
end
|
|
81
109
|
|
|
82
110
|
# Convert virtual memory address into file offset.
|
|
@@ -14,10 +14,14 @@ module ELFTools
|
|
|
14
14
|
# Streaming object.
|
|
15
15
|
# @param [Method] offset_from_vma
|
|
16
16
|
# The method to get offset of file, given virtual memory address.
|
|
17
|
-
|
|
17
|
+
# @param [Integer] machine
|
|
18
|
+
# The machine of the ELF file, which decides what the entries a
|
|
19
|
+
# segment points at mean. This should be +e_machine+ of the ELF header.
|
|
20
|
+
def initialize(header, stream, offset_from_vma: nil, machine: nil, **_kwargs)
|
|
18
21
|
@header = header
|
|
19
22
|
@stream = stream
|
|
20
23
|
@offset_from_vma = offset_from_vma
|
|
24
|
+
@machine = machine
|
|
21
25
|
end
|
|
22
26
|
|
|
23
27
|
# Return +header.p_type+ in a simpler way.
|
|
@@ -37,19 +41,19 @@ module ELFTools
|
|
|
37
41
|
# Is this segment readable?
|
|
38
42
|
# @return [Boolean] True or false.
|
|
39
43
|
def readable?
|
|
40
|
-
|
|
44
|
+
header.p_flags.allbits?(4)
|
|
41
45
|
end
|
|
42
46
|
|
|
43
47
|
# Is this segment writable?
|
|
44
48
|
# @return [Boolean] True or false.
|
|
45
49
|
def writable?
|
|
46
|
-
|
|
50
|
+
header.p_flags.allbits?(2)
|
|
47
51
|
end
|
|
48
52
|
|
|
49
53
|
# Is this segment executable?
|
|
50
54
|
# @return [Boolean] True or false.
|
|
51
55
|
def executable?
|
|
52
|
-
|
|
56
|
+
header.p_flags.allbits?(1)
|
|
53
57
|
end
|
|
54
58
|
end
|
|
55
59
|
end
|
data/lib/elftools/structs.rb
CHANGED
|
@@ -18,37 +18,88 @@ 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
|
#
|
|
33
91
|
# +BinData::Record+ doesn't allow us to override +#initialize+, so we hack +new+ here.
|
|
92
|
+
#
|
|
93
|
+
# Keyword arguments have to be taken as a trailing +Hash+ instead of +**kwargs+: bindata
|
|
94
|
+
# defines +new+ on each record class taking +*args+ only, then re-dispatches it to the
|
|
95
|
+
# endian-specific subclass this method actually runs on, which collapses the caller's
|
|
96
|
+
# keywords into a positional +Hash+ on the way. See +override_new_in_class+ in
|
|
97
|
+
# https://github.com/dmendel/bindata/blob/master/lib/bindata/dsl.rb, which is the same in
|
|
98
|
+
# 2.5.1, 3.0.0, and master.
|
|
34
99
|
def new(*args)
|
|
35
|
-
# XXX: The better implementation is +new(*args, **kwargs)+, but we can't do this unless bindata changed
|
|
36
|
-
# lib/bindata/dsl.rb#override_new_in_class to invoke +new+ with both +args+ and +kwargs+.
|
|
37
100
|
kwargs = args.last.is_a?(Hash) ? args.last : {}
|
|
38
101
|
offset = kwargs.delete(:offset)
|
|
39
|
-
super.tap
|
|
40
|
-
obj.offset = offset
|
|
41
|
-
obj.field_names.each do |f|
|
|
42
|
-
m = "#{f}=".to_sym
|
|
43
|
-
old_method = obj.singleton_method(m)
|
|
44
|
-
obj.singleton_class.send(:undef_method, m)
|
|
45
|
-
obj.define_singleton_method(m) do |val|
|
|
46
|
-
org = obj.send(f)
|
|
47
|
-
obj.patches[org.abs_offset] = ELFStruct.pack(val, org.num_bytes)
|
|
48
|
-
old_method.call(val)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
102
|
+
super.tap { |obj| obj.offset = offset }
|
|
52
103
|
end
|
|
53
104
|
|
|
54
105
|
# Gets the endianness of current class.
|
|
@@ -58,6 +109,10 @@ module ELFTools
|
|
|
58
109
|
end
|
|
59
110
|
|
|
60
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.
|
|
61
116
|
# @param [Integer] val
|
|
62
117
|
# @param [Integer] bytes
|
|
63
118
|
# @return [String]
|
|
@@ -179,6 +234,65 @@ module ELFTools
|
|
|
179
234
|
64 => ELF64_sym
|
|
180
235
|
}.freeze
|
|
181
236
|
|
|
237
|
+
# Header of the symbol hash table +DT_HASH+ points at.
|
|
238
|
+
class ELF_Hash < ELFStruct
|
|
239
|
+
endian :big_and_little
|
|
240
|
+
uint32 :nbucket # Number of buckets
|
|
241
|
+
uint32 :nchain # Number of chains, which is how many symbols there are
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Header of the symbol hash table +DT_GNU_HASH+ points at.
|
|
245
|
+
class ELF_GnuHash < ELFStruct
|
|
246
|
+
endian :big_and_little
|
|
247
|
+
uint32 :nbuckets # Number of buckets
|
|
248
|
+
uint32 :symndx # The first symbol index the table indexes
|
|
249
|
+
uint32 :maskwords # Number of words the bloom filter takes
|
|
250
|
+
uint32 :shift2 # The second shift the bloom filter is built with
|
|
251
|
+
end
|
|
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
|
+
|
|
182
296
|
# Note header.
|
|
183
297
|
class ELF_Nhdr < ELFStruct
|
|
184
298
|
endian :big_and_little
|