elftools 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +82 -0
- data/lib/elftools/constants.rb +53 -0
- data/lib/elftools/dynamic/hash_table.rb +16 -0
- data/lib/elftools/dynamic/symbols.rb +54 -19
- data/lib/elftools/dynamic/tag.rb +28 -6
- data/lib/elftools/dynamic/versions.rb +87 -0
- data/lib/elftools/dynamic.rb +33 -35
- data/lib/elftools/elf_file.rb +61 -11
- data/lib/elftools/relative_relocations.rb +87 -0
- data/lib/elftools/relocation.rb +72 -9
- data/lib/elftools/sections/relative_relocation_section.rb +46 -0
- data/lib/elftools/sections/relocation_section.rb +16 -5
- data/lib/elftools/sections/section.rb +31 -0
- data/lib/elftools/sections/sections.rb +8 -0
- data/lib/elftools/sections/sym_tab_section.rb +64 -10
- data/lib/elftools/sections/symbol.rb +112 -10
- data/lib/elftools/sections/version_definition_section.rb +40 -0
- data/lib/elftools/sections/version_need_section.rb +40 -0
- data/lib/elftools/sections/version_section.rb +44 -0
- data/lib/elftools/structs.rb +373 -17
- data/lib/elftools/util.rb +68 -11
- data/lib/elftools/version.rb +1 -1
- data/lib/elftools/version_tables.rb +155 -0
- metadata +9 -2
data/lib/elftools/structs.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require 'bindata'
|
|
4
4
|
|
|
5
|
+
require 'elftools/exceptions'
|
|
6
|
+
|
|
5
7
|
module ELFTools
|
|
6
8
|
# Define ELF related structures in this module.
|
|
7
9
|
#
|
|
@@ -15,18 +17,93 @@ module ELFTools
|
|
|
15
17
|
{ selection: :elf_class, choices: { 32 => :"#{t}32", 64 => :"#{t}64" }, copy_on_change: true }
|
|
16
18
|
end
|
|
17
19
|
|
|
20
|
+
# How an integer of each width is packed, for +String#unpack+, whether it
|
|
21
|
+
# records a sign or not.
|
|
22
|
+
UNPACK_TEMPLATES = {
|
|
23
|
+
little: {
|
|
24
|
+
false => { 1 => 'C', 2 => 'v', 4 => 'V', 8 => 'Q<' },
|
|
25
|
+
true => { 1 => 'c', 2 => 's<', 4 => 'l<', 8 => 'q<' }
|
|
26
|
+
},
|
|
27
|
+
big: {
|
|
28
|
+
false => { 1 => 'C', 2 => 'n', 4 => 'N', 8 => 'Q>' },
|
|
29
|
+
true => { 1 => 'c', 2 => 's>', 4 => 'l>', 8 => 'q>' }
|
|
30
|
+
}
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
# Bytes of set bits to read a prototype from, longer than any structure
|
|
34
|
+
# here, so that reading one never runs short of them.
|
|
35
|
+
SET_BITS = ("\xff" * 256).b.freeze
|
|
36
|
+
|
|
18
37
|
attr_accessor :elf_class # @return [Integer] 32 or 64.
|
|
19
38
|
attr_accessor :offset # @return [Integer] The file offset of this header.
|
|
20
39
|
|
|
21
|
-
#
|
|
22
|
-
#
|
|
40
|
+
# Reads the structure, remembering the bytes it was read from.
|
|
41
|
+
#
|
|
42
|
+
# They are taken back off the stream. A stream that cannot be seeked is
|
|
43
|
+
# serialized instead.
|
|
44
|
+
# @param [#pos=, #read] io The streaming object.
|
|
45
|
+
# @return [ELFTools::Structs::ELFStruct] Itself.
|
|
46
|
+
def read(io)
|
|
47
|
+
start = io.pos if io.respond_to?(:pos)
|
|
48
|
+
super.tap { @source = start.nil? ? to_binary_s : bytes_read(io, start) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Which bytes of this structure have been changed since it was read.
|
|
52
|
+
#
|
|
53
|
+
# Every field answers alike, however deeply it is nested, because what is
|
|
54
|
+
# compared is the bytes the structure occupies rather than the
|
|
55
|
+
# assignments that were made to it. A field assigned the value it
|
|
56
|
+
# already held leaves nothing behind.
|
|
57
|
+
# @return [Hash{Integer => String}]
|
|
58
|
+
# Where each run of changed bytes starts, as an offset into the
|
|
59
|
+
# structure, and the bytes it is to be replaced with.
|
|
60
|
+
# @example
|
|
61
|
+
# header.e_ident.ei_abiversion = 41
|
|
62
|
+
# header.patches
|
|
63
|
+
# #=> { 8 => "\x29" }
|
|
23
64
|
def patches
|
|
24
|
-
|
|
65
|
+
return {} if @source.nil?
|
|
66
|
+
|
|
67
|
+
changed_runs(@source, to_binary_s)
|
|
25
68
|
end
|
|
26
69
|
|
|
27
70
|
# BinData hash(Snapshot) that behaves like HashWithIndifferentAccess
|
|
28
71
|
alias to_h snapshot
|
|
29
72
|
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
# The bytes a read has just taken from a stream, leaving it where the
|
|
76
|
+
# read left it.
|
|
77
|
+
# @param [#pos=, #read] io The streaming object.
|
|
78
|
+
# @param [Integer] start Where the read began.
|
|
79
|
+
# @return [String] The bytes.
|
|
80
|
+
def bytes_read(io, start)
|
|
81
|
+
here = io.pos
|
|
82
|
+
io.pos = start
|
|
83
|
+
io.read(here - start).tap { io.pos = here }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Where two strings of bytes differ, as the runs of bytes that differ.
|
|
87
|
+
#
|
|
88
|
+
# Only as far as +before+ reaches, so that a patch never covers more of
|
|
89
|
+
# the file than the structure it came from.
|
|
90
|
+
# @param [String] before The bytes as they were.
|
|
91
|
+
# @param [String] after The bytes as they are.
|
|
92
|
+
# @return [Hash{Integer => String}] Where each run starts, and its bytes.
|
|
93
|
+
def changed_runs(before, after)
|
|
94
|
+
runs = {}
|
|
95
|
+
start = nil
|
|
96
|
+
(0..before.bytesize).each do |i|
|
|
97
|
+
if i < before.bytesize && before.getbyte(i) != after.getbyte(i)
|
|
98
|
+
start ||= i
|
|
99
|
+
elsif start
|
|
100
|
+
runs[start] = after.byteslice(start, i - start)
|
|
101
|
+
start = nil
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
runs
|
|
105
|
+
end
|
|
106
|
+
|
|
30
107
|
class << self
|
|
31
108
|
# Hooks the constructor.
|
|
32
109
|
#
|
|
@@ -41,28 +118,60 @@ module ELFTools
|
|
|
41
118
|
def new(*args)
|
|
42
119
|
kwargs = args.last.is_a?(Hash) ? args.last : {}
|
|
43
120
|
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
|
|
121
|
+
super.tap { |obj| obj.offset = offset }
|
|
57
122
|
end
|
|
58
123
|
|
|
59
124
|
# Gets the endianness of current class.
|
|
125
|
+
#
|
|
126
|
+
# A class is of one endianness for as long as it exists, and asking
|
|
127
|
+
# bindata what it is named costs more than remembering the answer.
|
|
60
128
|
# @return [:little, :big] The endianness.
|
|
61
129
|
def self_endian
|
|
62
|
-
bindata_name[-2..] == 'be' ? :big : :little
|
|
130
|
+
@self_endian ||= bindata_name[-2..] == 'be' ? :big : :little
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# What the fields of a structure record, read straight from its bytes.
|
|
134
|
+
#
|
|
135
|
+
# Reading a table of structures costs a structure for every entry of
|
|
136
|
+
# it otherwise, which is most of what reading the table costs. Nothing
|
|
137
|
+
# is remembered of the bytes, so a caller that means to assign to a
|
|
138
|
+
# field wants a structure instead.
|
|
139
|
+
# @param [String] bytes The bytes a structure is recorded in.
|
|
140
|
+
# @param [Integer] elf_class 32 or 64, which decides how wide the fields recording an address are.
|
|
141
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
142
|
+
# @return [Hash{Symbol => Integer}] Each field, and what it records.
|
|
143
|
+
# @raise [ELFTools::ELFError] If this is not a structure of integers.
|
|
144
|
+
# @example
|
|
145
|
+
# ELF64_sym.unpack_fields(bytes, elf_class: 64, endian: :little)
|
|
146
|
+
# #=> { st_name: 1, st_info: 18, st_other: 0, st_shndx: 15, st_value: 4198864, st_size: 101 }
|
|
147
|
+
def unpack_fields(bytes, elf_class:, endian:)
|
|
148
|
+
values = bytes.unpack(unpack_template(elf_class, endian))
|
|
149
|
+
fields = {}
|
|
150
|
+
# Paired by hand rather than zipped, which would make an array for
|
|
151
|
+
# every field of every structure read.
|
|
152
|
+
field_names(elf_class, endian).each_with_index { |name, i| fields[name] = values[i] }
|
|
153
|
+
fields
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# How many bytes a structure of this kind takes.
|
|
157
|
+
# @param [Integer] elf_class 32 or 64, which decides how wide the fields recording an address are.
|
|
158
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
159
|
+
# @return [Integer] The number.
|
|
160
|
+
# @example
|
|
161
|
+
# ELF64_sym.num_bytes(elf_class: 64, endian: :little)
|
|
162
|
+
# #=> 24
|
|
163
|
+
def num_bytes(elf_class:, endian:)
|
|
164
|
+
@num_bytes ||= {}
|
|
165
|
+
# Nested rather than keyed by the pair, which would make an array of
|
|
166
|
+
# it for every structure read.
|
|
167
|
+
(@num_bytes[elf_class] ||= {})[endian] ||= prototype(elf_class, endian).num_bytes
|
|
63
168
|
end
|
|
64
169
|
|
|
65
170
|
# Packs an integer to string.
|
|
171
|
+
#
|
|
172
|
+
# @deprecated
|
|
173
|
+
# Nothing here packs a patch by hand anymore, see {ELFStruct#patches}.
|
|
174
|
+
# This is kept for anyone who called it and goes in the next major.
|
|
66
175
|
# @param [Integer] val
|
|
67
176
|
# @param [Integer] bytes
|
|
68
177
|
# @return [String]
|
|
@@ -78,6 +187,210 @@ module ELFTools
|
|
|
78
187
|
out = out.pack('C*')
|
|
79
188
|
self_endian == :little ? out : out.reverse
|
|
80
189
|
end
|
|
190
|
+
|
|
191
|
+
private
|
|
192
|
+
|
|
193
|
+
# A structure of this kind with every bit of it set, which the layout is
|
|
194
|
+
# read off: how wide each field is, and whether it records a sign.
|
|
195
|
+
#
|
|
196
|
+
# Every structure of a kind and a class is laid out alike, so one is
|
|
197
|
+
# built for the kind rather than for each question asked about it.
|
|
198
|
+
# @param [Integer] elf_class 32 or 64, which decides how wide the fields recording an address are.
|
|
199
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
200
|
+
# @return [ELFTools::Structs::ELFStruct] The structure.
|
|
201
|
+
def prototype(elf_class, endian)
|
|
202
|
+
@prototypes ||= {}
|
|
203
|
+
(@prototypes[elf_class] ||= {})[endian] ||= begin
|
|
204
|
+
struct = new(endian: endian)
|
|
205
|
+
struct.elf_class = elf_class
|
|
206
|
+
struct.read(SET_BITS)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# What the fields of this structure are named, in the order they are
|
|
211
|
+
# recorded.
|
|
212
|
+
# @param [Integer] elf_class 32 or 64, which decides how wide the fields recording an address are.
|
|
213
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
214
|
+
# @return [Array<Symbol>] The names.
|
|
215
|
+
def field_names(elf_class, endian)
|
|
216
|
+
@field_names ||= {}
|
|
217
|
+
(@field_names[elf_class] ||= {})[endian] ||= prototype(elf_class, endian).field_names
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# How the fields of this structure are packed, as a template for
|
|
221
|
+
# +String#unpack+.
|
|
222
|
+
# @param [Integer] elf_class 32 or 64, which decides how wide the fields recording an address are.
|
|
223
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
224
|
+
# @return [String] The template.
|
|
225
|
+
def unpack_template(elf_class, endian)
|
|
226
|
+
@unpack_templates ||= {}
|
|
227
|
+
(@unpack_templates[elf_class] ||= {})[endian] ||=
|
|
228
|
+
prototype(elf_class, endian).each_pair.map { |_, field| field_template(field, endian) }.join
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# How one field is packed, which is its width and whether what it read
|
|
232
|
+
# from a field of set bits came back negative.
|
|
233
|
+
# @param [BinData::Base] field The field, read from bits that are all set.
|
|
234
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
235
|
+
# @return [String] The template.
|
|
236
|
+
# @raise [ELFTools::ELFError] If the field is not an integer of 1, 2, 4, or 8 bytes.
|
|
237
|
+
def field_template(field, endian)
|
|
238
|
+
width = field.num_bytes
|
|
239
|
+
template = UNPACK_TEMPLATES.fetch(endian)
|
|
240
|
+
raise ELFError, format('%s is not a structure of integers', name) unless template[false].key?(width)
|
|
241
|
+
|
|
242
|
+
template.fetch(field.to_i.negative?)[width]
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# What a structure the file records at an offset holds, read without
|
|
248
|
+
# building the structure.
|
|
249
|
+
#
|
|
250
|
+
# Reading a table of structures costs a structure for every entry of it
|
|
251
|
+
# otherwise, and setting up the fields of one is most of what it costs.
|
|
252
|
+
# {#struct} builds one for whatever wants the structure itself, which
|
|
253
|
+
# is what assigning to a field takes, and what is assigned is answered
|
|
254
|
+
# with from then on.
|
|
255
|
+
class Fields
|
|
256
|
+
attr_reader :elf_class # @return [Integer] 32 or 64.
|
|
257
|
+
attr_reader :endian # @return [:little, :big] The endianness the file records it in.
|
|
258
|
+
|
|
259
|
+
# What a structure already built holds, for a caller that has one.
|
|
260
|
+
# @param [ELFTools::Structs::ELFStruct] struct The structure.
|
|
261
|
+
# @return [ELFTools::Structs::Fields] The fields.
|
|
262
|
+
def self.of(struct)
|
|
263
|
+
allocate.tap { |fields| fields.send(:built_from, struct) }
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Fields nothing in the file records, for what a file states some other
|
|
267
|
+
# way than by recording a structure of it.
|
|
268
|
+
#
|
|
269
|
+
# There is nothing to read and nothing to patch, so a structure is only
|
|
270
|
+
# built if something asks for one, and is built from these.
|
|
271
|
+
# @param [Class] klass The structure class.
|
|
272
|
+
# @param [Hash{Symbol => Integer}] fields What each field is to record.
|
|
273
|
+
# @param [Integer] elf_class 32 or 64.
|
|
274
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
275
|
+
# @param [Integer] offset Where in the file this came from.
|
|
276
|
+
# @return [ELFTools::Structs::Fields] The fields.
|
|
277
|
+
# @example
|
|
278
|
+
# Fields.from(ELF_Rel, { r_offset: 0x1000 }, elf_class: 64, endian: :little, offset: 0x40)
|
|
279
|
+
def self.from(klass, fields, elf_class:, endian:, offset:)
|
|
280
|
+
allocate.tap { |made| made.send(:made_of, klass, fields, elf_class, endian, offset) }
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# @param [Class] klass The structure class.
|
|
284
|
+
# @param [#pos=, #read] stream The streaming object.
|
|
285
|
+
# @param [Integer] offset The file offset the structure is recorded at.
|
|
286
|
+
# @param [Integer] elf_class 32 or 64.
|
|
287
|
+
# @param [:little, :big] endian The endianness the file records it in.
|
|
288
|
+
# @raise [EOFError] If the file does not reach that far.
|
|
289
|
+
def initialize(klass, stream, offset, elf_class:, endian:)
|
|
290
|
+
@klass = klass
|
|
291
|
+
@stream = stream
|
|
292
|
+
@offset = offset
|
|
293
|
+
@elf_class = elf_class
|
|
294
|
+
@endian = endian
|
|
295
|
+
@fields = unpack
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# What one field of the structure records.
|
|
299
|
+
#
|
|
300
|
+
# The structure answers once there is one, so that a field assigned to
|
|
301
|
+
# reads back as it was assigned.
|
|
302
|
+
# @param [Symbol] name The name of the field.
|
|
303
|
+
# @return [Integer] The value.
|
|
304
|
+
# @example
|
|
305
|
+
# fields[:st_value]
|
|
306
|
+
# #=> 4198864
|
|
307
|
+
def [](name)
|
|
308
|
+
return @struct[name].to_i if @struct
|
|
309
|
+
|
|
310
|
+
@fields[name]
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# Assigns to one field.
|
|
314
|
+
#
|
|
315
|
+
# The structure takes the assignment wherever the file records one, so
|
|
316
|
+
# that {ELFTools::ELFFile#patches} reports it. Fields the file records
|
|
317
|
+
# no structure of take it themselves, there being nothing to patch.
|
|
318
|
+
# @param [Symbol] name The name of the field.
|
|
319
|
+
# @param [Integer] value What it is to record.
|
|
320
|
+
# @return [void]
|
|
321
|
+
def []=(name, value)
|
|
322
|
+
if @struct.nil? && @stream.nil?
|
|
323
|
+
@fields[name] = value
|
|
324
|
+
else
|
|
325
|
+
struct[name] = value
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# The structure itself, read from where the file records it, or built
|
|
330
|
+
# from these fields where it records none.
|
|
331
|
+
#
|
|
332
|
+
# The same one however often it is asked for, so that assigning to a
|
|
333
|
+
# field of it is not forgotten.
|
|
334
|
+
# @return [ELFTools::Structs::ELFStruct] The structure.
|
|
335
|
+
def struct
|
|
336
|
+
@struct ||= @stream.nil? ? build : read
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
private
|
|
340
|
+
|
|
341
|
+
# Takes a structure that is already built as what these fields hold.
|
|
342
|
+
# @param [ELFTools::Structs::ELFStruct] struct The structure.
|
|
343
|
+
# @return [void]
|
|
344
|
+
def built_from(struct)
|
|
345
|
+
@struct = struct
|
|
346
|
+
@elf_class = struct.elf_class
|
|
347
|
+
@endian = struct.class.self_endian
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Takes fields nothing in the file records a structure of.
|
|
351
|
+
# @return [void]
|
|
352
|
+
def made_of(klass, fields, elf_class, endian, offset)
|
|
353
|
+
@klass = klass
|
|
354
|
+
@fields = fields
|
|
355
|
+
@elf_class = elf_class
|
|
356
|
+
@endian = endian
|
|
357
|
+
@offset = offset
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# The structure the file records, read from where it records it.
|
|
361
|
+
# @return [ELFTools::Structs::ELFStruct] The structure.
|
|
362
|
+
def read
|
|
363
|
+
struct = new_struct
|
|
364
|
+
@stream.pos = @offset
|
|
365
|
+
struct.read(@stream)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# A structure holding what these fields hold, for fields the file
|
|
369
|
+
# records no structure of.
|
|
370
|
+
# @return [ELFTools::Structs::ELFStruct] The structure.
|
|
371
|
+
def build
|
|
372
|
+
new_struct.tap { |struct| @fields.each { |name, value| struct[name] = value } }
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# A structure of this kind, of the class and the order of the file, and
|
|
376
|
+
# of where in it this came from.
|
|
377
|
+
# @return [ELFTools::Structs::ELFStruct] The structure.
|
|
378
|
+
def new_struct
|
|
379
|
+
struct = @klass.new(endian: @endian, offset: @offset)
|
|
380
|
+
struct.elf_class = @elf_class
|
|
381
|
+
struct
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# What the fields record, read from the bytes recording them.
|
|
385
|
+
# @return [Hash{Symbol => Integer}] Each field, and what it records.
|
|
386
|
+
# @raise [EOFError] If the file does not reach that far, as reading the structure itself does.
|
|
387
|
+
def unpack
|
|
388
|
+
num_bytes = @klass.num_bytes(elf_class: @elf_class, endian: @endian)
|
|
389
|
+
@stream.pos = @offset
|
|
390
|
+
bytes = @stream.read(num_bytes)
|
|
391
|
+
raise EOFError, 'End of file reached' if bytes.nil? || bytes.bytesize < num_bytes
|
|
392
|
+
|
|
393
|
+
@klass.unpack_fields(bytes, elf_class: @elf_class, endian: @endian)
|
|
81
394
|
end
|
|
82
395
|
end
|
|
83
396
|
|
|
@@ -200,6 +513,49 @@ module ELFTools
|
|
|
200
513
|
uint32 :shift2 # The second shift the bloom filter is built with
|
|
201
514
|
end
|
|
202
515
|
|
|
516
|
+
# An entry of the table of versions a file needs of another, which the
|
|
517
|
+
# +vn_next+ of the one before it points at.
|
|
518
|
+
class ELF_Verneed < ELFStruct
|
|
519
|
+
endian :big_and_little
|
|
520
|
+
uint16 :vn_version # Revision of this structure
|
|
521
|
+
uint16 :vn_cnt # How many versions of the file are needed
|
|
522
|
+
uint32 :vn_file # Name of the file, as an offset into the string table
|
|
523
|
+
uint32 :vn_aux # Where the versions start, as an offset from here
|
|
524
|
+
uint32 :vn_next # Where the next entry is, as an offset from here
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
# A version an {ELF_Verneed} needs, which the +vna_next+ of the one before
|
|
528
|
+
# it points at.
|
|
529
|
+
class ELF_Vernaux < ELFStruct
|
|
530
|
+
endian :big_and_little
|
|
531
|
+
uint32 :vna_hash # Hash of the name
|
|
532
|
+
uint16 :vna_flags # Flags
|
|
533
|
+
uint16 :vna_other # The index the symbols name this version with
|
|
534
|
+
uint32 :vna_name # The name, as an offset into the string table
|
|
535
|
+
uint32 :vna_next # Where the next version is, as an offset from here
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
# An entry of the table of versions a file defines, which the +vd_next+ of
|
|
539
|
+
# the one before it points at.
|
|
540
|
+
class ELF_Verdef < ELFStruct
|
|
541
|
+
endian :big_and_little
|
|
542
|
+
uint16 :vd_version # Revision of this structure
|
|
543
|
+
uint16 :vd_flags # Flags
|
|
544
|
+
uint16 :vd_ndx # The index the symbols name this version with
|
|
545
|
+
uint16 :vd_cnt # How many names follow, the version and its ancestors
|
|
546
|
+
uint32 :vd_hash # Hash of the name
|
|
547
|
+
uint32 :vd_aux # Where the names start, as an offset from here
|
|
548
|
+
uint32 :vd_next # Where the next entry is, as an offset from here
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
# A name an {ELF_Verdef} records, its own or an ancestor's, which the
|
|
552
|
+
# +vda_next+ of the one before it points at.
|
|
553
|
+
class ELF_Verdaux < ELFStruct
|
|
554
|
+
endian :big_and_little
|
|
555
|
+
uint32 :vda_name # The name, as an offset into the string table
|
|
556
|
+
uint32 :vda_next # Where the next name is, as an offset from here
|
|
557
|
+
end
|
|
558
|
+
|
|
203
559
|
# Note header.
|
|
204
560
|
class ELF_Nhdr < ELFStruct
|
|
205
561
|
endian :big_and_little
|
data/lib/elftools/util.rb
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
module ELFTools
|
|
4
4
|
# Define some util methods.
|
|
5
5
|
module Util
|
|
6
|
+
# How many bytes {ClassMethods#cstring} takes from a stream at a time. Long
|
|
7
|
+
# enough that a name is usually read in one, short enough that reading one
|
|
8
|
+
# never reaches far past its end.
|
|
9
|
+
CSTRING_CHUNK = 64
|
|
10
|
+
|
|
6
11
|
# Class methods.
|
|
7
12
|
module ClassMethods
|
|
8
13
|
# Round up the number to be multiple of
|
|
@@ -23,6 +28,26 @@ module ELFTools
|
|
|
23
28
|
(num + n) & ~(n - 1)
|
|
24
29
|
end
|
|
25
30
|
|
|
31
|
+
# Checks a value is one the bits recording it can hold.
|
|
32
|
+
#
|
|
33
|
+
# Several of the values a file records share a byte with others, so a
|
|
34
|
+
# value too large for its bits would be written over its neighbours
|
|
35
|
+
# instead of being rejected.
|
|
36
|
+
# @param [Integer] value The value.
|
|
37
|
+
# @param [Integer] bits How many bits record it.
|
|
38
|
+
# @param [String] name What the value is, for the error to name.
|
|
39
|
+
# @return [Integer] The value.
|
|
40
|
+
# @raise [ArgumentError] If the bits cannot hold it.
|
|
41
|
+
# @example
|
|
42
|
+
# Util.fits!(16, 4, 'Symbol binding')
|
|
43
|
+
# #=> ArgumentError: Symbol binding must be in 0..15, got 16
|
|
44
|
+
def fits!(value, bits, name)
|
|
45
|
+
highest = (1 << bits) - 1
|
|
46
|
+
return value if value.is_a?(Integer) && value.between?(0, highest)
|
|
47
|
+
|
|
48
|
+
raise ArgumentError, format('%s must be in 0..%d, got %p', name, highest, value)
|
|
49
|
+
end
|
|
50
|
+
|
|
26
51
|
# Fetch the correct value from module +mod+.
|
|
27
52
|
#
|
|
28
53
|
# See {ELFTools::ELFFile#segment_by_type} for how to
|
|
@@ -38,20 +63,22 @@ module ELFTools
|
|
|
38
63
|
module_name = mod.name.sub('ELFTools::', '')
|
|
39
64
|
# if val is an integer, check if exists in mod
|
|
40
65
|
if val.is_a?(Integer)
|
|
41
|
-
return val if mod.
|
|
66
|
+
return val if values_of(mod).key?(val)
|
|
42
67
|
|
|
43
68
|
raise ArgumentError, "No constants in #{module_name} is #{val}"
|
|
44
69
|
end
|
|
45
70
|
val = val.to_s.upcase
|
|
46
71
|
prefix = module_name.split('::')[-1]
|
|
47
72
|
val = "#{prefix}_#{val}" unless val.start_with?(prefix)
|
|
48
|
-
|
|
49
|
-
raise ArgumentError, "No constants in #{module_name} named \"#{val}\""
|
|
73
|
+
value = constants_of(mod)[val]
|
|
74
|
+
raise ArgumentError, "No constants in #{module_name} named \"#{val}\"" if value.nil?
|
|
50
75
|
|
|
51
|
-
|
|
76
|
+
value
|
|
52
77
|
end
|
|
53
78
|
|
|
54
79
|
# Read from stream until reach a null-byte.
|
|
80
|
+
#
|
|
81
|
+
# The stream is left just past the null-byte.
|
|
55
82
|
# @param [#pos=, #read] stream Streaming object.
|
|
56
83
|
# @param [Integer] offset Start from here.
|
|
57
84
|
# @return [String] Result string will never contain null byte.
|
|
@@ -60,16 +87,19 @@ module ELFTools
|
|
|
60
87
|
# #=> "\x7FELF\x02\x01\x01"
|
|
61
88
|
def cstring(stream, offset)
|
|
62
89
|
stream.pos = offset
|
|
63
|
-
|
|
64
|
-
ret = ''
|
|
90
|
+
ret = +''
|
|
65
91
|
loop do
|
|
66
|
-
|
|
67
|
-
return nil if
|
|
68
|
-
|
|
92
|
+
chunk = stream.read(CSTRING_CHUNK)
|
|
93
|
+
return nil if chunk.nil? # reach EOF
|
|
94
|
+
|
|
95
|
+
stop = chunk.index("\x00")
|
|
96
|
+
if stop
|
|
97
|
+
stream.pos = offset + ret.bytesize + stop + 1
|
|
98
|
+
return ret << chunk.byteslice(0, stop)
|
|
99
|
+
end
|
|
69
100
|
|
|
70
|
-
ret
|
|
101
|
+
ret << chunk
|
|
71
102
|
end
|
|
72
|
-
ret
|
|
73
103
|
end
|
|
74
104
|
|
|
75
105
|
# Select objects from enumerator with +.type+ property
|
|
@@ -93,6 +123,33 @@ module ELFTools
|
|
|
93
123
|
end
|
|
94
124
|
end
|
|
95
125
|
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
# What a module names each of its constants, and what each is worth.
|
|
130
|
+
#
|
|
131
|
+
# Names are compared upcased, which is how a constant keeping the case
|
|
132
|
+
# the ABI wrote it in, +SHT_GNU_verneed+ for one, is found by the name it
|
|
133
|
+
# is asked for. Read once per module, because reading them is more work
|
|
134
|
+
# than the lookup it is for.
|
|
135
|
+
# @param [Module] mod The module.
|
|
136
|
+
# @return [Hash{String => Integer}] The constants, by their upcased name.
|
|
137
|
+
def constants_of(mod)
|
|
138
|
+
(@constants_of ||= {})[mod] ||= mod.constants.each_with_object({}) do |constant, table|
|
|
139
|
+
# A constant still waiting to be autoloaded is left alone, so that
|
|
140
|
+
# asking after one never loads what nothing has asked for.
|
|
141
|
+
next if mod.autoload?(constant)
|
|
142
|
+
|
|
143
|
+
table[constant.to_s.upcase] ||= mod.const_get(constant)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Which values a module names a constant for.
|
|
148
|
+
# @param [Module] mod The module.
|
|
149
|
+
# @return [Hash{Integer => Boolean}] The values, as the keys.
|
|
150
|
+
def values_of(mod)
|
|
151
|
+
(@values_of ||= {})[mod] ||= constants_of(mod).values.to_h { |value| [value, true] }
|
|
152
|
+
end
|
|
96
153
|
end
|
|
97
154
|
extend ClassMethods
|
|
98
155
|
end
|
data/lib/elftools/version.rb
CHANGED