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,206 @@
|
|
1
|
+
class ElfUtils::Section::DebugLine
|
2
|
+
class LineNumberProgram::StateMachine
|
3
|
+
include CTypes::Helpers
|
4
|
+
|
5
|
+
StandardOpCodes = ElfUtils::Types::Dwarf::StandardOpCodes
|
6
|
+
ExtendedOpCodes = ElfUtils::Types::Dwarf::ExtendedOpCodes
|
7
|
+
ULEB128 = ElfUtils::Types::ULEB128
|
8
|
+
SLEB128 = ElfUtils::Types::SLEB128
|
9
|
+
|
10
|
+
StateRegisters = Struct.new("StateRegisters", :address, :op_index, :file,
|
11
|
+
:line, :column, :is_stmt, :basic_block, :end_sequence, :prologue_end,
|
12
|
+
:epilogue_begin, :isa, :discriminator, keyword_init: true) do
|
13
|
+
def initialize(args)
|
14
|
+
# save off values we need for `#advance`
|
15
|
+
@maximum_operations_per_instruction =
|
16
|
+
args.delete(:maximum_operations_per_instruction)
|
17
|
+
@minimum_instruction_length = args.delete(:minimum_instruction_length)
|
18
|
+
|
19
|
+
# add the hard-coded defaults
|
20
|
+
args.merge!(
|
21
|
+
address: 0,
|
22
|
+
op_index: 0,
|
23
|
+
file: 1,
|
24
|
+
line: 1,
|
25
|
+
column: 0,
|
26
|
+
basic_block: false,
|
27
|
+
end_sequence: false,
|
28
|
+
prologue_end: false,
|
29
|
+
epilogue_begin: false,
|
30
|
+
isa: 0,
|
31
|
+
discriminator: 0
|
32
|
+
)
|
33
|
+
|
34
|
+
# save off the initial values to support `#reset`
|
35
|
+
@initial_values = args
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
# advance `address` & `op_index` by the provided `operation_advance`
|
40
|
+
def advance(operation_advance)
|
41
|
+
self.address += @minimum_instruction_length *
|
42
|
+
((op_index + operation_advance) /
|
43
|
+
@maximum_operations_per_instruction)
|
44
|
+
self.op_index = (op_index + operation_advance) %
|
45
|
+
@maximum_operations_per_instruction
|
46
|
+
end
|
47
|
+
|
48
|
+
# reset the registers to their initial values
|
49
|
+
def reset
|
50
|
+
@initial_values.each { |k, v| self[k] = v }
|
51
|
+
end
|
52
|
+
|
53
|
+
def flags
|
54
|
+
flags = []
|
55
|
+
%i[is_stmt basic_block end_sequence prologue_end epilogue_begin]
|
56
|
+
.each { |f| flags << f if send(f) }
|
57
|
+
flags
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_s
|
61
|
+
"0x%016x %6d %6d %6d %3d %13d %s" % [
|
62
|
+
address, line, column, file, isa, discriminator, flags.join(" ")
|
63
|
+
]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize(program)
|
68
|
+
@opcode_base = program.header.opcode_base
|
69
|
+
@standard_opcode_lengths = program.standard_opcode_lengths
|
70
|
+
@special_opcodes = build_special_opcode_table(program.header)
|
71
|
+
@addr_type = program.file.elf_type(:Addr)
|
72
|
+
|
73
|
+
# initial state
|
74
|
+
@initial_registers = StateRegisters.new(
|
75
|
+
minimum_instruction_length: program.header.minimum_instruction_length,
|
76
|
+
maximum_operations_per_instruction: (program.header.version >= 4) ?
|
77
|
+
program.header.maximum_operations_per_instruction : 1,
|
78
|
+
is_stmt: program.header.default_is_stmt == 1
|
79
|
+
).freeze
|
80
|
+
end
|
81
|
+
|
82
|
+
def process(opcodes)
|
83
|
+
buf = opcodes
|
84
|
+
opcode_base = @opcode_base
|
85
|
+
matrix = []
|
86
|
+
registers = @initial_registers.dup
|
87
|
+
|
88
|
+
# TODO merge unpack & eval steps for extended & special opcodes
|
89
|
+
until buf.empty?
|
90
|
+
byte = uint8.unpack(buf)
|
91
|
+
if byte == 0
|
92
|
+
opcode, args, buf = unpack_extended_op(buf[1..])
|
93
|
+
eval_extended_opcode(matrix, registers, opcode, args)
|
94
|
+
elsif byte < opcode_base
|
95
|
+
buf = eval_standard_opcode(buf, matrix, registers)
|
96
|
+
else
|
97
|
+
opcode, buf = byte, buf[1..]
|
98
|
+
opcode = @special_opcodes[opcode]
|
99
|
+
eval_special_opcode(matrix, registers, opcode)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
matrix
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# construct a special opcode table to lookup the values for each special
|
109
|
+
# opcode
|
110
|
+
def build_special_opcode_table(program_header)
|
111
|
+
opcode_base = program_header.opcode_base
|
112
|
+
|
113
|
+
(opcode_base..255).each_with_object([]) do |opcode, o|
|
114
|
+
adjusted_opcode = opcode - opcode_base
|
115
|
+
|
116
|
+
o[opcode] = {
|
117
|
+
line_increment: program_header.line_base +
|
118
|
+
(adjusted_opcode % program_header.line_range),
|
119
|
+
operation_advance: adjusted_opcode / program_header.line_range
|
120
|
+
}
|
121
|
+
o
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def unpack_extended_op(buf)
|
126
|
+
size, buf = ULEB128.unpack_one(buf)
|
127
|
+
opcode, buf = uint8.unpack_one(buf)
|
128
|
+
args, buf = if size > 1
|
129
|
+
string(size - 1, trim: false).unpack_one(buf)
|
130
|
+
else
|
131
|
+
["", buf]
|
132
|
+
end
|
133
|
+
[ExtendedOpCodes[opcode] || opcode, args, buf]
|
134
|
+
end
|
135
|
+
|
136
|
+
def eval_special_opcode(matrix, reg, opcode)
|
137
|
+
reg.advance(opcode[:operation_advance])
|
138
|
+
reg.line += opcode[:line_increment]
|
139
|
+
matrix << reg.dup
|
140
|
+
reg.basic_block = false
|
141
|
+
reg.prologue_end = false
|
142
|
+
reg.epilogue_begin = false
|
143
|
+
reg.discriminator = 0
|
144
|
+
end
|
145
|
+
|
146
|
+
def eval_standard_opcode(buf, matrix, reg)
|
147
|
+
opcode, buf = uint8.unpack_one(buf)
|
148
|
+
case StandardOpCodes[opcode]
|
149
|
+
when :copy
|
150
|
+
matrix << reg.dup
|
151
|
+
reg.discriminator = 0
|
152
|
+
reg.basic_block = false
|
153
|
+
reg.prologue_end = false
|
154
|
+
reg.epilogue_begin = false
|
155
|
+
when :advance_pc
|
156
|
+
operation_advance, buf = ULEB128.unpack_one(buf)
|
157
|
+
reg.advance(operation_advance)
|
158
|
+
when :advance_line
|
159
|
+
lines, buf = SLEB128.unpack_one(buf)
|
160
|
+
reg.line += lines
|
161
|
+
when :set_file
|
162
|
+
reg.file, buf = ULEB128.unpack_one(buf)
|
163
|
+
when :set_column
|
164
|
+
reg.column, buf = ULEB128.unpack_one(buf)
|
165
|
+
when :negate_stmt
|
166
|
+
reg.is_stmt = !reg.is_stmt
|
167
|
+
when :set_basic_block
|
168
|
+
reg.basic_block = true
|
169
|
+
when :const_add_pc
|
170
|
+
reg.advance(@special_opcodes[255][:operation_advance])
|
171
|
+
when :fixed_advance_pc
|
172
|
+
offset, buf = uint16.unpack_one(buf)
|
173
|
+
reg.address += offset
|
174
|
+
reg.op_index = 0
|
175
|
+
when :set_prologue_end
|
176
|
+
reg.prologue_end = true
|
177
|
+
when :set_epilogue_begin
|
178
|
+
reg.epilogue_begin = true
|
179
|
+
when :set_isa
|
180
|
+
reg.isa, buf = ULEB128.unpack_one(buf)
|
181
|
+
else
|
182
|
+
# for unsupported opcodes, skip the arguments
|
183
|
+
_, buf = array(ULEB128, @standard_opcode_lengths[opcode])
|
184
|
+
.unpack_one(buf)
|
185
|
+
end
|
186
|
+
|
187
|
+
buf
|
188
|
+
end
|
189
|
+
|
190
|
+
def eval_extended_opcode(matrix, reg, opcode, args)
|
191
|
+
case opcode
|
192
|
+
when :end_sequence
|
193
|
+
reg.end_sequence = true
|
194
|
+
matrix << reg.dup
|
195
|
+
reg.reset
|
196
|
+
when :set_address
|
197
|
+
reg.address = @addr_type.unpack(args)
|
198
|
+
reg.op_index = 0
|
199
|
+
when :set_discriminator
|
200
|
+
reg.discriminator = ULEB128.unpack(args)
|
201
|
+
else
|
202
|
+
raise ElfUtils::Error, "unknown opcode: %p %p" % [opcode, args]
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
class ElfUtils::Section::DebugLine
|
2
|
+
class LineNumberProgram
|
3
|
+
include CTypes::Helpers
|
4
|
+
|
5
|
+
def initialize(file:, offset:, header:, body:)
|
6
|
+
@file = file
|
7
|
+
@offset = offset
|
8
|
+
@header = header
|
9
|
+
@body = body
|
10
|
+
end
|
11
|
+
attr_reader :body, :file, :header
|
12
|
+
|
13
|
+
def addr_type
|
14
|
+
@header.addr_type
|
15
|
+
end
|
16
|
+
|
17
|
+
def standard_opcode_lengths
|
18
|
+
unpack_header_tail unless @standard_opcode_lengths
|
19
|
+
@standard_opcode_lengths
|
20
|
+
end
|
21
|
+
|
22
|
+
def directories
|
23
|
+
unpack_header_tail unless @directories
|
24
|
+
@directories
|
25
|
+
end
|
26
|
+
|
27
|
+
def file_names
|
28
|
+
unpack_header_tail unless @file_names
|
29
|
+
@file_names
|
30
|
+
end
|
31
|
+
|
32
|
+
def file_name(index)
|
33
|
+
file_name = file_names[index]
|
34
|
+
dir = directories[file_name[:directory_index]]
|
35
|
+
File.join(dir[:path], file_name[:path])
|
36
|
+
end
|
37
|
+
|
38
|
+
def address_table
|
39
|
+
@address_table ||= begin
|
40
|
+
state = StateMachine.new(self)
|
41
|
+
state.process(body).each_with_object({}) do |entry, o|
|
42
|
+
o[entry.address] = entry.freeze
|
43
|
+
end.freeze
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def source_location(addr)
|
48
|
+
entry = address_table[addr] || return
|
49
|
+
{
|
50
|
+
file: file_name(entry.file),
|
51
|
+
line: entry.line,
|
52
|
+
column: entry.column
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def unpack_header_tail
|
59
|
+
buf = header._rest
|
60
|
+
@standard_opcode_lengths, buf = array(uint8, header.opcode_base - 1)
|
61
|
+
.unpack_one(buf)
|
62
|
+
@standard_opcode_lengths.unshift(nil)
|
63
|
+
|
64
|
+
# unpack directory entries
|
65
|
+
if header.version < 5
|
66
|
+
@directories, buf = unpack_terminated_entries(buf,
|
67
|
+
Header::INCLUDE_DIRECTORIES_FORMAT)
|
68
|
+
|
69
|
+
# Prior to DWARF v5, the include directories were 1-indexed. Insert a
|
70
|
+
# nil at index 0, shifting all the entires up one index.
|
71
|
+
@directories.unshift(nil)
|
72
|
+
else
|
73
|
+
format, count, buf = unpack_format_and_count(buf)
|
74
|
+
@directories, buf = unpack_count_entries(buf, format, count)
|
75
|
+
end
|
76
|
+
|
77
|
+
# unpack file_name entries
|
78
|
+
if header.version < 5
|
79
|
+
@file_names, = unpack_terminated_entries(buf, Header::FILE_NAMES_FORMAT)
|
80
|
+
@file_names.unshift(nil)
|
81
|
+
else
|
82
|
+
format, count, buf = unpack_format_and_count(buf)
|
83
|
+
@file_names, = unpack_count_entries(buf, format, count)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def unpack_format_and_count(buf)
|
88
|
+
format_count, buf = uint8.unpack_one(buf)
|
89
|
+
format, buf = array(Header::EntryFormat, format_count).unpack_one(buf)
|
90
|
+
count, buf = ElfUtils::Types::ULEB128.unpack_one(buf)
|
91
|
+
[format, count, buf]
|
92
|
+
end
|
93
|
+
|
94
|
+
def unpack_count_entries(buf, format, count)
|
95
|
+
entries = count.times.map do
|
96
|
+
entry, buf = unpack_entry(buf, format)
|
97
|
+
entry
|
98
|
+
end
|
99
|
+
[entries, buf]
|
100
|
+
end
|
101
|
+
|
102
|
+
def unpack_terminated_entries(buf, format)
|
103
|
+
entries = []
|
104
|
+
until buf[0] == "\0"
|
105
|
+
entry, buf = unpack_entry(buf, format)
|
106
|
+
entries << entry
|
107
|
+
end
|
108
|
+
[entries, buf[1..]]
|
109
|
+
end
|
110
|
+
|
111
|
+
def unpack_entry(buf, format)
|
112
|
+
entry = {}
|
113
|
+
format.each do |field|
|
114
|
+
case field.form
|
115
|
+
when :string
|
116
|
+
entry[field.type], buf = string.terminated.unpack_one(buf)
|
117
|
+
when :line_strp
|
118
|
+
offset, buf = addr_type.unpack_one(buf)
|
119
|
+
entry[field.type] = @file.section(".debug_line_str")[offset]
|
120
|
+
when :udata
|
121
|
+
entry[field.type], buf = ElfUtils::Types::ULEB128.unpack_one(buf)
|
122
|
+
when :data16
|
123
|
+
entry[field.type], buf = string(16, trim: false).unpack_one(buf)
|
124
|
+
else
|
125
|
+
raise ElfUtils::Error, "unsupported entry field form: %p" % [field]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
[entry, buf]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
require_relative "line_number_program/header"
|
134
|
+
require_relative "line_number_program/state_machine"
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
class Section::DebugLine < Section::Base
|
3
|
+
def line_number_programs
|
4
|
+
out = []
|
5
|
+
buf = bytes
|
6
|
+
until buf.empty?
|
7
|
+
program, buf = unpack_line_number_program(buf)
|
8
|
+
out << program
|
9
|
+
end
|
10
|
+
out
|
11
|
+
end
|
12
|
+
|
13
|
+
def line_number_program(offset)
|
14
|
+
program, = unpack_line_number_program(bytes[offset..])
|
15
|
+
program
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def unpack_line_number_program(buf)
|
21
|
+
header, rest = LineNumberProgram::Header
|
22
|
+
.with_endian(@file.endian)
|
23
|
+
.unpack_one(buf)
|
24
|
+
header.freeze
|
25
|
+
header_size = buf.size - rest.size
|
26
|
+
body_size = header.unit.unit_size - header_size
|
27
|
+
body = rest[0, body_size]
|
28
|
+
program = LineNumberProgram
|
29
|
+
.new(file: @file, offset:, header: header.inner, body:)
|
30
|
+
[program, buf[header.unit.unit_size..]]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
require_relative "debug_line/line_number_program"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
class Section::DebugRanges < Section::Base
|
3
|
+
include CTypes::Helpers
|
4
|
+
|
5
|
+
# get the range list at a given offset
|
6
|
+
def get(offset:, base:)
|
7
|
+
array(array(@file.addr_type, 2), terminator: [0, 0])
|
8
|
+
.unpack(bytes[offset..])
|
9
|
+
.map do |first, last|
|
10
|
+
if first == @file.addr_type.max
|
11
|
+
# TODO figure out how to generate a test file that makes use of
|
12
|
+
# DW_AT_ranges & has a base address range so we can add tests for
|
13
|
+
# this.
|
14
|
+
base = last
|
15
|
+
nil
|
16
|
+
else
|
17
|
+
(first + base)..(last + base - 1)
|
18
|
+
end
|
19
|
+
end.compact
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
class Section::DebugStrOffsets < Section::Base
|
3
|
+
def get(base:, offset:, format:)
|
4
|
+
type = case format
|
5
|
+
when :dwarf32
|
6
|
+
CTypes::UInt32.with_endian(@file.endian)
|
7
|
+
when :dwarf64
|
8
|
+
CTypes::UInt64.with_endian(@file.endian)
|
9
|
+
else
|
10
|
+
raise Error, "unsupported format: %p" % [format]
|
11
|
+
end
|
12
|
+
pos = base + offset * type.size
|
13
|
+
type.unpack(bytes[pos..])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative "../symbol"
|
2
|
+
|
3
|
+
module ElfUtils
|
4
|
+
class Section::Dynsym < Section::Base
|
5
|
+
def symbols
|
6
|
+
@symbols ||= begin
|
7
|
+
dynstr = @file.section(".dynstr")
|
8
|
+
@file.elf_type(:Sym).unpack_all(bytes).map do |sym|
|
9
|
+
Symbol.new(@file, sym, dynstr)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
module Section
|
3
|
+
def self.from_header(elf_file, header)
|
4
|
+
case header.sh_type
|
5
|
+
when :strtab
|
6
|
+
return Section::Strtab.new(elf_file, header)
|
7
|
+
when :symtab
|
8
|
+
return Section::Symtab.new(elf_file, header)
|
9
|
+
when :dynsym
|
10
|
+
return Section::Dynsym.new(elf_file, header)
|
11
|
+
end
|
12
|
+
|
13
|
+
name = elf_file.shstrtab[header.sh_name]
|
14
|
+
case name
|
15
|
+
when ".debug_info"
|
16
|
+
Section::DebugInfo.new(elf_file, header)
|
17
|
+
when ".debug_abbrev"
|
18
|
+
Section::DebugAbbrev.new(elf_file, header)
|
19
|
+
when ".debug_str"
|
20
|
+
Section::Strtab.new(elf_file, header)
|
21
|
+
when ".debug_str_offsets"
|
22
|
+
Section::DebugStrOffsets.new(elf_file, header)
|
23
|
+
when ".debug_aranges"
|
24
|
+
Section::DebugArange.new(elf_file, header)
|
25
|
+
when ".debug_line"
|
26
|
+
Section::DebugLine.new(elf_file, header)
|
27
|
+
when ".debug_line_str"
|
28
|
+
Section::Strtab.new(elf_file, header)
|
29
|
+
when ".debug_addr"
|
30
|
+
Section::DebugAddr.new(elf_file, header)
|
31
|
+
when ".debug_ranges"
|
32
|
+
Section::DebugRanges.new(elf_file, header)
|
33
|
+
else
|
34
|
+
Section::Base.new(elf_file, header)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
require_relative "section/base"
|
41
|
+
require_relative "section/strtab"
|
42
|
+
require_relative "section/symtab"
|
43
|
+
require_relative "section/dynsym"
|
44
|
+
require_relative "section/debug_info"
|
45
|
+
require_relative "section/debug_abbrev"
|
46
|
+
require_relative "section/debug_str_offsets"
|
47
|
+
require_relative "section/debug_arange"
|
48
|
+
require_relative "section/debug_line"
|
49
|
+
require_relative "section/debug_addr"
|
50
|
+
require_relative "section/debug_ranges"
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
class Segment::Base
|
3
|
+
def initialize(file, header)
|
4
|
+
@file = file
|
5
|
+
@header = header
|
6
|
+
@offset = 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def type
|
10
|
+
@header.p_type
|
11
|
+
end
|
12
|
+
|
13
|
+
def addr
|
14
|
+
@header.p_vaddr + @offset
|
15
|
+
end
|
16
|
+
|
17
|
+
def offset
|
18
|
+
@header.p_offset
|
19
|
+
end
|
20
|
+
|
21
|
+
def filesize
|
22
|
+
@header.p_filesz
|
23
|
+
end
|
24
|
+
|
25
|
+
def size
|
26
|
+
@header.p_memsz
|
27
|
+
end
|
28
|
+
|
29
|
+
def align
|
30
|
+
@header.p_align
|
31
|
+
end
|
32
|
+
|
33
|
+
def flags
|
34
|
+
@header.p_flags
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_range
|
38
|
+
addr...(addr + size)
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
@header.inspect
|
43
|
+
end
|
44
|
+
|
45
|
+
def sections
|
46
|
+
a = addr...(addr + size)
|
47
|
+
@file.sections.select do |section|
|
48
|
+
next unless section.alloc?
|
49
|
+
a.include?(section.addr) ||
|
50
|
+
a.include?(section.addr + section.size - 1)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# relocate this segement and all sections within it
|
55
|
+
#
|
56
|
+
# @param addr address for section; `nil` clears relocation
|
57
|
+
def relocate(addr)
|
58
|
+
if addr.nil?
|
59
|
+
sections.relocate(nil)
|
60
|
+
@offset = 0
|
61
|
+
return
|
62
|
+
end
|
63
|
+
|
64
|
+
# calculate the offset, then relocate the sections by that offset
|
65
|
+
offset = addr - @header.p_vaddr
|
66
|
+
sections.each do |section|
|
67
|
+
section.relocate(offset, relative: true)
|
68
|
+
end
|
69
|
+
@offset = offset
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ElfUtils
|
2
|
+
# provide a #pread method for String
|
3
|
+
#
|
4
|
+
# This refinement is to allow String instances to be used as the underlying
|
5
|
+
# IO object for ElfFile. Intended as a lighter-weight approach than
|
6
|
+
# IOString.
|
7
|
+
module StringPread
|
8
|
+
refine String do
|
9
|
+
# read up to `maxlen` bytes from the String starting at `offset`
|
10
|
+
# @param maxlen [Integer] maximum number of bytes to read
|
11
|
+
# @param offset [Integer] offset to begin reading at
|
12
|
+
# @return [String, nil] bytes read or nil
|
13
|
+
def pread(maxlen, offset)
|
14
|
+
byteslice(offset, maxlen)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|