elf-mithril 0.0.1
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.
- data/.gitignore +17 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/bin/mithril-ld +76 -0
- data/bin/mithril-rewrite +14 -0
- data/bin/mithril-section-symbols +12 -0
- data/lib/mithril.rb +6 -0
- data/lib/mithril/elf.rb +220 -0
- data/lib/mithril/elf_enums.rb +581 -0
- data/lib/mithril/elf_generative.rb +0 -0
- data/lib/mithril/elf_structs.rb +335 -0
- data/lib/mithril/inject_symbols.rb +22 -0
- data/lib/mithril/parser.rb +706 -0
- data/lib/mithril/policy.rb +335 -0
- data/lib/mithril/version.rb +3 -0
- data/lib/mithril/writer.rb +774 -0
- data/lib/mithril/writer2.rb +29 -0
- data/mithril.gemspec +30 -0
- data/test/hash_test.rb +7 -0
- data/tools/#elf_enums.sh# +26 -0
- data/tools/elf.h +991 -0
- data/tools/elf_enums.sh +26 -0
- data/tools/elf_h_processor.sh +5 -0
- data/tools/elf_structs.sh +1 -0
- data/tools/gnu_elf_hash_test +0 -0
- data/tools/gnu_elf_hash_test.cxx +22 -0
- data/tools/hash_test +0 -0
- data/tools/hash_test.c +70 -0
- metadata +192 -0
File without changes
|
@@ -0,0 +1,335 @@
|
|
1
|
+
|
2
|
+
require 'bindata'
|
3
|
+
require_relative 'elf_enums'
|
4
|
+
module ElfStructs
|
5
|
+
class ElfIdentification < BinData::Record
|
6
|
+
endian :little
|
7
|
+
string :id_magic, :length=>4 , :initial_value => "\x7FELF" #TODO: validate?
|
8
|
+
uint8 :id_class
|
9
|
+
uint8 :id_data
|
10
|
+
uint8 :id_version
|
11
|
+
skip :length => 9
|
12
|
+
end
|
13
|
+
|
14
|
+
def alias_recordtype(from,to)
|
15
|
+
self.class.class_eval do
|
16
|
+
define_method from do |*args|
|
17
|
+
send(to,*args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
def hdr
|
22
|
+
elf_identification :ident
|
23
|
+
#end of identification
|
24
|
+
half :type #File type.
|
25
|
+
half :machine #Machine architecture.
|
26
|
+
word :version #ELF format version.
|
27
|
+
addr :entry #Entry point.
|
28
|
+
off :phoff #Program header file offset.
|
29
|
+
off :shoff #Section header file offset.
|
30
|
+
word :flags #Architecture-specific flags.
|
31
|
+
half :ehsize #Size of ELF header in bytes.
|
32
|
+
half :phentsize #Size of program header entry.
|
33
|
+
half :phnum #Number of program header entries.
|
34
|
+
half :shentsize #Size of section header entry.
|
35
|
+
half :shnum #Number of section header entries.
|
36
|
+
half :shstrndx #Section name strings section.
|
37
|
+
end
|
38
|
+
|
39
|
+
def note
|
40
|
+
uint32 :namesz, :value => lambda{name.num_bytes} #Length of name.
|
41
|
+
uint32 :descsz, :value => lambda{desc.num_bytes} # Length of descriptor.
|
42
|
+
uint32 :type # Type of this note.
|
43
|
+
string :name, :read_length => lambda{ (namesz.to_i * 4 + 3)/4 } # Round up
|
44
|
+
string :desc, :read_length => lambda{ (descsz.to_i * 4 + 3)/4 }
|
45
|
+
attr_accessor :section_name
|
46
|
+
end
|
47
|
+
|
48
|
+
def shdr
|
49
|
+
word :name #Section name (index into section header string table
|
50
|
+
word :type #Section type.
|
51
|
+
xword :flags #Section flags.
|
52
|
+
addr :vaddr #Address in memory image.
|
53
|
+
off :off #Offset in file.
|
54
|
+
xword :siz#Size in bytes. Patch: Had to change to siz
|
55
|
+
word :link #Index of a related section.
|
56
|
+
word :info #Depends on section type.
|
57
|
+
xword :addralign #Alignment in bytes.
|
58
|
+
xword :entsize #Size of each entry in section.
|
59
|
+
|
60
|
+
|
61
|
+
attr_accessor :index # So we retain the index after parsing
|
62
|
+
end
|
63
|
+
def phdr32 # FAIL different layout with 32 and 64
|
64
|
+
word :type #Entry type.
|
65
|
+
off :off #File offset of contents.
|
66
|
+
addr :vaddr #Virtual address in memory image.
|
67
|
+
addr :paddr #Physical address (not used).
|
68
|
+
xword :filesz #Size of contents in file.
|
69
|
+
xword :memsz #Size of contents in memory.
|
70
|
+
word :flags #Access permission flags.
|
71
|
+
xword :align #Alignment in memory and file.
|
72
|
+
end
|
73
|
+
def phdr64
|
74
|
+
word :type
|
75
|
+
word :flags
|
76
|
+
off :off
|
77
|
+
addr :vaddr
|
78
|
+
addr :paddr
|
79
|
+
xword :filesz
|
80
|
+
xword :memsz
|
81
|
+
xword :align
|
82
|
+
end
|
83
|
+
# Dynamic structure. The ".dynamic" section contains an array of them.
|
84
|
+
def dyn
|
85
|
+
sxword :tag #Entry type.
|
86
|
+
addr :val #Address value or raw value
|
87
|
+
end
|
88
|
+
# * Relocation entries
|
89
|
+
# Relocations that don't need an addend field. */
|
90
|
+
def rel_common
|
91
|
+
case @bits
|
92
|
+
when 32
|
93
|
+
define_method :sym do
|
94
|
+
info.to_i >> 8
|
95
|
+
end
|
96
|
+
define_method :type do
|
97
|
+
info.to_i & 0xff
|
98
|
+
end
|
99
|
+
define_method :sym= do |val|
|
100
|
+
self.info = type | (val << 8)
|
101
|
+
end
|
102
|
+
define_method :type= do |val|
|
103
|
+
self.info = (val &0xff) | (sym << 8)
|
104
|
+
end
|
105
|
+
when 64
|
106
|
+
define_method :sym do
|
107
|
+
info.to_i >> 32
|
108
|
+
end
|
109
|
+
define_method :type do
|
110
|
+
info.to_i & 0xffffffff
|
111
|
+
end
|
112
|
+
define_method :sym= do |val|
|
113
|
+
self.info = type | (val << 32)
|
114
|
+
end
|
115
|
+
define_method :type= do |val|
|
116
|
+
self.info = (val &0xffffffff) | (sym << 32)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
def rel
|
121
|
+
addr :off #Location to be relocated.
|
122
|
+
xword :info #Relocation type and symbol index.
|
123
|
+
define_method :addend do
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
rel_common
|
127
|
+
end
|
128
|
+
|
129
|
+
# Relocations that need an addend field. */
|
130
|
+
def rela
|
131
|
+
addr :off #Location to be relocated.
|
132
|
+
xword :info #Relocation type and symbol index.
|
133
|
+
sxword :addend #Addend.
|
134
|
+
rel_common
|
135
|
+
end
|
136
|
+
|
137
|
+
#Elf Symbol
|
138
|
+
def sym_common
|
139
|
+
define_method :type do
|
140
|
+
info & 0xf
|
141
|
+
end
|
142
|
+
define_method :binding do
|
143
|
+
info >> 4
|
144
|
+
end
|
145
|
+
define_method :type= do |val|
|
146
|
+
raise RuntimeError.new "Invalid param" unless val & 0xf == val
|
147
|
+
self.info = (info&0xf0) | val
|
148
|
+
end
|
149
|
+
define_method :binding= do |val|
|
150
|
+
raise RuntimeError.new "Invalid param" unless val & 0xf == val
|
151
|
+
self.info = (info&0xf) | (val << 4)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
def sym32
|
155
|
+
word :name #String table index of name.
|
156
|
+
addr :val #Symbol value. PATCH: change to val so as to avoid name conflict
|
157
|
+
word :siz #Size of associated object. PATCH: Change to val
|
158
|
+
char :info #Type and binding information.
|
159
|
+
char :other #Reserved (not used).
|
160
|
+
half :shndx #Section index of symbol.
|
161
|
+
sym_common
|
162
|
+
end
|
163
|
+
def sym64
|
164
|
+
word :name
|
165
|
+
uint8 :info
|
166
|
+
uint8 :other
|
167
|
+
half :shndx
|
168
|
+
addr :val
|
169
|
+
xword :siz
|
170
|
+
sym_common
|
171
|
+
end
|
172
|
+
def versym
|
173
|
+
half :veridx
|
174
|
+
end
|
175
|
+
def verdaux
|
176
|
+
word :name
|
177
|
+
word :nextoff
|
178
|
+
end
|
179
|
+
def verdef
|
180
|
+
half :version
|
181
|
+
half :flags
|
182
|
+
half :ndx
|
183
|
+
half :cnt
|
184
|
+
word :hsh
|
185
|
+
word :aux
|
186
|
+
word :nextoff
|
187
|
+
end
|
188
|
+
def verneed
|
189
|
+
half :version
|
190
|
+
half :cnt
|
191
|
+
word :file
|
192
|
+
word :aux
|
193
|
+
word :nextoff
|
194
|
+
end
|
195
|
+
def vernaux
|
196
|
+
word :hsh
|
197
|
+
half :flags
|
198
|
+
half :other
|
199
|
+
word :name
|
200
|
+
word :nextoff
|
201
|
+
end
|
202
|
+
def stringtable
|
203
|
+
array :strings, :type => :stringz, :initial_length=> 0
|
204
|
+
end
|
205
|
+
#ELFP redesign - in progress
|
206
|
+
=begin
|
207
|
+
def elfp_chunk_desc # Each chunk of ELFbac metadata is also a section
|
208
|
+
uint32 :type
|
209
|
+
uint32 :offset
|
210
|
+
uint32 :size
|
211
|
+
end
|
212
|
+
def elfp_header
|
213
|
+
uint32 :sectioncount, value: lambda { sectionheaders.length }
|
214
|
+
array :sectionheaders, type: @@factory.elfp_chunk_desc, read_length: initia
|
215
|
+
end
|
216
|
+
=end
|
217
|
+
#ELFP old
|
218
|
+
def elfp_header
|
219
|
+
uint32 :chunkcount, value: lambda {states.size + tags.size+ calls.size + data.size}
|
220
|
+
array :states, type: @factory.elfp_state
|
221
|
+
array :tags, type: @factory.elfp_tag
|
222
|
+
array :calls, type: @factory.elfp_call
|
223
|
+
array :data, type: @factory.elfp_data
|
224
|
+
end
|
225
|
+
def elfp_state
|
226
|
+
uint32 :chunkid , value: 1
|
227
|
+
uint32 :id
|
228
|
+
uint32 :stackid, default_value: 0
|
229
|
+
end
|
230
|
+
def elfp_call
|
231
|
+
uint32 :chunktype, value: 2
|
232
|
+
uint32 :from
|
233
|
+
uint32 :to
|
234
|
+
uint64 :off
|
235
|
+
uint16 :parambytes
|
236
|
+
uint16 :returnbytes
|
237
|
+
end
|
238
|
+
def elfp_data
|
239
|
+
uint32 :chunktype, value: 3
|
240
|
+
uint32 :from
|
241
|
+
uint32 :to
|
242
|
+
uint64 :tag
|
243
|
+
uint32 :type
|
244
|
+
end
|
245
|
+
def elfp_tag
|
246
|
+
uint32 :chunktype, value:4
|
247
|
+
uint64 :tag
|
248
|
+
uint64 :addr
|
249
|
+
uint64 :siz
|
250
|
+
end
|
251
|
+
ELF_OBJECTS = [:sym, :rela, :rel, :dyn, :phdr, :shdr, :hdr, :note, :vernaux, :verneed, :verdef, :verdaux, :versym, :elfp_state, :elfp_call, :elfp_data, :elfp_tag, :elfp_header]
|
252
|
+
Split = {
|
253
|
+
phdr: {
|
254
|
+
32 => :phdr32,
|
255
|
+
64 => :phdr64
|
256
|
+
},
|
257
|
+
sym: {
|
258
|
+
32 => :sym32,
|
259
|
+
64 => :sym64
|
260
|
+
}
|
261
|
+
}
|
262
|
+
def bitness(bits)
|
263
|
+
@bits = bits
|
264
|
+
alias_recordtype :char, :uint8
|
265
|
+
case bits
|
266
|
+
when 32
|
267
|
+
alias_recordtype :word, :uint32
|
268
|
+
alias_recordtype :sword, :int32
|
269
|
+
alias_recordtype :half, :uint16
|
270
|
+
alias_recordtype :off, :uint32
|
271
|
+
alias_recordtype :addr, :uint32
|
272
|
+
alias_recordtype :xword, :uint32
|
273
|
+
alias_recordtype :sxword, :uint32
|
274
|
+
when 64
|
275
|
+
alias_recordtype :addr, :uint64
|
276
|
+
alias_recordtype :off, :uint64
|
277
|
+
alias_recordtype :half, :uint16
|
278
|
+
alias_recordtype :word, :uint32
|
279
|
+
alias_recordtype :sword, :int32
|
280
|
+
alias_recordtype :xword, :uint64
|
281
|
+
alias_recordtype :sxword, :int64
|
282
|
+
else
|
283
|
+
raise RuntimeError.new "We only know about 32-bit or 64-bit ELF formats, not about #{bits}"
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
class ElfStructFactory
|
288
|
+
attr_reader *ElfStructs::ELF_OBJECTS
|
289
|
+
def self.instance(endian, bits)
|
290
|
+
@@instances[endian][bits]
|
291
|
+
end
|
292
|
+
def rel_info_sym(info)
|
293
|
+
if @width == 32
|
294
|
+
info.to_i >> 8
|
295
|
+
else
|
296
|
+
info.to_i >> 32
|
297
|
+
end
|
298
|
+
end
|
299
|
+
def rel_info_type(info)
|
300
|
+
if @width == 32
|
301
|
+
info.to_i & 0xff
|
302
|
+
else
|
303
|
+
info.to_i & 0xffffffff
|
304
|
+
end
|
305
|
+
end
|
306
|
+
def rel_build_info(sym,type)
|
307
|
+
if @width == 32
|
308
|
+
(sym.to_i << 8) | (type.to_i &0xff)
|
309
|
+
else
|
310
|
+
(sym.to_i << 32) | (type.to_i &0xffffffff)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def initialize(endian,width)
|
315
|
+
@endian = endian
|
316
|
+
@width = width
|
317
|
+
factory = self
|
318
|
+
ElfStructs::ELF_OBJECTS.each do |object|
|
319
|
+
klass = Class.new BinData::Record do
|
320
|
+
extend ElfStructs
|
321
|
+
endian endian
|
322
|
+
bitness width
|
323
|
+
@factory = factory
|
324
|
+
if(ElfStructs::Split.include? object)
|
325
|
+
self.send(ElfStructs::Split[object][width])
|
326
|
+
else
|
327
|
+
self.send(object)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
instance_variable_set("@#{object.to_s}",klass)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
@@instances = {:little => {32 => ElfStructFactory.new(:little,32), 64=>ElfStructFactory.new(:little,64) },
|
334
|
+
:big => {32 => ElfStructFactory.new(:big,32) , 64 => ElfStructFactory.new(:big,64)} }
|
335
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Elf::Policy
|
2
|
+
def self.inject_symbols(file)
|
3
|
+
case file.filetype
|
4
|
+
when ElfFlags::Type::ET_EXEC
|
5
|
+
filename = ""
|
6
|
+
when ElfFlags::Type::ET_DYN
|
7
|
+
filename = file.dynamic.soname
|
8
|
+
else
|
9
|
+
raise RuntimeError.new "section_symbols works only for ET_DYN and ET_EXEC files"
|
10
|
+
end
|
11
|
+
(file.progbits + file.nobits).each{|section|
|
12
|
+
name = Elf::Policy::section_symbol_name(filename,section.name)
|
13
|
+
next if file.symbols.include? name
|
14
|
+
sym = Elf::Symbol.new(name,section,ElfFlags::SymbolType::STT_OBJECT,0,ElfFlags::SymbolBinding::STB_GLOBAL,section.size) # STT_SECTION is ignored for lookup!
|
15
|
+
# print "Injecting #{name}"
|
16
|
+
sym.gnu_version = :global
|
17
|
+
sym.hidden = false
|
18
|
+
sym.is_dynamic = true
|
19
|
+
file.symbols << sym
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,706 @@
|
|
1
|
+
require_relative 'elf'
|
2
|
+
|
3
|
+
$UNSAFE_PARSER = ! ENV['HOWDY_NEIGHBOURS_BX'].nil? # shoutout to one
|
4
|
+
# of the best ELF hackers on the block.
|
5
|
+
|
6
|
+
def expect_value(desc,is,should)
|
7
|
+
unless $UNSAFE_PARSER
|
8
|
+
raise RuntimeError.new "Invalid #{desc}, expected #{should} instead of #{is}" if is != should
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Elf
|
13
|
+
module Parser
|
14
|
+
class StringTable
|
15
|
+
def initialize(data)
|
16
|
+
expect_value "First byte of string table", data.bytes.first, 0
|
17
|
+
@data = StringIO.new(data)
|
18
|
+
end
|
19
|
+
def [](offset)
|
20
|
+
@data.seek offset
|
21
|
+
obj = BinData::Stringz.new
|
22
|
+
obj.read(@data)
|
23
|
+
obj.snapshot
|
24
|
+
end
|
25
|
+
end
|
26
|
+
class NilStringTable
|
27
|
+
def [](offset)
|
28
|
+
return ""
|
29
|
+
end
|
30
|
+
end
|
31
|
+
class Parser
|
32
|
+
attr_reader :file
|
33
|
+
def initialize(string)
|
34
|
+
@data = StringIO.new(string)
|
35
|
+
@file = ElfFile.new
|
36
|
+
ident = ElfStructs::ElfIdentification.read(@data)
|
37
|
+
print ident.snapshot.inspect
|
38
|
+
raise RuntimeError.new "Invalid ELF version #{ident.id_version}" if ident.id_version != ElfFlags::Version::EV_CURRENT
|
39
|
+
case ident.id_class
|
40
|
+
when ElfFlags::IdentClass::ELFCLASS64
|
41
|
+
@file.bits = 64
|
42
|
+
when ElfFlags::IdentClass::ELFCLASS32
|
43
|
+
@file.bits = 32
|
44
|
+
else
|
45
|
+
RuntimeError.new "Invalid ELF class #{ident.id_class}"
|
46
|
+
end
|
47
|
+
case ident.id_data
|
48
|
+
when ElfFlags::IdentData::ELFDATA2LSB
|
49
|
+
@file.endian = :little
|
50
|
+
when ElfFlags::IdentData::ELFDATA2MSB
|
51
|
+
@file.endian = :big
|
52
|
+
else
|
53
|
+
RuntimeError.new "Invalid ELF endianness #{ident.id_data}"
|
54
|
+
end
|
55
|
+
@versions = {}
|
56
|
+
@reladyn_indices = []
|
57
|
+
@factory = ElfStructFactory.instance(@file.endian,@file.bits)
|
58
|
+
parse_with_factory()
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def unique_section(sects, type)
|
63
|
+
if sects.include? type
|
64
|
+
expect_value "Number of #{type} sections", sects[type].size, 1
|
65
|
+
return sects[type].first
|
66
|
+
else
|
67
|
+
return nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
def safe_strtab(index)
|
71
|
+
if(index ==0)
|
72
|
+
NilStringTable.new()
|
73
|
+
else
|
74
|
+
hdr = @shdrs[index]
|
75
|
+
expect_value "STRTAB type", hdr.type, ElfFlags::SectionType::SHT_STRTAB
|
76
|
+
@data.seek hdr.off
|
77
|
+
@unparsed_sections.delete index
|
78
|
+
StringTable.new(@data.read(hdr.siz))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
def parse_symtable(sect,strtab)
|
82
|
+
return [] if sect.nil?
|
83
|
+
expect_value "Size of symbol table entry", @factory.sym.new.num_bytes, sect.entsize
|
84
|
+
@data.seek sect.off
|
85
|
+
@unparsed_sections.delete sect.index
|
86
|
+
BinData::Array.new( :type=> @factory.sym, :initial_length => sect.siz / sect.entsize).read(@data).map do |sym|
|
87
|
+
#TODO: find appropriate section
|
88
|
+
unless @bits_by_index.include? sym.shndx.to_i
|
89
|
+
section = nil
|
90
|
+
value = sym.val.to_i
|
91
|
+
else
|
92
|
+
section = @bits_by_index[sym.shndx.to_i]
|
93
|
+
if([ET::ET_EXEC, ET::ET_DYN].include? @file.filetype)
|
94
|
+
value = sym.val.to_i - section.addr #TODO: Only if absolute.
|
95
|
+
else
|
96
|
+
value = sym.val.to_i
|
97
|
+
end
|
98
|
+
section = ".interp" if section.name == ".interp" #HACK: this should not be the case
|
99
|
+
# expect_value "Section index #{sym.shndx.to_i} in symbol should be in progbits", false, section.nil?
|
100
|
+
end
|
101
|
+
x= Symbol.new(strtab[sym.name],section, sym.type.to_i, value, sym.binding.to_i, sym.siz.to_i)
|
102
|
+
if(x.name.empty? and x.type == STT::STT_SECTION)
|
103
|
+
x.name = @shstrtab[@shdrs[sym.shndx.to_i].name]
|
104
|
+
end
|
105
|
+
x.visibility = sym.other.to_i & 0x3
|
106
|
+
if [SHN::SHN_ABS, SHN::SHN_COMMON, SHN::SHN_UNDEF, SHN::SHN_XINDEX].include? sym.shndx.to_i
|
107
|
+
x.semantics = sym.shndx.to_i
|
108
|
+
else
|
109
|
+
x.semantics = nil
|
110
|
+
end
|
111
|
+
x
|
112
|
+
end
|
113
|
+
end
|
114
|
+
def parse_progbits(shdr)
|
115
|
+
@data.seek shdr.off
|
116
|
+
@unparsed_sections.delete shdr.index
|
117
|
+
expect_value "PROGBITS link",shdr.link,0
|
118
|
+
if shdr.type.to_i == SHT::SHT_NOBITS
|
119
|
+
NoBits.new(@shstrtab[shdr.name],shdr.snapshot)
|
120
|
+
else
|
121
|
+
ProgBits.new(@shstrtab[shdr.name], shdr.snapshot, @data.read(shdr.siz))
|
122
|
+
end
|
123
|
+
end
|
124
|
+
def parse_verdef(shdr)
|
125
|
+
@unparsed_sections.delete shdr.index
|
126
|
+
strtab = safe_strtab(shdr.link)
|
127
|
+
verdefoff = shdr.off
|
128
|
+
file = @file.dynamic.soname
|
129
|
+
versions = {}
|
130
|
+
parents = {}
|
131
|
+
shdr.info.to_i.times {
|
132
|
+
@data.seek verdefoff
|
133
|
+
verdef = @factory.verdef.read(@data)
|
134
|
+
expect_value "VERDEF version", verdef.version, 1
|
135
|
+
verdauxoff = verdefoff + verdef.aux
|
136
|
+
aux = []
|
137
|
+
verdef.cnt.to_i.times {
|
138
|
+
@data.seek verdauxoff
|
139
|
+
verdaux = @factory.verdaux.read(@data)
|
140
|
+
aux << strtab[verdaux.name]
|
141
|
+
verdauxoff += verdaux.nextoff.to_i
|
142
|
+
# expect_value "Nonzero verdaux.nextoff #{verdaux.nextoff}", true,verdaux.nextoff != 0 unless
|
143
|
+
}
|
144
|
+
name = aux.first
|
145
|
+
par = aux[1..-1] || []
|
146
|
+
expect_value "Name present", false, name.nil?
|
147
|
+
parents[name] = par
|
148
|
+
# expect_value "Version #{verdef.idx} unique", false, @versions.include? verdef.idx.to_i
|
149
|
+
@versions[verdef.ndx.to_i] = GnuVersion.new(file,name,verdef.flags.to_i,false)
|
150
|
+
versions[name] = @versions[verdef.ndx.to_i]
|
151
|
+
# expect_value "Nonzero verdef.nextoff #{verdef.nextoff}", true,verdef.nextoff != 0
|
152
|
+
verdefoff += verdef.nextoff.to_i
|
153
|
+
}
|
154
|
+
parents.each{|version,p|
|
155
|
+
p.each{|x|
|
156
|
+
# expect_value "Valid parent version #{x}", true, versions.include? x
|
157
|
+
versions[version].parents << versions[x]
|
158
|
+
}
|
159
|
+
}
|
160
|
+
if @versions.include? 1
|
161
|
+
@file.dynamic.gnu_version_basename = @versions[1].version
|
162
|
+
end
|
163
|
+
end
|
164
|
+
def parse_verneed(shdr)
|
165
|
+
@unparsed_sections.delete shdr.index
|
166
|
+
strtab = safe_strtab(shdr.link)
|
167
|
+
@data.seek shdr.off
|
168
|
+
data = StringIO.new(@data.read(shdr.siz.to_i))
|
169
|
+
# This is the weird, screwed up 'array', that actually is a
|
170
|
+
# linked list
|
171
|
+
verneedoff = 0
|
172
|
+
shdr.info.to_i.times{ #SHDR.info has number of entries
|
173
|
+
data.seek verneedoff
|
174
|
+
verneed = @factory.verneed.read(data)
|
175
|
+
expect_value "VERNEED version", verneed.version, 1
|
176
|
+
file = strtab[verneed.file]
|
177
|
+
vernauxoff = verneedoff + verneed.aux
|
178
|
+
|
179
|
+
verneed.cnt.times {
|
180
|
+
data.seek vernauxoff
|
181
|
+
|
182
|
+
vernaux = @factory.vernaux.read(data)
|
183
|
+
versionname = strtab[vernaux.name]
|
184
|
+
flags = vernaux.flags.to_i
|
185
|
+
version = vernaux.other.to_i
|
186
|
+
# expect_value "Nonzero vernaux.nextoff #{vernaux.nextoff}", true,vernaux.nextoff != 0
|
187
|
+
|
188
|
+
@versions[version] = GnuVersion.new( file, versionname, flags,true)
|
189
|
+
vernauxoff += vernaux.nextoff
|
190
|
+
}
|
191
|
+
# expect_value "Nonzero verneedoff ",true, verneed.nextoff != 0
|
192
|
+
verneedoff += verneed.nextoff
|
193
|
+
}
|
194
|
+
end
|
195
|
+
VERSYM_HIDDEN = 0x8000
|
196
|
+
VERSYM_IDX_MASK = 0xffff & ~VERSYM_HIDDEN
|
197
|
+
def parse_versym(shdr,dynsym)
|
198
|
+
@data.seek shdr.off
|
199
|
+
data = StringIO.new(@data.read(shdr.siz.to_i))
|
200
|
+
BinData::Array.new(type: @factory.versym, initial_length: shdr.siz / @factory.versym.new.num_bytes).read(data).to_enum.with_index {|versym,index|
|
201
|
+
veridx = versym.veridx & VERSYM_IDX_MASK
|
202
|
+
dynsym[index].gnu_version =case veridx
|
203
|
+
when 0
|
204
|
+
:local
|
205
|
+
when 1
|
206
|
+
:global
|
207
|
+
else
|
208
|
+
unless @versions.include? veridx
|
209
|
+
raise RuntimeError.new "Invalid veridx #{versym.veridx} in dynamic symbol #{index}"
|
210
|
+
end
|
211
|
+
@versions[veridx]
|
212
|
+
end
|
213
|
+
dynsym[index].hidden = (versym.veridx & VERSYM_HIDDEN != 0)
|
214
|
+
}
|
215
|
+
end
|
216
|
+
DYNAMIC_FLAGS = {
|
217
|
+
DT::DT_TEXTREL=>:@textrel,
|
218
|
+
DT::DT_BIND_NOW => :@bind_now,
|
219
|
+
DT::DT_SYMBOLIC => :@symbolic
|
220
|
+
}
|
221
|
+
def parse_rel_common(relocations,sect_idx,symtab_idx, uses_addresses,is_jmprel)
|
222
|
+
case @shdrs[symtab_idx].type.to_i
|
223
|
+
when SHT::SHT_DYNSYM
|
224
|
+
symtab= @dynsym
|
225
|
+
when SHT::SHT_SYMTAB
|
226
|
+
symtab= @symtab
|
227
|
+
else
|
228
|
+
raise ArgumentError.new "Invalid link field #{symtab_idx} in relocation section"
|
229
|
+
end
|
230
|
+
|
231
|
+
if sect_idx == 0 and uses_addresses
|
232
|
+
applies_to = nil
|
233
|
+
else
|
234
|
+
applies_to = @bits_by_index[sect_idx]
|
235
|
+
raise ArgumentError.new "Section index #{sect_idx} not referring to PROGBITS for relocation table" if applies_to.nil?
|
236
|
+
end
|
237
|
+
|
238
|
+
relocations.map {|rel_entry|
|
239
|
+
Relocation.new.tap { |rel|
|
240
|
+
if uses_addresses
|
241
|
+
rel.section = @relocatable_sections.find(rel_entry.off.to_i).andand(&:value)
|
242
|
+
print "Warning: Invalid relocation address 0x#{rel_entry.off.snapshot.to_s(16)}\n" unless rel.section
|
243
|
+
rel.offset = rel_entry.off.to_i - rel.section.addr
|
244
|
+
else
|
245
|
+
rel.section = applies_to
|
246
|
+
rel.offset = rel_entry.off.to_i
|
247
|
+
end
|
248
|
+
rel.type = rel_entry.type
|
249
|
+
if rel_entry.sym == 0
|
250
|
+
rel.symbol = nil
|
251
|
+
else
|
252
|
+
rel.symbol = @canonical_symbol[symtab[ rel_entry.sym]] or raise(RuntimeError.new("Symbol could not be canonicalized"))
|
253
|
+
end
|
254
|
+
rel.addend = rel_entry.addend.to_i
|
255
|
+
rel.is_lazy = is_jmprel
|
256
|
+
}
|
257
|
+
}
|
258
|
+
end
|
259
|
+
def is_jmprel(shdr)
|
260
|
+
@jmprel_addr == shdr.vaddr
|
261
|
+
end
|
262
|
+
def parse_rela(shdr,has_addrs)
|
263
|
+
@unparsed_sections.delete shdr.index
|
264
|
+
@data.seek shdr.off
|
265
|
+
expect_value "RELA entsize", shdr.entsize, @factory.rela.new.num_bytes
|
266
|
+
rela = BinData::Array.new(:type => @factory.rela, :initial_length => shdr.siz/shdr.entsize).read(@data)
|
267
|
+
parse_rel_common(rela,shdr.info, shdr.link,has_addrs,is_jmprel(shdr))
|
268
|
+
end
|
269
|
+
def parse_rel(shdr,has_addrs)
|
270
|
+
@unparsed_sections.delete shdr.index
|
271
|
+
@data.seek shdr.off
|
272
|
+
expect_value "REL entsize", shdr.entsize, @factory.rel.new.num_bytes
|
273
|
+
rela = BinData::Array.new(:type => @factory.rel, :initial_length => shdr.siz/shdr.entsize).read(@data)
|
274
|
+
parse_rel_common(rela,shdr.info, shdr.link,has_addrs,is_jmprel(shdr))
|
275
|
+
end
|
276
|
+
def parse_dynamic(shdr)
|
277
|
+
retval = Dynamic.new
|
278
|
+
|
279
|
+
@data.seek shdr.off
|
280
|
+
#TODO: find unused dynamic entries
|
281
|
+
expect_value "Size of dynamic entry", @factory.dyn.new.num_bytes, shdr.entsize
|
282
|
+
dynamic = BinData::Array.new(:type=> @factory.dyn, :initial_length => shdr.siz/ shdr.entsize).read(@data)
|
283
|
+
@unparsed_sections.delete shdr.index
|
284
|
+
by_type = dynamic.group_by {|x| x.tag.to_i}
|
285
|
+
expect_unique = lambda do |sym,optional| # Validates that either one
|
286
|
+
# or zero entries of this type exist, returning the one entry
|
287
|
+
# if it exists
|
288
|
+
if(by_type.include? sym)
|
289
|
+
expect_value "Dynamic entry #{sym} count", by_type[sym].size,1
|
290
|
+
by_type[sym].first
|
291
|
+
else
|
292
|
+
if(optional)
|
293
|
+
nil
|
294
|
+
else
|
295
|
+
raise ArgumentError.new "Missing mandatory dynamic entry #{sym}"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
expect_value "DT_NULL", dynamic.last, @factory.dyn.new()
|
301
|
+
|
302
|
+
by_type.delete DT::DT_NULL
|
303
|
+
|
304
|
+
expect_unique.call DT::DT_STRTAB,false
|
305
|
+
expect_unique.call DT::DT_STRSZ,false #TODO: check that this a strtab and get a strtab
|
306
|
+
strtab_hdr= @sect_types[SHT::SHT_STRTAB].group_by(&:vaddr)[by_type[DT::DT_STRTAB].first.val].andand(&:first)
|
307
|
+
|
308
|
+
expect_value "Some STRTAB section should be mapped at DT_STRTAB", strtab_hdr.nil?,false
|
309
|
+
by_type.delete DT::DT_STRTAB
|
310
|
+
expect_value "STRSZ", by_type[DT::DT_STRSZ].first.val, strtab_hdr.siz
|
311
|
+
by_type.delete DT::DT_STRSZ
|
312
|
+
@dynstr = safe_strtab(strtab_hdr.index)
|
313
|
+
|
314
|
+
expect_unique.call DT::DT_SYMENT, false
|
315
|
+
expect_value "Dynamic SYMENT",by_type[DT::DT_SYMENT].first.val, @factory.sym.new.num_bytes
|
316
|
+
by_type.delete DT::DT_SYMENT
|
317
|
+
expect_unique.call DT::DT_SYMTAB, false
|
318
|
+
expect_value "Dynamic symbol table needs to be mapped", by_type[DT::DT_SYMTAB].first.val,@sect_types[SHT::SHT_DYNSYM].first.vaddr
|
319
|
+
by_type.delete DT::DT_SYMTAB
|
320
|
+
expect_unique.call DT::DT_HASH, true# We totally ignore the hash
|
321
|
+
by_type.delete DT::DT_HASH
|
322
|
+
expect_unique.call DT::DT_GNU_HASH,true
|
323
|
+
by_type.delete DT::DT_GNU_HASH
|
324
|
+
|
325
|
+
retval.needed = []
|
326
|
+
by_type[DT::DT_NEEDED].andand.each do |needed|
|
327
|
+
retval.needed << @dynstr[needed.val]
|
328
|
+
end
|
329
|
+
by_type.delete DT::DT_NEEDED
|
330
|
+
|
331
|
+
DYNAMIC_FLAGS.each do |tag, var|
|
332
|
+
val = false
|
333
|
+
expect_unique.call(tag,true) { |x| val = true}
|
334
|
+
instance_variable_set(var,val)
|
335
|
+
by_type.delete tag
|
336
|
+
end
|
337
|
+
|
338
|
+
|
339
|
+
progbits_by_addr = (@progbits+@nobits).group_by(&:addr) #TODO: check
|
340
|
+
#that vaddrs don't overlap
|
341
|
+
|
342
|
+
expect_unique.call(DT::DT_INIT,true).andand { |init|
|
343
|
+
# expect_value "DT_INIT should point to a valid progbits section",
|
344
|
+
# progbits_by_addr.include?(init.val), true
|
345
|
+
|
346
|
+
retval.init = init.val # progbits_by_addr[init.val].first
|
347
|
+
}
|
348
|
+
by_type.delete DT::DT_INIT
|
349
|
+
expect_unique.call(DT::DT_FINI,true).andand { |init|
|
350
|
+
# expect_value "DT_FINI should point to a valid progbits section",
|
351
|
+
# progbits_by_addr.include?(init.val), true
|
352
|
+
|
353
|
+
retval.fini = init.val #progbits_by_addr[init.val].first
|
354
|
+
}
|
355
|
+
by_type.delete DT::DT_FINI
|
356
|
+
|
357
|
+
expect_unique.call(DT::DT_INIT_ARRAY,true).andand{|initarray|
|
358
|
+
expect_value "DT_INITARRAY needs to point to a section", true, progbits_by_addr.include?(initarray.val)
|
359
|
+
sect = progbits_by_addr[initarray.val].first
|
360
|
+
expect_value "DT_INITARRAY section type", SHT::SHT_INIT_ARRAY,sect.sect_type
|
361
|
+
retval.init_array = sect
|
362
|
+
by_type.delete DT::DT_INIT_ARRAYSZ
|
363
|
+
}
|
364
|
+
by_type.delete DT::DT_INIT_ARRAY
|
365
|
+
expect_unique.call(DT::DT_FINI_ARRAY,true).andand{|finiarray|
|
366
|
+
expect_value "DT_FINIARRAY needs to point to a section", true, progbits_by_addr.include?(finiarray.val)
|
367
|
+
sect = progbits_by_addr[finiarray.val].first
|
368
|
+
expect_value "DT_FINIARRAY section type", SHT::SHT_FINI_ARRAY,sect.sect_type
|
369
|
+
retval.fini_array = sect
|
370
|
+
by_type.delete DT::DT_FINI_ARRAYSZ
|
371
|
+
}
|
372
|
+
by_type.delete DT::DT_FINI_ARRAY
|
373
|
+
expect_unique.call(DT::DT_PLTGOT,true).andand { |init|
|
374
|
+
expect_value "DT_PLTGOT should point to a valid progbits section",
|
375
|
+
progbits_by_addr.include?(init.val), true
|
376
|
+
|
377
|
+
retval.pltgot = progbits_by_addr[init.val].first
|
378
|
+
}#TODO: check processor supplements
|
379
|
+
by_type.delete DT::DT_PLTGOT
|
380
|
+
expect_unique.call(DT::DT_SONAME,true).andand {|soname|
|
381
|
+
retval.soname = @dynstr[soname.val]
|
382
|
+
}
|
383
|
+
by_type.delete DT::DT_SONAME
|
384
|
+
expect_unique.call(DT::DT_RPATH,true).andand{|rpath|
|
385
|
+
retval.rpath = @dynstr[rpath.val]
|
386
|
+
}
|
387
|
+
by_type.delete DT::DT_RPATH
|
388
|
+
#TODO: write 'expect_group'
|
389
|
+
expect_unique.call(DT::DT_RELA,true).andand{ |rela|
|
390
|
+
x= @sect_types[SHT::SHT_RELA].group_by{|x| x.vaddr.to_i}
|
391
|
+
expect_value "DT_RELA should point to a valid relocation section", x.include?(rela.val), true
|
392
|
+
#assert that no overlap?
|
393
|
+
reladyn_hdr = x[rela.val].first #TODO: Use parsed relocations!
|
394
|
+
expect_unique.call(DT::DT_RELAENT,false).andand {|relaent|
|
395
|
+
expect_value "DT_RELAENT size", relaent.val, @factory.rela.new.num_bytes
|
396
|
+
}
|
397
|
+
expect_unique.call(DT::DT_RELASZ,false).andand {|relasz|
|
398
|
+
expect_value "DT_RELASZ", relasz.val, reladyn_hdr.siz
|
399
|
+
}
|
400
|
+
# expect_unique.call(DT::DT_RELACOUNT,false).andand{|relacount|
|
401
|
+
# expect_value "DT_RELACOUNT", reladyn_hdr.siz / @factory.rela.new.num_bytes, relacount.val
|
402
|
+
# }
|
403
|
+
#RELACOUNT is a different beast
|
404
|
+
@reladyn_indices << reladyn_hdr.index
|
405
|
+
}
|
406
|
+
#TODO: maybe use eval to delete duplication?
|
407
|
+
expect_unique.call(DT::DT_REL,true).andand{ |rela|
|
408
|
+
x= @sect_types[SHT::SHT_REL].group_by{|x| x.vaddr.to_i}
|
409
|
+
expect_value "DT_REL should point to a valid relocation section", x.include?(rela.val), true
|
410
|
+
reladyn_hdr = x[rela.val] #TODO: Use parsed relocations!
|
411
|
+
expect_unique.call(DT::DT_RELENT,false).andand {|relaent|
|
412
|
+
expect_value "DT_RELENT size", relaent.val, @factory.rela.new.num_bytes
|
413
|
+
}
|
414
|
+
expect_unique.call(DT::DT_RELSZ,false).andand {|relasz|
|
415
|
+
expect_value "DT_RELSZ", relasz.val, reladyn_hdr.siz
|
416
|
+
}
|
417
|
+
@reladyn_indices << reladyn_hdr.index
|
418
|
+
}
|
419
|
+
[DT::DT_RELA, DT::DT_RELAENT, DT::DT_RELASZ, DT::DT_REL, DT::DT_RELENT, DT::DT_RELSZ, DT::DT_RELACOUNT].each {|x| by_type.delete x}
|
420
|
+
expect_unique.call(DT::DT_VERSYM,true).andand{|versym|
|
421
|
+
x = @sect_types[SHT::SHT_GNU_VERSYM].first
|
422
|
+
expect_value "One versym", @sect_types[SHT::SHT_GNU_VERSYM].size, 1
|
423
|
+
expect_value "SHT_GNU_VERSYM points to versym", x.vaddr == versym.val.to_i, true
|
424
|
+
}
|
425
|
+
by_type.delete DT::DT_VERSYM
|
426
|
+
expect_unique.call(DT::DT_VERDEF,true).andand{|verdef|
|
427
|
+
expect_value "Only one verdef", @sect_types[SHT::SHT_GNU_VERDEF].size, 1
|
428
|
+
x= @sect_types[SHT::SHT_GNU_VERDEF].first
|
429
|
+
expect_value "DT_VERDEF points to a SHT_GNU_VERDEF", x.vaddr.to_i, verdef.val.to_i
|
430
|
+
expect_unique.call(DT::DT_VERDEFNUM,false).andand{|verdefnum|
|
431
|
+
expect_value "DT_VERDEFNUM",x.info,verdefnum.val.to_i
|
432
|
+
}
|
433
|
+
}
|
434
|
+
expect_unique.call(DT::DT_VERNEED,true).andand{|verdef|
|
435
|
+
expect_value "Only one verdef", @sect_types[SHT::SHT_GNU_VERNEED].size, 1
|
436
|
+
x= @sect_types[SHT::SHT_GNU_VERNEED].first
|
437
|
+
expect_value "DT_VERNEED points to a SHT_GNU_VERNEED", x.vaddr.to_i, verdef.val.to_i
|
438
|
+
expect_unique.call(DT::DT_VERNEEDNUM,true).andand{|verdefnum|
|
439
|
+
expect_value "DT_VERNEEDNUM",x.info,verdefnum.val.to_i
|
440
|
+
}
|
441
|
+
}
|
442
|
+
[DT::DT_VERDEFNUM,DT::DT_VERNEEDNUM,DT::DT_VERNEED,DT::DT_VERDEF].each{|x| by_type.delete x}
|
443
|
+
#Parse RELA.plt or REL.plt
|
444
|
+
expect_unique.call(DT::DT_FLAGS,true).andand{|flags|
|
445
|
+
retval.flags = flags.val.to_i
|
446
|
+
}
|
447
|
+
expect_unique.call(DT::DT_FLAGS_1,true).andand{|flags1|
|
448
|
+
retval.flags1 = flags1.val.to_i
|
449
|
+
}
|
450
|
+
by_type.delete DT::DT_FLAGS
|
451
|
+
by_type.delete DT::DT_FLAGS_1
|
452
|
+
expect_unique.call(DT::DT_JMPREL,true).andand{ |rela| #TODO:Make
|
453
|
+
#this better too!!!
|
454
|
+
@jmprel_addr = rela.val
|
455
|
+
expect_unique.call(DT::DT_PLTREL,false).andand {|pltrel|
|
456
|
+
if pltrel.val == DT::DT_RELA
|
457
|
+
type = SHT::SHT_RELA
|
458
|
+
elsif pltrel.val == DT::DT_REL
|
459
|
+
type = SHT::SHT_REL
|
460
|
+
else
|
461
|
+
raise ArgumentError.new "Invalid DT_PLTREL"
|
462
|
+
end
|
463
|
+
x= @sect_types[type].group_by{|x| x.vaddr.to_i}
|
464
|
+
expect_value "DT_PLREL should point to a valid relocation section", x.include?(rela.val), true
|
465
|
+
reladyn_hdr = x[rela.val].first
|
466
|
+
#TODO: Use parsed #relocations!
|
467
|
+
expect_unique.call(DT::DT_PLTRELSZ,false).andand {|relasz|
|
468
|
+
expect_value "DT_PLTRELSZ", relasz.val, reladyn_hdr.siz
|
469
|
+
}
|
470
|
+
@reladyn_indices << reladyn_hdr.index
|
471
|
+
by_type.delete DT::DT_PLTRELSZ
|
472
|
+
}
|
473
|
+
by_type.delete DT::DT_PLTREL
|
474
|
+
}
|
475
|
+
by_type.delete DT::DT_JMPREL
|
476
|
+
|
477
|
+
retval.debug_val = []
|
478
|
+
(by_type[DT::DT_DEBUG] || []).each {|x| retval.debug_val << x.val}
|
479
|
+
by_type.delete DT::DT_DEBUG
|
480
|
+
|
481
|
+
#TODO: gnu extensions
|
482
|
+
retval.extra_dynamic = by_type.values.flatten.map(&:snapshot)
|
483
|
+
unless by_type.empty?
|
484
|
+
print "Warning, unparsed dynamic entries \n"
|
485
|
+
pp by_type
|
486
|
+
end
|
487
|
+
retval
|
488
|
+
end
|
489
|
+
def parse_note(note)
|
490
|
+
note_name = @shstrtab[note.name] || ".note.unk.#{note.off}"
|
491
|
+
@data.seek note.off
|
492
|
+
expect_value "Note alignment", note.addralign, NOTE_ALIGN
|
493
|
+
expect_value "Note flags", note.flags, NOTE_FLAGS
|
494
|
+
expect_value "Note entsize", note.entsize, NOTE_ENTSIZE
|
495
|
+
@unparsed_sections.delete @data
|
496
|
+
[note_name, @factory.note.read(@data).tap {|n|
|
497
|
+
expect_value "Note size",n.num_bytes, note.siz
|
498
|
+
} ]
|
499
|
+
end
|
500
|
+
|
501
|
+
def parse_phdrs()
|
502
|
+
#TODO: validate flags
|
503
|
+
by_type = @phdrs.group_by{|x| x.type.to_i}
|
504
|
+
by_type.delete PT::PT_NULL
|
505
|
+
process_unique = lambda do |sym| # Validates that either one
|
506
|
+
# or zero entries of this type exist, returning the one entry
|
507
|
+
# if it exists
|
508
|
+
if(by_type.include? sym)
|
509
|
+
expect_value "PHDR #{sym} count", by_type[sym].size,1
|
510
|
+
by_type[sym].first.tap { by_type.delete sym }
|
511
|
+
else
|
512
|
+
nil
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
process_unique.call(PT::PT_PHDR).andand do |pt_phdr|
|
517
|
+
expect_value "PHD offset",pt_phdr.off, @hdr.phoff
|
518
|
+
expect_value "PHDR size",pt_phdr.filesz, @hdr.phnum * @hdr.phentsize
|
519
|
+
end
|
520
|
+
|
521
|
+
by_type.delete PT::PT_LOAD # TODO:: validate range and that
|
522
|
+
# section vaddr is correct!
|
523
|
+
=begin all notes go into one or multiple program headers.
|
524
|
+
by_type[PT::PT_NOTE].each {|note|
|
525
|
+
expect_value "SHT_NOTE at this address",
|
526
|
+
@sect_types[SHT::SHT_NOTE].find{|n| note.vaddr.to_i == n.vaddr.to_i}.andand {|n|
|
527
|
+
[n.off.to_i,n.siz.to_i]
|
528
|
+
}, [note.off.to_i,note.filesz.to_i] }
|
529
|
+
=end
|
530
|
+
by_type.delete PT::PT_NOTE
|
531
|
+
|
532
|
+
process_unique.call(PT::PT_INTERP).andand do |pt_interp| #Technically
|
533
|
+
#not needed according to spec, INTERP doesn't need to have its
|
534
|
+
#own section. Instead just check what is at that vaddr
|
535
|
+
interp_section = @progbits.select {|x| x.addr == pt_interp.vaddr.to_i}.first
|
536
|
+
expect_value ".interp section", interp_section.nil?, false
|
537
|
+
@file.interp = BinData::Stringz.read(interp_section.data.read).snapshot
|
538
|
+
@progbits.delete interp_section
|
539
|
+
end
|
540
|
+
process_unique.call(PT::PT_DYNAMIC).andand do |pt_dynamic|
|
541
|
+
dynamic_section = @sect_types[SHT::SHT_DYNAMIC].first
|
542
|
+
expect_value "PT_dynamic address", pt_dynamic.vaddr, dynamic_section.vaddr
|
543
|
+
expect_value "PT_dynamic offset" , pt_dynamic.off, dynamic_section.off
|
544
|
+
expect_value "PT_dynamic size", pt_dynamic.filesz, dynamic_section.siz
|
545
|
+
end
|
546
|
+
@file.extra_phdrs = by_type.values.flatten
|
547
|
+
unless(@file.extra_phdrs.empty?)
|
548
|
+
print "Unparsed PHDR\n"
|
549
|
+
pp @file.extra_phdrs
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
def parse_with_factory()
|
554
|
+
@data.rewind
|
555
|
+
@hdr = @factory.hdr.read(@data)
|
556
|
+
@file.filetype = @hdr.type
|
557
|
+
@file.machine = @hdr.machine
|
558
|
+
@file.version = @hdr.version # Shouldn't this always be the current one
|
559
|
+
@file.flags = @hdr.flags
|
560
|
+
@file.entry = @hdr.entry
|
561
|
+
expect_value "ELF version",@file.version, ElfFlags::Version::EV_CURRENT
|
562
|
+
#pp hdr.snapshot
|
563
|
+
if @hdr.phnum != 0
|
564
|
+
expect_value "PHT size", @factory.phdr.new.num_bytes, @hdr.phentsize
|
565
|
+
@data.seek @hdr.phoff
|
566
|
+
@phdrs = BinData::Array.new(:type => @factory.phdr, :initial_length => @hdr.phnum)
|
567
|
+
@phdrs.read(@data)
|
568
|
+
else
|
569
|
+
@phdrs =[]
|
570
|
+
end
|
571
|
+
|
572
|
+
@data.seek @hdr.shoff
|
573
|
+
@shdrs = BinData::Array.new(:type => @factory.shdr, :initial_length => @hdr.shnum)
|
574
|
+
@shdrs.read(@data)
|
575
|
+
@unparsed_sections = Set.new []
|
576
|
+
expect_value "SHT size", @shdrs[0].num_bytes, @hdr.shentsize
|
577
|
+
@shstrtab = safe_strtab(@hdr.shstrndx)
|
578
|
+
|
579
|
+
@shdrs.to_enum.with_index.each do |elem, i|
|
580
|
+
elem.index = i
|
581
|
+
@unparsed_sections.add i
|
582
|
+
unless elem.flags & SHF::SHF_ALLOC
|
583
|
+
expect_value "Unallocated section address", elem.vaddr, 0
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
|
588
|
+
#Keep a hash of sections by type
|
589
|
+
@sect_types = @shdrs.group_by {|x| x.type.to_i}
|
590
|
+
#TODO: keep track which #sections we have already parsed to find unparsed sections
|
591
|
+
@bits_by_index = Hash.new.tap{|h| (@sect_types.values_at SHT::SHT_PROGBITS,SHT::SHT_NOBITS,SHT::SHT_INIT_ARRAY,SHT::SHT_FINI_ARRAY).reject(&:nil?).flatten.each { |s| h[s.index] = parse_progbits(s)} }
|
592
|
+
@progbits = @bits_by_index.values.select {|x| x.sect_type != SHT::SHT_NOBITS}
|
593
|
+
#TODO: just make NOBITS progbits for now
|
594
|
+
@file.progbits = @progbits
|
595
|
+
|
596
|
+
@progbits.select{|x| x.flags & SHF::SHF_TLS != 0}.each {|tdata|
|
597
|
+
@file.gnu_tls ||= TLS.new
|
598
|
+
expect_value "Only one .tdata per file",@file.gnu_tls.tdata.nil?,true
|
599
|
+
@file.gnu_tls.tdata = tdata
|
600
|
+
tdata.phdr = PT::PT_TLS
|
601
|
+
tdata.phdr_flags = PF::PF_R
|
602
|
+
}
|
603
|
+
@nobits = @bits_by_index.values.select {|x| x.sect_type == SHT::SHT_NOBITS}
|
604
|
+
@nobits.each{|nobit|
|
605
|
+
if nobit.flags & SHF::SHF_TLS != 0
|
606
|
+
@file.gnu_tls ||= TLS.new
|
607
|
+
@file.gnu_tls.tbss = nobit
|
608
|
+
else
|
609
|
+
pagesize = 4096
|
610
|
+
pagemask = 4095
|
611
|
+
nobit.size += pagesize - ( (nobit.size + nobit.addr.to_i) % pagesize)#HACK: This makes the BSS
|
612
|
+
expect_value "Nobit end alignment", (nobit.addr + nobit.size) % pagesize, 0
|
613
|
+
#section span a page, which it implicitly does due to the semantics of the mmap call
|
614
|
+
#HACK: TODO: Hardcoded pagesize
|
615
|
+
end
|
616
|
+
}
|
617
|
+
@file.nobits = @nobits
|
618
|
+
|
619
|
+
@relocatable_sections = SegmentTree.new(Hash.new.tap{|h|
|
620
|
+
(@progbits + @nobits).each{ |pb|
|
621
|
+
h[(pb.addr)..(pb.addr + pb.size)]=pb
|
622
|
+
}
|
623
|
+
})
|
624
|
+
|
625
|
+
parse_phdrs()
|
626
|
+
|
627
|
+
@file.dynamic = unique_section(@sect_types, ElfFlags::SectionType::SHT_DYNAMIC).andand{|dynamic| parse_dynamic dynamic} || Elf::Dynamic.new
|
628
|
+
|
629
|
+
@symtab = unique_section(@sect_types, ElfFlags::SectionType::SHT_SYMTAB).andand {|symtab| parse_symtable symtab, safe_strtab(symtab.link) } || []
|
630
|
+
@dynsym = unique_section(@sect_types, ElfFlags::SectionType::SHT_DYNSYM).andand {|symtab| parse_symtable symtab, safe_strtab(symtab.link) } || []
|
631
|
+
|
632
|
+
unique_section(@sect_types,ElfFlags::SectionType::SHT_GNU_VERNEED).andand{|verneed| parse_verneed verneed}
|
633
|
+
unique_section(@sect_types,ElfFlags::SectionType::SHT_GNU_VERDEF).andand{|verdef| parse_verdef verdef}
|
634
|
+
unique_section(@sect_types,ElfFlags::SectionType::SHT_GNU_VERSYM).andand{|versym|
|
635
|
+
expect_value "Need a dynsym when we have versym", @dynsym.nil?, false
|
636
|
+
parse_versym versym,@dynsym
|
637
|
+
}
|
638
|
+
#TODO: Parse versions in static symbols
|
639
|
+
@file.symbols = SymbolTable.new.tap{|h| (@symtab || []).each{|sym|
|
640
|
+
h<< sym if sym.name != "" #TODO: Represent nameless symbols - why did we drop these?
|
641
|
+
}}
|
642
|
+
if(@file.symbols.include? "_DYNAMIC" or @file.dynamic.soname =~ /^ld/) #HACK: This is how we detect ld.so and friends
|
643
|
+
@file.pinned_sections ||= {}
|
644
|
+
dyn_section = unique_section(@sect_types,Elf::SHT::SHT_DYNAMIC)
|
645
|
+
#expect_value "_DYNAMIC symbol points to dynamic section", @file.symbols["_DYNAMIC"].section, nil
|
646
|
+
#expect_value "_DYNAMIC symbol points to dynamic section", @file.symbols["_DYNAMIC"].sectoffset, dyn_section.vaddr.to_i
|
647
|
+
@file.pinned_sections[".dynamic"] = {vaddr: dyn_section.vaddr.to_i, size: dyn_section.siz.to_i}
|
648
|
+
end
|
649
|
+
dyn_by_name = {}
|
650
|
+
@dynsym.each{|d|
|
651
|
+
d.is_dynamic= true
|
652
|
+
dyn_by_name[d.name]||=[];
|
653
|
+
dyn_by_name[d.name] << d
|
654
|
+
}
|
655
|
+
@canonical_symbol = {}
|
656
|
+
@file.symbols.each {|staticsym|
|
657
|
+
@canonical_symbol[staticsym] = staticsym
|
658
|
+
next if staticsym.name == ""
|
659
|
+
next if staticsym.visibility == STB::STB_LOCAL
|
660
|
+
dyn_by_name[staticsym.name].andand.each {|sym|
|
661
|
+
next if sym.sectoffset != dynsym.sectoffset
|
662
|
+
next if sym.section != dynsym.section
|
663
|
+
@canonical_symbol[dynsym] = sym
|
664
|
+
staticsym.is_dynamic = true
|
665
|
+
staticsym.gnu_version = sym.gnu_version
|
666
|
+
expect_value "Dynamic #{sym.name} size", sym.size, staticsym.size
|
667
|
+
}
|
668
|
+
}
|
669
|
+
@dynsym.each {|sym|
|
670
|
+
unless @canonical_symbol.include? sym
|
671
|
+
@canonical_symbol[sym]= sym
|
672
|
+
@file.symbols << sym
|
673
|
+
end
|
674
|
+
}
|
675
|
+
# symtab_array = @file.symbols.to_a
|
676
|
+
# @canonical_symbol.each_value{|x| expect_value "x",symtab_array.include?(x), true}
|
677
|
+
|
678
|
+
rels_addrs = [ET::ET_EXEC, ET::ET_DYN].include? @hdr.type
|
679
|
+
rel = (@sect_types[SHT::SHT_RELA] || []).map {|rela| [rela.index, parse_rela(rela,rels_addrs)] }+ (@sect_types[SHT::SHT_REL] || []).map{|rel| [rela.index,parse_rel(rela,rels_addrs)]}
|
680
|
+
|
681
|
+
rels_by_index = Hash[*rel.flatten(1)]
|
682
|
+
@reladyn_indices.each {|reladyn|
|
683
|
+
rels_by_index[reladyn].each{|r|
|
684
|
+
r.is_dynamic = true
|
685
|
+
}
|
686
|
+
}
|
687
|
+
|
688
|
+
|
689
|
+
|
690
|
+
|
691
|
+
|
692
|
+
@file.notes = Hash[(@sect_types[SHT::SHT_NOTE] || []).map{|note| parse_note note}]
|
693
|
+
#TODO: expect non-nil dynamic for some types
|
694
|
+
@file.relocations = rels_by_index.values.flatten
|
695
|
+
|
696
|
+
#TODO: Validate flags
|
697
|
+
#TODO: Validate header?
|
698
|
+
|
699
|
+
end
|
700
|
+
end
|
701
|
+
def self.from_file(filename)
|
702
|
+
contents = IO.read(filename)
|
703
|
+
Parser.new(contents).file
|
704
|
+
end
|
705
|
+
end
|
706
|
+
end
|