metasm 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
require 'metasm/main'
|
|
8
|
+
require 'metasm/x86_64/parse'
|
|
9
|
+
require 'metasm/x86_64/encode'
|
|
10
|
+
require 'metasm/x86_64/decode'
|
|
11
|
+
require 'metasm/x86_64/debug'
|
|
12
|
+
require 'metasm/x86_64/compile_c'
|
|
@@ -0,0 +1,1025 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
require 'metasm/x86_64/parse'
|
|
8
|
+
require 'metasm/compile_c'
|
|
9
|
+
|
|
10
|
+
module Metasm
|
|
11
|
+
class X86_64
|
|
12
|
+
class CCompiler < C::Compiler
|
|
13
|
+
# holds compiler state information for a function
|
|
14
|
+
# registers are saved as register number (see Reg)
|
|
15
|
+
class State
|
|
16
|
+
# variable => offset from ebp (::Integer or CExpression)
|
|
17
|
+
attr_accessor :offset
|
|
18
|
+
# the current function
|
|
19
|
+
attr_accessor :func
|
|
20
|
+
# register => CExpression
|
|
21
|
+
attr_accessor :cache
|
|
22
|
+
# array of register values used in the function (to save/restore at prolog/epilog)
|
|
23
|
+
attr_accessor :dirty
|
|
24
|
+
# the array of register values currently not available
|
|
25
|
+
attr_accessor :used
|
|
26
|
+
# the array of args in use (reg/modrm) the reg dependencies are in +used+
|
|
27
|
+
attr_accessor :inuse
|
|
28
|
+
# variable => register for current scope (variable never on the stack)
|
|
29
|
+
# bound registers are also in +used+
|
|
30
|
+
attr_accessor :bound
|
|
31
|
+
# list of reg values that are used as func args in current ABI
|
|
32
|
+
attr_accessor :regargs
|
|
33
|
+
# stack space reserved for subfunction in ABI
|
|
34
|
+
attr_accessor :args_space
|
|
35
|
+
# list of reg values that are not kept across function call
|
|
36
|
+
attr_accessor :abi_flushregs_call
|
|
37
|
+
# list of regs we can trash without restoring them
|
|
38
|
+
attr_accessor :abi_trashregs
|
|
39
|
+
|
|
40
|
+
# +used+ includes ebp if true
|
|
41
|
+
# nil if ebp is not reserved for stack variable addressing
|
|
42
|
+
# Reg if used
|
|
43
|
+
attr_accessor :saved_rbp
|
|
44
|
+
|
|
45
|
+
def initialize(func)
|
|
46
|
+
@func = func
|
|
47
|
+
@offset = {}
|
|
48
|
+
@cache = {}
|
|
49
|
+
@dirty = []
|
|
50
|
+
@used = [4] # rsp is always in use
|
|
51
|
+
@inuse = []
|
|
52
|
+
@bound = {}
|
|
53
|
+
@regargs = []
|
|
54
|
+
@args_space = 0
|
|
55
|
+
@abi_flushregs_call = [0, 1, 2, 6, 7, 8, 9, 10, 11]
|
|
56
|
+
@abi_trashregs = @abi_flushregs_call.dup
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# some address
|
|
61
|
+
class Address
|
|
62
|
+
attr_accessor :modrm, :target
|
|
63
|
+
def initialize(modrm, target=nil)
|
|
64
|
+
@modrm, @target = modrm, target
|
|
65
|
+
end
|
|
66
|
+
def sz; @modrm.adsz end
|
|
67
|
+
def to_s; "#<Address: #@modrm>" end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def initialize(*a)
|
|
72
|
+
super(*a)
|
|
73
|
+
@cpusz = 64
|
|
74
|
+
@regnummax = 15
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# shortcut to add an instruction to the source
|
|
78
|
+
def instr(name, *args)
|
|
79
|
+
# XXX parse_postfix ?
|
|
80
|
+
@source << Instruction.new(@exeformat.cpu, name, args)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# returns an available register, tries to find one not in @state.cache
|
|
84
|
+
# do not use with sz==8 (aliasing ah=>esp)
|
|
85
|
+
# does not put it in @state.inuse
|
|
86
|
+
def findreg(sz = @cpusz)
|
|
87
|
+
caching = @state.cache.keys.grep(Reg).map { |r| r.val }
|
|
88
|
+
if not regval = (@state.abi_trashregs - @state.used - caching).first ||
|
|
89
|
+
([*0..@regnummax] - @state.used).first
|
|
90
|
+
raise 'need more registers! (or a better compiler?)'
|
|
91
|
+
end
|
|
92
|
+
getreg(regval, sz)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# returns a Reg from a regval, mark it as dirty, flush old cache dependencies
|
|
96
|
+
def getreg(regval, sz=@cpusz)
|
|
97
|
+
flushcachereg(regval)
|
|
98
|
+
@state.dirty |= [regval]
|
|
99
|
+
Reg.new(regval, sz)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# remove the cache keys that depends on the register
|
|
103
|
+
def flushcachereg(regval)
|
|
104
|
+
@state.cache.delete_if { |e, val|
|
|
105
|
+
case e
|
|
106
|
+
when Reg; e.val == regval
|
|
107
|
+
when Address; e = e.modrm ; redo
|
|
108
|
+
when ModRM; e.b && (e.b.val == regval) or e.i && (e.i.val == regval)
|
|
109
|
+
end
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# removes elements from @state.inuse, free @state.used if unreferenced
|
|
114
|
+
# must be the exact object present in inuse
|
|
115
|
+
def unuse(*vals)
|
|
116
|
+
vals.each { |val|
|
|
117
|
+
val = val.modrm if val.kind_of? Address
|
|
118
|
+
@state.inuse.delete val
|
|
119
|
+
}
|
|
120
|
+
# XXX cache exempt
|
|
121
|
+
exempt = @state.bound.values.map { |r| r.val }
|
|
122
|
+
exempt << 4
|
|
123
|
+
exempt << 5 if @state.saved_rbp
|
|
124
|
+
@state.used.delete_if { |regval|
|
|
125
|
+
next if exempt.include? regval
|
|
126
|
+
not @state.inuse.find { |val|
|
|
127
|
+
case val
|
|
128
|
+
when Reg; val.val == regval
|
|
129
|
+
when ModRM; (val.b and val.b.val == regval) or (val.i and val.i.val == regval)
|
|
130
|
+
else raise 'internal error - inuse ' + val.inspect
|
|
131
|
+
end
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# marks an arg as in use, returns the arg
|
|
137
|
+
def inuse(v)
|
|
138
|
+
case v
|
|
139
|
+
when Reg; @state.used |= [v.val]
|
|
140
|
+
when ModRM
|
|
141
|
+
@state.used |= [v.i.val] if v.i
|
|
142
|
+
@state.used |= [v.b.val] if v.b
|
|
143
|
+
when Address; inuse v.modrm ; return v
|
|
144
|
+
else return v
|
|
145
|
+
end
|
|
146
|
+
@state.inuse |= [v]
|
|
147
|
+
v
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# returns a variable storage (ModRM for stack/global, Reg/Composite for register-bound)
|
|
151
|
+
def findvar(var)
|
|
152
|
+
if ret = @state.bound[var]
|
|
153
|
+
return ret
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
if ret = @state.cache.index(var)
|
|
157
|
+
ret = ret.dup
|
|
158
|
+
inuse ret
|
|
159
|
+
return ret
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
sz = 8*sizeof(var) rescue nil # extern char foo[];
|
|
163
|
+
|
|
164
|
+
case off = @state.offset[var]
|
|
165
|
+
when C::CExpression
|
|
166
|
+
# stack, dynamic address
|
|
167
|
+
# TODO
|
|
168
|
+
# no need to update state.cache here, never recursive
|
|
169
|
+
v = raise "find dynamic addr of #{var.name}"
|
|
170
|
+
when ::Integer
|
|
171
|
+
# stack
|
|
172
|
+
# TODO -fomit-frame-pointer ( => state.cache dependant on stack_offset... )
|
|
173
|
+
v = ModRM.new(@cpusz, sz, nil, nil, @state.saved_rbp, Expression[-off])
|
|
174
|
+
when nil
|
|
175
|
+
# global
|
|
176
|
+
if @exeformat.cpu.generate_PIC
|
|
177
|
+
v = ModRM.new(@cpusz, sz, nil, nil, Reg.from_str('rip'), Expression[var.name, :-, '$_'])
|
|
178
|
+
else
|
|
179
|
+
v = ModRM.new(@cpusz, sz, nil, nil, nil, Expression[var.name])
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
case var.type
|
|
184
|
+
when C::Array; inuse Address.new(v)
|
|
185
|
+
else inuse v
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# resolves the Address to Reg/Expr (may encode an 'lea')
|
|
190
|
+
def resolve_address(e)
|
|
191
|
+
r = e.modrm
|
|
192
|
+
unuse e
|
|
193
|
+
if r.imm and not r.b and not r.i
|
|
194
|
+
reg = r.imm
|
|
195
|
+
elsif not r.imm and ((not r.b and r.s == 1) or not r.i)
|
|
196
|
+
reg = r.b || r.i
|
|
197
|
+
elsif reg = @state.cache.index(e)
|
|
198
|
+
reg = reg.dup
|
|
199
|
+
else
|
|
200
|
+
reg = findreg
|
|
201
|
+
r.sz = reg.sz
|
|
202
|
+
instr 'lea', reg, r
|
|
203
|
+
end
|
|
204
|
+
inuse reg
|
|
205
|
+
@state.cache[reg] = e
|
|
206
|
+
reg
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# copies the arg e to a volatile location (register/composite) if it is not already
|
|
210
|
+
# unuses the old storage
|
|
211
|
+
# may return a register bigger than the type size (eg __int8 are stored in full reg size)
|
|
212
|
+
def make_volatile(e, type, rsz=@cpusz)
|
|
213
|
+
if e.kind_of? ModRM or @state.bound.index(e)
|
|
214
|
+
if type.integral? or type.pointer?
|
|
215
|
+
oldval = @state.cache[e]
|
|
216
|
+
unuse e
|
|
217
|
+
sz = typesize[type.pointer? ? :ptr : type.name]*8
|
|
218
|
+
if sz < @cpusz or sz < rsz or e.sz < rsz
|
|
219
|
+
e2 = inuse findreg(rsz)
|
|
220
|
+
op = ((type.specifier == :unsigned) ? 'movzx' : 'movsx')
|
|
221
|
+
op = 'mov' if e.sz == e2.sz
|
|
222
|
+
if e2.sz == 64 and e.sz == 32
|
|
223
|
+
if op == 'movsx'
|
|
224
|
+
instr 'movsxd', e2, e
|
|
225
|
+
else
|
|
226
|
+
instr 'mov', Reg.new(e2.val, 32), e
|
|
227
|
+
end
|
|
228
|
+
else
|
|
229
|
+
instr op, e2, e
|
|
230
|
+
end
|
|
231
|
+
else
|
|
232
|
+
e2 = inuse findreg(sz)
|
|
233
|
+
instr 'mov', e2, e
|
|
234
|
+
end
|
|
235
|
+
@state.cache[e2] = oldval if oldval and e.kind_of? ModRM
|
|
236
|
+
e2
|
|
237
|
+
elsif type.float?
|
|
238
|
+
raise 'float unhandled'
|
|
239
|
+
else raise
|
|
240
|
+
end
|
|
241
|
+
elsif e.kind_of? Address
|
|
242
|
+
make_volatile resolve_address(e), type, rsz
|
|
243
|
+
elsif e.kind_of? Expression
|
|
244
|
+
if type.integral? or type.pointer?
|
|
245
|
+
e2 = inuse findreg
|
|
246
|
+
instr 'mov', e2, e
|
|
247
|
+
e2
|
|
248
|
+
elsif type.float?
|
|
249
|
+
raise 'float unhandled'
|
|
250
|
+
end
|
|
251
|
+
else
|
|
252
|
+
e
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# takes an argument, if the argument is an integer that does not fit in an i32, moves it to a temp reg
|
|
257
|
+
# the reg is unused, so use this only right when generating the offending instr (eg cmp, add..)
|
|
258
|
+
def i_to_i32(v)
|
|
259
|
+
if v.kind_of? Expression and i = v.reduce and i.kind_of?(Integer)
|
|
260
|
+
i &= 0xffff_ffff_ffff_ffff
|
|
261
|
+
if i <= 0x7fff_ffff
|
|
262
|
+
elsif i >= (1<<64)-0x8000_0000
|
|
263
|
+
v = Expression[Expression.make_signed(i, 64)]
|
|
264
|
+
else
|
|
265
|
+
v = make_volatile(v)
|
|
266
|
+
unuse v
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
v
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# returns the instruction suffix for a comparison operator
|
|
273
|
+
def getcc(op, type)
|
|
274
|
+
case op
|
|
275
|
+
when :'=='; 'z'
|
|
276
|
+
when :'!='; 'nz'
|
|
277
|
+
when :'<' ; 'b'
|
|
278
|
+
when :'>' ; 'a'
|
|
279
|
+
when :'<='; 'be'
|
|
280
|
+
when :'>='; 'ae'
|
|
281
|
+
else raise "bad comparison op #{op}"
|
|
282
|
+
end.tr((type.specifier == :unsigned ? '' : 'ab'), 'gl')
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# compiles a c expression, returns an X64 instruction argument
|
|
286
|
+
def c_cexpr_inner(expr)
|
|
287
|
+
case expr
|
|
288
|
+
when ::Integer; Expression[expr]
|
|
289
|
+
when C::Variable; findvar(expr)
|
|
290
|
+
when C::CExpression
|
|
291
|
+
if not expr.lexpr or not expr.rexpr
|
|
292
|
+
inuse c_cexpr_inner_nol(expr)
|
|
293
|
+
else
|
|
294
|
+
inuse c_cexpr_inner_l(expr)
|
|
295
|
+
end
|
|
296
|
+
when C::Label; findvar(C::Variable.new(expr.name, C::Array.new(C::BaseType.new(:void), 1)))
|
|
297
|
+
else puts "c_ce_i: unsupported #{expr}" if $VERBOSE
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# compile a CExpression with no lexpr
|
|
302
|
+
def c_cexpr_inner_nol(expr)
|
|
303
|
+
case expr.op
|
|
304
|
+
when nil
|
|
305
|
+
r = c_cexpr_inner(expr.rexpr)
|
|
306
|
+
if (expr.rexpr.kind_of? C::CExpression or expr.rexpr.kind_of? C::Variable) and
|
|
307
|
+
expr.type.kind_of? C::BaseType and expr.rexpr.type.kind_of? C::BaseType
|
|
308
|
+
r = c_cexpr_inner_cast(expr, r)
|
|
309
|
+
end
|
|
310
|
+
r
|
|
311
|
+
when :+
|
|
312
|
+
c_cexpr_inner(expr.rexpr)
|
|
313
|
+
when :-
|
|
314
|
+
r = c_cexpr_inner(expr.rexpr)
|
|
315
|
+
r = make_volatile(r, expr.type)
|
|
316
|
+
if expr.type.integral? or expr.type.pointer?
|
|
317
|
+
instr 'neg', r
|
|
318
|
+
elsif expr.type.float?
|
|
319
|
+
raise 'float unhandled'
|
|
320
|
+
else raise
|
|
321
|
+
end
|
|
322
|
+
r
|
|
323
|
+
when :'++', :'--'
|
|
324
|
+
r = c_cexpr_inner(expr.rexpr)
|
|
325
|
+
inc = true if expr.op == :'++'
|
|
326
|
+
if expr.type.integral? or expr.type.pointer?
|
|
327
|
+
op = (inc ? 'inc' : 'dec')
|
|
328
|
+
instr op, r
|
|
329
|
+
elsif expr.type.float?
|
|
330
|
+
raise 'float unhandled'
|
|
331
|
+
end
|
|
332
|
+
r
|
|
333
|
+
when :&
|
|
334
|
+
raise 'bad precompiler ' + expr.to_s if not expr.rexpr.kind_of? C::Variable
|
|
335
|
+
@state.cache.each { |r_, c|
|
|
336
|
+
return inuse(r_) if c.kind_of? Address and c.target == expr.rexpr
|
|
337
|
+
}
|
|
338
|
+
r = c_cexpr_inner(expr.rexpr)
|
|
339
|
+
raise 'bad lvalue' if not r.kind_of? ModRM
|
|
340
|
+
unuse r
|
|
341
|
+
r = Address.new(r)
|
|
342
|
+
inuse r
|
|
343
|
+
r.target = expr.rexpr
|
|
344
|
+
r
|
|
345
|
+
when :*
|
|
346
|
+
expr.rexpr.type.name = :ptr if expr.rexpr.kind_of? C::CExpression and expr.rexpr.type.kind_of? C::BaseType and typesize[expr.rexpr.type.name] == typesize[:ptr] # hint to use Address
|
|
347
|
+
e = c_cexpr_inner(expr.rexpr)
|
|
348
|
+
sz = 8*sizeof(expr)
|
|
349
|
+
case e
|
|
350
|
+
when Address
|
|
351
|
+
unuse e
|
|
352
|
+
e = e.modrm.dup
|
|
353
|
+
e.sz = sz
|
|
354
|
+
inuse e
|
|
355
|
+
when ModRM; e = make_volatile(e, expr.rexpr.type)
|
|
356
|
+
end
|
|
357
|
+
case e
|
|
358
|
+
when Reg; unuse e ; e = inuse ModRM.new(@cpusz, sz, nil, nil, e, nil)
|
|
359
|
+
when Expression; e = inuse ModRM.new(@cpusz, sz, nil, nil, nil, e)
|
|
360
|
+
end
|
|
361
|
+
e
|
|
362
|
+
when :'!'
|
|
363
|
+
r = c_cexpr_inner(expr.rexpr)
|
|
364
|
+
r = make_volatile(r, expr.rexpr.type)
|
|
365
|
+
if expr.rexpr.type.integral? or expr.type.pointer?
|
|
366
|
+
r = make_volatile(r, expr.rexpr.type)
|
|
367
|
+
instr 'test', r, r
|
|
368
|
+
elsif expr.rexpr.type.float?
|
|
369
|
+
raise 'float unhandled'
|
|
370
|
+
else raise 'bad comparison ' + expr.to_s
|
|
371
|
+
end
|
|
372
|
+
instr 'setz', Reg.new(r.val, 8)
|
|
373
|
+
instr 'and', r, Expression[1]
|
|
374
|
+
r
|
|
375
|
+
else raise 'mmh ? ' + expr.to_s
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# compile a cast (BaseType to BaseType)
|
|
380
|
+
def c_cexpr_inner_cast(expr, r)
|
|
381
|
+
if expr.type.float? or expr.rexpr.type.float?
|
|
382
|
+
raise 'float unhandled'
|
|
383
|
+
elsif (expr.type.integral? or expr.type.pointer?) and (expr.rexpr.type.integral? or expr.rexpr.type.pointer?)
|
|
384
|
+
tto = typesize[expr.type.pointer? ? :ptr : expr.type.name]*8
|
|
385
|
+
tfrom = typesize[expr.rexpr.type.pointer? ? :ptr : expr.rexpr.type.name]*8
|
|
386
|
+
r = resolve_address r if r.kind_of? Address
|
|
387
|
+
if r.kind_of? Expression
|
|
388
|
+
r = make_volatile r, expr.type
|
|
389
|
+
elsif tfrom > tto
|
|
390
|
+
case r
|
|
391
|
+
when ModRM
|
|
392
|
+
unuse r
|
|
393
|
+
r = r.dup
|
|
394
|
+
r.sz = tto
|
|
395
|
+
inuse r
|
|
396
|
+
when Reg
|
|
397
|
+
if r.sz == 64 and tto == 32
|
|
398
|
+
instr 'mov', Reg.new(r.val, tto), Reg.new(r.val, tto)
|
|
399
|
+
else
|
|
400
|
+
instr 'and', r, Expression[(1<<tto)-1] if r.sz > tto
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
elsif tto > tfrom
|
|
404
|
+
if not r.kind_of? Reg or r.sz != @cpusz
|
|
405
|
+
unuse r
|
|
406
|
+
reg = inuse findreg
|
|
407
|
+
op = (r.sz == reg.sz ? 'mov' : (expr.rexpr.type.specifier == :unsigned ? 'movzx' : 'movsx'))
|
|
408
|
+
if reg.sz == 64 and r.sz == 32
|
|
409
|
+
if op == 'movsx'
|
|
410
|
+
instr 'movsxd', reg, r
|
|
411
|
+
else
|
|
412
|
+
instr 'mov', Reg.new(reg.val, 32), r
|
|
413
|
+
end
|
|
414
|
+
else
|
|
415
|
+
instr op, reg, r
|
|
416
|
+
end
|
|
417
|
+
r = reg
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
else raise
|
|
421
|
+
end
|
|
422
|
+
r
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# compiles a CExpression, not arithmetic (assignment, comparison etc)
|
|
426
|
+
def c_cexpr_inner_l(expr)
|
|
427
|
+
case expr.op
|
|
428
|
+
when :funcall
|
|
429
|
+
c_cexpr_inner_funcall(expr)
|
|
430
|
+
when :'+=', :'-=', :'*=', :'/=', :'%=', :'^=', :'&=', :'|=', :'<<=', :'>>='
|
|
431
|
+
l = c_cexpr_inner(expr.lexpr)
|
|
432
|
+
raise 'bad lvalue' if not l.kind_of? ModRM and not @state.bound.index(l)
|
|
433
|
+
r = c_cexpr_inner(expr.rexpr)
|
|
434
|
+
op = expr.op.to_s.chop.to_sym
|
|
435
|
+
c_cexpr_inner_arith(l, op, r, expr.type)
|
|
436
|
+
l
|
|
437
|
+
when :'+', :'-', :'*', :'/', :'%', :'^', :'&', :'|', :'<<', :'>>'
|
|
438
|
+
# both sides are already cast to the same type by the precompiler
|
|
439
|
+
# XXX fptrs are not #integral? ...
|
|
440
|
+
if expr.type.integral? and expr.type.name == :ptr and expr.lexpr.type.kind_of? C::BaseType and
|
|
441
|
+
typesize[expr.lexpr.type.name] == typesize[:ptr]
|
|
442
|
+
expr.lexpr.type.name = :ptr
|
|
443
|
+
end
|
|
444
|
+
l = c_cexpr_inner(expr.lexpr)
|
|
445
|
+
l = make_volatile(l, expr.type) if not l.kind_of? Address
|
|
446
|
+
if expr.type.integral? and expr.type.name == :ptr and l.kind_of? Reg
|
|
447
|
+
unuse l
|
|
448
|
+
l = Address.new ModRM.new(l.sz, @cpusz, nil, nil, l, nil)
|
|
449
|
+
inuse l
|
|
450
|
+
end
|
|
451
|
+
if l.kind_of? Address and expr.type.integral?
|
|
452
|
+
l.modrm.imm = nil if l.modrm.imm and not l.modrm.imm.op and l.modrm.imm.rexpr == 0
|
|
453
|
+
if l.modrm.b and l.modrm.i and l.modrm.s == 1 and l.modrm.b.val == l.modrm.i.val
|
|
454
|
+
unuse l.modrm.b if l.modrm.b != l.modrm.i
|
|
455
|
+
l.modrm.b = nil
|
|
456
|
+
l.modrm.s = 2
|
|
457
|
+
end
|
|
458
|
+
case expr.op
|
|
459
|
+
when :+
|
|
460
|
+
rexpr = expr.rexpr
|
|
461
|
+
rexpr = rexpr.rexpr while rexpr.kind_of? C::CExpression and not rexpr.op and rexpr.type.integral? and
|
|
462
|
+
rexpr.rexpr.kind_of? C::CExpression and rexpr.rexpr.type.integral? and
|
|
463
|
+
typesize[rexpr.type.name] == typesize[rexpr.rexpr.type.name]
|
|
464
|
+
if rexpr.kind_of? C::CExpression and rexpr.op == :* and rexpr.lexpr
|
|
465
|
+
r1 = c_cexpr_inner(rexpr.lexpr)
|
|
466
|
+
r2 = c_cexpr_inner(rexpr.rexpr)
|
|
467
|
+
r1, r2 = r2, r1 if r1.kind_of? Expression
|
|
468
|
+
if r2.kind_of? Expression and [1, 2, 4, 8].include?(rr2 = r2.reduce)
|
|
469
|
+
case r1
|
|
470
|
+
when ModRM, Address, Reg
|
|
471
|
+
r1 = make_volatile(r1, rexpr.type) if not r1.kind_of? Reg
|
|
472
|
+
if not l.modrm.i or (l.modrm.i.val == r1.val and l.modrm.s == 1 and rr2 == 1)
|
|
473
|
+
unuse l, r1, r2
|
|
474
|
+
l = Address.new(l.modrm.dup)
|
|
475
|
+
inuse l
|
|
476
|
+
l.modrm.i = r1
|
|
477
|
+
l.modrm.s = (l.modrm.s || 0) + rr2
|
|
478
|
+
return l
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
r = make_volatile(r1, rexpr.type)
|
|
483
|
+
c_cexpr_inner_arith(r, :*, r2, rexpr.type)
|
|
484
|
+
else
|
|
485
|
+
r = c_cexpr_inner(rexpr)
|
|
486
|
+
end
|
|
487
|
+
r = resolve_address r if r.kind_of? Address
|
|
488
|
+
r = make_volatile(r, rexpr.type) if r.kind_of? ModRM
|
|
489
|
+
case r
|
|
490
|
+
when Reg
|
|
491
|
+
unuse l
|
|
492
|
+
l = Address.new(l.modrm.dup)
|
|
493
|
+
inuse l
|
|
494
|
+
if l.modrm.b
|
|
495
|
+
if not l.modrm.i or (l.modrm.i.val == r.val and l.modrm.s == 1)
|
|
496
|
+
l.modrm.i = r
|
|
497
|
+
l.modrm.s = (l.modrm.s || 0) + 1
|
|
498
|
+
unuse r
|
|
499
|
+
return l
|
|
500
|
+
end
|
|
501
|
+
else
|
|
502
|
+
l.modrm.b = r
|
|
503
|
+
unuse r
|
|
504
|
+
return l
|
|
505
|
+
end
|
|
506
|
+
when Expression
|
|
507
|
+
unuse l, r
|
|
508
|
+
l = Address.new(l.modrm.dup)
|
|
509
|
+
inuse l
|
|
510
|
+
l.modrm.imm = Expression[l.modrm.imm, :+, r]
|
|
511
|
+
return l
|
|
512
|
+
end
|
|
513
|
+
when :-
|
|
514
|
+
r = c_cexpr_inner(expr.rexpr)
|
|
515
|
+
if r.kind_of? Expression
|
|
516
|
+
unuse l, r
|
|
517
|
+
l = Address.new(l.modrm.dup)
|
|
518
|
+
inuse l
|
|
519
|
+
l.modrm.imm = Expression[l.modrm.imm, :-, r]
|
|
520
|
+
return l
|
|
521
|
+
end
|
|
522
|
+
when :*
|
|
523
|
+
r = c_cexpr_inner(expr.rexpr)
|
|
524
|
+
if r.kind_of? Expression and [1, 2, 4, 8].includre?(rr = r.reduce)
|
|
525
|
+
if l.modrm.b and not l.modrm.i
|
|
526
|
+
if rr != 1
|
|
527
|
+
l.modrm.i = l.modrm.b
|
|
528
|
+
l.modrm.s = rr
|
|
529
|
+
l.modrm.imm = Expression[l.modrm.imm, :*, rr] if l.modrm.imm
|
|
530
|
+
end
|
|
531
|
+
unuse r
|
|
532
|
+
return l
|
|
533
|
+
elsif l.modrm.i and not l.modrm.b and l.modrm.s*rr <= 8
|
|
534
|
+
l.modrm.s *= rr
|
|
535
|
+
l.modrm.imm = Expression[l.modrm.imm, :*, rr] if l.modrm.imm and rr != 1
|
|
536
|
+
unuse r
|
|
537
|
+
return l
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
l = make_volatile(l, expr.type) if l.kind_of? Address
|
|
543
|
+
r ||= c_cexpr_inner(expr.rexpr)
|
|
544
|
+
c_cexpr_inner_arith(l, expr.op, r, expr.type)
|
|
545
|
+
l
|
|
546
|
+
when :'='
|
|
547
|
+
r = c_cexpr_inner(expr.rexpr)
|
|
548
|
+
l = c_cexpr_inner(expr.lexpr)
|
|
549
|
+
raise 'bad lvalue ' + l.inspect if not l.kind_of? ModRM and not @state.bound.index(l)
|
|
550
|
+
r = resolve_address r if r.kind_of? Address
|
|
551
|
+
r = make_volatile(r, expr.type) if l.kind_of? ModRM and r.kind_of? ModRM
|
|
552
|
+
unuse r
|
|
553
|
+
if expr.type.integral? or expr.type.pointer?
|
|
554
|
+
if r.kind_of? Address
|
|
555
|
+
m = r.modrm.dup
|
|
556
|
+
m.sz = l.sz
|
|
557
|
+
instr 'lea', l, m
|
|
558
|
+
else
|
|
559
|
+
if l.kind_of? ModRM and r.kind_of? Reg and l.sz != r.sz
|
|
560
|
+
raise if l.sz > r.sz
|
|
561
|
+
if l.sz == 8 and r.val >= 4
|
|
562
|
+
reg = ([0, 1, 2, 3] - @state.used).first
|
|
563
|
+
if not reg
|
|
564
|
+
rax = Reg.new(0, r.sz)
|
|
565
|
+
instr 'push', rax
|
|
566
|
+
instr 'mov', rax, r
|
|
567
|
+
instr 'mov', l, Reg.new(rax.val, 8)
|
|
568
|
+
instr 'pop', rax
|
|
569
|
+
else
|
|
570
|
+
flushcachereg(reg)
|
|
571
|
+
instr 'mov', Reg.new(reg, r.sz), r
|
|
572
|
+
instr 'mov', l, Reg.new(reg, 8)
|
|
573
|
+
end
|
|
574
|
+
else
|
|
575
|
+
instr 'mov', l, Reg.new(r.val, l.sz)
|
|
576
|
+
end
|
|
577
|
+
else
|
|
578
|
+
instr 'mov', l, r
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
elsif expr.type.float?
|
|
582
|
+
raise 'float unhandled'
|
|
583
|
+
end
|
|
584
|
+
l
|
|
585
|
+
when :>, :<, :>=, :<=, :==, :'!='
|
|
586
|
+
l = c_cexpr_inner(expr.lexpr)
|
|
587
|
+
l = make_volatile(l, expr.type)
|
|
588
|
+
r = c_cexpr_inner(expr.rexpr)
|
|
589
|
+
unuse r
|
|
590
|
+
if expr.lexpr.type.integral? or expr.lexpr.type.pointer?
|
|
591
|
+
instr 'cmp', l, i_to_i32(r)
|
|
592
|
+
elsif expr.lexpr.type.float?
|
|
593
|
+
raise 'float unhandled'
|
|
594
|
+
else raise 'bad comparison ' + expr.to_s
|
|
595
|
+
end
|
|
596
|
+
opcc = getcc(expr.op, expr.type)
|
|
597
|
+
instr 'set'+opcc, Reg.new(l.val, 8)
|
|
598
|
+
instr 'and', l, 1
|
|
599
|
+
l
|
|
600
|
+
else
|
|
601
|
+
raise 'unhandled cexpr ' + expr.to_s
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
# compiles a subroutine call
|
|
606
|
+
def c_cexpr_inner_funcall(expr)
|
|
607
|
+
backup = []
|
|
608
|
+
rax = Reg.new(0, 64)
|
|
609
|
+
|
|
610
|
+
ft = expr.lexpr.type
|
|
611
|
+
ft = ft.pointed if ft.pointer?
|
|
612
|
+
ft = nil if not ft.kind_of? C::Function
|
|
613
|
+
|
|
614
|
+
arglist = expr.rexpr.dup
|
|
615
|
+
regargsmask = @state.regargs.dup
|
|
616
|
+
if ft
|
|
617
|
+
ft.args.each_with_index { |a, i|
|
|
618
|
+
if rn = a.has_attribute_var('register')
|
|
619
|
+
regargsmask.insert(i, Reg.from_str(rn).val)
|
|
620
|
+
end
|
|
621
|
+
}
|
|
622
|
+
end
|
|
623
|
+
regargsmask = regargsmask[0, expr.rexpr.length]
|
|
624
|
+
|
|
625
|
+
(@state.abi_flushregs_call | regargsmask.compact.uniq).each { |reg|
|
|
626
|
+
next if reg == 4
|
|
627
|
+
next if reg == 5 and @state.saved_rbp
|
|
628
|
+
if not @state.used.include? reg
|
|
629
|
+
if not @state.abi_trashregs.include? reg
|
|
630
|
+
@state.dirty |= [reg]
|
|
631
|
+
end
|
|
632
|
+
next
|
|
633
|
+
end
|
|
634
|
+
backup << reg
|
|
635
|
+
instr 'push', Reg.new(reg, 64)
|
|
636
|
+
@state.used.delete reg
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
stackargs = expr.rexpr.zip(regargsmask).map { |a, r| a if not r }.compact
|
|
640
|
+
|
|
641
|
+
# preserve 16byte stack align under windows
|
|
642
|
+
stackalign = true if (stackargs + backup).length & 1 == 1
|
|
643
|
+
instr 'push', rax if stackalign
|
|
644
|
+
|
|
645
|
+
stackargs.reverse_each { |arg|
|
|
646
|
+
raise 'arg unhandled' if not arg.type.integral? or arg.type.pointer?
|
|
647
|
+
a = c_cexpr_inner(arg)
|
|
648
|
+
a = resolve_address a if a.kind_of? Address
|
|
649
|
+
a = make_volatile(a, arg.type) if a.kind_of? ModRM and arg.type.name != :__int64
|
|
650
|
+
unuse a
|
|
651
|
+
instr 'push', a
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
regargs_unuse = []
|
|
655
|
+
regargsmask.zip(expr.rexpr).each { |ra, arg|
|
|
656
|
+
next if not arg or not ra
|
|
657
|
+
a = c_cexpr_inner(arg)
|
|
658
|
+
a = resolve_address a if a.kind_of? Address
|
|
659
|
+
r = Reg.new(ra, a.respond_to?(:sz) ? a.sz : 64)
|
|
660
|
+
instr 'mov', r, a if not a.kind_of? Reg or a.val != r.val
|
|
661
|
+
unuse a
|
|
662
|
+
regargs_unuse << r if not @state.inuse.include? ra
|
|
663
|
+
inuse r
|
|
664
|
+
}
|
|
665
|
+
instr 'sub', Reg.new(4, 64), Expression[@state.args_space] if @state.args_space > 0 # TODO prealloc that at func start
|
|
666
|
+
|
|
667
|
+
if ft.kind_of? C::Function and ft.varargs and @state.args_space == 0
|
|
668
|
+
# gcc stores here the nr of xmm args passed, real args are passed the standard way
|
|
669
|
+
# TODO check visualstudio/ms ABI
|
|
670
|
+
instr 'xor', rax, rax
|
|
671
|
+
inuse rax
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
if expr.lexpr.kind_of? C::Variable and expr.lexpr.type.kind_of? C::Function
|
|
676
|
+
instr 'call', Expression[expr.lexpr.name]
|
|
677
|
+
else
|
|
678
|
+
ptr = c_cexpr_inner(expr.lexpr)
|
|
679
|
+
unuse ptr
|
|
680
|
+
ptr = make_volatile(ptr, expr.lexpr.type) if ptr.kind_of? Address
|
|
681
|
+
instr 'call', ptr
|
|
682
|
+
end
|
|
683
|
+
regargs_unuse.each { |r| unuse r }
|
|
684
|
+
argsz = @state.args_space + stackargs.length * 8
|
|
685
|
+
argsz += 8 if stackalign
|
|
686
|
+
instr 'add', Reg.new(4, @cpusz), Expression[argsz] if argsz > 0
|
|
687
|
+
|
|
688
|
+
@state.abi_flushregs_call.each { |reg| flushcachereg reg }
|
|
689
|
+
@state.used |= backup
|
|
690
|
+
if @state.used.include?(0)
|
|
691
|
+
retreg = inuse findreg
|
|
692
|
+
else
|
|
693
|
+
retreg = inuse getreg(0)
|
|
694
|
+
end
|
|
695
|
+
backup.reverse_each { |reg|
|
|
696
|
+
if retreg.kind_of? Reg and reg == 0
|
|
697
|
+
instr 'pop', Reg.new(retreg.val, 64)
|
|
698
|
+
instr 'xchg', Reg.new(reg, 64), Reg.new(retreg.val, 64)
|
|
699
|
+
else
|
|
700
|
+
instr 'pop', Reg.new(reg, 64)
|
|
701
|
+
end
|
|
702
|
+
}
|
|
703
|
+
retreg
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
# compiles/optimizes arithmetic operations
|
|
707
|
+
def c_cexpr_inner_arith(l, op, r, type)
|
|
708
|
+
# optimizes *2 -> <<1
|
|
709
|
+
if r.kind_of? Expression and (rr = r.reduce).kind_of? ::Integer
|
|
710
|
+
if type.integral? or type.pointer?
|
|
711
|
+
log2 = lambda { |v|
|
|
712
|
+
# TODO lol
|
|
713
|
+
i = 0
|
|
714
|
+
i += 1 while (1 << i) < v
|
|
715
|
+
i if (1 << i) == v
|
|
716
|
+
}
|
|
717
|
+
if (lr = log2[rr]).kind_of? ::Integer
|
|
718
|
+
case op
|
|
719
|
+
when :*; return c_cexpr_inner_arith(l, :<<, Expression[lr], type)
|
|
720
|
+
when :/; return c_cexpr_inner_arith(l, :>>, Expression[lr], type)
|
|
721
|
+
when :%; return c_cexpr_inner_arith(l, :&, Expression[rr-1], type)
|
|
722
|
+
end
|
|
723
|
+
else
|
|
724
|
+
# TODO /r => *(r^(-1)), *3 => stuff with magic constants..
|
|
725
|
+
end
|
|
726
|
+
end
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
if type.float?
|
|
730
|
+
raise 'float unhandled'
|
|
731
|
+
else
|
|
732
|
+
c_cexpr_inner_arith_int(l, op, r, type)
|
|
733
|
+
end
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
# compile an integral arithmetic expression, reg-sized
|
|
737
|
+
def c_cexpr_inner_arith_int(l, op, r, type)
|
|
738
|
+
op = case op
|
|
739
|
+
when :+; 'add'
|
|
740
|
+
when :-; 'sub'
|
|
741
|
+
when :&; 'and'
|
|
742
|
+
when :|; 'or'
|
|
743
|
+
when :^; 'xor'
|
|
744
|
+
when :>>; type.specifier == :unsigned ? 'shr' : 'sar'
|
|
745
|
+
when :<<; 'shl'
|
|
746
|
+
when :*; 'mul'
|
|
747
|
+
when :/; 'div'
|
|
748
|
+
when :%; 'mod'
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
case op
|
|
752
|
+
when 'add', 'sub', 'and', 'or', 'xor'
|
|
753
|
+
r = make_volatile(r, type) if l.kind_of? ModRM and r.kind_of? ModRM
|
|
754
|
+
unuse r
|
|
755
|
+
instr op, l, i_to_i32(r)
|
|
756
|
+
when 'shr', 'sar', 'shl'
|
|
757
|
+
if r.kind_of? Expression
|
|
758
|
+
instr op, l, r
|
|
759
|
+
else
|
|
760
|
+
# XXX bouh
|
|
761
|
+
r = make_volatile(r, C::BaseType.new(:__int8, :unsigned))
|
|
762
|
+
unuse r
|
|
763
|
+
if r.val != 1
|
|
764
|
+
rcx = Reg.new(1, 64)
|
|
765
|
+
instr 'xchg', rcx, Reg.new(r.val, 64)
|
|
766
|
+
l = Reg.new(r.val, l.sz) if l.kind_of? Reg and l.val == 1
|
|
767
|
+
end
|
|
768
|
+
instr op, l, Reg.new(1, 8)
|
|
769
|
+
instr 'xchg', rcx, Reg.new(r.val, 64) if r.val != 1
|
|
770
|
+
end
|
|
771
|
+
when 'mul'
|
|
772
|
+
if l.kind_of? ModRM
|
|
773
|
+
if r.kind_of? Expression
|
|
774
|
+
ll = findreg
|
|
775
|
+
instr 'imul', ll, l, r
|
|
776
|
+
else
|
|
777
|
+
ll = make_volatile(l, type)
|
|
778
|
+
unuse ll
|
|
779
|
+
instr 'imul', ll, r
|
|
780
|
+
end
|
|
781
|
+
instr 'mov', l, ll
|
|
782
|
+
else
|
|
783
|
+
instr 'imul', l, r
|
|
784
|
+
end
|
|
785
|
+
unuse r
|
|
786
|
+
when 'div', 'mod'
|
|
787
|
+
lv = l.val if l.kind_of? Reg
|
|
788
|
+
rax = Reg.from_str 'rax'
|
|
789
|
+
rdx = Reg.from_str 'rdx'
|
|
790
|
+
if @state.used.include? rax.val and lv != rax.val
|
|
791
|
+
instr 'push', rax
|
|
792
|
+
saved_rax = true
|
|
793
|
+
end
|
|
794
|
+
if @state.used.include? rdx.val and lv != rdx.val
|
|
795
|
+
instr 'push', rdx
|
|
796
|
+
saved_rdx = true
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
instr 'mov', rax, l if lv != rax.val
|
|
800
|
+
|
|
801
|
+
if r.kind_of? Expression
|
|
802
|
+
instr 'push', r
|
|
803
|
+
rsp = Reg.from_str 'rsp'
|
|
804
|
+
r = ModRM.new(@cpusz, 64, nil, nil, rsp, nil)
|
|
805
|
+
need_pop = true
|
|
806
|
+
end
|
|
807
|
+
|
|
808
|
+
if type.specifier == :unsigned
|
|
809
|
+
instr 'mov', rdx, Expression[0]
|
|
810
|
+
instr 'div', r
|
|
811
|
+
else
|
|
812
|
+
instr 'cdq'
|
|
813
|
+
instr 'idiv', r
|
|
814
|
+
end
|
|
815
|
+
unuse r
|
|
816
|
+
|
|
817
|
+
instr 'add', rsp, 8 if need_pop
|
|
818
|
+
|
|
819
|
+
if op == 'div'
|
|
820
|
+
instr 'mov', l, rax if lv != rax.val
|
|
821
|
+
else
|
|
822
|
+
instr 'mov', l, rdx if lv != rdx.val
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
instr 'pop', rdx if saved_rdx
|
|
826
|
+
instr 'pop', rax if saved_rax
|
|
827
|
+
end
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
def c_cexpr(expr)
|
|
831
|
+
case expr.op
|
|
832
|
+
when :+, :-, :*, :/, :&, :|, :^, :%, :[], nil, :'.', :'->',
|
|
833
|
+
:>, :<, :<=, :>=, :==, :'!=', :'!'
|
|
834
|
+
# skip no-ops
|
|
835
|
+
c_cexpr(expr.lexpr) if expr.lexpr.kind_of? C::CExpression
|
|
836
|
+
c_cexpr(expr.rexpr) if expr.rexpr.kind_of? C::CExpression
|
|
837
|
+
else unuse c_cexpr_inner(expr)
|
|
838
|
+
end
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
def c_block_exit(block)
|
|
842
|
+
@state.cache.delete_if { |k, v|
|
|
843
|
+
case v
|
|
844
|
+
when C::Variable; block.symbol.index v
|
|
845
|
+
when Address; block.symbol.index v.target
|
|
846
|
+
end
|
|
847
|
+
}
|
|
848
|
+
block.symbol.each { |s|
|
|
849
|
+
unuse @state.bound.delete(s)
|
|
850
|
+
}
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
def c_decl(var)
|
|
854
|
+
if var.type.kind_of? C::Array and
|
|
855
|
+
var.type.length.kind_of? C::CExpression
|
|
856
|
+
reg = c_cexpr_inner(var.type.length)
|
|
857
|
+
unuse reg
|
|
858
|
+
instr 'sub', Reg.new(4, @cpusz), reg
|
|
859
|
+
# TODO
|
|
860
|
+
end
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
def c_ifgoto(expr, target)
|
|
864
|
+
case o = expr.op
|
|
865
|
+
when :<, :>, :<=, :>=, :==, :'!='
|
|
866
|
+
l = c_cexpr_inner(expr.lexpr)
|
|
867
|
+
r = c_cexpr_inner(expr.rexpr)
|
|
868
|
+
r = make_volatile(r, expr.type) if r.kind_of? ModRM and l.kind_of? ModRM
|
|
869
|
+
if l.kind_of? Expression
|
|
870
|
+
o = { :< => :>, :> => :<, :>= => :<=, :<= => :>= }[o] || o
|
|
871
|
+
l, r = r, l
|
|
872
|
+
end
|
|
873
|
+
unuse l, r
|
|
874
|
+
if expr.lexpr.type.integral? or expr.lexpr.type.pointer?
|
|
875
|
+
r = Reg.new(r.val, l.sz) if r.kind_of? Reg and r.sz != l.sz # XXX
|
|
876
|
+
instr 'cmp', l, i_to_i32(r)
|
|
877
|
+
elsif expr.lexpr.type.float?
|
|
878
|
+
raise 'float unhandled'
|
|
879
|
+
else raise 'bad comparison ' + expr.to_s
|
|
880
|
+
end
|
|
881
|
+
op = 'j' + getcc(o, expr.lexpr.type)
|
|
882
|
+
instr op, Expression[target]
|
|
883
|
+
when :'!'
|
|
884
|
+
r = c_cexpr_inner(expr.rexpr)
|
|
885
|
+
r = make_volatile(r, expr.rexpr.type)
|
|
886
|
+
unuse r
|
|
887
|
+
instr 'test', r, r
|
|
888
|
+
instr 'jz', Expression[target]
|
|
889
|
+
else
|
|
890
|
+
r = c_cexpr_inner(expr)
|
|
891
|
+
r = make_volatile(r, expr.type)
|
|
892
|
+
unuse r
|
|
893
|
+
instr 'test', r, r
|
|
894
|
+
instr 'jnz', Expression[target]
|
|
895
|
+
end
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
def c_goto(target)
|
|
899
|
+
instr 'jmp', Expression[target]
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
def c_label(name)
|
|
903
|
+
@state.cache.clear
|
|
904
|
+
@source << '' << Label.new(name)
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
def c_return(expr)
|
|
908
|
+
return if not expr
|
|
909
|
+
@state.cache.delete_if { |r, v| r.kind_of? Reg and r.val == 0 and expr != v }
|
|
910
|
+
r = c_cexpr_inner(expr)
|
|
911
|
+
r = make_volatile(r, expr.type)
|
|
912
|
+
unuse r
|
|
913
|
+
instr 'mov', Reg.new(0, r.sz), r if r.val != 0
|
|
914
|
+
end
|
|
915
|
+
|
|
916
|
+
def c_asm(stmt)
|
|
917
|
+
if stmt.output or stmt.input or stmt.clobber
|
|
918
|
+
raise # TODO (handle %%0 => rax, gas, etc)
|
|
919
|
+
else
|
|
920
|
+
raise 'asm refering variables unhandled' if @state.func.initializer.symbol.keys.find { |sym| stmt.body =~ /\b#{Regexp.escape(sym)}\b/ }
|
|
921
|
+
@source << stmt.body
|
|
922
|
+
end
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
def c_init_state(func)
|
|
926
|
+
@state = State.new(func)
|
|
927
|
+
args = func.type.args.dup
|
|
928
|
+
if @parser.lexer.definition['__MS_X86_64_ABI__']
|
|
929
|
+
@state.args_space = 32
|
|
930
|
+
@state.regargs = [1, 2, 8, 9]
|
|
931
|
+
else
|
|
932
|
+
@state.args_space = 0
|
|
933
|
+
@state.regargs = [7, 6, 2, 1, 8, 9]
|
|
934
|
+
end
|
|
935
|
+
c_reserve_stack(func.initializer)
|
|
936
|
+
off = @state.offset.values.max.to_i
|
|
937
|
+
off = 0 if off < 0
|
|
938
|
+
|
|
939
|
+
argoff = 2*8 + @state.args_space
|
|
940
|
+
rlist = @state.regargs.dup
|
|
941
|
+
args.each { |a|
|
|
942
|
+
if a.has_attribute_var('register')
|
|
943
|
+
off = c_reserve_stack_var(a, off)
|
|
944
|
+
@state.offset[a] = off
|
|
945
|
+
elsif r = rlist.shift
|
|
946
|
+
if @state.args_space > 0
|
|
947
|
+
# use reserved space to spill regargs
|
|
948
|
+
off = -16-8*@state.regargs.index(r)
|
|
949
|
+
else
|
|
950
|
+
off = c_reserve_stack_var(a, off)
|
|
951
|
+
end
|
|
952
|
+
@state.offset[a] = off
|
|
953
|
+
else
|
|
954
|
+
@state.offset[a] = -argoff
|
|
955
|
+
argoff = (argoff + sizeof(a) + 7) / 8 * 8
|
|
956
|
+
end
|
|
957
|
+
}
|
|
958
|
+
if not @state.offset.values.grep(::Integer).empty?
|
|
959
|
+
@state.saved_rbp = Reg.new(5, @cpusz)
|
|
960
|
+
@state.used << 5
|
|
961
|
+
end
|
|
962
|
+
end
|
|
963
|
+
|
|
964
|
+
def c_prolog
|
|
965
|
+
localspc = @state.offset.values.grep(::Integer).max
|
|
966
|
+
return if @state.func.attributes.to_a.include? 'naked'
|
|
967
|
+
@state.dirty -= @state.abi_trashregs
|
|
968
|
+
if localspc
|
|
969
|
+
localspc = (localspc + 7) / 8 * 8
|
|
970
|
+
if @state.args_space > 0 and (localspc/8 + @state.dirty.length) & 1 == 1
|
|
971
|
+
# ensure 16-o stack align on windows
|
|
972
|
+
localspc += 8
|
|
973
|
+
end
|
|
974
|
+
ebp = @state.saved_rbp
|
|
975
|
+
esp = Reg.new(4, ebp.sz)
|
|
976
|
+
instr 'push', ebp
|
|
977
|
+
instr 'mov', ebp, esp
|
|
978
|
+
instr 'sub', esp, Expression[localspc] if localspc > 0
|
|
979
|
+
|
|
980
|
+
rlist = @state.regargs.dup
|
|
981
|
+
@state.func.type.args.each { |a|
|
|
982
|
+
if rn = a.has_attribute_var('register')
|
|
983
|
+
r = Reg.from_str(rn).val
|
|
984
|
+
elsif r = rlist.shift
|
|
985
|
+
else next
|
|
986
|
+
end
|
|
987
|
+
v = findvar(a)
|
|
988
|
+
instr 'mov', v, Reg.new(r, v.sz)
|
|
989
|
+
}
|
|
990
|
+
elsif @state.args_space > 0 and @state.dirty.length & 1 == 0
|
|
991
|
+
instr 'sub', Reg.new(4, @cpusz), Expression[8]
|
|
992
|
+
end
|
|
993
|
+
@state.dirty.each { |reg|
|
|
994
|
+
instr 'push', Reg.new(reg, @cpusz)
|
|
995
|
+
}
|
|
996
|
+
end
|
|
997
|
+
|
|
998
|
+
def c_epilog
|
|
999
|
+
return if @state.func.attributes.to_a.include? 'naked'
|
|
1000
|
+
@state.dirty.reverse_each { |reg|
|
|
1001
|
+
instr 'pop', Reg.new(reg, @cpusz)
|
|
1002
|
+
}
|
|
1003
|
+
if ebp = @state.saved_rbp
|
|
1004
|
+
instr 'mov', Reg.new(4, ebp.sz), ebp
|
|
1005
|
+
instr 'pop', ebp
|
|
1006
|
+
elsif @state.args_space > 0 and @state.dirty.length & 1 == 0
|
|
1007
|
+
instr 'add', Reg.new(4, @cpusz), Expression[8]
|
|
1008
|
+
end
|
|
1009
|
+
instr 'ret'
|
|
1010
|
+
end
|
|
1011
|
+
|
|
1012
|
+
def c_program_epilog
|
|
1013
|
+
end
|
|
1014
|
+
|
|
1015
|
+
def check_reserved_name(var)
|
|
1016
|
+
Reg.s_to_i[var.name] or super(var)
|
|
1017
|
+
end
|
|
1018
|
+
end
|
|
1019
|
+
|
|
1020
|
+
def new_ccompiler(parser, exe=ExeFormat.new)
|
|
1021
|
+
exe.cpu = self if not exe.instance_variable_get('@cpu')
|
|
1022
|
+
CCompiler.new(parser, exe)
|
|
1023
|
+
end
|
|
1024
|
+
end
|
|
1025
|
+
end
|