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,189 @@
1
+ require "forwardable"
2
+
3
+ module ElfUtils
4
+ class Section::DebugInfo::CompilationUnit
5
+ extend Forwardable
6
+ include CTypes::Helpers
7
+
8
+ ULEB128 = Types::ULEB128
9
+
10
+ def initialize(file, offset, header)
11
+ @file = file
12
+ @offset = offset
13
+ @header = header
14
+ @debug_str = @file.section(".debug_str")
15
+
16
+ @endian = @file.endian
17
+ @addr_size = header.addr_size
18
+ @addr_type = case header.addr_size
19
+ when 4
20
+ uint32.with_endian(@endian)
21
+ when 8
22
+ uint64.with_endian(@endian)
23
+ else
24
+ raise Error,
25
+ "unsupported address size in DWARF header: %p" % [@header]
26
+ end
27
+ @offset_type = (header.format == :dwarf32) ?
28
+ uint32.with_endian(@endian) :
29
+ uint64.with_endian(@endian)
30
+
31
+ @types = {}
32
+ end
33
+ attr_reader :offset, :addr_type, :offset_type, :file
34
+
35
+ def_delegators :@file, :endian
36
+ def_delegators :@header, :format, :version, :addr_size
37
+ def_delegator :@header, :unit_length, :size
38
+ def_delegator :@header, :debug_abbrev_offset, :abbr_offset
39
+
40
+ def inspect
41
+ fmt = <<~FMT.chomp
42
+ <%s offset=0x%x, size=%d, format=%p, version=%d, abbr_offset=0x%x, \
43
+ addr_size=%d>
44
+ FMT
45
+ fmt %
46
+ [self.class, offset, size, format, version, abbr_offset, addr_size]
47
+ end
48
+
49
+ def abbrev_table
50
+ @abbrev_table ||= file.section(".debug_abbrev")
51
+ .abbreviation_table(@header.debug_abbrev_offset)
52
+ end
53
+
54
+ def die_range
55
+ @range ||= begin
56
+ start = @offset + @header.class.size
57
+ start...start + @header.data.size
58
+ end
59
+ end
60
+
61
+ def root_die
62
+ dies.first
63
+ end
64
+
65
+ def die(offset)
66
+ @dies ||= split_dies
67
+ @dies[offset] or raise Error, "invalid DIE offset: 0x%x" % offset
68
+ end
69
+
70
+ def dies
71
+ @dies ||= split_dies
72
+ @dies.values
73
+ end
74
+
75
+ def types
76
+ unless @cached_all_types
77
+ dies.each do |die|
78
+ name = die.type_name or next
79
+ @types[name] ||= die.ctype
80
+ end
81
+ @cached_all_types = true
82
+ end
83
+ @types
84
+ end
85
+
86
+ def type(arg)
87
+ case arg
88
+ when Integer
89
+ _, t = die(arg).ctype
90
+ t
91
+ when String
92
+ # check the cache first
93
+ if (type = @types[arg])
94
+ return type
95
+ end
96
+
97
+ # then try finding the die with the type
98
+ dies.each do |die|
99
+ next unless die.type_name == arg
100
+ @types[arg] = die.ctype
101
+ end
102
+ @types[arg]
103
+ else
104
+ raise Error, "unsupported type lookup key: %p" % [arg]
105
+ end
106
+ end
107
+
108
+ # helper for {Types::Dwarf::Expression} to get values from the .debug_addr
109
+ # section.
110
+ # @param index [Integer] index into the .debug_addr section to be adjusted
111
+ # by the base for this CompilationUnit
112
+ def debug_addr_get!(index)
113
+ debug_addr = @file.section(".debug_addr") or
114
+ raise Error, "`.debug_addr` section not present in #{@file}"
115
+ debug_addr.get(base: root_die[:addr_base], index:)
116
+ end
117
+
118
+ # helper for DIEs to get easy access to .debug_str_offsets & base
119
+ def debug_str_offsets
120
+ @debug_str_offsets ||=
121
+ [@file.section(".debug_str_offsets"), root_die[:str_offsets_base]]
122
+ end
123
+
124
+ # helper for accessing DW_TAG_str_offsets_base in the CU DIE
125
+ def str_offsets_base
126
+ @str_offsets_base ||= root_die.raw_attr(:str_offsets_base)
127
+ end
128
+
129
+ # helper for accessing DW_TAG_addr_base in the CU DIE
130
+ def addr_base
131
+ @addr_base ||= root_die.raw_attr(:addr_base)
132
+ end
133
+
134
+ def include_addr?(addr)
135
+ root_die.addr_ranges&.any? { |r| r.include?(addr) }
136
+ end
137
+
138
+ private
139
+
140
+ def split_dies
141
+ endian = file.endian
142
+ addr_size = @addr_type.size
143
+ offset_size = @offset_type.size
144
+ abbrevs = {}
145
+
146
+ dies = {}
147
+ parents = []
148
+
149
+ buf = @header.data
150
+ offset = @offset + @header.class.size
151
+ last_size = buf.size
152
+
153
+ until buf.empty?
154
+ offset += last_size - buf.size
155
+ last_size = buf.size
156
+
157
+ code, buf = ElfUtils::Types::ULEB128.unpack_one(buf)
158
+ if code == 0
159
+ parents.pop
160
+ next
161
+ end
162
+
163
+ ab, tag, size, children = abbrevs[code] ||= begin
164
+ ab = abbrev_table[code]
165
+ [ab, ab.tag, ab.die_size(addr_size:, offset_size:), ab.children?]
166
+ end
167
+
168
+ die = if size
169
+ d = dies[offset] = Section::DebugInfo::Die
170
+ .new(cu: self, offset:, tag:, children:, abbrev: ab, buf:)
171
+ buf = buf.byteslice(size..)
172
+ d
173
+ else
174
+ attrs, buf = ab.unpack_die(buf:, endian:, offset_type:, addr_type:)
175
+ dies[offset] = Section::DebugInfo::Die
176
+ .new(cu: self, offset:, tag:, children:, abbrev: ab, attrs:)
177
+ end
178
+
179
+ dies[offset] = die
180
+
181
+ parents[-1].add_child(die) unless parents.empty?
182
+ parents.push(die) if children
183
+ end
184
+ dies
185
+ end
186
+ end
187
+ end
188
+
189
+ require_relative "die"
@@ -0,0 +1,15 @@
1
+ module ElfUtils
2
+ class Section::DebugInfo::DebugStrOffsetsRef
3
+ def initialize(cu, offset)
4
+ @cu = cu
5
+ @offset = offset
6
+ end
7
+
8
+ def deref
9
+ base = @cu.root_die.str_offsets_base
10
+ str_offset = @cu.file.section(".debug_str_offsets")
11
+ .get(base:, offset: @offset, format: @cu.format)
12
+ @cu.file.section(".debug_str")[str_offset]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module ElfUtils
2
+ class Section::DebugInfo::DebugStrRef
3
+ def initialize(cu, offset)
4
+ @cu = cu
5
+ @offset = offset
6
+ end
7
+
8
+ def deref
9
+ @str ||= @cu.file.section(".debug_str")[@offset]
10
+ end
11
+
12
+ def inspect
13
+ "<%p cu=0x%x, offset=0x%08x, str=%p>" %
14
+ [self.class, @cu.offset, @offset, deref]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,130 @@
1
+ module ElfUtils
2
+ class Section::DebugInfo::Die::Base
3
+ include CTypes::Helpers
4
+
5
+ def initialize(cu:, offset:, attrs:, has_children:)
6
+ @cu = cu
7
+ @offset = offset
8
+ @attrs = attrs
9
+ @children = [] if has_children
10
+ end
11
+ attr_reader :cu, :offset, :tag
12
+
13
+ def <<(child)
14
+ raise Error, "This DIE cannot have children" unless @children
15
+ @children << child
16
+ end
17
+
18
+ def children?
19
+ !!@children
20
+ end
21
+
22
+ def children
23
+ # to make it easier to walk a tree of DIEs, we're going to return an
24
+ # empty array for DIEs that don't have children
25
+ @children || []
26
+ end
27
+
28
+ def inspect
29
+ buf = "<%p\n 0x%08x: DW_TAG_%s (children=%p)\n" %
30
+ [self.class, @offset, tag, @children ? @children.size : false]
31
+ @attrs.each do |name, value|
32
+ buf << " DW_AT_%-15s %p\n" % [name, value]
33
+ end
34
+ buf.chomp! << ">"
35
+ end
36
+
37
+ def has_attr?(key)
38
+ @attrs.has_key?(key)
39
+ end
40
+
41
+ def type?
42
+ tag == :typedef || tag.to_s.end_with?("_type")
43
+ end
44
+
45
+ def ctype
46
+ return unless type?
47
+ return @ctype if @ctype
48
+
49
+ name, type = case tag
50
+ when :base_type
51
+ unsigned = @attrs[:encoding].to_s.include?("unsigned")
52
+ type = case @attrs[:byte_size]
53
+ when 1
54
+ unsigned ? uint8 : int8
55
+ when 2
56
+ unsigned ? uint16 : int16
57
+ when 4
58
+ unsigned ? uint32 : int32
59
+ when 8
60
+ unsigned ? uint64 : int64
61
+ else
62
+ raise "unsupported base_type size: %p" % die
63
+ end
64
+ [@attrs[:name].deref, type.with_endian(@cu.file.endian)]
65
+ when :volatile_type
66
+ name, type = @attrs[:type].deref.ctype
67
+ ["volatile #{name}", type]
68
+ when :typedef
69
+ _, type = @attrs[:type].deref.ctype
70
+ [@attrs[:name].deref, type]
71
+ when :array_type
72
+ name, inner_type = @attrs[:type].deref.ctype
73
+
74
+ subrange = children.find { |c| c.tag == :subrange_type }
75
+ size = if subrange.has_attr?(:count)
76
+ subrange.count
77
+ elsif subrange.has_attr?(:upper_bound)
78
+ subrange.upper_bound
79
+ end
80
+
81
+ # XXX need flexible array member test
82
+
83
+ # if we have a signed char array, we'll use that
84
+ if inner_type.is_a?(CTypes::Int) &&
85
+ inner_type.size == 1 &&
86
+ inner_type.signed?
87
+ ["#{name}[#{size}]", string(size)]
88
+ else
89
+ ["#{name}[#{size}]", array(inner_type, size)]
90
+ end
91
+ when :pointer_type
92
+ type = @attrs[:type].deref
93
+ name, = type.ctype
94
+ [(type.tag == :pointer_type) ? "#{name}*" : "#{name} *", @cu.addr_type]
95
+ when :structure_type
96
+ fields, = @children.inject([{}, 0]) do |(o, offset), child|
97
+ next [o, offset] unless child.tag == :member
98
+
99
+ pad = child.data_member_location - offset
100
+ o[:"__pad_#{offset}"] = string(pad, trim: false) if pad != 0
101
+
102
+ _, type = child.type.deref.ctype
103
+ o[child.name.deref.to_sym] = type
104
+ [o, child.data_member_location + type.size]
105
+ end
106
+
107
+ name = @attrs[:name]&.deref || ("anon_%x" % offset)
108
+
109
+ ["struct #{name}", struct(**fields)]
110
+ when :union_type
111
+ layout = @children.each_with_object({}) do |child, o|
112
+ next o unless child.tag == :member
113
+
114
+ _, type = child.type.deref.ctype
115
+ o[child.name.deref.to_sym] = type
116
+ end
117
+
118
+ name = @attrs[:name]&.deref || ("anon_%x" % offset)
119
+
120
+ ["union #{name}", union(**layout)]
121
+ when :subrange_type, :subroutine_type
122
+ return
123
+ else
124
+ raise Error, "unsupported DIE: %p" % [die]
125
+ end
126
+
127
+ @ctype = [name, type]
128
+ end
129
+ end
130
+ end