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,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
|