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
@@ -0,0 +1,335 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
#Nothing to see here. For background, see Dartmouth College CS
|
3
|
+
#TR2013-727 (ELFbac)
|
4
|
+
module Elf
|
5
|
+
module Policy
|
6
|
+
def self.section_symbol_name(file_name,section_name)
|
7
|
+
"_elfp_#{file_name}_#{section_name}"
|
8
|
+
end
|
9
|
+
R = ElfFlags::Relocation
|
10
|
+
ELFP = ElfFlags::ElfPData
|
11
|
+
class Transition
|
12
|
+
attr_accessor :from, :to
|
13
|
+
end
|
14
|
+
class Call < Transition
|
15
|
+
attr_accessor :symbol
|
16
|
+
attr_accessor :parambytes,:returnbytes
|
17
|
+
def initialize(from,to, symbol, parambytes, returnbytes)
|
18
|
+
@from, @to, @symbol, @parambytes, @returnbytes = from,to, symbol, parambytes, returnbytes
|
19
|
+
end
|
20
|
+
def allows_return?
|
21
|
+
@returnbytes >= 0
|
22
|
+
end
|
23
|
+
end
|
24
|
+
class Data < Transition
|
25
|
+
attr_accessor :tag
|
26
|
+
attr_accessor :read,:write, :exec
|
27
|
+
def initialize(from,to,tag , read=false ,write=false,exec=false)
|
28
|
+
@from, @to, @tag, @read,@write,@exec = from,to,tag,read,write,exec
|
29
|
+
end
|
30
|
+
end
|
31
|
+
class MemoryRange
|
32
|
+
attr_accessor :low, :high
|
33
|
+
def initialize(from,to)
|
34
|
+
@low,@high = from,to
|
35
|
+
end
|
36
|
+
end
|
37
|
+
class Policy
|
38
|
+
attr_accessor :data, :calls, :start, :tags
|
39
|
+
attr_accessor :imported_symbols
|
40
|
+
def states
|
41
|
+
t = data.keys + data.values.map(&:keys).flatten + calls.map(&:from) +
|
42
|
+
calls.map(&:to)
|
43
|
+
t.uniq
|
44
|
+
end
|
45
|
+
def <<(*transitions)
|
46
|
+
transitions.each do |t|
|
47
|
+
if t.is_a? Data
|
48
|
+
x= data
|
49
|
+
x[t.from] ||= {}
|
50
|
+
x[t.from][t.to] ||= {}
|
51
|
+
x[t.from][t.to][t.tag] ||= t
|
52
|
+
x[t.from][t.to][t.tag].read ||= t.read
|
53
|
+
x[t.from][t.to][t.tag].write ||= t.write
|
54
|
+
x[t.from][t.to][t.tag].exec ||= t.exec
|
55
|
+
elsif t.is_a? Call
|
56
|
+
calls << t
|
57
|
+
else
|
58
|
+
raise ArgumentError.new "#{t.class} is not a valid transition"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
def initialize
|
63
|
+
@data={}
|
64
|
+
@calls=[]
|
65
|
+
@states ={}
|
66
|
+
@tags = {}
|
67
|
+
@imported_symbols = {}
|
68
|
+
end
|
69
|
+
def resolve_reference(elffile, relocations,offset, ref)
|
70
|
+
if(ref.is_a? Integer)
|
71
|
+
ref.to_i
|
72
|
+
elsif(ref == "_dl_runtime_resolve") #HACK:HACK:HACK: I couldn't hack ld.so to fix this, so
|
73
|
+
#here comes a nasty hack
|
74
|
+
#note that the address of _dl_runtime_resolve is 16 bytes into PLT.GOT
|
75
|
+
if !elffile.symbols.include? "_elfp_hidden_trampolineaddr"
|
76
|
+
pltgot = elffile.dynamic.pltgot or raise RuntimeError.new "No plt.got for _dl_runtime_resolve hack"
|
77
|
+
elffile.symbols << Elf::Symbol.new("_elfp_hidden_trampolineaddr", pltgot,STT::STT_OBJECT,16, STB::STB_LOCAL,8)
|
78
|
+
end
|
79
|
+
symb = elffile.symbols["_elfp_hidden_trampolineaddr"]
|
80
|
+
relocations << Elf::Relocation.new.tap{|x|
|
81
|
+
x.type = R::R_X86_64_COPY
|
82
|
+
x.offset = offset
|
83
|
+
x.symbol = symb
|
84
|
+
x.is_dynamic = true
|
85
|
+
x.addend = 0
|
86
|
+
}
|
87
|
+
0xDEADBEEF
|
88
|
+
else
|
89
|
+
raise RuntimeError.new "Symbol #{ref} not found" unless elffile.symbols.include? ref
|
90
|
+
relocations << Elf::Relocation.new.tap{|x|
|
91
|
+
x.type = R::R_X86_64_64
|
92
|
+
x.offset = offset
|
93
|
+
x.symbol = elffile.symbols[ref]
|
94
|
+
x.is_dynamic = true
|
95
|
+
x.addend = 0
|
96
|
+
}
|
97
|
+
2**64-1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
def resolve_size(elffile,relocations, offset, ref)
|
101
|
+
if(ref.is_a? Integer)
|
102
|
+
ref.to_i
|
103
|
+
else
|
104
|
+
raise RuntimeError.new "Symbol #{ref} not found" unless elffile.symbols.include? ref
|
105
|
+
relocations << Elf::Relocation.new.tap{|x|
|
106
|
+
x.type = R::R_X86_64_SIZE64
|
107
|
+
x.offset = offset
|
108
|
+
x.symbol = elffile.symbols[ref]
|
109
|
+
x.is_dynamic = true
|
110
|
+
x.addend = 0
|
111
|
+
}
|
112
|
+
2**64-1
|
113
|
+
end
|
114
|
+
end
|
115
|
+
def write_amd64(elffile)
|
116
|
+
factory = ElfStructFactory.instance(:little,64)
|
117
|
+
@imported_symbols.each_key {|symbol|
|
118
|
+
if elffile.symbols.include?(symbol)
|
119
|
+
elffile.symbols[symbol].is_dynamic = true
|
120
|
+
else
|
121
|
+
elffile.symbols << Elf::Symbol.new(symbol,nil,Elf::STT::STT_OBJECT, 0, Elf::STB::STB_GLOBAL, 0).tap {|x|
|
122
|
+
x.semantics = Elf::SHN::SHN_UNDEF
|
123
|
+
}
|
124
|
+
end
|
125
|
+
}
|
126
|
+
out = factory.elfp_header.new()
|
127
|
+
state_ids = {}
|
128
|
+
tag_ids = {}
|
129
|
+
relocations = []
|
130
|
+
states = states()
|
131
|
+
@start = states.first unless states.include? @start
|
132
|
+
#These have to be filled in the order in which they are written
|
133
|
+
#FIXME: Make these aware of double transitions to the same range/ state
|
134
|
+
states.each_with_index do |state,index|
|
135
|
+
id = index + 2
|
136
|
+
id = 1 if @start == state
|
137
|
+
out.states << factory.elfp_state.new.tap {|x|
|
138
|
+
x.id = id
|
139
|
+
x.stackid = 0
|
140
|
+
}
|
141
|
+
state_ids[state] = id
|
142
|
+
print "State #{state} #{id}\n"
|
143
|
+
end
|
144
|
+
tag_ids[:default] = 0
|
145
|
+
@tags.each_with_index do |(name,ranges),index|
|
146
|
+
tag_ids[name] = index+1
|
147
|
+
ranges.each do |data|
|
148
|
+
out.tags << factory.elfp_tag.new.tap {|x|
|
149
|
+
x.tag = index + 1
|
150
|
+
x.addr = 0
|
151
|
+
x.siz = 0
|
152
|
+
}
|
153
|
+
out.tags.last.tap {|x|
|
154
|
+
x.addr = resolve_reference(elffile,relocations,x.addr.offset,data.low)
|
155
|
+
if data.high.nil?
|
156
|
+
x.siz = resolve_size(elffile,relocations,x.siz.offset,data.low)
|
157
|
+
else
|
158
|
+
pp "Warning, emitting SIZE symbol with value #{ data.high.to_i rescue data.high.name}"
|
159
|
+
x.siz = resolve_reference(elffile,relocations,x.siz.offset,data.high)
|
160
|
+
end
|
161
|
+
}
|
162
|
+
end
|
163
|
+
print "Tag #{name} #{index + 1} \n"
|
164
|
+
end
|
165
|
+
self.calls.each do |call|
|
166
|
+
out.calls << factory.elfp_call.new.tap {|x|
|
167
|
+
x.from = state_ids[call.from]
|
168
|
+
x.to = state_ids[call.to]
|
169
|
+
x.parambytes = call.parambytes
|
170
|
+
x.returnbytes = call.parambytes
|
171
|
+
}
|
172
|
+
out.calls.last.off = resolve_reference(elffile,relocations,out.calls.last.off.offset, call.symbol)
|
173
|
+
end
|
174
|
+
self.data.values.map(&:values).flatten.map(&:values).flatten.each do |data|
|
175
|
+
out.data << factory.elfp_data.new.tap {|x|
|
176
|
+
x.from = state_ids[data.from]
|
177
|
+
x.to = state_ids[data.to]
|
178
|
+
x.type = 0
|
179
|
+
x.type |= ELFP::ELFP_RW_READ if data.read
|
180
|
+
x.type |= ELFP::ELFP_RW_WRITE if data.write
|
181
|
+
x.type |= ELFP::ELFP_RW_EXEC if data.exec
|
182
|
+
raise RuntimeError.new "Unknown tag #{data.tag}" unless tag_ids.include? data.tag
|
183
|
+
x.tag = tag_ids[data.tag]
|
184
|
+
}
|
185
|
+
end
|
186
|
+
out = Elf::ProgBits.new(".elfbac",nil,out.to_binary_s)
|
187
|
+
out.align = 8
|
188
|
+
out.flags = SHF::SHF_ALLOC | SHF::SHF_WRITE
|
189
|
+
out.sect_type = SHT::SHT_PROGBITS
|
190
|
+
out.phdr = ElfFlags::PhdrType::PT_ELFBAC
|
191
|
+
out.phdr_flags = ElfFlags::PhdrFlags::PF_R
|
192
|
+
relocations.each { |rel|
|
193
|
+
rel.section = out
|
194
|
+
elffile.relocations << rel
|
195
|
+
}
|
196
|
+
elffile.progbits << out
|
197
|
+
|
198
|
+
end
|
199
|
+
def inject(file)
|
200
|
+
case file.machine
|
201
|
+
when ElfFlags::Machine::EM_X86_64
|
202
|
+
write_amd64(file)
|
203
|
+
else
|
204
|
+
raise RuntimeError.new "Wrong architecture for ARM64"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
module BuilderHelper
|
209
|
+
def section_start(name, file_name="")
|
210
|
+
Elf::Policy.section_symbol_name(file_name,name).tap{|x| @policy.imported_symbols[x] = true}
|
211
|
+
end
|
212
|
+
end
|
213
|
+
class TagBuilder
|
214
|
+
include BuilderHelper
|
215
|
+
attr_accessor :ranges
|
216
|
+
def initialize(pol)
|
217
|
+
@policy = pol
|
218
|
+
@ranges = []
|
219
|
+
end
|
220
|
+
def section(name,file_name="")
|
221
|
+
range(section_start(name,file_name))
|
222
|
+
end
|
223
|
+
def range(low,high=nil)
|
224
|
+
@ranges << MemoryRange.new(low,high)
|
225
|
+
end
|
226
|
+
def symbol(sym)
|
227
|
+
range(sym)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
class DataBuilder
|
231
|
+
include BuilderHelper
|
232
|
+
def initialize(transition)
|
233
|
+
@transition = transition
|
234
|
+
end
|
235
|
+
def read(v=true) #TODO: Unify transitions? Intervaltree?
|
236
|
+
@transition.read = v
|
237
|
+
end
|
238
|
+
def write(v=true)
|
239
|
+
@transition.write = v
|
240
|
+
@transition.read ||= v
|
241
|
+
end
|
242
|
+
def exec(v=true)
|
243
|
+
@transition.exec = v
|
244
|
+
@transition.read ||= v
|
245
|
+
end
|
246
|
+
end
|
247
|
+
class StateBuilder
|
248
|
+
include BuilderHelper
|
249
|
+
def initialize(from,to,pol)
|
250
|
+
@from = from
|
251
|
+
@to = to
|
252
|
+
@policy = pol
|
253
|
+
end
|
254
|
+
|
255
|
+
{
|
256
|
+
text: ".text",
|
257
|
+
data: ".data",
|
258
|
+
bss:".bss"
|
259
|
+
}.each{|function,name|
|
260
|
+
define_method function, lambda{|library=''| section(name,library)}
|
261
|
+
}
|
262
|
+
def call(symbol, parambytes= 0, returnbytes=0)
|
263
|
+
raise RuntimeError.new "Call has to have a destination" if @from == @to
|
264
|
+
@policy << Call.new(@from,@to, symbol, parambytes, returnbytes)
|
265
|
+
end
|
266
|
+
def call_noreturn(symbol,parambytes=0)
|
267
|
+
call(symbol, parambytes,-1)
|
268
|
+
end
|
269
|
+
def mem(tag, &block)
|
270
|
+
d = Data.new(@from,@to,tag)
|
271
|
+
DataBuilder.new(d).instance_eval(&block)
|
272
|
+
@policy << d
|
273
|
+
end
|
274
|
+
def exec(tag)
|
275
|
+
mem(tag){
|
276
|
+
exec }
|
277
|
+
end
|
278
|
+
def read(tag)
|
279
|
+
mem(tag){
|
280
|
+
read
|
281
|
+
}
|
282
|
+
end
|
283
|
+
def write(tag)
|
284
|
+
mem(tag){
|
285
|
+
write
|
286
|
+
}
|
287
|
+
end
|
288
|
+
def readwrite(tag)
|
289
|
+
mem(tag){
|
290
|
+
read
|
291
|
+
write
|
292
|
+
}
|
293
|
+
end
|
294
|
+
|
295
|
+
def to(name,&block)
|
296
|
+
raise RuntimeError.new "Cannot nest to{} blocks" if @from != @to# or name == @from
|
297
|
+
StateBuilder.new(@from,name, @policy).instance_eval(&block)
|
298
|
+
end
|
299
|
+
def transition(trans)
|
300
|
+
@policy << trans
|
301
|
+
end
|
302
|
+
end
|
303
|
+
class PolicyBuilder
|
304
|
+
include BuilderHelper
|
305
|
+
attr_reader :policy
|
306
|
+
def initialize()
|
307
|
+
@policy = Policy.new()
|
308
|
+
end
|
309
|
+
def state(name, &block)
|
310
|
+
StateBuilder.new(name,name,@policy).instance_eval(&block)
|
311
|
+
end
|
312
|
+
def tag(name, &block)
|
313
|
+
policy.tags[name] ||= []
|
314
|
+
x =TagBuilder.new(@policy)
|
315
|
+
x.instance_eval(&block)
|
316
|
+
policy.tags[name] += x.ranges
|
317
|
+
end
|
318
|
+
def call_noreturn(from,to,symbol, parambytes=0)
|
319
|
+
@policy << Call.new(from,to, symbol, parambytes,-1)
|
320
|
+
end
|
321
|
+
def read
|
322
|
+
@policy << Data.new(from,to,from,to )
|
323
|
+
end
|
324
|
+
def start(name)
|
325
|
+
@policy.start = name
|
326
|
+
end
|
327
|
+
end
|
328
|
+
def self.build(&block)
|
329
|
+
x= PolicyBuilder.new()
|
330
|
+
x.instance_eval(&block)
|
331
|
+
x.policy
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
@@ -0,0 +1,774 @@
|
|
1
|
+
require_relative 'elf'
|
2
|
+
module Elf
|
3
|
+
module Writer
|
4
|
+
def self.to_file(filename,elf)
|
5
|
+
Elf::Writer::Writer.to_file(filename,elf)
|
6
|
+
end
|
7
|
+
def self.elf_hash(value)
|
8
|
+
h=0
|
9
|
+
g=0
|
10
|
+
value.chars.map(&:ord).each {|char|
|
11
|
+
h = ((h << 4) + (char % 256) )
|
12
|
+
g = h & 0xf0000000
|
13
|
+
if g!=0
|
14
|
+
h = h ^ (g>> 24) # This simulates overflow; elf spec is clever
|
15
|
+
end
|
16
|
+
h &= ~g
|
17
|
+
}
|
18
|
+
h
|
19
|
+
end
|
20
|
+
def self.gnu_hash(value)
|
21
|
+
h = 5381
|
22
|
+
value.chars.map(&:ord).each {|char|
|
23
|
+
h = (h*33 + char) & 0xffffffff
|
24
|
+
}
|
25
|
+
pp "Gnu_hash #{value} #{h}"
|
26
|
+
h
|
27
|
+
end
|
28
|
+
class StringTable #Replace with compacting string table
|
29
|
+
attr_reader :buf
|
30
|
+
def initialize
|
31
|
+
@buf = StringIO.new("\0")
|
32
|
+
@buf.seek 1
|
33
|
+
@strings = {} #TODO: Do substring matching, compress the string
|
34
|
+
#table.
|
35
|
+
# Actually, make all string tables except dynstr one, might save
|
36
|
+
# a bit
|
37
|
+
end
|
38
|
+
def add_string(string)
|
39
|
+
unless @strings.include? string
|
40
|
+
@strings[string] = @buf.tell.tap {|x|
|
41
|
+
BinData::Stringz::new(string).write(@buf)
|
42
|
+
}
|
43
|
+
end
|
44
|
+
@strings[string]
|
45
|
+
end
|
46
|
+
def buf()
|
47
|
+
@buf
|
48
|
+
end
|
49
|
+
end
|
50
|
+
class OutputSection
|
51
|
+
attr_accessor :name, :type,:flags,:vaddr,:siz,:link,:info, :align, :entsize, :data, :shdr, :off
|
52
|
+
attr_accessor :index
|
53
|
+
# TODO: Use params array
|
54
|
+
def initialize(name,type,flags,vaddr,siz,link,info,align,entsize,data) #link and info are strings, offset is done by output stage
|
55
|
+
@name,@type,@flags, @vaddr, @siz, @link, @info, @align, @entsize, @data= name,type,flags,vaddr,siz,link,info,align,entsize, data
|
56
|
+
@off = nil
|
57
|
+
@index = nil
|
58
|
+
end
|
59
|
+
def end
|
60
|
+
@vaddr + @siz
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
class Layout
|
66
|
+
def initialize(factory)
|
67
|
+
@layout_by_flags = {}
|
68
|
+
@layout = RBTree.new()
|
69
|
+
@shstrtab = StringTable.new()
|
70
|
+
@factory = factory
|
71
|
+
@phdrs = [] #BinData::Array.new(:type => @factory.phdr)
|
72
|
+
@unallocated =[]
|
73
|
+
@sections = [OutputSection.new("", SHT::SHT_NULL, 0,0,0,0,0,0,0,"")]
|
74
|
+
@sections[0].index = 0
|
75
|
+
|
76
|
+
@pinned_sections = {} # Name to vaddr
|
77
|
+
end
|
78
|
+
def pin_section(name,size,vaddr)
|
79
|
+
x = OutputSection.new(".pin_dummy",SHT::SHT_NULL,0,vaddr,0,0,0,0,0,
|
80
|
+
BinData::Array.new(type: :int8, initial_length: size).to_binary_s)
|
81
|
+
raise RuntimeError.new "Invalid pin" unless range_available?(@layout,vaddr,vaddr+size)
|
82
|
+
@layout[vaddr] = x
|
83
|
+
@pinned_sections[name] = vaddr
|
84
|
+
end
|
85
|
+
def [](idx)
|
86
|
+
@sections[idx]
|
87
|
+
end
|
88
|
+
def add(*sections) #Ordering as follows: Fixed
|
89
|
+
#(non-nil vaddrs) go where they have to go
|
90
|
+
# Flexible sections are added to lowest hole after section of
|
91
|
+
# the same type
|
92
|
+
retval = []
|
93
|
+
return [] if sections.empty?
|
94
|
+
sections.each{|sect|
|
95
|
+
sect.index = @sections.size
|
96
|
+
expect_value "Correct size", sect.data.bytesize, sect.siz
|
97
|
+
@sections << sect
|
98
|
+
retval << sect.index
|
99
|
+
}
|
100
|
+
flags = sections.first.flags
|
101
|
+
@layout_by_flags[flags] ||= RBTree.new()
|
102
|
+
if sections.length > 1 || sections.first.vaddr.nil?
|
103
|
+
sections.each { |i|
|
104
|
+
expect_value "All adjacent sections have the same flags", i.flags,flags
|
105
|
+
expect_value "All sections must float", i.vaddr, nil
|
106
|
+
}
|
107
|
+
if sections.length == 1 && @pinned_sections.include?(sections.first.name)
|
108
|
+
sections.first.vaddr = @pinned_sections[sections.first.name]
|
109
|
+
@layout.delete sections.first.vaddr
|
110
|
+
@pinned_sections.delete @sections.first.name
|
111
|
+
else
|
112
|
+
allocate_sections(sections)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
#TODO: Handle the damn nobits
|
116
|
+
if(flags & SHF::SHF_ALLOC == 0)
|
117
|
+
sections.each {|i| @unallocated << i}
|
118
|
+
else
|
119
|
+
sections.each {|i|
|
120
|
+
|
121
|
+
expect_value "Sections shouldn't overlap", range_available?(@layout,i.vaddr,i.end), true
|
122
|
+
@layout[i.vaddr] = i
|
123
|
+
@layout_by_flags[i.flags][i.vaddr] = i
|
124
|
+
}
|
125
|
+
end
|
126
|
+
retval
|
127
|
+
end
|
128
|
+
def add_with_phdr(sections,type,flags)
|
129
|
+
raise RuntimeError.new "need one section in phdr" if sections.empty?
|
130
|
+
x=self.add *sections
|
131
|
+
@phdrs << [sections,type,flags]
|
132
|
+
x
|
133
|
+
end
|
134
|
+
def shstrtab(buf) # Last section written, TODO: Move to layout
|
135
|
+
name = ".shstrtab"
|
136
|
+
idx = @shstrtab.add_string name
|
137
|
+
x=@factory.shdr.new
|
138
|
+
x.name= idx
|
139
|
+
x.type = SHT::SHT_STRTAB
|
140
|
+
x.off = buf.tell
|
141
|
+
x.siz = @shstrtab.buf.size
|
142
|
+
buf.write @shstrtab.buf.string
|
143
|
+
x
|
144
|
+
end
|
145
|
+
|
146
|
+
LoadMap = Struct.new(:off,:end,:vaddr,:memsz,:flags,:align)
|
147
|
+
def pagealign_loadmap(loadmaps)
|
148
|
+
#TODO: Be smarter about this..
|
149
|
+
loadmaps.sort_by(&:vaddr).each_cons(2) do |low,high|
|
150
|
+
if rounddown(low.vaddr + low.memsz,PAGESIZE) >= rounddown(high.vaddr,PAGESIZE)
|
151
|
+
low.flags |= high.flags
|
152
|
+
high.flags |= low.flags
|
153
|
+
end
|
154
|
+
end
|
155
|
+
loadmaps
|
156
|
+
end
|
157
|
+
attr_accessor :tbss_size
|
158
|
+
def write_phdrs(buf,filehdr,load)
|
159
|
+
phdrs = BinData::Array.new(:type => @factory.phdr)
|
160
|
+
expect_value "Too many PHDRS",true, @phdrs.size + load.size < RESERVED_PHDRS
|
161
|
+
@phdrs.each {|x|
|
162
|
+
sections, type,flags =*x
|
163
|
+
x = @factory.phdr.new
|
164
|
+
x.align = sections.map(&:align).max
|
165
|
+
x.vaddr = sections.map(&:vaddr).min
|
166
|
+
x.paddr = x.vaddr
|
167
|
+
x.filesz = sections.map(&:end).max - x.vaddr
|
168
|
+
if type == PT::PT_TLS and @tbss_size #HACK
|
169
|
+
x.memsz = x.filesz + @tbss_size
|
170
|
+
else
|
171
|
+
x.memsz = x.filesz
|
172
|
+
end
|
173
|
+
x.flags = flags
|
174
|
+
x.off = sections.map(&:off).min
|
175
|
+
x.type = type
|
176
|
+
phdrs.push x
|
177
|
+
}
|
178
|
+
#
|
179
|
+
load.first.andand.tap{|l|
|
180
|
+
l.vaddr -= l.off
|
181
|
+
l.memsz += l.off
|
182
|
+
l.off = 0
|
183
|
+
}
|
184
|
+
pagealign_loadmap(load)
|
185
|
+
load.each {|l|
|
186
|
+
phdrs.push @factory.phdr.new.tap {|x|
|
187
|
+
x.align = [PAGESIZE,l.align].max
|
188
|
+
x.vaddr = l.vaddr
|
189
|
+
x.paddr = x.vaddr
|
190
|
+
x.type = PT::PT_LOAD
|
191
|
+
x.filesz = l.end - l.off#TODO: handle nobits
|
192
|
+
x.memsz = x.filesz # l.memsz
|
193
|
+
x.off = l.off
|
194
|
+
x.flags = PF::PF_R
|
195
|
+
x.flags |= PF::PF_W if(l.flags & SHF::SHF_WRITE != 0)
|
196
|
+
x.flags |= PF::PF_X if(l.flags & SHF::SHF_EXECINSTR != 0)
|
197
|
+
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
#phdrs.unshift @factory.phdr.new.tap {|x|
|
202
|
+
# x.align = 4
|
203
|
+
# x.vaddr =
|
204
|
+
# x.type = PT::PT_PHDRS
|
205
|
+
#}
|
206
|
+
# pp load
|
207
|
+
filehdr.phoff = buf.tell
|
208
|
+
filehdr.phentsize = @factory.phdr.new.num_bytes
|
209
|
+
filehdr.phnum = phdrs.size
|
210
|
+
phdrs.write buf
|
211
|
+
|
212
|
+
end
|
213
|
+
RESERVED_PHDRS = 16
|
214
|
+
def write_sections(buf,filehdr)
|
215
|
+
first_shdr = @sections.first
|
216
|
+
first_shdr.index = 0
|
217
|
+
|
218
|
+
#Get more clever about mapping files
|
219
|
+
# We put actual program headers right at the beginning.
|
220
|
+
phdr_off = buf.tell
|
221
|
+
buf.seek phdr_off + RESERVED_PHDRS * @factory.phdr.new.num_bytes
|
222
|
+
offset = buf.tell
|
223
|
+
@sections[0].off = 0
|
224
|
+
(@layout.to_a.sort_by(&:first).map(&:last) + @unallocated).each {|s|
|
225
|
+
if s.flags & SHF::SHF_ALLOC != 0
|
226
|
+
offset = align_mod(offset,s.vaddr, PAGESIZE)
|
227
|
+
end
|
228
|
+
s.off = offset
|
229
|
+
offset += s.data.bytesize
|
230
|
+
|
231
|
+
}
|
232
|
+
idx = 0
|
233
|
+
@sections.each{|s| expect_value "Size field correct", s.siz, s.data.bytesize}
|
234
|
+
@sections.sort_by(&:off).each_cons(2){|low,high|
|
235
|
+
expect_value "Offsets should not overlap", true, low.off + low.data.bytesize <= high.off
|
236
|
+
}
|
237
|
+
shdrs = BinData::Array.new(:type=>@factory.shdr).push *@sections.map{ |s|
|
238
|
+
expect_value "aligned to vaddr", 0,s.vaddr % s.align if s.align != 0
|
239
|
+
expect_value "idx", idx,s.index
|
240
|
+
idx +=1
|
241
|
+
#expect_value "aligned to pagesize",0, PAGESIZE % s.align
|
242
|
+
|
243
|
+
buf.seek s.off
|
244
|
+
old_off = buf.tell
|
245
|
+
buf.write(s.data)
|
246
|
+
# pp "#{idx }#{s.name} written to offset #{old_off} to #{buf.tell}"
|
247
|
+
|
248
|
+
|
249
|
+
expect_value "size", s.data.bytesize, s.siz
|
250
|
+
link_value = lambda do |name|
|
251
|
+
if name.is_a? String
|
252
|
+
@sections.to_enum.with_index.select {|sect,idx| sect.name == name}.first.last rescue raise RuntimeError.new("Invalid Section reference #{name}") #Index of first match TODO: check unique
|
253
|
+
else
|
254
|
+
name || 0
|
255
|
+
end
|
256
|
+
end
|
257
|
+
x= @factory.shdr.new
|
258
|
+
x.name = @shstrtab.add_string(s.name)
|
259
|
+
x.type = s.type
|
260
|
+
x.flags = s.flags
|
261
|
+
x.vaddr = s.vaddr
|
262
|
+
x.off = s.off
|
263
|
+
x.siz = s.siz
|
264
|
+
x.link = link_value.call(s.link)
|
265
|
+
x.info = link_value.call(s.info)
|
266
|
+
x.addralign = s.align
|
267
|
+
x.entsize= s.entsize
|
268
|
+
# x.flags |= SHF::SHF_ALLOC if(@file.gnu_tls.tbss == s)
|
269
|
+
x
|
270
|
+
}
|
271
|
+
#remove
|
272
|
+
mapped = @sections.select{|x| x.flags & SHF::SHF_ALLOC != 0}.sort_by(&:vaddr)
|
273
|
+
mapped.each_cons(2){|low,high| expect_value "Mapped sections should not overlap", true,low.vaddr + low.siz<= high.vaddr}
|
274
|
+
load = mapped.group_by(&:flags).map{|flags,sections|
|
275
|
+
sections.group_by{|x| x.vaddr - x.off}.map do |align,sections|
|
276
|
+
memsize = sections.last.off + sections.last.siz - sections.first.off
|
277
|
+
# if sections.last.type == SHT::SHT_ end
|
278
|
+
# sections.select{|x| x.type == SHT::SHT_NOBITS}.tap {|nobits|
|
279
|
+
# if nobits.size > 0
|
280
|
+
# expect_value "At most one NOBITS", nobits.size,1
|
281
|
+
# expect_value "NOBITS is the last section",sections.last.type, SHT::SHT_NOBITS
|
282
|
+
# last_file_addr = nobits.first.vaddr
|
283
|
+
# memsize = sections.last.off - sections.first.off
|
284
|
+
# end
|
285
|
+
# }
|
286
|
+
LoadMap.new(sections.first.off, sections.last.off + sections.last.siz,
|
287
|
+
sections.first.vaddr, memsize ,flags,sections.map(&:align).max) #TODO: LCM?
|
288
|
+
end
|
289
|
+
}.flatten
|
290
|
+
buf.seek 0,IO::SEEK_END
|
291
|
+
|
292
|
+
shdrs << shstrtab(buf)
|
293
|
+
filehdr.shstrndx = shdrs.size - 1
|
294
|
+
filehdr.shoff = buf.tell
|
295
|
+
filehdr.shentsize = shdrs[0].num_bytes
|
296
|
+
filehdr.shnum = shdrs.size
|
297
|
+
shdrs.write buf
|
298
|
+
|
299
|
+
buf.seek roundup(buf.tell, PAGESIZE)-1
|
300
|
+
buf.write '\0' # pad to pagesize
|
301
|
+
buf.seek phdr_off
|
302
|
+
write_phdrs(buf,filehdr,load)
|
303
|
+
end
|
304
|
+
private
|
305
|
+
def max_address()
|
306
|
+
unless @layout.last.nil?
|
307
|
+
@layout.last[1].end
|
308
|
+
else
|
309
|
+
0x40000 # TODO: Make start_address a parameter
|
310
|
+
end
|
311
|
+
end
|
312
|
+
def allocate_sections(chunk)
|
313
|
+
return unless chunk.first.flags & SHF::SHF_ALLOC != 0
|
314
|
+
align = chunk.max_by{ |x| x.align}.align #Should technically be the
|
315
|
+
#lcm
|
316
|
+
|
317
|
+
#TODO: check that they are powers of two
|
318
|
+
size = chunk.reduce(0) {|i,x| i+roundup(x.siz,align)}
|
319
|
+
addr = @layout_by_flags[chunk.first.flags].last.andand { |x| roundup(x[1].end,align)}
|
320
|
+
if(addr.nil? or !range_available?(@layout,addr,addr+size))
|
321
|
+
addr = roundup(max_address,align)
|
322
|
+
expect_value "Address space has room", range_available?(@layout,addr,addr+size),true #TODO: This can actually overflow! Properly do arithemetic mod 2**64
|
323
|
+
end
|
324
|
+
chunk.each do |section|
|
325
|
+
section.vaddr = roundup(addr,align)
|
326
|
+
addr = section.end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
def rounddown(number,align)
|
330
|
+
return number if align == 0
|
331
|
+
case number % align
|
332
|
+
when 0
|
333
|
+
number
|
334
|
+
else
|
335
|
+
number - (number % align)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
def roundup(number, align)
|
339
|
+
return number if align == 0
|
340
|
+
case number % align
|
341
|
+
when 0
|
342
|
+
number
|
343
|
+
else
|
344
|
+
number + align - (number % align)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
def align_mod(number,align,mod)
|
348
|
+
align = align % mod
|
349
|
+
x = number + align - number % mod
|
350
|
+
if(x<number)
|
351
|
+
x+mod
|
352
|
+
else
|
353
|
+
x
|
354
|
+
end
|
355
|
+
end
|
356
|
+
def range_available?(tree,from,to)
|
357
|
+
if tree.empty?
|
358
|
+
true
|
359
|
+
else
|
360
|
+
( (tree.upper_bound(from).andand.last.andand.end || -1) <= from ) &&
|
361
|
+
( (tree.lower_bound(to).andand.last.andand.vaddr || +1.0/0.0) >= to)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
PAGESIZE = 1 << 12 #KLUDGE: largest pagesize , align everything to
|
366
|
+
|
367
|
+
UINT64_MOD = 2**64
|
368
|
+
#TODO: Needs a unique class for 'allocatable' sections.
|
369
|
+
#Then just sort, and write them out
|
370
|
+
class Writer #TODO: Completely refactor this
|
371
|
+
|
372
|
+
#pagesizes
|
373
|
+
def initialize(file,factory)
|
374
|
+
@factory = factory
|
375
|
+
@file = file
|
376
|
+
@layout = Layout.new(@factory)
|
377
|
+
@shdrs= BinData::Array::new(type: @factory.shdr,initial_length: 0)
|
378
|
+
@phdrs= BinData::Array::new(type: @factory.phdr,initial_length: 0)
|
379
|
+
@buf = StringIO.new()
|
380
|
+
@progbit_indices = {}
|
381
|
+
@section_vaddrs = {}
|
382
|
+
|
383
|
+
@file.pinned_sections.andand.each {|name,pin| @layout.pin_section(name,pin[:size],pin[:vaddr])
|
384
|
+
}
|
385
|
+
write_to_buf
|
386
|
+
end
|
387
|
+
def self.to_file(filename,elf)
|
388
|
+
factory = ElfStructFactory.instance(elf.endian,elf.bits)
|
389
|
+
writer = Writer.new(elf,factory)
|
390
|
+
IO.write filename,writer.buf.string
|
391
|
+
end
|
392
|
+
attr_reader :buf
|
393
|
+
private
|
394
|
+
def progbits
|
395
|
+
@file.gnu_tls.andand(&:tbss).andand{|x|
|
396
|
+
x.flags &= ~SHF::SHF_ALLOC
|
397
|
+
@layout.tbss_size = x.size
|
398
|
+
}
|
399
|
+
bits = (@file.progbits + @file.nobits).sort {|a,b|( a.addr and b.addr ) ? a.addr <=> b.addr : ( a.addr ? -1 : 1 ) }
|
400
|
+
bits.each do |sect|
|
401
|
+
|
402
|
+
out = OutputSection.new(sect.name,sect.sect_type, sect.flags, sect.addr, sect.size,0,0,sect.align, sect.entsize, sect.data.string)
|
403
|
+
# binding.pry if sect.sect_type == SHT::SHT_INIT_ARRAY
|
404
|
+
if sect.phdr.nil?
|
405
|
+
@layout.add out
|
406
|
+
else
|
407
|
+
@layout.add_with_phdr [out], sect.phdr, sect.phdr_flags
|
408
|
+
end
|
409
|
+
@progbit_indices[sect] = out.index
|
410
|
+
@section_vaddrs[sect] = out.vaddr
|
411
|
+
end
|
412
|
+
|
413
|
+
end
|
414
|
+
def note
|
415
|
+
os= @file.notes.map {|name,note|
|
416
|
+
OutputSection.new(name, SHT::SHT_NOTE, NOTE_FLAGS, nil, note.num_bytes,0,0,NOTE_ALIGN, NOTE_ENTSIZE,note.to_binary_s)
|
417
|
+
}
|
418
|
+
@layout.add_with_phdr os, PT::PT_NOTE, PF::PF_R unless os.empty?
|
419
|
+
#TODO: add phdr
|
420
|
+
end
|
421
|
+
def interp
|
422
|
+
if(@file.interp)
|
423
|
+
interp = BinData::Stringz.new(@file.interp)
|
424
|
+
@interp_section = OutputSection.new(".interp",SHT::SHT_PROGBITS, SHF::SHF_ALLOC, nil, interp.num_bytes,0,0,1,0,interp.to_binary_s)
|
425
|
+
idx, _ =@layout.add_with_phdr [@interp_section], PT::PT_INTERP, PF::PF_R
|
426
|
+
@progbit_indices[".interp"]=idx
|
427
|
+
end
|
428
|
+
end
|
429
|
+
def hashtab(table)
|
430
|
+
hashtab = BinData::Array.new(:type => "uint32#{@file.endian == :big ? 'be' : 'le'}".to_sym)
|
431
|
+
nbuckets = 5
|
432
|
+
nchain = table.size
|
433
|
+
buckets = Array.new(nbuckets,0)
|
434
|
+
chain = Array.new(nchain,0)
|
435
|
+
|
436
|
+
table.each {|sym,idx|
|
437
|
+
|
438
|
+
expect_value "Valid symbol index", idx<table.size,true
|
439
|
+
i = Elf::Writer::elf_hash(sym.name) % nbuckets
|
440
|
+
if(buckets[i] == 0)
|
441
|
+
buckets[i] = idx
|
442
|
+
else
|
443
|
+
i = buckets[i]
|
444
|
+
while(chain[i] != 0)
|
445
|
+
i= chain[i]
|
446
|
+
end
|
447
|
+
chain[i] = idx
|
448
|
+
end
|
449
|
+
}
|
450
|
+
hashtab.assign [nbuckets,nchain] + buckets + chain
|
451
|
+
|
452
|
+
sect = OutputSection.new(".hash",SHT::SHT_HASH,SHF::SHF_ALLOC,nil, hashtab.num_bytes, ".dynsym", 0,8,@file.bits/8, hashtab.to_binary_s)
|
453
|
+
@layout.add sect
|
454
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_HASH, val: sect.vaddr)
|
455
|
+
# pp hashtab.snapshot
|
456
|
+
end
|
457
|
+
def versions(dynsym,dynstrtab) #TODO: Use parser combinator
|
458
|
+
@versions = {}
|
459
|
+
gnu_versions = dynsym.map(&:gnu_version).uniq.select{|x|x.is_a? GnuVersion}
|
460
|
+
|
461
|
+
defined_versions = gnu_versions.select{|x| x.needed == false}
|
462
|
+
unless @file.dynamic.gnu_version_basename.nil?
|
463
|
+
defined_versions.unshift GnuVersion.new(@file.dynamic.soname,@file.dynamic.gnu_version_basename, ElfFlags::GnuVerFlags::VERFLAG_BASE, false)
|
464
|
+
end
|
465
|
+
buffer = StringIO.new()
|
466
|
+
defined_versions.each_with_index {|ver,definedidx|
|
467
|
+
expect_value "Defined SONAME",false, @file.dynamic.gnu_version_basename.nil?
|
468
|
+
expect_value "Defined version file name",ver.file, @file.dynamic.soname
|
469
|
+
verdef = @factory.verdef.new
|
470
|
+
verdef.version = 1
|
471
|
+
verdef.flags = ver.flags
|
472
|
+
if ver.flags & ElfFlags::GnuVerFlags::VERFLAG_BASE != 0
|
473
|
+
verdef.ndx = 1
|
474
|
+
else
|
475
|
+
verdef.ndx = @versions.size + 2
|
476
|
+
@versions[ver] = @versions.size + 2
|
477
|
+
end
|
478
|
+
verdef.hsh = Elf::Writer::elf_hash(ver.version)
|
479
|
+
verdaux = BinData::Array.new(type: @factory.verdaux)
|
480
|
+
verdaux << @factory.verdaux.new.tap{|x|
|
481
|
+
x.name = dynstrtab.add_string(ver.version)
|
482
|
+
x.nextoff = if(ver.parents.size == 0)
|
483
|
+
0
|
484
|
+
else
|
485
|
+
x.num_bytes
|
486
|
+
end
|
487
|
+
}
|
488
|
+
ver.parents.each_with_index {|parent,idx|
|
489
|
+
aux = @factory.verdaux.new
|
490
|
+
aux.name = dynstrtab.add_string(parent.version)
|
491
|
+
aux.nextoff = if(idx == ver.parents.size - 1)
|
492
|
+
0
|
493
|
+
else
|
494
|
+
aux.num_bytes
|
495
|
+
end
|
496
|
+
verdaux << aux
|
497
|
+
}
|
498
|
+
verdef.cnt = verdaux.size
|
499
|
+
verdef.aux = verdef.num_bytes
|
500
|
+
if(defined_versions.size - 1 == definedidx)
|
501
|
+
verdef.nextoff = 0
|
502
|
+
else
|
503
|
+
verdef.nextoff = verdaux.num_bytes + verdef.num_bytes
|
504
|
+
end
|
505
|
+
verdef.write(buffer)
|
506
|
+
verdaux.write(buffer)
|
507
|
+
}
|
508
|
+
unless defined_versions.empty?
|
509
|
+
sect = OutputSection.new(".gnu.version_d", SHT::SHT_GNU_VERDEF,SHF::SHF_ALLOC,nil,buffer.size,".dynstr",defined_versions.size,8,0,buffer.string)
|
510
|
+
@layout.add sect
|
511
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_VERDEF, val: sect.vaddr)
|
512
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_VERDEFNUM, val: defined_versions.size)
|
513
|
+
end
|
514
|
+
buffer = StringIO.new()
|
515
|
+
needed_versions = gnu_versions.select{|x| x.needed == true}.group_by(&:file)
|
516
|
+
needed_idx = 0
|
517
|
+
needed_versions.each {|file, versions|
|
518
|
+
needed_idx+=1
|
519
|
+
#Create VERNEED for this file
|
520
|
+
verneed = @factory.verneed.new
|
521
|
+
verneed.version = 1
|
522
|
+
verneed.cnt = versions.size
|
523
|
+
verneed.file = dynstrtab.add_string(file)
|
524
|
+
verneed.aux = verneed.num_bytes
|
525
|
+
vernauxs = BinData::Array.new(type: @factory.vernaux)
|
526
|
+
versions.each {|ver|
|
527
|
+
vernauxs.push @factory.vernaux.new.tap {|x|
|
528
|
+
x.hsh = Elf::Writer::elf_hash(ver.version)
|
529
|
+
x.flags = ver.flags
|
530
|
+
x.other = @versions.size + 2
|
531
|
+
x.name = dynstrtab.add_string(ver.version)
|
532
|
+
x.nextoff = if vernauxs.size == versions.size - 1
|
533
|
+
0
|
534
|
+
else
|
535
|
+
x.num_bytes
|
536
|
+
end
|
537
|
+
@versions[ver] = x.other.to_i
|
538
|
+
}
|
539
|
+
}
|
540
|
+
if (needed_versions.size == needed_idx)
|
541
|
+
verneed.nextoff = 0 # 0 for last
|
542
|
+
else
|
543
|
+
verneed.nextoff = verneed.num_bytes + vernauxs.num_bytes
|
544
|
+
end
|
545
|
+
verneed.write(buffer)
|
546
|
+
vernauxs.write(buffer)
|
547
|
+
}
|
548
|
+
unless needed_versions.empty?
|
549
|
+
sect = OutputSection.new(".gnu.version_r", SHT::SHT_GNU_VERNEED,SHF::SHF_ALLOC,nil,buffer.size,".dynstr",needed_versions.size,8,0,buffer.string)
|
550
|
+
@layout.add sect
|
551
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_VERNEED, val: sect.vaddr)
|
552
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_VERNEEDNUM, val: needed_versions.size)
|
553
|
+
end
|
554
|
+
|
555
|
+
@versions
|
556
|
+
end
|
557
|
+
def versym(versions,symbols)
|
558
|
+
return if versions.empty?
|
559
|
+
data = BinData::Array.new(type: @factory.versym)
|
560
|
+
symbols.each{|sym|
|
561
|
+
veridx = case sym.gnu_version
|
562
|
+
when :local
|
563
|
+
0
|
564
|
+
when :global
|
565
|
+
1
|
566
|
+
else
|
567
|
+
versions[sym.gnu_version]
|
568
|
+
end
|
569
|
+
data.push @factory.versym.new(veridx: veridx)
|
570
|
+
}
|
571
|
+
sect = OutputSection.new(".gnu.version",SHT::SHT_GNU_VERSYM, SHF::SHF_ALLOC, nil, data.num_bytes, ".dynsym",0, 8,2, data.to_binary_s)
|
572
|
+
@layout.add sect
|
573
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_VERSYM, val: sect.vaddr)
|
574
|
+
end
|
575
|
+
def dynsym(dynstrtab)
|
576
|
+
symtab = BinData::Array.new(:type => @factory.sym) #TODO: use initial length here to save on
|
577
|
+
#some allocations
|
578
|
+
syms = [Elf::Symbol.new("",nil,STT::STT_NOTYPE,0,STB::STB_LOCAL,0).tap{
|
579
|
+
|x| x.gnu_version = :local; x.semantics = 0}] + @file.symbols.select(&:is_dynamic)
|
580
|
+
versions = versions(syms, dynstrtab)
|
581
|
+
@dynsym = {}
|
582
|
+
#symtab << @factory.sym.new
|
583
|
+
syms.each_with_index.each do |sym,idx|
|
584
|
+
s = @factory.sym.new
|
585
|
+
s.name = dynstrtab.add_string(sym.name)
|
586
|
+
s.type = sym.type
|
587
|
+
s.binding = sym.bind
|
588
|
+
if sym.semantics.nil?
|
589
|
+
s.shndx = @progbit_indices[sym.section] || nil
|
590
|
+
unless s.shndx
|
591
|
+
pp "warning, symbol ", s,"does not have a valid progbits index"
|
592
|
+
s.shndx = nil
|
593
|
+
end
|
594
|
+
else
|
595
|
+
s.shndx = sym.semantics
|
596
|
+
end
|
597
|
+
s.other = sym.visibility
|
598
|
+
unless sym.section.nil?
|
599
|
+
expect_value "valid symbol offset", sym.sectoffset <= sym.section.size,true #Symbol can point to end of section
|
600
|
+
end
|
601
|
+
|
602
|
+
s.val = (@layout[s.shndx.to_i].andand.vaddr || 0) + sym.sectoffset #TODO: find output section
|
603
|
+
|
604
|
+
s.siz = sym.size
|
605
|
+
@dynsym[sym] = symtab.size
|
606
|
+
symtab.push s
|
607
|
+
end
|
608
|
+
last_local_idx = syms.to_enum.with_index.select{|v,i| v.bind == STB::STB_LOCAL}.andand.last.andand.last || -1
|
609
|
+
dynsym = OutputSection.new(".dynsym",SHT::SHT_DYNSYM, SHF::SHF_ALLOC, nil,symtab.num_bytes,".dynstr", last_local_idx+1,1,@factory.sym.new.num_bytes, symtab.to_binary_s)
|
610
|
+
versym(versions,syms)
|
611
|
+
@layout.add dynsym
|
612
|
+
hashtab(@dynsym)
|
613
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_SYMTAB,val: dynsym.vaddr)
|
614
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_SYMENT,val: @factory.sym.new.num_bytes)
|
615
|
+
|
616
|
+
end # Produce a hash and a GNU_HASH as well
|
617
|
+
def dynamic
|
618
|
+
@dynamic = BinData::Array.new(:type => @factory.dyn)
|
619
|
+
dynstrtab = StringTable.new()
|
620
|
+
reladyn(dynstrtab)
|
621
|
+
#PHDR
|
622
|
+
@file.dynamic.needed.each{|lib|
|
623
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_NEEDED, val: dynstrtab.add_string(lib))
|
624
|
+
}
|
625
|
+
section_tag = lambda {|tag,value|
|
626
|
+
unless(value.nil?)
|
627
|
+
@dynamic << @factory.dyn.new(tag: tag, val: value.addr)
|
628
|
+
true
|
629
|
+
else
|
630
|
+
nil
|
631
|
+
end
|
632
|
+
}
|
633
|
+
unless @file.dynamic.soname.nil?
|
634
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_SONAME, val: dynstrtab.add_string(@file.dynamic.soname))
|
635
|
+
end
|
636
|
+
unless @file.dynamic.rpath.nil?
|
637
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_RPATH, val: dynstrtab.add_string(@file.dynamic.rpath))
|
638
|
+
end
|
639
|
+
section_tag.call(DT::DT_PLTGOT,@file.dynamic.pltgot)
|
640
|
+
if section_tag.call(DT::DT_INIT_ARRAY,@file.dynamic.init_array)
|
641
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_INIT_ARRAYSZ,val: @file.dynamic.init_array.size)
|
642
|
+
end
|
643
|
+
if section_tag.call(DT::DT_FINI_ARRAY,@file.dynamic.fini_array)
|
644
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_FINI_ARRAYSZ,val: @file.dynamic.fini_array.size)
|
645
|
+
end
|
646
|
+
# section_tag.call(DT::DT_INIT,@file.dynamic.init)
|
647
|
+
# section_tag.call(DT::DT_FINI,@file.dynamic.fini)
|
648
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_INIT, val: @file.dynamic.init) if @file.dynamic.init
|
649
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_FINI, val: @file.dynamic.fini) if @file.dynamic.fini
|
650
|
+
|
651
|
+
#@file.dynamic.debug_val.each {|dbg|
|
652
|
+
# @dynamic << @factory.dyn.new(tag: DT::DT_DEBUG, val: dbg)
|
653
|
+
#}
|
654
|
+
#@file.dynamic.extra_dynamic.each{ |extra|
|
655
|
+
# @dynamic << @factory.dyn.new(tag: extra[:tag], val: extra[:val])
|
656
|
+
#}
|
657
|
+
if @file.dynamic.flags
|
658
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_FLAGS, val: @file.dynamic.flags)
|
659
|
+
end
|
660
|
+
if @file.dynamic.flags1
|
661
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_FLAGS_1, val: @file.dynamic.flags1)
|
662
|
+
end
|
663
|
+
#string table
|
664
|
+
dynstr = OutputSection.new(".dynstr", SHT::SHT_STRTAB, SHF::SHF_ALLOC,nil, dynstrtab.buf.size, 0,0,1,0,dynstrtab.buf.string)
|
665
|
+
@layout.add dynstr
|
666
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_STRTAB, val: dynstr.vaddr)
|
667
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_STRSZ, val: dynstr.siz)
|
668
|
+
|
669
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_NULL, val: 0) # End marker
|
670
|
+
@layout.add_with_phdr [OutputSection.new(".dynamic", SHT::SHT_DYNAMIC, SHF::SHF_ALLOC | SHF::SHF_WRITE, nil, @dynamic.num_bytes,".dynstr",0,8,@factory.dyn.new.num_bytes,@dynamic.to_binary_s)], PT::PT_DYNAMIC, PF::PF_R
|
671
|
+
#dynstr -> STRTAB STRSZ
|
672
|
+
#dynsym -> DT_SYMENT, D
|
673
|
+
end # Write note, etc for the above
|
674
|
+
RELATIVE_RELOCS=[R::R_X86_64_RELATIVE]
|
675
|
+
def rela_buffer(relocations)
|
676
|
+
relative_rela_count = 0
|
677
|
+
buf = BinData::Array.new(:type =>@factory.rela).new.tap{|retval|
|
678
|
+
relocations.sort_by{|x| RELATIVE_RELOCS.include?(x.type) ? 0 : 1}.each_with_index {|rel,idx|
|
679
|
+
entry = @factory.rela.new
|
680
|
+
entry.off = @section_vaddrs[rel.section] + rel.offset
|
681
|
+
if rel.symbol.nil?
|
682
|
+
entry.sym = ElfFlags::SymbolName::STN_UNDEF
|
683
|
+
else
|
684
|
+
entry.sym = @dynsym[rel.symbol]
|
685
|
+
end
|
686
|
+
relative_rela_count +=1 if(RELATIVE_RELOCS.include? rel.type)
|
687
|
+
entry.type = rel.type
|
688
|
+
entry.addend = rel.addend
|
689
|
+
retval.push entry
|
690
|
+
}
|
691
|
+
}
|
692
|
+
[buf, relative_rela_count]
|
693
|
+
end
|
694
|
+
|
695
|
+
def reladyn(dynstrtab)
|
696
|
+
@file.relocations.each{ |r|
|
697
|
+
r.symbol.andand {|x| x.is_dynamic = true}
|
698
|
+
}
|
699
|
+
dynsym(dynstrtab)
|
700
|
+
relaentsize = @factory.rela.new.num_bytes
|
701
|
+
|
702
|
+
pltrel = @file.relocations.select(&:is_dynamic).select(&:is_lazy)
|
703
|
+
dynrel = @file.relocations.select(&:is_dynamic).select{|x| not x.is_lazy}
|
704
|
+
rela_sections = []
|
705
|
+
unless dynrel.empty?
|
706
|
+
#All other relocations go into .rela.dyn
|
707
|
+
relabuf,relacount = rela_buffer(dynrel)
|
708
|
+
reladyn = OutputSection.new(".rela.dyn", SHT::SHT_RELA, SHF::SHF_ALLOC,nil, relabuf.num_bytes,".dynsym",0,8, relaentsize, relabuf.to_binary_s)
|
709
|
+
rela_sections << reladyn
|
710
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_RELACOUNT, val: relacount)
|
711
|
+
end
|
712
|
+
unless(pltrel.empty?)
|
713
|
+
relabuf,_=rela_buffer(pltrel)
|
714
|
+
relaplt = OutputSection.new(".rela.plt", SHT::SHT_RELA, SHF::SHF_ALLOC, nil, relabuf.num_bytes,".dynsym",@file.dynamic.pltgot.name,8, relaentsize, relabuf.to_binary_s)
|
715
|
+
rela_sections << relaplt
|
716
|
+
end
|
717
|
+
@layout.add *rela_sections # They need to in this order to produce a correct ld.so
|
718
|
+
unless pltrel.empty?
|
719
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_PLTRELSZ, val: relabuf.num_bytes)
|
720
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_PLTREL, val: DT::DT_RELA)
|
721
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_JMPREL, val: relaplt.vaddr)
|
722
|
+
end
|
723
|
+
unless dynrel.empty?
|
724
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_RELA, val: reladyn.vaddr)
|
725
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_RELASZ, val: reladyn.siz)
|
726
|
+
@dynamic << @factory.dyn.new(tag: DT::DT_RELAENT, val: relaentsize)
|
727
|
+
end
|
728
|
+
end
|
729
|
+
def write_headers
|
730
|
+
hdr = @factory.hdr.new
|
731
|
+
case @file.endian
|
732
|
+
when :big
|
733
|
+
hdr.ident.id_data = ElfFlags::IdentData::ELFDATA2MSB
|
734
|
+
when :little
|
735
|
+
hdr.ident.id_data = ElfFlags::IdentData::ELFDATA2LSB
|
736
|
+
else
|
737
|
+
raise ArgumentError.new "Invalid endianness"
|
738
|
+
end
|
739
|
+
case @file.bits
|
740
|
+
when 64
|
741
|
+
hdr.ident.id_class = ElfFlags::IdentClass::ELFCLASS64
|
742
|
+
when 32
|
743
|
+
hdr.ident.id_class = ElfFlags::IdentClass::ELFCLASS32
|
744
|
+
else
|
745
|
+
raise ArgumentError.new "Invalid class"
|
746
|
+
end
|
747
|
+
hdr.ident.id_version = ElfFlags::Version::EV_CURRENT
|
748
|
+
hdr.ehsize = hdr.num_bytes
|
749
|
+
hdr.type = @file.filetype
|
750
|
+
hdr.machine = @file.machine
|
751
|
+
hdr.version = @file.version
|
752
|
+
hdr.entry = @file.entry
|
753
|
+
hdr.flags = @file.flags
|
754
|
+
|
755
|
+
@layout.write_sections(@buf,hdr)
|
756
|
+
|
757
|
+
@buf.seek 0
|
758
|
+
hdr.write @buf
|
759
|
+
end
|
760
|
+
#TODO: Fix things like INTERP
|
761
|
+
|
762
|
+
|
763
|
+
def write_to_buf #Make this memoized
|
764
|
+
@buf.seek @factory.hdr.new.num_bytes #Leave space for header.
|
765
|
+
#this is pretty bad
|
766
|
+
progbits
|
767
|
+
interp
|
768
|
+
note
|
769
|
+
dynamic
|
770
|
+
write_headers
|
771
|
+
end
|
772
|
+
end
|
773
|
+
end
|
774
|
+
end
|