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
@@ -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
|