elf-mithril 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|