elf_utils 0.3.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 +7 -0
- data/.rspec +3 -0
- data/.standard.yml +3 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/CONTRIBUTING.md +55 -0
- data/Gemfile +23 -0
- data/LICENSE.txt +21 -0
- data/MAINTAINERS.md +3 -0
- data/README.md +126 -0
- data/Rakefile +76 -0
- data/SECURITY.md +57 -0
- data/elf_utils.gemspec +41 -0
- data/ext/elf_utils/elf_utils.c +53 -0
- data/ext/elf_utils/extconf.rb +3 -0
- data/lib/elf_utils/elf_file.rb +312 -0
- data/lib/elf_utils/section/base.rb +77 -0
- data/lib/elf_utils/section/debug_abbrev/abbreviation.rb +171 -0
- data/lib/elf_utils/section/debug_abbrev/abbreviation_table.rb +27 -0
- data/lib/elf_utils/section/debug_abbrev.rb +15 -0
- data/lib/elf_utils/section/debug_addr.rb +9 -0
- data/lib/elf_utils/section/debug_arange.rb +54 -0
- data/lib/elf_utils/section/debug_info/compilation_unit.rb +189 -0
- data/lib/elf_utils/section/debug_info/debug_str_offsets_ref.rb +15 -0
- data/lib/elf_utils/section/debug_info/debug_str_ref.rb +17 -0
- data/lib/elf_utils/section/debug_info/die/base.rb +130 -0
- data/lib/elf_utils/section/debug_info/die.rb +470 -0
- data/lib/elf_utils/section/debug_info/die_ref.rb +22 -0
- data/lib/elf_utils/section/debug_info/header.rb +26 -0
- data/lib/elf_utils/section/debug_info.rb +93 -0
- data/lib/elf_utils/section/debug_line/line_number_program/header.rb +48 -0
- data/lib/elf_utils/section/debug_line/line_number_program/state_machine.rb +206 -0
- data/lib/elf_utils/section/debug_line/line_number_program.rb +134 -0
- data/lib/elf_utils/section/debug_line.rb +35 -0
- data/lib/elf_utils/section/debug_ranges.rb +22 -0
- data/lib/elf_utils/section/debug_str_offsets.rb +16 -0
- data/lib/elf_utils/section/dynsym.rb +14 -0
- data/lib/elf_utils/section/strtab.rb +9 -0
- data/lib/elf_utils/section/symtab.rb +11 -0
- data/lib/elf_utils/section.rb +50 -0
- data/lib/elf_utils/segment/base.rb +72 -0
- data/lib/elf_utils/segment.rb +9 -0
- data/lib/elf_utils/string_pread.rb +18 -0
- data/lib/elf_utils/symbol.rb +144 -0
- data/lib/elf_utils/types/dwarf/expression.rb +34 -0
- data/lib/elf_utils/types/dwarf.rb +639 -0
- data/lib/elf_utils/types/dwarf32/v2.rb +44 -0
- data/lib/elf_utils/types/dwarf32/v3.rb +40 -0
- data/lib/elf_utils/types/dwarf32/v4.rb +41 -0
- data/lib/elf_utils/types/dwarf32/v5.rb +44 -0
- data/lib/elf_utils/types/dwarf32.rb +12 -0
- data/lib/elf_utils/types/dwarf64/v3.rb +42 -0
- data/lib/elf_utils/types/dwarf64/v4.rb +43 -0
- data/lib/elf_utils/types/dwarf64/v5.rb +46 -0
- data/lib/elf_utils/types/dwarf64.rb +8 -0
- data/lib/elf_utils/types/sleb128.rb +66 -0
- data/lib/elf_utils/types/uleb128.rb +56 -0
- data/lib/elf_utils/types/unit_length.rb +51 -0
- data/lib/elf_utils/types.rb +328 -0
- data/lib/elf_utils/version.rb +5 -0
- data/lib/elf_utils.rb +83 -0
- data/sig/elf_utils.rbs +4 -0
- metadata +120 -0
@@ -0,0 +1,470 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module ElfUtils
|
4
|
+
class Section::DebugInfo::Die
|
5
|
+
def initialize(cu:, offset:, tag:, children:, abbrev:, attrs: nil, buf: nil)
|
6
|
+
@cu = cu
|
7
|
+
@offset = offset
|
8
|
+
@tag = tag
|
9
|
+
@children = children ? [] : false
|
10
|
+
@abbrev = abbrev
|
11
|
+
@unpacked_attrs = attrs
|
12
|
+
@buf = buf
|
13
|
+
end
|
14
|
+
attr_reader :tag, :offset, :abbrev, :cu
|
15
|
+
|
16
|
+
def pretty_print(q)
|
17
|
+
q.group(4,
|
18
|
+
"<%p 0x%08x: DW_TAG_%s (%d children)" %
|
19
|
+
[self.class, @offset, @tag, @children ? @children.size : 0],
|
20
|
+
">") do
|
21
|
+
q.text("\n" + " " * q.indent)
|
22
|
+
q.group_sub do
|
23
|
+
attrs = if @attrs
|
24
|
+
@attrs.map do |name, value|
|
25
|
+
["DW_AT_%-15s " % [name], value]
|
26
|
+
end
|
27
|
+
elsif @unpacked_attrs
|
28
|
+
@unpacked_attrs.map do |name, form, value|
|
29
|
+
["DW_AT_%-15s [DW_FORM_%s] " % [name, form], value]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if attrs
|
34
|
+
q.seplist(attrs) do |label, value|
|
35
|
+
q.group(4, label) do
|
36
|
+
case value
|
37
|
+
when self.class
|
38
|
+
q.text("<%p 0x%08x: DW_TAG_%s ...>" %
|
39
|
+
[self.class, value.offset, value.tag])
|
40
|
+
else
|
41
|
+
q.pp(value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
else
|
46
|
+
q.group(1, "abbrev=") { q.pp(@abbrev) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
alias_method :inspect, :pretty_inspect
|
52
|
+
|
53
|
+
def children?
|
54
|
+
@children != false
|
55
|
+
end
|
56
|
+
|
57
|
+
def children
|
58
|
+
@children || []
|
59
|
+
end
|
60
|
+
|
61
|
+
def has_attr?(attr)
|
62
|
+
@attrs ? @attrs.has_key?(attr) : @abbrev.attrs.has_key?(attr)
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_child(child)
|
66
|
+
raise Error, "This DIE cannot have children" unless @children
|
67
|
+
@children << child
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def type?
|
72
|
+
@is_a_type ||= case @tag
|
73
|
+
when :base_type,
|
74
|
+
:typedef,
|
75
|
+
:volatile_type,
|
76
|
+
:const_type,
|
77
|
+
:pointer_type,
|
78
|
+
:array_type,
|
79
|
+
:union_type,
|
80
|
+
:enumeration_type,
|
81
|
+
:structure_type,
|
82
|
+
:subroutine_type
|
83
|
+
true
|
84
|
+
else
|
85
|
+
false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def [](name)
|
90
|
+
@attrs ||= parse_attrs
|
91
|
+
@attrs[name]
|
92
|
+
end
|
93
|
+
|
94
|
+
def []=(name, value)
|
95
|
+
@attrs ||= parse_attrs
|
96
|
+
@attrs[name] = value
|
97
|
+
end
|
98
|
+
|
99
|
+
def attrs
|
100
|
+
@attrs ||= parse_attrs
|
101
|
+
@attrs.keys
|
102
|
+
end
|
103
|
+
|
104
|
+
# get the unprocessed value for an attribute
|
105
|
+
def raw_attr(key)
|
106
|
+
unpacked_attrs.each do |name, _, value|
|
107
|
+
return value if name == key
|
108
|
+
end
|
109
|
+
raise Error, "no attribute %p found in DIE: %p" % [key, self]
|
110
|
+
end
|
111
|
+
|
112
|
+
def addr_ranges
|
113
|
+
@attrs ||= parse_attrs
|
114
|
+
@addr_ranges ||=
|
115
|
+
if (low = @attrs[:low_pc]) && (high = @attrs[:high_pc])
|
116
|
+
[low..(high - 1)]
|
117
|
+
elsif @attrs[:ranges]
|
118
|
+
@cu.file.section(".debug_ranges")
|
119
|
+
.get(offset: @attrs[:ranges], base: @attrs[:low] || 0)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def has_addr?(addr)
|
124
|
+
case tag
|
125
|
+
when :subprogram
|
126
|
+
addr_ranges&.any? { |r| r.include?(addr) }
|
127
|
+
when :variable
|
128
|
+
type = self[:type].ctype
|
129
|
+
[location...location + type.size]
|
130
|
+
else
|
131
|
+
false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# return the number of elements in the subrange
|
136
|
+
def subrange_len
|
137
|
+
@attrs ||= parse_attrs
|
138
|
+
|
139
|
+
raise "not a subrange DIE: %p" % [self] unless @tag == :subrange_type
|
140
|
+
return @attrs[:count] if @attrs.has_key?(:count)
|
141
|
+
|
142
|
+
# upper bound may be DW_TAG_variable, so special handling
|
143
|
+
if (upper_bound = @attrs[:upper_bound])
|
144
|
+
return @attrs[:upper_bound] + 1 if upper_bound.is_a?(Integer)
|
145
|
+
end
|
146
|
+
|
147
|
+
# XXX we'll need to do some work to support flexible array members, or
|
148
|
+
# arrays with an upper bound defined by DW_TAG_variable
|
149
|
+
0
|
150
|
+
end
|
151
|
+
|
152
|
+
# return the value of DW_AT_location
|
153
|
+
def location
|
154
|
+
value = self[:location]
|
155
|
+
|
156
|
+
if value.is_a?(Types::Dwarf::Expression)
|
157
|
+
value = value.evaluate(addr_type: cu.addr_type, stack: [])[-1]
|
158
|
+
end
|
159
|
+
value
|
160
|
+
end
|
161
|
+
|
162
|
+
def type_name
|
163
|
+
return nil unless type?
|
164
|
+
|
165
|
+
@type_name ||= begin
|
166
|
+
@attrs ||= parse_attrs
|
167
|
+
|
168
|
+
case @tag
|
169
|
+
when :base_type, :typedef
|
170
|
+
@attrs[:name]
|
171
|
+
when :volatile_type
|
172
|
+
"volatile #{@attrs[:type]&.type_name || "void"}"
|
173
|
+
when :const_type
|
174
|
+
"const #{@attrs[:type]&.type_name || "void"}"
|
175
|
+
when :array_type
|
176
|
+
sizes = children
|
177
|
+
.inject("") do |buf, child|
|
178
|
+
buf << "[%d]" % child.subrange_len if child.tag == :subrange_type
|
179
|
+
buf
|
180
|
+
end
|
181
|
+
"#{@attrs[:type].type_name}#{sizes}"
|
182
|
+
when :pointer_type
|
183
|
+
inner = @attrs[:type]&.type_name || "void"
|
184
|
+
if inner.end_with?("*")
|
185
|
+
"#{inner}*"
|
186
|
+
else
|
187
|
+
"#{inner} *"
|
188
|
+
end
|
189
|
+
when :structure_type
|
190
|
+
name = @attrs[:name] || ("anon_%x" % @offset)
|
191
|
+
"struct #{name}"
|
192
|
+
when :union_type
|
193
|
+
name = @attrs[:name] || ("anon_%x" % @offset)
|
194
|
+
"union #{name}"
|
195
|
+
when :enumeration_type
|
196
|
+
name = @attrs[:name] || ("anon_%x" % @offset)
|
197
|
+
"enum #{name}"
|
198
|
+
when :subrange_type, :subroutine_type
|
199
|
+
return
|
200
|
+
else
|
201
|
+
raise Error, "unsupported DIE: %p" % [self]
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def ctype
|
207
|
+
return unless type?
|
208
|
+
return @ctype if @ctype
|
209
|
+
|
210
|
+
@attrs ||= parse_attrs
|
211
|
+
|
212
|
+
@ctype = case tag
|
213
|
+
when :base_type
|
214
|
+
signed = !@attrs[:encoding].to_s.include?("unsigned")
|
215
|
+
int_type(size: @attrs[:byte_size], signed:)
|
216
|
+
when :typedef
|
217
|
+
@attrs[:type].ctype
|
218
|
+
when :const_type, :volatile_type
|
219
|
+
# const void has no equivalent ctype... maybe empty struct?
|
220
|
+
@attrs[:type]&.ctype
|
221
|
+
when :pointer_type, :subroutine_type
|
222
|
+
@cu.addr_type
|
223
|
+
when :array_type
|
224
|
+
sizes = children
|
225
|
+
.filter_map { |c| c.subrange_len if c.tag == :subrange_type }
|
226
|
+
|
227
|
+
# if we have a signed char array, we'll use a string to hold it
|
228
|
+
inner_type = @attrs[:type].ctype
|
229
|
+
if inner_type.is_a?(CTypes::Int) &&
|
230
|
+
inner_type.size == 1 &&
|
231
|
+
inner_type.signed?
|
232
|
+
inner_type = CTypes::String.new(size: sizes.pop)
|
233
|
+
end
|
234
|
+
|
235
|
+
while (size = sizes.pop)
|
236
|
+
inner_type = CTypes::Array.new(type: inner_type, size:)
|
237
|
+
end
|
238
|
+
|
239
|
+
inner_type
|
240
|
+
when :structure_type
|
241
|
+
offset = 0
|
242
|
+
struct = CTypes::Struct.builder
|
243
|
+
.name(self[:name])
|
244
|
+
.endian(@endian ||= @cu.file.endian)
|
245
|
+
bitfield = nil
|
246
|
+
|
247
|
+
(@children || []).each do |child|
|
248
|
+
next unless child.tag == :member
|
249
|
+
|
250
|
+
name = child[:name]&.to_sym
|
251
|
+
type = child[:type].ctype
|
252
|
+
loc = child[:data_member_location] || 0
|
253
|
+
if loc.is_a?(Types::Dwarf::Expression)
|
254
|
+
child[:data_member_location] = loc =
|
255
|
+
loc.evaluate(addr_type: @cu.addr_type)[0]
|
256
|
+
end
|
257
|
+
delta = loc - offset
|
258
|
+
|
259
|
+
# add any bitfield if we're moving past it
|
260
|
+
if bitfield && delta >= 0
|
261
|
+
struct.attribute bitfield.build
|
262
|
+
bitfield = nil
|
263
|
+
end
|
264
|
+
|
265
|
+
# handle any padding we need to do
|
266
|
+
struct.pad delta if delta > 0
|
267
|
+
|
268
|
+
# check for v2, v3 bitfield
|
269
|
+
if (bits = child[:bit_size]) && (bit_offset = child[:bit_offset])
|
270
|
+
bitfield ||= CTypes::Bitfield.builder.endian(@endian)
|
271
|
+
|
272
|
+
# convert the left-hand offset to a right-hand offset when we're
|
273
|
+
# on a little-endian system
|
274
|
+
if CTypes.host_endian == :little
|
275
|
+
bit_offset = child[:byte_size] * 8 - bit_offset - bits
|
276
|
+
end
|
277
|
+
|
278
|
+
bitfield.field(name,
|
279
|
+
offset: bit_offset,
|
280
|
+
bits: bits,
|
281
|
+
signed: type.signed?)
|
282
|
+
|
283
|
+
# v4, v5 bitfield
|
284
|
+
elsif (bits = child[:bit_size]) &&
|
285
|
+
(bit_offset = child[:data_bit_offset])
|
286
|
+
|
287
|
+
bitfield ||= CTypes::Bitfield.builder.endian(@endian)
|
288
|
+
|
289
|
+
# If the endian of the type doesn't match the host-endian, we
|
290
|
+
# need to flip the offset
|
291
|
+
if @endian != CTypes.host_endian
|
292
|
+
bit_offset = self[:byte_size] * 8 - bit_offset - bits
|
293
|
+
end
|
294
|
+
|
295
|
+
bitfield.field(name,
|
296
|
+
offset: bit_offset,
|
297
|
+
bits: bits,
|
298
|
+
signed: type.signed?)
|
299
|
+
|
300
|
+
# not a bitfield
|
301
|
+
elsif name.nil?
|
302
|
+
# handle unnamed field
|
303
|
+
struct.attribute type
|
304
|
+
|
305
|
+
# must be a named field
|
306
|
+
else
|
307
|
+
struct.attribute name, type
|
308
|
+
end
|
309
|
+
offset = loc + type.size
|
310
|
+
end
|
311
|
+
|
312
|
+
# ensure we include the bitfield if it was the last element
|
313
|
+
struct.attribute(bitfield.build) if bitfield
|
314
|
+
|
315
|
+
struct.build
|
316
|
+
when :union_type
|
317
|
+
union = CTypes::Union.builder
|
318
|
+
.name(self[:name])
|
319
|
+
.endian(@endian ||= @cu.file.endian)
|
320
|
+
(@children || []).each do |child|
|
321
|
+
next unless child.tag == :member
|
322
|
+
|
323
|
+
name = child[:name]
|
324
|
+
type = child[:type].ctype
|
325
|
+
if name
|
326
|
+
union.member name.to_sym, type
|
327
|
+
else
|
328
|
+
union.member type
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
union.build
|
333
|
+
when :enumeration_type
|
334
|
+
type = int_type(size: @attrs[:byte_size], signed: false)
|
335
|
+
|
336
|
+
values = {}
|
337
|
+
@children.each do |child|
|
338
|
+
values[child[:name].downcase.to_sym] = child[:const_value]
|
339
|
+
end
|
340
|
+
|
341
|
+
CTypes::Enum.new(type.with_endian(@cu.file.endian), values)
|
342
|
+
when :subrange_type
|
343
|
+
return
|
344
|
+
else
|
345
|
+
raise Error, "unsupported DIE: %p" % [self]
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
private
|
350
|
+
|
351
|
+
def unpacked_attrs
|
352
|
+
@unpacked_attrs ||= begin
|
353
|
+
o, _ = @abbrev.unpack_die(buf: @buf,
|
354
|
+
endian: @cu.file.endian,
|
355
|
+
offset_type: @cu.offset_type,
|
356
|
+
addr_type: @cu.addr_type)
|
357
|
+
o
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def parse_attrs
|
362
|
+
attrs = {}
|
363
|
+
unpacked_attrs.each do |name, form, value|
|
364
|
+
case name
|
365
|
+
when :encoding
|
366
|
+
value = Types::Dwarf::Encoding[value]
|
367
|
+
when :data_member_location
|
368
|
+
value = Types::Dwarf::Expression.new(@cu, value) if
|
369
|
+
value.is_a?(String)
|
370
|
+
when :location
|
371
|
+
value = Types::Dwarf::Expression.new(@cu, value) if
|
372
|
+
value.is_a?(String)
|
373
|
+
when :high_pc
|
374
|
+
value += attrs[:low_pc] if form == :data4
|
375
|
+
end
|
376
|
+
|
377
|
+
case form
|
378
|
+
when :ref1, :ref2, :ref4, :ref8
|
379
|
+
value = @cu.die(value + @cu.offset)
|
380
|
+
when :strp
|
381
|
+
value = @cu.file.section(".debug_str")[value]
|
382
|
+
when :addrx
|
383
|
+
value = @cu.file.section(".debug_addr")
|
384
|
+
.get(base: @cu.addr_base, index: value)
|
385
|
+
when :strx1
|
386
|
+
base = @cu.str_offsets_base
|
387
|
+
offset = @cu.file.section(".debug_str_offsets")
|
388
|
+
.get(base:, offset: value, format: @cu.format)
|
389
|
+
value = @cu.file.section(".debug_str")[offset]
|
390
|
+
end
|
391
|
+
|
392
|
+
attrs[name] = value
|
393
|
+
end
|
394
|
+
attrs
|
395
|
+
end
|
396
|
+
|
397
|
+
def attr_value(name, form, value)
|
398
|
+
case name
|
399
|
+
when :encoding
|
400
|
+
return Types::Dwarf::Encoding[value]
|
401
|
+
when :data_member_location
|
402
|
+
if value.is_a?(String)
|
403
|
+
return Types::Dwarf::Expression.new(@cu, value)
|
404
|
+
else
|
405
|
+
return value
|
406
|
+
end
|
407
|
+
when :location
|
408
|
+
if value.is_a?(String)
|
409
|
+
return Types::Dwarf::Expression.new(@cu, value)
|
410
|
+
else
|
411
|
+
return value
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
case form
|
416
|
+
when :addrx
|
417
|
+
return @cu.file.section(".debug_addr")[@cu.addr_base + value]
|
418
|
+
when :ref1, :ref2, :ref4, :ref8
|
419
|
+
return @cu.die(value + @cu.offset)
|
420
|
+
when :strp
|
421
|
+
return @cu.file.section(".debug_str")[value]
|
422
|
+
when :strx1
|
423
|
+
# if we're working on the compile unit, we need the unparsed
|
424
|
+
# :str_offsets_base in this DIE to resolve str offset values. For any
|
425
|
+
# other DIE, we can use the parsed compile unit to get the offset.
|
426
|
+
str_offsets, base = if @tag == :compile_unit
|
427
|
+
@str_offsets_base = @unpacked_attrs.find do |name, form, value|
|
428
|
+
next unless name == :str_offsets_base
|
429
|
+
break value
|
430
|
+
end or raise Error, ":str_offsets_base not found in %p" % [self]
|
431
|
+
[@cu.file.section(".debug_str_offsets"), @str_offsets_base]
|
432
|
+
else
|
433
|
+
@cu.debug_str_offsets
|
434
|
+
end
|
435
|
+
offset = str_offsets.get(base:, offset: value, format: @cu.format)
|
436
|
+
return @cu.file.section(".debug_str")[offset]
|
437
|
+
end
|
438
|
+
|
439
|
+
value
|
440
|
+
end
|
441
|
+
|
442
|
+
def int_type(size:, signed:)
|
443
|
+
@endian ||= @cu.endian
|
444
|
+
|
445
|
+
case size
|
446
|
+
when 0
|
447
|
+
CTypes::String.new(size: 0)
|
448
|
+
when 1
|
449
|
+
signed ? CTypes::Int8 : CTypes::UInt8
|
450
|
+
when 2
|
451
|
+
signed ? CTypes::Int16 : CTypes::UInt16
|
452
|
+
when 4
|
453
|
+
signed ? CTypes::Int32 : CTypes::UInt32
|
454
|
+
when 8
|
455
|
+
signed ? CTypes::Int64 : CTypes::UInt64
|
456
|
+
when 16
|
457
|
+
CTypes::Array.new(type: CTypes::UInt64, size: 2)
|
458
|
+
when 32
|
459
|
+
CTypes::Array.new(type: CTypes::UInt64, size: 4)
|
460
|
+
else
|
461
|
+
raise "unsupported base_type size %d in: %p" % [size, self]
|
462
|
+
end.with_endian(@endian)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
require_relative "die/base"
|
468
|
+
require_relative "die_ref"
|
469
|
+
require_relative "debug_str_ref"
|
470
|
+
require_relative "debug_str_offsets_ref"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
class Section::DebugInfo::DieRef
|
3
|
+
def initialize(cu, offset)
|
4
|
+
@cu = cu
|
5
|
+
@offset = offset
|
6
|
+
end
|
7
|
+
|
8
|
+
def deref
|
9
|
+
@die ||= @cu.die(@offset + @cu.offset)
|
10
|
+
end
|
11
|
+
|
12
|
+
def inspect
|
13
|
+
if (die = deref)
|
14
|
+
"<%p cu=0x%x, offset=0x%08x, die=(0x%08x %s)>" %
|
15
|
+
[self.class, @cu.offset, @offset, die.offset, die.tag]
|
16
|
+
else
|
17
|
+
"<%p cu=0x%x, offset=0x%x, die=:invalid>" %
|
18
|
+
[self.class, @cu.offset, @offset]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
class Section::DebugInfo::Header < CTypes::Union
|
3
|
+
extend Forwardable
|
4
|
+
include CTypes::Helpers
|
5
|
+
|
6
|
+
layout do
|
7
|
+
member :unit, Types::Dwarf::UnitHeader
|
8
|
+
member :dwarf32_v2, Types::Dwarf32::V2::CU_Header
|
9
|
+
member :dwarf32_v3, Types::Dwarf32::V3::CU_Header
|
10
|
+
member :dwarf32_v4, Types::Dwarf32::V4::CU_Header
|
11
|
+
member :dwarf32_v5, Types::Dwarf32::V5::CU_Header
|
12
|
+
member :dwarf64_v3, Types::Dwarf64::V3::CU_Header
|
13
|
+
member :dwarf64_v4, Types::Dwarf64::V4::CU_Header
|
14
|
+
member :dwarf64_v5, Types::Dwarf64::V5::CU_Header
|
15
|
+
|
16
|
+
size { |header| header[:unit].unit_size }
|
17
|
+
end
|
18
|
+
def_delegators :unit, :format, :version
|
19
|
+
|
20
|
+
def inner
|
21
|
+
# PERF: we're caching this here because the Union type will do a bunch of
|
22
|
+
# extra work if we keep accessing the union via different members.
|
23
|
+
@inner ||= send(:"#{format}_v#{version}").freeze
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
class Section::DebugInfo < Section::Base
|
3
|
+
def compilation_units
|
4
|
+
@cus ||= begin
|
5
|
+
offset = 0
|
6
|
+
Header.with_endian(@file.endian).unpack_all(bytes).map do |header|
|
7
|
+
# PERF: we freeze the header to prevent CTypes::Union from
|
8
|
+
# re-encoding the header when accessing the inner value. This is
|
9
|
+
# the common performance work-around for union change tracking.
|
10
|
+
#
|
11
|
+
# The proper fix is probably to break up the convenience Header union
|
12
|
+
# and just parse the unit header directly, then parse the proper
|
13
|
+
# header version.
|
14
|
+
header.freeze
|
15
|
+
cu = CompilationUnit.new(@file, offset, header.inner)
|
16
|
+
offset += (cu.format == :dwarf32) ? 4 : 12
|
17
|
+
offset += cu.size
|
18
|
+
cu
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
alias_method :cus, :compilation_units
|
23
|
+
|
24
|
+
# get a compilation unit
|
25
|
+
def compilation_unit(arg)
|
26
|
+
# XXX do this in a way where we don't need to load all the CUs
|
27
|
+
case arg
|
28
|
+
when String
|
29
|
+
cus.find { |cu| cu.root_die[:name] == arg }
|
30
|
+
when Integer
|
31
|
+
cus.find { |cu| cu.offset == arg }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
alias_method :cu, :compilation_unit
|
35
|
+
|
36
|
+
# return the CompilationUnit that contains the provided offset
|
37
|
+
def compilation_unit_for_offset(offset)
|
38
|
+
cus.find { |cu| cu.die_range.include?(offset) }
|
39
|
+
end
|
40
|
+
alias_method :cu_for_offset, :compilation_unit_for_offset
|
41
|
+
|
42
|
+
# get a enumerator to iterate through the {Die}s present in all
|
43
|
+
# {CompilationUnit}s
|
44
|
+
#
|
45
|
+
# By default, the Enumerator returned by this method will yield every
|
46
|
+
# {Die} in every {CompilationUnit} in the order they were declared in. If
|
47
|
+
# `top` is set to true, the Enumerator will instead only yield those {Die}s
|
48
|
+
# that are children of the root {Die} in each {CompilationUnit}.
|
49
|
+
#
|
50
|
+
# @param top [Boolean] set to true to only return the children of the root
|
51
|
+
# die
|
52
|
+
# @param root [Boolean] set to true to yield the root DIE of each
|
53
|
+
# CompilationUnit prior to its children
|
54
|
+
def dies(top: false, root: false)
|
55
|
+
Enumerator.new do |yielder|
|
56
|
+
cus.each do |cu|
|
57
|
+
if top
|
58
|
+
yielder << cu.root_die if root
|
59
|
+
cu.root_die.children.each { |die| yielder << die }
|
60
|
+
else
|
61
|
+
cu.dies.each { |die| yielder << die }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def die(offset)
|
68
|
+
cu = compilation_unit_for_offset(offset) or
|
69
|
+
raise Error, "No CU found containing offset %x" % offset
|
70
|
+
cu.die(offset)
|
71
|
+
end
|
72
|
+
|
73
|
+
def type(name)
|
74
|
+
compilation_units.each do |cu|
|
75
|
+
type = cu.type(name)
|
76
|
+
return type if type
|
77
|
+
end
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def type_die(name)
|
82
|
+
compilation_units.each do |cu|
|
83
|
+
cu.dies.each do |die|
|
84
|
+
return die if die.type_name == name
|
85
|
+
end
|
86
|
+
end
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
require_relative "debug_info/header"
|
93
|
+
require_relative "debug_info/compilation_unit"
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
class Section::DebugLine::LineNumberProgram::Header < CTypes::Union
|
3
|
+
extend Forwardable
|
4
|
+
include CTypes::Helpers
|
5
|
+
|
6
|
+
# struct used for `directory_entry_format` and `file_name_entry_format` in
|
7
|
+
# DWARF v5.
|
8
|
+
EntryFormat = CTypes::Helpers.struct(
|
9
|
+
type: Types::Dwarf::EntryFormatType,
|
10
|
+
form: Types::Dwarf::Form
|
11
|
+
)
|
12
|
+
|
13
|
+
# entry format for `include_directories` prior to DWARF v5
|
14
|
+
INCLUDE_DIRECTORIES_FORMAT = [EntryFormat.new(type: :path, form: :string)]
|
15
|
+
|
16
|
+
# entry format for `file_names` prior for DWARF v5
|
17
|
+
FILE_NAMES_FORMAT = [
|
18
|
+
EntryFormat.new(type: :path, form: :string),
|
19
|
+
EntryFormat.new(type: :directory_index, form: :udata),
|
20
|
+
EntryFormat.new(type: :timestamp, form: :udata),
|
21
|
+
EntryFormat.new(type: :size, form: :udata)
|
22
|
+
]
|
23
|
+
|
24
|
+
layout do
|
25
|
+
member :unit, Types::Dwarf::UnitHeader
|
26
|
+
member :dwarf32_v2, Types::Dwarf32::V2::LineNumberProgramHeader
|
27
|
+
member :dwarf32_v3, Types::Dwarf32::V3::LineNumberProgramHeader
|
28
|
+
member :dwarf32_v4, Types::Dwarf32::V4::LineNumberProgramHeader
|
29
|
+
member :dwarf32_v5, Types::Dwarf32::V5::LineNumberProgramHeader
|
30
|
+
member :dwarf64_v3, Types::Dwarf64::V3::LineNumberProgramHeader
|
31
|
+
member :dwarf64_v4, Types::Dwarf64::V4::LineNumberProgramHeader
|
32
|
+
member :dwarf64_v5, Types::Dwarf64::V5::LineNumberProgramHeader
|
33
|
+
|
34
|
+
size do |union|
|
35
|
+
type = union[:unit].type
|
36
|
+
header = union[type]
|
37
|
+
header.class.offsetof(:minimum_instruction_length) +
|
38
|
+
header[:header_length]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
def_delegators :inner, :version, :type
|
42
|
+
def_delegators :unit, :format, :addr_type
|
43
|
+
|
44
|
+
def inner
|
45
|
+
@inner = send(unit.type)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|