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,312 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require_relative "string_pread"
|
3
|
+
|
4
|
+
module ElfUtils
|
5
|
+
# Read ELF and DWARF data from any class that provides a #pread method.
|
6
|
+
#
|
7
|
+
# @example dump the symbols found in an ELF file
|
8
|
+
# ElfUtils::ElfFile.open("spec/data/complex_64be-dwarf64-v5") do |elf_file|
|
9
|
+
# pp elf_file.symbols
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# @example read complex structure from running process
|
13
|
+
# # open an ELF file, in this case an executable
|
14
|
+
# elf_file = ElfUtils.open("a.out")
|
15
|
+
#
|
16
|
+
# # get the Symbol instance for a global variable
|
17
|
+
# symbol = elf_file.symbol(:some_global_var) # => #<ElfUtils::Symbol ...>
|
18
|
+
#
|
19
|
+
# # get the address of the symbol
|
20
|
+
# addr = symbol.addr # => 0x40001000
|
21
|
+
#
|
22
|
+
# # relocate the load segments to account for ASLR. Addresses for load segments
|
23
|
+
# # gleaned manually from /proc/pid/map.
|
24
|
+
# elf_file.load_segments[0].relocate(text_addr)
|
25
|
+
# elf_file.load_segments[1].relocate(data_addr)
|
26
|
+
#
|
27
|
+
# # get the address of the symbol after relocation
|
28
|
+
# addr = symbol.addr # => 0x90301000
|
29
|
+
#
|
30
|
+
# # get the data type for the symbol; requires DWARF debug information
|
31
|
+
# type = symbol.ctype # => #<CTypes::Struct ...>
|
32
|
+
#
|
33
|
+
# # open the memory for a process running this executable, and read the value of
|
34
|
+
# # the global variable.
|
35
|
+
# value = File.open(File.join("/proc", pid, "mem")) do |mem|
|
36
|
+
# # read the raw bytes for the variable
|
37
|
+
# bytes = mem.pread(type.size, addr) # => "\xef\x99\xde... "
|
38
|
+
#
|
39
|
+
# # unpack the bytes
|
40
|
+
# type.unpack_one(bytes) # => { field: val, ... }
|
41
|
+
# end
|
42
|
+
class ElfFile
|
43
|
+
extend Forwardable
|
44
|
+
using StringPread
|
45
|
+
|
46
|
+
# open a file path, and return an ElfFile instance
|
47
|
+
# @overload open(path)
|
48
|
+
# @param path [String] file path
|
49
|
+
# @return [ElfFile]
|
50
|
+
#
|
51
|
+
# @overload open(path)
|
52
|
+
# @param path [String] file path
|
53
|
+
# @yield [ElfFile] invokes block with opened file, will close when block
|
54
|
+
# returns
|
55
|
+
#
|
56
|
+
# @example
|
57
|
+
# elf_file = ElfFile.open("spec/data/complex_64be-dwarf64-v5")
|
58
|
+
# pp(elf_file.symbols)
|
59
|
+
# elf_file.close
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# ElfFile.open("spec/data/complex_64be-dwarf64-v5") do |elf_file|
|
63
|
+
# pp(elf_file.symbols)
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
def self.open(path)
|
67
|
+
if block_given?
|
68
|
+
File.open(path) do |f|
|
69
|
+
yield new(f)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
new(File.open(path))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# create an instance of ElfFile
|
77
|
+
# @param io [IO, String, #pread] Anything that provides
|
78
|
+
# the #pread(max_len, offset) method
|
79
|
+
def initialize(io)
|
80
|
+
io.force_encoding("ASCII-8BIT") if io.is_a?(String)
|
81
|
+
@io = io
|
82
|
+
@header = load_header(io)
|
83
|
+
@type_prefix = (elf_class == :elf32) ? "Elf32" : "Elf64"
|
84
|
+
end
|
85
|
+
|
86
|
+
# @api private
|
87
|
+
attr_reader :header
|
88
|
+
|
89
|
+
# return the path of the ElfFile
|
90
|
+
# @return [String, nil] path of the ElfFile
|
91
|
+
def path
|
92
|
+
@io.path if @io.respond_to?(:path)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Return the ELF class according to the ident in the header
|
96
|
+
# @return [Symbol] :elf32 or :elf64
|
97
|
+
def elf_class
|
98
|
+
case @header.e_ident.ei_class
|
99
|
+
when Types::ELFCLASS32
|
100
|
+
:elf32
|
101
|
+
when Types::ELFCLASS64
|
102
|
+
:elf64
|
103
|
+
else
|
104
|
+
raise InvalidFormat, "Unsupported ELF ident.ei_class: %p", @header
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# get the endian according to the ELF header
|
109
|
+
# @return [Symbol] :big or :little
|
110
|
+
def endian
|
111
|
+
case @header.e_ident.ei_data
|
112
|
+
when :lsb
|
113
|
+
:little
|
114
|
+
when :msb
|
115
|
+
:big
|
116
|
+
else
|
117
|
+
raise "Invalid EI_DATA value in ELF header ident: %p", @header
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# check if this is a relocatable ELF file
|
122
|
+
# @return [Boolean] true if the file is relocatable
|
123
|
+
def relocatable?
|
124
|
+
@header.e_type == :rel
|
125
|
+
end
|
126
|
+
|
127
|
+
# Get the segments
|
128
|
+
# @return [Array<Segment::Base>] segments present in ELF file
|
129
|
+
def segments
|
130
|
+
@segments ||= pread([:Phdr, @header.e_phnum], @header.e_phoff).map do |hdr|
|
131
|
+
Segment.from_header(self, hdr)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Return the list of load segments
|
136
|
+
# @return [Array<Segment::Base>] load segments present in ELF file
|
137
|
+
def load_segments
|
138
|
+
segments.select { |s| s.type == :load }
|
139
|
+
end
|
140
|
+
|
141
|
+
# Return the .shstrtab section
|
142
|
+
# @return [Section]
|
143
|
+
def shstrtab
|
144
|
+
# XXX we end up with a duplicate copy of this section, but we need to
|
145
|
+
# get the shstrtab while building the full sections list
|
146
|
+
@shstrtab ||= begin
|
147
|
+
hdr_class = elf_type(:Shdr)
|
148
|
+
offset = hdr_class.size * @header.e_shstrndx
|
149
|
+
hdr = pread(hdr_class, @header.e_shoff + offset)
|
150
|
+
Section.from_header(self, hdr)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Return the .strtab section
|
155
|
+
# @return [Section::Strtab]
|
156
|
+
def strtab
|
157
|
+
section(".strtab")
|
158
|
+
end
|
159
|
+
|
160
|
+
# return the .symtab section
|
161
|
+
# @return [Section::Symtab]
|
162
|
+
def symtab
|
163
|
+
@symtab ||= section(".symtab") || section(".dynsym")
|
164
|
+
end
|
165
|
+
|
166
|
+
# return the .symtab section
|
167
|
+
# @return [Section::DebugInfo]
|
168
|
+
def debug_info
|
169
|
+
section(".debug_info")
|
170
|
+
end
|
171
|
+
|
172
|
+
# get the sections present in the ELF file
|
173
|
+
# @return [Array<Section::Base>]
|
174
|
+
def sections
|
175
|
+
@sections ||= pread([:Shdr, @header.e_shnum], @header.e_shoff)
|
176
|
+
.map do |hdr|
|
177
|
+
Section.from_header(self, hdr)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# lookup a section by name
|
182
|
+
# @return [Section::Base, nil] section instance if found, nil otherwise
|
183
|
+
def section(name)
|
184
|
+
@sections_by_name ||= sections.each_with_object({}) do |section, cache|
|
185
|
+
cache[section.name] = section
|
186
|
+
end
|
187
|
+
@sections_by_name[name]
|
188
|
+
end
|
189
|
+
|
190
|
+
# return the list of symbols found in the ELF file
|
191
|
+
# @return [Array<Symbol>]
|
192
|
+
def symbols
|
193
|
+
symtab&.symbols || []
|
194
|
+
end
|
195
|
+
|
196
|
+
# lookup a symbol by name
|
197
|
+
# @param [String, Symbol] symbol name
|
198
|
+
# @return [Symbol, name]
|
199
|
+
def symbol(name)
|
200
|
+
name = name.to_s
|
201
|
+
symbols.find { |s| s.name == name }
|
202
|
+
end
|
203
|
+
|
204
|
+
# lookup a symbol by memory address
|
205
|
+
# @param [Integer] in-memory address
|
206
|
+
# @return [Symbol, name]
|
207
|
+
def symbol_at_addr(addr)
|
208
|
+
symbols.find do |sym|
|
209
|
+
next unless sym.section&.alloc?
|
210
|
+
sym.to_range.include?(addr)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Get the ctypes datatype for a type defined in the .debug_info section
|
215
|
+
# @param name [String] name of type
|
216
|
+
#
|
217
|
+
# @example lookup a structure type by name from .debug_info section
|
218
|
+
# ElfFile.open("spec/data/complex_64be-dwarf64-v5") do |elf_file|
|
219
|
+
# elf_file.type("struct tlv")
|
220
|
+
# end # => #<CTypes::Struct ... >
|
221
|
+
def type(name)
|
222
|
+
raise Error, "File does not contain .debug_info section: #{path}" unless
|
223
|
+
(debug_info = section(".debug_info"))
|
224
|
+
debug_info.type(name)
|
225
|
+
end
|
226
|
+
alias_method :ctype, :type
|
227
|
+
|
228
|
+
# pread wrapper providing support for type lookup by name
|
229
|
+
# @api private
|
230
|
+
def pread(type_or_size, offset)
|
231
|
+
type, size = parse_type(type_or_size)
|
232
|
+
buf = @io.pread(size, offset)
|
233
|
+
return buf unless type
|
234
|
+
type.unpack(buf)
|
235
|
+
end
|
236
|
+
|
237
|
+
# lookup the ctypes datatype for this file's bitsize (ELF32 vs ELF64)
|
238
|
+
# @param name [Symbol, String] name of type
|
239
|
+
# @see Types
|
240
|
+
# @api private
|
241
|
+
def elf_type(name)
|
242
|
+
Types.const_get("#{@type_prefix}_#{name}").with_endian(endian)
|
243
|
+
end
|
244
|
+
|
245
|
+
# get the ctype that represents an address for this ELF file
|
246
|
+
# @api private
|
247
|
+
def addr_type
|
248
|
+
elf_type(:Addr)
|
249
|
+
end
|
250
|
+
|
251
|
+
private
|
252
|
+
|
253
|
+
# load the correct header type from the file
|
254
|
+
# @api private
|
255
|
+
def load_header(io)
|
256
|
+
ident = pread(Types::Elf_Ident, 0)
|
257
|
+
raise InvalidFormat, "Invalid ELF ident: %p" % ident unless
|
258
|
+
ident.ei_magic == Types::ELFMAGIC
|
259
|
+
endian = case ident.ei_data
|
260
|
+
when :lsb
|
261
|
+
:little
|
262
|
+
when :msb
|
263
|
+
:big
|
264
|
+
else
|
265
|
+
raise "Invalid EI_DATA value in ELF header ident: %p", @header
|
266
|
+
end
|
267
|
+
|
268
|
+
case ident.ei_class
|
269
|
+
when Types::ELFCLASS32
|
270
|
+
pread(Types::Elf32_Ehdr.with_endian(endian), 0)
|
271
|
+
when Types::ELFCLASS64
|
272
|
+
pread(Types::Elf64_Ehdr.with_endian(endian), 0)
|
273
|
+
else
|
274
|
+
raise InvalidFormat, "Unsupported ELF ident.ei_class: %p",
|
275
|
+
ident
|
276
|
+
end
|
277
|
+
rescue Dry::Types::ConstraintError
|
278
|
+
raise InvalidFormat, "Invalid ELF header"
|
279
|
+
end
|
280
|
+
|
281
|
+
# Return a type & size given a type or size argument; used by pread()
|
282
|
+
# @api private
|
283
|
+
def parse_type(type)
|
284
|
+
case type
|
285
|
+
when Integer
|
286
|
+
[nil, type] # just a plain old size
|
287
|
+
when CTypes::Type
|
288
|
+
[type, type.size]
|
289
|
+
when ::Symbol
|
290
|
+
# This is an elf type we'll need to conver to the correct size
|
291
|
+
type = elf_type(type)
|
292
|
+
[type, type.size]
|
293
|
+
when Array
|
294
|
+
if type.size != 2
|
295
|
+
raise Error, "array type must be of the form `[type, count]`; got %p" %
|
296
|
+
[type]
|
297
|
+
end
|
298
|
+
|
299
|
+
type, count = type
|
300
|
+
inner, _ = parse_type(type)
|
301
|
+
type = CTypes::Helpers
|
302
|
+
.array(inner, count).with_endian(inner.default_endian)
|
303
|
+
[type, type.size]
|
304
|
+
else
|
305
|
+
raise Error, "unsupported type: %p" % [type]
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
require_relative "section"
|
312
|
+
require_relative "segment"
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
class Section::Base
|
3
|
+
def initialize(file, header)
|
4
|
+
@file = file
|
5
|
+
@header = header
|
6
|
+
@offset = 0
|
7
|
+
end
|
8
|
+
attr_reader :header
|
9
|
+
|
10
|
+
def name
|
11
|
+
@file.shstrtab[@header.sh_name]
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"<%p %s %p 0x%08x %p>" % [self.class, name, @header.sh_type, addr, flags]
|
16
|
+
end
|
17
|
+
|
18
|
+
def bytes
|
19
|
+
@file.pread(@header.sh_size, @header.sh_offset)
|
20
|
+
end
|
21
|
+
|
22
|
+
def addr
|
23
|
+
@header.sh_addr + @offset
|
24
|
+
end
|
25
|
+
|
26
|
+
def size
|
27
|
+
@header.sh_size
|
28
|
+
end
|
29
|
+
|
30
|
+
def flags
|
31
|
+
@header.sh_flags
|
32
|
+
end
|
33
|
+
|
34
|
+
def offset
|
35
|
+
@header.sh_offset
|
36
|
+
end
|
37
|
+
|
38
|
+
def alloc?
|
39
|
+
flags.include?(:alloc)
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_range
|
43
|
+
(addr...addr + size)
|
44
|
+
end
|
45
|
+
|
46
|
+
def symbols
|
47
|
+
@file.symtab.symbols.select { |s| s.section == self }
|
48
|
+
end
|
49
|
+
|
50
|
+
def symbol(name)
|
51
|
+
@file.symtab.symbols.find { |s| s.section == self && s.name == name }
|
52
|
+
end
|
53
|
+
|
54
|
+
# relocate this section
|
55
|
+
#
|
56
|
+
# @param addr address for section; `nil` clears relocation
|
57
|
+
def relocate(addr, relative: false)
|
58
|
+
@offset = if addr.nil?
|
59
|
+
0
|
60
|
+
elsif relative
|
61
|
+
addr
|
62
|
+
else
|
63
|
+
addr - @header.sh_addr
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# return the relocation offset for this section
|
68
|
+
def relocation_offset
|
69
|
+
@offset
|
70
|
+
end
|
71
|
+
|
72
|
+
# get the load_segment this section belongs to
|
73
|
+
def load_segment
|
74
|
+
@file.load_segments.find { |s| s.sections.include?(self) }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module ElfUtils
|
4
|
+
class Section::DebugAbbrev::Abbreviation
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
class UnsupportedDwarfAttributeFormError < ElfUtils::Error; end
|
8
|
+
|
9
|
+
def initialize(table, decl)
|
10
|
+
@table = table
|
11
|
+
@decl = decl
|
12
|
+
end
|
13
|
+
attr_reader :decl
|
14
|
+
def_delegators :@decl, :code, :tag
|
15
|
+
|
16
|
+
def pretty_print(q)
|
17
|
+
q.group(4,
|
18
|
+
"<%p [%d] DW_TAG_%s DW_CHILDREN_%s" %
|
19
|
+
[self.class, code, tag, children? ? "yes" : "no"],
|
20
|
+
">") do
|
21
|
+
q.text("\n" + " " * q.indent)
|
22
|
+
q.seplist(@decl.attribute_specifications) do |attr|
|
23
|
+
q.text("DW_AT_%-15s DW_FORM_%s" % [attr.name, attr.form])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
alias_method :inspect, :pretty_inspect
|
28
|
+
|
29
|
+
def children?
|
30
|
+
@decl.children == 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def attrs
|
34
|
+
@attrs ||= begin
|
35
|
+
attrs = {}
|
36
|
+
@decl.attribute_specifications.each do |attr|
|
37
|
+
attrs[attr.name] = attr.form
|
38
|
+
end
|
39
|
+
attrs.freeze
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def die_size(addr_size:, offset_size:)
|
44
|
+
return nil if @variable_size
|
45
|
+
|
46
|
+
size = 0
|
47
|
+
@decl.attribute_specifications.each do |attr|
|
48
|
+
if (s = form_size(form: attr.form, addr_size:, offset_size:))
|
49
|
+
size += s
|
50
|
+
else
|
51
|
+
@variable_size = true
|
52
|
+
return nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
size
|
56
|
+
end
|
57
|
+
|
58
|
+
def variable_sized_attrs(addr_size:, offset_size:)
|
59
|
+
out = []
|
60
|
+
@decl.attribute_specifications.each do |attr|
|
61
|
+
next if form_size(form: attr.form, addr_size:, offset_size:)
|
62
|
+
out << attr
|
63
|
+
end
|
64
|
+
out
|
65
|
+
end
|
66
|
+
|
67
|
+
def unpack_die(buf:, endian:, offset_type:, addr_type:)
|
68
|
+
out = []
|
69
|
+
@decl.attribute_specifications.each do |attr|
|
70
|
+
value, buf = case attr.form
|
71
|
+
when :addr
|
72
|
+
addr_type.unpack_one(buf, endian: endian)
|
73
|
+
when :strp, :sec_offset
|
74
|
+
offset_type.unpack_one(buf, endian: endian)
|
75
|
+
when :data1, :flag, :ref1, :strx1
|
76
|
+
CTypes::UInt8.unpack_one(buf, endian: endian)
|
77
|
+
when :data2, :ref2
|
78
|
+
CTypes::UInt16.unpack_one(buf, endian: endian)
|
79
|
+
when :data4, :ref4
|
80
|
+
CTypes::UInt32.unpack_one(buf, endian: endian)
|
81
|
+
when :data8, :ref8
|
82
|
+
CTypes::UInt64.unpack_one(buf, endian: endian)
|
83
|
+
when :udata, :addrx
|
84
|
+
Types::ULEB128.unpack_one(buf)
|
85
|
+
when :sdata
|
86
|
+
Types::SLEB128.unpack_one(buf)
|
87
|
+
when :string
|
88
|
+
CTypes::String.terminated.unpack_one(buf, endian: endian)
|
89
|
+
when :block1
|
90
|
+
len, buf = CTypes::UInt8.unpack_one(buf, endian: endian)
|
91
|
+
[buf.byteslice(0, len), buf.byteslice(len..)]
|
92
|
+
when :block2
|
93
|
+
len, buf = CTypes::UInt16.unpack_one(buf, endian: endian)
|
94
|
+
[buf.byteslice(0, len), buf.byteslice(len..)]
|
95
|
+
when :exprloc
|
96
|
+
len, buf = Types::ULEB128.unpack_one(buf)
|
97
|
+
[buf.byteslice(0, len), buf.byteslice(len..)]
|
98
|
+
when :loclistx
|
99
|
+
Types::ULEB128.unpack_one(buf)
|
100
|
+
when :flag_present
|
101
|
+
[1, buf]
|
102
|
+
else
|
103
|
+
raise UnsupportedDwarfAttributeFormError,
|
104
|
+
"unsupported DWARF form: %p" % [attr.form]
|
105
|
+
end
|
106
|
+
|
107
|
+
out << [attr.name, attr.form, value]
|
108
|
+
end
|
109
|
+
[out, buf]
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
# return the size of a given dwarf attribute form
|
115
|
+
def form_size(form:, addr_size:, offset_size:)
|
116
|
+
case form
|
117
|
+
when :addr
|
118
|
+
addr_size
|
119
|
+
when :data1, :ref1
|
120
|
+
1
|
121
|
+
when :data2, :ref2
|
122
|
+
2
|
123
|
+
when :data4, :ref4
|
124
|
+
4
|
125
|
+
when :data8, :ref8
|
126
|
+
8
|
127
|
+
when :flag
|
128
|
+
1
|
129
|
+
when :strp
|
130
|
+
offset_size
|
131
|
+
when :ref_addr
|
132
|
+
addr_size
|
133
|
+
when :sec_offset
|
134
|
+
offset_size
|
135
|
+
when :flag_present
|
136
|
+
0
|
137
|
+
when :ref_sup4
|
138
|
+
4
|
139
|
+
when :strp_sup
|
140
|
+
offset_size
|
141
|
+
when :data16
|
142
|
+
16
|
143
|
+
when :line_strp
|
144
|
+
offset_size
|
145
|
+
when :ref_sig8
|
146
|
+
8
|
147
|
+
when :ref_sup8
|
148
|
+
8
|
149
|
+
when :strx1
|
150
|
+
1
|
151
|
+
when :strx2
|
152
|
+
2
|
153
|
+
when :strx3
|
154
|
+
3
|
155
|
+
when :strx4
|
156
|
+
4
|
157
|
+
when :addrx1
|
158
|
+
1
|
159
|
+
when :addrx2
|
160
|
+
2
|
161
|
+
when :addrx3
|
162
|
+
3
|
163
|
+
when :addrx4
|
164
|
+
4
|
165
|
+
else
|
166
|
+
# variable sized
|
167
|
+
nil
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class ElfUtils::Section::DebugAbbrev
|
2
|
+
class AbbreviationTable
|
3
|
+
def initialize(buf)
|
4
|
+
@declarations = {}
|
5
|
+
@abbrevs = {}
|
6
|
+
|
7
|
+
# XXX CTypes needs a terminated array where we check the remaining bytes
|
8
|
+
# before decoding.
|
9
|
+
until buf[0].ord == 0
|
10
|
+
decl, buf = ElfUtils::Types::Dwarf::AbbreviationDeclaration.unpack_one(buf)
|
11
|
+
@declarations[decl.code] = decl
|
12
|
+
@abbrevs[decl.code] = Abbreviation.new(self, decl)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
attr_reader :declarations
|
16
|
+
|
17
|
+
def [](code)
|
18
|
+
@abbrevs[code]
|
19
|
+
end
|
20
|
+
|
21
|
+
def entries
|
22
|
+
@abbrevs.values
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
require_relative "abbreviation"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
class Section::DebugAbbrev < Section::Base
|
3
|
+
def initialize(*args)
|
4
|
+
super
|
5
|
+
@abbreviation_tables = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def abbreviation_table(offset)
|
9
|
+
@abbreviation_tables[offset] ||= AbbreviationTable.new(bytes[offset..])
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require_relative "debug_abbrev/abbreviation_table"
|
15
|
+
require_relative "debug_abbrev/abbreviation"
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
class Section::DebugArange < Section::Base
|
3
|
+
extend CTypes::Helpers
|
4
|
+
|
5
|
+
Tuple32 = struct(addr: uint32, size: uint32)
|
6
|
+
Tuple64 = struct(addr: uint64, size: uint64)
|
7
|
+
|
8
|
+
class Entry < CTypes::Struct
|
9
|
+
layout do
|
10
|
+
attribute :length, uint32
|
11
|
+
attribute :version, uint16
|
12
|
+
attribute :debug_info_offset, uint32
|
13
|
+
attribute :addr_size, uint8
|
14
|
+
attribute :seg_desc_size, uint8
|
15
|
+
pad 4 # XXX not needed for dwarf64
|
16
|
+
attribute :tuple_bytes, string(trim: false)
|
17
|
+
size { |hdr| offsetof(:version) + hdr[:length] }
|
18
|
+
end
|
19
|
+
|
20
|
+
def tuples
|
21
|
+
@tuples ||= begin
|
22
|
+
type = (addr_size == 4) ? Tuple32 : Tuple64
|
23
|
+
type.unpack_all(self[:tuple_data], endian: @file.endian)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def entries
|
29
|
+
@entries ||= Entry.unpack_all(bytes, endian: @file.endian)
|
30
|
+
end
|
31
|
+
|
32
|
+
def ranges
|
33
|
+
@ranges ||= begin
|
34
|
+
ranges = []
|
35
|
+
entries.each do |entry|
|
36
|
+
tuple_type = (entry.addr_size == 4) ? Tuple32 : Tuple64
|
37
|
+
tuple_type
|
38
|
+
.unpack_all(entry.tuple_bytes, endian: @file.endian)
|
39
|
+
.each do |tuple|
|
40
|
+
next if tuple.addr == 0 && tuple.size == 0
|
41
|
+
range = tuple.addr...(tuple.addr + tuple.size)
|
42
|
+
ranges << [range, entry.debug_info_offset]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
ranges
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def cu_offset(addr)
|
50
|
+
ranges.each { |range, offset| return offset if range.include?(addr) }
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|