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
data/lib/elftools/dynamic.rb
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'elftools/constants'
|
|
4
|
+
require 'elftools/dynamic/string_table'
|
|
5
|
+
require 'elftools/dynamic/symbols'
|
|
6
|
+
require 'elftools/dynamic/versions'
|
|
7
|
+
require 'elftools/dynamic/tag'
|
|
8
|
+
require 'elftools/exceptions'
|
|
9
|
+
require 'elftools/relative_relocations'
|
|
10
|
+
require 'elftools/relocation'
|
|
11
|
+
require 'elftools/structs'
|
|
12
|
+
|
|
3
13
|
module ELFTools
|
|
4
14
|
# Define common methods for dynamic sections and dynamic segments.
|
|
5
15
|
#
|
|
@@ -8,6 +18,9 @@ module ELFTools
|
|
|
8
18
|
# and {ELFTools::Segments::DynamicSegment} because methods here assume some
|
|
9
19
|
# attributes exist.
|
|
10
20
|
module Dynamic
|
|
21
|
+
include Symbols
|
|
22
|
+
include Versions
|
|
23
|
+
|
|
11
24
|
# Iterate all tags.
|
|
12
25
|
#
|
|
13
26
|
# @note
|
|
@@ -18,8 +31,8 @@ module ELFTools
|
|
|
18
31
|
# @return [Enumerator<ELFTools::Dynamic::Tag>, Array<ELFTools::Dynamic::Tag>]
|
|
19
32
|
# If block is not given, an enumerator will be returned.
|
|
20
33
|
# Otherwise, return array of tags.
|
|
21
|
-
def
|
|
22
|
-
return enum_for(:
|
|
34
|
+
def each_tag(&block)
|
|
35
|
+
return enum_for(:each_tag) unless block_given?
|
|
23
36
|
|
|
24
37
|
arr = []
|
|
25
38
|
0.step do |i|
|
|
@@ -30,11 +43,14 @@ module ELFTools
|
|
|
30
43
|
arr
|
|
31
44
|
end
|
|
32
45
|
|
|
46
|
+
# The name this used to go by, kept so that it keeps working.
|
|
47
|
+
alias each_tags each_tag
|
|
48
|
+
|
|
33
49
|
# Use {#tags} to get all tags.
|
|
34
50
|
# @return [Array<ELFTools::Dynamic::Tag>]
|
|
35
51
|
# Array of tags.
|
|
36
52
|
def tags
|
|
37
|
-
@tags ||=
|
|
53
|
+
@tags ||= each_tag.to_a
|
|
38
54
|
end
|
|
39
55
|
|
|
40
56
|
# Get a tag of specific type.
|
|
@@ -63,7 +79,7 @@ module ELFTools
|
|
|
63
79
|
# #=> #<ELFTools::Dynamic::Tag:0x0055d3d2d91b28 @header={:d_tag=>3, :d_val=>6295552}>
|
|
64
80
|
def tag_by_type(type)
|
|
65
81
|
type = Util.to_constant(Constants::DT, type)
|
|
66
|
-
|
|
82
|
+
each_tag.find { |tag| tag.header.d_tag == type }
|
|
67
83
|
end
|
|
68
84
|
|
|
69
85
|
# Get tags of specific type.
|
|
@@ -75,7 +91,7 @@ module ELFTools
|
|
|
75
91
|
# @see #tag_by_type
|
|
76
92
|
def tags_by_type(type)
|
|
77
93
|
type = Util.to_constant(Constants::DT, type)
|
|
78
|
-
|
|
94
|
+
each_tag.select { |tag| tag.header.d_tag == type }
|
|
79
95
|
end
|
|
80
96
|
|
|
81
97
|
# Get the +n+-th tag.
|
|
@@ -100,7 +116,25 @@ module ELFTools
|
|
|
100
116
|
dyn.elf_class = header.elf_class
|
|
101
117
|
stream.pos = tag_start + n * dyn.num_bytes
|
|
102
118
|
dyn.offset = stream.pos
|
|
103
|
-
@tag_at_map[n] = Tag.new(dyn.read(stream), stream,
|
|
119
|
+
@tag_at_map[n] = Tag.new(dyn.read(stream), stream, string_table)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# The relocations the tags point at.
|
|
123
|
+
#
|
|
124
|
+
# Three tables record them: the one +DT_REL+ or +DT_RELA+ names, the one
|
|
125
|
+
# +DT_JMPREL+ names, whose entries are of the kind +DT_PLTREL+ names, and
|
|
126
|
+
# the one +DT_RELR+ names, which packs the relocations that only add the
|
|
127
|
+
# load bias into a bitmap and so records no type of its own.
|
|
128
|
+
# @return [Array<ELFTools::Relocation>] The relocations, in the order the
|
|
129
|
+
# tags record them, the packed ones last.
|
|
130
|
+
# @raise [ELFTools::ELFError]
|
|
131
|
+
# If a table is not in any loadable segment.
|
|
132
|
+
# @example
|
|
133
|
+
# elf.dynamic.relocations.map(&:type_name).uniq
|
|
134
|
+
# #=> ['R_X86_64_GLOB_DAT', 'R_X86_64_JUMP_SLOT']
|
|
135
|
+
def relocations
|
|
136
|
+
@relocations ||= relocation_tables.flat_map { |start, size, rela| read_relocations(start, size, rela) } +
|
|
137
|
+
packed_relocations
|
|
104
138
|
end
|
|
105
139
|
|
|
106
140
|
private
|
|
@@ -109,69 +143,92 @@ module ELFTools
|
|
|
109
143
|
header.class.self_endian
|
|
110
144
|
end
|
|
111
145
|
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
146
|
+
# Where each table of relocations starts, the tag recording how many bytes
|
|
147
|
+
# it takes, and whether its entries record an addend.
|
|
148
|
+
# @return [Array<Array(ELFTools::Dynamic::Tag, ELFTools::Dynamic::Tag, Boolean)>] The tables.
|
|
149
|
+
def relocation_tables
|
|
150
|
+
tables = %i[rel rela].filter_map do |type|
|
|
151
|
+
tag = tag_by_type(type)
|
|
152
|
+
[tag, tag_by_type(:"#{type}sz"), type == :rela] if tag
|
|
153
|
+
end
|
|
154
|
+
jmprel = tag_by_type(:jmprel)
|
|
155
|
+
return tables if jmprel.nil?
|
|
156
|
+
|
|
157
|
+
tables << [jmprel, tag_by_type(:pltrelsz), tag_by_type(:pltrel).header.d_val.to_i == Constants::DT_RELA]
|
|
116
158
|
end
|
|
117
159
|
|
|
118
|
-
#
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
# Instantiate a {ELFTools::Dynamic::Tag} object.
|
|
124
|
-
# @param [ELF_Dyn] header The dynamic tag header.
|
|
125
|
-
# @param [#pos=, #read] stream Streaming object.
|
|
126
|
-
# @param [Method] str_offset
|
|
127
|
-
# Call this method to get the string offset related
|
|
128
|
-
# to file.
|
|
129
|
-
def initialize(header, stream, str_offset)
|
|
130
|
-
@header = header
|
|
131
|
-
@stream = stream
|
|
132
|
-
@str_offset = str_offset
|
|
133
|
-
end
|
|
160
|
+
# Reads the table +DT_RELR+ names, which is absent from most files.
|
|
161
|
+
# @return [Array<ELFTools::Relocation>] The relocations, empty without it.
|
|
162
|
+
def packed_relocations
|
|
163
|
+
start = tag_by_type(:relr)
|
|
164
|
+
return [] if start.nil?
|
|
134
165
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
Constants::DT_RUNPATH].freeze
|
|
140
|
-
# Return the content of this tag records.
|
|
141
|
-
#
|
|
142
|
-
# For normal tags, this method just return
|
|
143
|
-
# +header.d_val+. For tags with +header.d_val+
|
|
144
|
-
# in meaning of string offset (e.g. DT_NEEDED), this method would
|
|
145
|
-
# return the string it specified.
|
|
146
|
-
# Tags with type in {TYPE_WITH_NAME} are those tags with name.
|
|
147
|
-
# @return [Integer, String] The content this tag records.
|
|
148
|
-
# @example
|
|
149
|
-
# dynamic = elf.segment_by_type(:dynamic)
|
|
150
|
-
# dynamic.tag_by_type(:init).value
|
|
151
|
-
# #=> 4195600 # 0x400510
|
|
152
|
-
# dynamic.tag_by_type(:needed).value
|
|
153
|
-
# #=> 'libc.so.6'
|
|
154
|
-
def value
|
|
155
|
-
name || header.d_val.to_i
|
|
156
|
-
end
|
|
166
|
+
offset = offset_of(start)
|
|
167
|
+
RelativeRelocations.new(stream, offset...(offset + tag_by_type(:relrsz).header.d_val.to_i),
|
|
168
|
+
elf_class: header.elf_class, endian:, machine: @machine).to_a
|
|
169
|
+
end
|
|
157
170
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
171
|
+
# Reads one table of relocations.
|
|
172
|
+
# @return [Array<ELFTools::Relocation>] The relocations.
|
|
173
|
+
def read_relocations(start, size, rela)
|
|
174
|
+
klass = rela ? Structs::ELF_Rela : Structs::ELF_Rel
|
|
175
|
+
offset = offset_of(start)
|
|
176
|
+
# An entry takes what its structure takes. DT_RELAENT and DT_RELENT
|
|
177
|
+
# record the same number, which a file has no way of disagreeing with
|
|
178
|
+
# and every file here agrees with.
|
|
179
|
+
entsize = struct(klass).num_bytes
|
|
180
|
+
Array.new(size.header.d_val.to_i / entsize) do |i|
|
|
181
|
+
Relocation.new(read_struct(klass, offset + (i * entsize)), stream, machine: @machine)
|
|
164
182
|
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# A structure of the endianness and the class the file records it in.
|
|
186
|
+
# @param [Class] klass The structure class.
|
|
187
|
+
# @return [ELFTools::Structs::ELFStruct] The structure, before it is read.
|
|
188
|
+
def struct(klass)
|
|
189
|
+
struct = klass.new(endian:)
|
|
190
|
+
struct.elf_class = header.elf_class
|
|
191
|
+
struct
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Reads a structure the file records at a file offset.
|
|
195
|
+
# @param [Class] klass The structure class.
|
|
196
|
+
# @param [Integer] offset The file offset.
|
|
197
|
+
# @return [ELFTools::Structs::ELFStruct] The structure.
|
|
198
|
+
def read_struct(klass, offset)
|
|
199
|
+
struct = struct(klass)
|
|
200
|
+
struct.offset = offset
|
|
201
|
+
stream.pos = offset
|
|
202
|
+
struct.read(stream)
|
|
203
|
+
end
|
|
165
204
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
205
|
+
# The file offset the address a tag records points at.
|
|
206
|
+
# @param [ELFTools::Dynamic::Tag] tag The tag.
|
|
207
|
+
# @return [Integer] The file offset.
|
|
208
|
+
# @raise [ELFTools::ELFError]
|
|
209
|
+
# If the address is not in any loadable segment.
|
|
210
|
+
def offset_of(tag)
|
|
211
|
+
vma = tag.header.d_val.to_i
|
|
212
|
+
@offset_from_vma.call(vma) ||
|
|
213
|
+
raise(ELFError, format('Invalid %s address 0x%x', Constants::DT.mapping(@machine, tag.header.d_tag.to_i), vma))
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# The names the tags and the symbols point at.
|
|
217
|
+
# @return [ELFTools::Dynamic::StringTable] The string table.
|
|
218
|
+
def string_table
|
|
219
|
+
@string_table ||= StringTable.new(stream, method(:str_offset))
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Get the DT_STRTAB's +d_val+ offset related to file.
|
|
223
|
+
# @return [Integer] The file offset.
|
|
224
|
+
# @raise [ELFTools::ELFError]
|
|
225
|
+
# If DT_STRTAB is absent, or its address is not in any loadable segment.
|
|
226
|
+
def str_offset
|
|
227
|
+
@str_offset ||= begin
|
|
228
|
+
strtab = tag_by_type(:strtab)
|
|
229
|
+
raise ELFError, 'DT_STRTAB not found' if strtab.nil?
|
|
173
230
|
|
|
174
|
-
|
|
231
|
+
offset_of(strtab)
|
|
175
232
|
end
|
|
176
233
|
end
|
|
177
234
|
end
|
data/lib/elftools/elf_file.rb
CHANGED
|
@@ -59,6 +59,25 @@ module ELFTools
|
|
|
59
59
|
note.desc.unpack1('H*')
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# The dynamic tags of this file, read from the view that its type makes
|
|
63
|
+
# authoritative.
|
|
64
|
+
#
|
|
65
|
+
# A relocatable file is linked by its sections, so its sections answer and
|
|
66
|
+
# any segment it carries is disregarded, being something no linker reads.
|
|
67
|
+
# An executable or a shared object is loaded by its segments alone, so the
|
|
68
|
+
# segment answers and the section recording the same tags is metadata a
|
|
69
|
+
# tool may have stripped or rewritten.
|
|
70
|
+
# @return [ELFTools::Segments::DynamicSegment, ELFTools::Sections::DynamicSection, nil]
|
|
71
|
+
# The tags, +nil+ if the view that decides records none.
|
|
72
|
+
# @example
|
|
73
|
+
# elf.dynamic.tag_by_type(:needed).name
|
|
74
|
+
# #=> 'libc.so.6'
|
|
75
|
+
def dynamic
|
|
76
|
+
return sections_by_type(:dynamic).first if header.e_type.to_i == Constants::ET_REL
|
|
77
|
+
|
|
78
|
+
segment_by_type(:dynamic) || sections_by_type(:dynamic).first
|
|
79
|
+
end
|
|
80
|
+
|
|
62
81
|
# Get machine architecture.
|
|
63
82
|
#
|
|
64
83
|
# Mappings of architecture can be found
|
|
@@ -67,7 +86,7 @@ module ELFTools
|
|
|
67
86
|
# Name of architecture.
|
|
68
87
|
# @example
|
|
69
88
|
# elf.machine
|
|
70
|
-
# #=> 'Advanced Micro Devices X86-64'
|
|
89
|
+
# #=> 'Advanced Micro Devices X86-64 processor'
|
|
71
90
|
def machine
|
|
72
91
|
ELFTools::Constants::EM.mapping(header.e_machine)
|
|
73
92
|
end
|
|
@@ -105,7 +124,7 @@ module ELFTools
|
|
|
105
124
|
# elf.section_by_name('no such section')
|
|
106
125
|
# #=> nil
|
|
107
126
|
def section_by_name(name)
|
|
108
|
-
|
|
127
|
+
each_section.find { |sec| sec.name == name }
|
|
109
128
|
end
|
|
110
129
|
|
|
111
130
|
# Iterate all sections.
|
|
@@ -119,19 +138,22 @@ module ELFTools
|
|
|
119
138
|
# @return [Enumerator<ELFTools::Sections::Section>, Array<ELFTools::Sections::Section>]
|
|
120
139
|
# As +Array#each+, if block is not given, a enumerator will be returned,
|
|
121
140
|
# otherwise, the whole sections will be returned.
|
|
122
|
-
def
|
|
123
|
-
return enum_for(:
|
|
141
|
+
def each_section(&block)
|
|
142
|
+
return enum_for(:each_section) unless block_given?
|
|
124
143
|
|
|
125
144
|
Array.new(num_sections) do |i|
|
|
126
145
|
section_at(i).tap(&block)
|
|
127
146
|
end
|
|
128
147
|
end
|
|
129
148
|
|
|
149
|
+
# The name this used to go by, kept so that it keeps working.
|
|
150
|
+
alias each_sections each_section
|
|
151
|
+
|
|
130
152
|
# Simply use {#sections} to get all sections.
|
|
131
153
|
# @return [Array<ELFTools::Sections::Section>]
|
|
132
154
|
# Whole sections.
|
|
133
155
|
def sections
|
|
134
|
-
|
|
156
|
+
each_section.to_a
|
|
135
157
|
end
|
|
136
158
|
|
|
137
159
|
# Acquire the +n+-th section, 0-based.
|
|
@@ -160,17 +182,21 @@ module ELFTools
|
|
|
160
182
|
# elf.sections_by_type(:rela)
|
|
161
183
|
# #=> [#<ELFTools::Sections::RelocationSection:0x00563cd3219970>,
|
|
162
184
|
# # #<ELFTools::Sections::RelocationSection:0x00563cd3b89d70>]
|
|
163
|
-
def sections_by_type(type, &
|
|
185
|
+
def sections_by_type(type, &)
|
|
164
186
|
type = Util.to_constant(Constants::SHT, type)
|
|
165
|
-
Util.select_by_type(
|
|
187
|
+
Util.select_by_type(each_section, type, &)
|
|
166
188
|
end
|
|
167
189
|
|
|
168
|
-
#
|
|
190
|
+
# The section the names of the sections are recorded in, which the ELF
|
|
191
|
+
# header names by index.
|
|
169
192
|
#
|
|
170
|
-
#
|
|
171
|
-
#
|
|
172
|
-
# @return [ELFTools::Sections::StrTabSection] The
|
|
173
|
-
|
|
193
|
+
# It is not the section the names of the symbols are recorded in, which
|
|
194
|
+
# {ELFTools::Sections::SymTabSection#symstr} answers.
|
|
195
|
+
# @return [ELFTools::Sections::StrTabSection] The section.
|
|
196
|
+
# @example
|
|
197
|
+
# elf.section_name_table.name
|
|
198
|
+
# #=> '.shstrtab'
|
|
199
|
+
def section_name_table
|
|
174
200
|
section_at(header.e_shstrndx)
|
|
175
201
|
end
|
|
176
202
|
|
|
@@ -192,19 +218,22 @@ module ELFTools
|
|
|
192
218
|
# @yieldreturn [void]
|
|
193
219
|
# @return [Array<ELFTools::Segments::Segment>]
|
|
194
220
|
# Whole segments will be returned.
|
|
195
|
-
def
|
|
196
|
-
return enum_for(:
|
|
221
|
+
def each_segment(&block)
|
|
222
|
+
return enum_for(:each_segment) unless block_given?
|
|
197
223
|
|
|
198
224
|
Array.new(num_segments) do |i|
|
|
199
225
|
segment_at(i).tap(&block)
|
|
200
226
|
end
|
|
201
227
|
end
|
|
202
228
|
|
|
229
|
+
# The name this used to go by, kept so that it keeps working.
|
|
230
|
+
alias each_segments each_segment
|
|
231
|
+
|
|
203
232
|
# Simply use {#segments} to get all segments.
|
|
204
233
|
# @return [Array<ELFTools::Segments::Segment>]
|
|
205
234
|
# Whole segments.
|
|
206
235
|
def segments
|
|
207
|
-
|
|
236
|
+
each_segment.to_a
|
|
208
237
|
end
|
|
209
238
|
|
|
210
239
|
# Get the first segment with +p_type=type+.
|
|
@@ -250,7 +279,7 @@ module ELFTools
|
|
|
250
279
|
# #=> nil # no such segment exists
|
|
251
280
|
def segment_by_type(type)
|
|
252
281
|
type = Util.to_constant(Constants::PT, type)
|
|
253
|
-
|
|
282
|
+
each_segment.find { |seg| seg.header.p_type == type }
|
|
254
283
|
end
|
|
255
284
|
|
|
256
285
|
# Fetch all segments with specific type.
|
|
@@ -263,9 +292,9 @@ module ELFTools
|
|
|
263
292
|
# @yieldparam [ELFTools::Segments::Segment] segment A segment in specific type.
|
|
264
293
|
# @yieldreturn [void]
|
|
265
294
|
# @return [Array<ELFTools::Segments::Segment>] The target segments.
|
|
266
|
-
def segments_by_type(type, &
|
|
295
|
+
def segments_by_type(type, &)
|
|
267
296
|
type = Util.to_constant(Constants::PT, type)
|
|
268
|
-
Util.select_by_type(
|
|
297
|
+
Util.select_by_type(each_segment, type, &)
|
|
269
298
|
end
|
|
270
299
|
|
|
271
300
|
# Acquire the +n+-th segment, 0-based.
|
|
@@ -285,7 +314,7 @@ module ELFTools
|
|
|
285
314
|
# This method should work no matter ELF is a PIE or not.
|
|
286
315
|
# This method refers from (actually equals to) binutils/readelf.c#offset_from_vma.
|
|
287
316
|
# @param [Integer] vma The virtual address to be queried.
|
|
288
|
-
# @return [Integer] Related file offset.
|
|
317
|
+
# @return [Integer?] Related file offset. +nil+ if the queried region has no content in file.
|
|
289
318
|
# @example
|
|
290
319
|
# elf = ELFTools::ELFFile.new(File.open('/bin/cat'))
|
|
291
320
|
# elf.offset_from_vma(0x401337)
|
|
@@ -294,6 +323,25 @@ module ELFTools
|
|
|
294
323
|
segments_by_type(:load) do |seg|
|
|
295
324
|
return seg.vma_to_offset(vma) if seg.vma_in?(vma, size)
|
|
296
325
|
end
|
|
326
|
+
|
|
327
|
+
nil
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Get virtual address given offset in file
|
|
331
|
+
#
|
|
332
|
+
# @param [Integer] offset The file offset to be queried.
|
|
333
|
+
# @return [Integer?] Related virtual address. Note this is raw address, not always adjusted for base address.
|
|
334
|
+
# +nil+ if offset is invalid.
|
|
335
|
+
# @example
|
|
336
|
+
# elf = ELFTools::ELFFile.new(File.open('/bin/cat'))
|
|
337
|
+
# elf.vma_from_offset(0x1337)
|
|
338
|
+
# #=> 0x401337
|
|
339
|
+
def vma_from_offset(offset, size = 1)
|
|
340
|
+
segments_by_type(:load) do |seg|
|
|
341
|
+
return seg.offset_to_vma(offset) if seg.offset_in?(offset, size)
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
nil
|
|
297
345
|
end
|
|
298
346
|
|
|
299
347
|
# The patch status.
|
|
@@ -363,15 +411,19 @@ module ELFTools
|
|
|
363
411
|
shdr.read(stream)
|
|
364
412
|
Sections::Section.create(shdr, stream,
|
|
365
413
|
offset_from_vma: method(:offset_from_vma),
|
|
366
|
-
|
|
367
|
-
section_at: method(:section_at)
|
|
414
|
+
section_name_table: method(:section_name_table),
|
|
415
|
+
section_at: method(:section_at),
|
|
416
|
+
sections: method(:sections),
|
|
417
|
+
machine: header.e_machine.to_i)
|
|
368
418
|
end
|
|
369
419
|
|
|
370
420
|
def create_segment(n)
|
|
371
421
|
stream.pos = header.e_phoff + n * header.e_phentsize
|
|
372
422
|
phdr = Structs::ELF_Phdr[elf_class].new(endian:, offset: stream.pos)
|
|
373
423
|
phdr.elf_class = elf_class
|
|
374
|
-
Segments::Segment.create(phdr.read(stream), stream,
|
|
424
|
+
Segments::Segment.create(phdr.read(stream), stream,
|
|
425
|
+
offset_from_vma: method(:offset_from_vma),
|
|
426
|
+
machine: header.e_machine.to_i)
|
|
375
427
|
end
|
|
376
428
|
end
|
|
377
429
|
end
|
data/lib/elftools/lazy_array.rb
CHANGED
|
@@ -7,6 +7,9 @@ module ELFTools
|
|
|
7
7
|
# 'lazy loading' objects.
|
|
8
8
|
# Mainly used when loading sections, segments, and
|
|
9
9
|
# symbols.
|
|
10
|
+
#
|
|
11
|
+
# Only {#[]} loads an element on demand, any other method
|
|
12
|
+
# of +Array+ loads all elements before it operates.
|
|
10
13
|
class LazyArray < SimpleDelegator
|
|
11
14
|
# Instantiate a {LazyArray} object.
|
|
12
15
|
# @param [Integer] size
|
|
@@ -28,7 +31,8 @@ module ELFTools
|
|
|
28
31
|
# p arr[3]
|
|
29
32
|
# # 9
|
|
30
33
|
def initialize(size, &block)
|
|
31
|
-
|
|
34
|
+
@array = Array.new(size)
|
|
35
|
+
super(@array)
|
|
32
36
|
@block = block
|
|
33
37
|
end
|
|
34
38
|
|
|
@@ -36,14 +40,48 @@ module ELFTools
|
|
|
36
40
|
#
|
|
37
41
|
# Elements are lazy loaded at the first time
|
|
38
42
|
# access it.
|
|
43
|
+
# @param [Integer] i
|
|
44
|
+
# The index, negative index is *not* supported.
|
|
39
45
|
# @return [Object]
|
|
40
46
|
# The element, returned type is the
|
|
41
47
|
# return type of block given in {#initialize}.
|
|
48
|
+
# +nil+ if +i+ is out of bound.
|
|
42
49
|
def [](i)
|
|
43
|
-
|
|
44
|
-
return nil unless i.between?(0, __getobj__.size - 1)
|
|
50
|
+
return nil unless i.between?(0, size - 1)
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
@array[i] ||= @block.call(i)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The size of this array.
|
|
56
|
+
#
|
|
57
|
+
# This method never loads any element.
|
|
58
|
+
# @return [Integer]
|
|
59
|
+
# The size given in {#initialize}.
|
|
60
|
+
def size
|
|
61
|
+
@array.size
|
|
62
|
+
end
|
|
63
|
+
alias length size
|
|
64
|
+
|
|
65
|
+
# Loads all elements.
|
|
66
|
+
#
|
|
67
|
+
# Called whenever a method other than {#[]} and {#size} is invoked, so that
|
|
68
|
+
# those methods operate on a fully loaded array.
|
|
69
|
+
# @return [Array]
|
|
70
|
+
# The loaded array.
|
|
71
|
+
# @example
|
|
72
|
+
# arr = LazyArray.new(3) { |i| i * i }
|
|
73
|
+
# p arr.map { |v| v + 1 }
|
|
74
|
+
# # [1, 2, 5]
|
|
75
|
+
def __getobj__
|
|
76
|
+
@array.each_index { |i| self[i] }
|
|
77
|
+
@array
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
# Queries the array without loading any element.
|
|
83
|
+
def respond_to_missing?(name, include_private = false)
|
|
84
|
+
@array.respond_to?(name, include_private)
|
|
47
85
|
end
|
|
48
86
|
end
|
|
49
87
|
end
|
data/lib/elftools/note.rb
CHANGED
|
@@ -41,8 +41,8 @@ module ELFTools
|
|
|
41
41
|
# @return [Enumerator<ELFTools::Note::Note>, Array<ELFTools::Note::Note>]
|
|
42
42
|
# If block is not given, an enumerator will be returned.
|
|
43
43
|
# Otherwise, return the array of notes.
|
|
44
|
-
def
|
|
45
|
-
return enum_for(:
|
|
44
|
+
def each_note
|
|
45
|
+
return enum_for(:each_note) unless block_given?
|
|
46
46
|
|
|
47
47
|
@notes_offset_map ||= {}
|
|
48
48
|
cur = note_start
|
|
@@ -61,11 +61,14 @@ module ELFTools
|
|
|
61
61
|
notes
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
# The name this used to go by, kept so that it keeps working.
|
|
65
|
+
alias each_notes each_note
|
|
66
|
+
|
|
64
67
|
# Simply +#notes+ to get all notes.
|
|
65
68
|
# @return [Array<ELFTools::Note::Note>]
|
|
66
69
|
# Whole notes.
|
|
67
70
|
def notes
|
|
68
|
-
|
|
71
|
+
each_note.to_a
|
|
69
72
|
end
|
|
70
73
|
|
|
71
74
|
private
|
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
rel = Structs::ELF_Rel.new(endian: @endian, offset: from)
|
|
43
|
+
rel.elf_class = @elf_class
|
|
44
|
+
rel.r_offset = address
|
|
45
|
+
relocation = Relocation.new(rel, @stream, machine: @machine)
|
|
46
|
+
# Through the relocation, so that the type is laid out in +r_info+ the
|
|
47
|
+
# way the machine lays it out.
|
|
48
|
+
relocation.type = type if type
|
|
49
|
+
relocation
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# How many bytes an entry takes, which is the width of an address.
|
|
56
|
+
# @return [Integer] The number.
|
|
57
|
+
def width
|
|
58
|
+
@elf_class / 8
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Every address the table relocates, and the entry it was read from.
|
|
62
|
+
# @return [Array<Array(Integer, Integer)>] The addresses.
|
|
63
|
+
def addresses
|
|
64
|
+
found = []
|
|
65
|
+
# Where a bitmap counts from, which an address moves to just past itself.
|
|
66
|
+
here = 0
|
|
67
|
+
entries.each do |entry, from|
|
|
68
|
+
if entry.even?
|
|
69
|
+
found << [entry, from]
|
|
70
|
+
here = entry + width
|
|
71
|
+
else
|
|
72
|
+
# The lowest bit says the entry is a bitmap rather than an address.
|
|
73
|
+
(1...(width * 8)).each { |bit| found << [here + ((bit - 1) * width), from] if entry[bit] == 1 }
|
|
74
|
+
here += ((width * 8) - 1) * width
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
found
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# What the table records, and where each was read from.
|
|
81
|
+
# @return [Array<Array(Integer, Integer)>] The entries.
|
|
82
|
+
def entries
|
|
83
|
+
@stream.pos = @bytes.begin
|
|
84
|
+
format = "#{width == 8 ? 'Q' : 'L'}#{@endian == :big ? '>' : '<'}"
|
|
85
|
+
@stream.read(@bytes.size).to_s.unpack("#{format}*").each_with_index.map { |e, i| [e, @bytes.begin + (i * width)] }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|