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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.standard.yml +3 -0
  4. data/CODE_OF_CONDUCT.md +132 -0
  5. data/CONTRIBUTING.md +55 -0
  6. data/Gemfile +23 -0
  7. data/LICENSE.txt +21 -0
  8. data/MAINTAINERS.md +3 -0
  9. data/README.md +126 -0
  10. data/Rakefile +76 -0
  11. data/SECURITY.md +57 -0
  12. data/elf_utils.gemspec +41 -0
  13. data/ext/elf_utils/elf_utils.c +53 -0
  14. data/ext/elf_utils/extconf.rb +3 -0
  15. data/lib/elf_utils/elf_file.rb +312 -0
  16. data/lib/elf_utils/section/base.rb +77 -0
  17. data/lib/elf_utils/section/debug_abbrev/abbreviation.rb +171 -0
  18. data/lib/elf_utils/section/debug_abbrev/abbreviation_table.rb +27 -0
  19. data/lib/elf_utils/section/debug_abbrev.rb +15 -0
  20. data/lib/elf_utils/section/debug_addr.rb +9 -0
  21. data/lib/elf_utils/section/debug_arange.rb +54 -0
  22. data/lib/elf_utils/section/debug_info/compilation_unit.rb +189 -0
  23. data/lib/elf_utils/section/debug_info/debug_str_offsets_ref.rb +15 -0
  24. data/lib/elf_utils/section/debug_info/debug_str_ref.rb +17 -0
  25. data/lib/elf_utils/section/debug_info/die/base.rb +130 -0
  26. data/lib/elf_utils/section/debug_info/die.rb +470 -0
  27. data/lib/elf_utils/section/debug_info/die_ref.rb +22 -0
  28. data/lib/elf_utils/section/debug_info/header.rb +26 -0
  29. data/lib/elf_utils/section/debug_info.rb +93 -0
  30. data/lib/elf_utils/section/debug_line/line_number_program/header.rb +48 -0
  31. data/lib/elf_utils/section/debug_line/line_number_program/state_machine.rb +206 -0
  32. data/lib/elf_utils/section/debug_line/line_number_program.rb +134 -0
  33. data/lib/elf_utils/section/debug_line.rb +35 -0
  34. data/lib/elf_utils/section/debug_ranges.rb +22 -0
  35. data/lib/elf_utils/section/debug_str_offsets.rb +16 -0
  36. data/lib/elf_utils/section/dynsym.rb +14 -0
  37. data/lib/elf_utils/section/strtab.rb +9 -0
  38. data/lib/elf_utils/section/symtab.rb +11 -0
  39. data/lib/elf_utils/section.rb +50 -0
  40. data/lib/elf_utils/segment/base.rb +72 -0
  41. data/lib/elf_utils/segment.rb +9 -0
  42. data/lib/elf_utils/string_pread.rb +18 -0
  43. data/lib/elf_utils/symbol.rb +144 -0
  44. data/lib/elf_utils/types/dwarf/expression.rb +34 -0
  45. data/lib/elf_utils/types/dwarf.rb +639 -0
  46. data/lib/elf_utils/types/dwarf32/v2.rb +44 -0
  47. data/lib/elf_utils/types/dwarf32/v3.rb +40 -0
  48. data/lib/elf_utils/types/dwarf32/v4.rb +41 -0
  49. data/lib/elf_utils/types/dwarf32/v5.rb +44 -0
  50. data/lib/elf_utils/types/dwarf32.rb +12 -0
  51. data/lib/elf_utils/types/dwarf64/v3.rb +42 -0
  52. data/lib/elf_utils/types/dwarf64/v4.rb +43 -0
  53. data/lib/elf_utils/types/dwarf64/v5.rb +46 -0
  54. data/lib/elf_utils/types/dwarf64.rb +8 -0
  55. data/lib/elf_utils/types/sleb128.rb +66 -0
  56. data/lib/elf_utils/types/uleb128.rb +56 -0
  57. data/lib/elf_utils/types/unit_length.rb +51 -0
  58. data/lib/elf_utils/types.rb +328 -0
  59. data/lib/elf_utils/version.rb +5 -0
  60. data/lib/elf_utils.rb +83 -0
  61. data/sig/elf_utils.rbs +4 -0
  62. 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