metasm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (192) hide show
  1. data/BUGS +11 -0
  2. data/CREDITS +17 -0
  3. data/README +270 -0
  4. data/TODO +114 -0
  5. data/doc/code_organisation.txt +146 -0
  6. data/doc/const_missing.txt +16 -0
  7. data/doc/core_classes.txt +75 -0
  8. data/doc/feature_list.txt +53 -0
  9. data/doc/index.txt +59 -0
  10. data/doc/install_notes.txt +170 -0
  11. data/doc/style.css +3 -0
  12. data/doc/use_cases.txt +18 -0
  13. data/lib/metasm.rb +80 -0
  14. data/lib/metasm/arm.rb +12 -0
  15. data/lib/metasm/arm/debug.rb +39 -0
  16. data/lib/metasm/arm/decode.rb +167 -0
  17. data/lib/metasm/arm/encode.rb +77 -0
  18. data/lib/metasm/arm/main.rb +75 -0
  19. data/lib/metasm/arm/opcodes.rb +177 -0
  20. data/lib/metasm/arm/parse.rb +130 -0
  21. data/lib/metasm/arm/render.rb +55 -0
  22. data/lib/metasm/compile_c.rb +1457 -0
  23. data/lib/metasm/dalvik.rb +8 -0
  24. data/lib/metasm/dalvik/decode.rb +196 -0
  25. data/lib/metasm/dalvik/main.rb +60 -0
  26. data/lib/metasm/dalvik/opcodes.rb +366 -0
  27. data/lib/metasm/decode.rb +213 -0
  28. data/lib/metasm/decompile.rb +2659 -0
  29. data/lib/metasm/disassemble.rb +2068 -0
  30. data/lib/metasm/disassemble_api.rb +1280 -0
  31. data/lib/metasm/dynldr.rb +1329 -0
  32. data/lib/metasm/encode.rb +333 -0
  33. data/lib/metasm/exe_format/a_out.rb +194 -0
  34. data/lib/metasm/exe_format/autoexe.rb +82 -0
  35. data/lib/metasm/exe_format/bflt.rb +189 -0
  36. data/lib/metasm/exe_format/coff.rb +455 -0
  37. data/lib/metasm/exe_format/coff_decode.rb +901 -0
  38. data/lib/metasm/exe_format/coff_encode.rb +1078 -0
  39. data/lib/metasm/exe_format/dex.rb +457 -0
  40. data/lib/metasm/exe_format/dol.rb +145 -0
  41. data/lib/metasm/exe_format/elf.rb +923 -0
  42. data/lib/metasm/exe_format/elf_decode.rb +979 -0
  43. data/lib/metasm/exe_format/elf_encode.rb +1375 -0
  44. data/lib/metasm/exe_format/macho.rb +827 -0
  45. data/lib/metasm/exe_format/main.rb +228 -0
  46. data/lib/metasm/exe_format/mz.rb +164 -0
  47. data/lib/metasm/exe_format/nds.rb +172 -0
  48. data/lib/metasm/exe_format/pe.rb +437 -0
  49. data/lib/metasm/exe_format/serialstruct.rb +246 -0
  50. data/lib/metasm/exe_format/shellcode.rb +114 -0
  51. data/lib/metasm/exe_format/xcoff.rb +167 -0
  52. data/lib/metasm/gui.rb +23 -0
  53. data/lib/metasm/gui/cstruct.rb +373 -0
  54. data/lib/metasm/gui/dasm_coverage.rb +199 -0
  55. data/lib/metasm/gui/dasm_decomp.rb +369 -0
  56. data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
  57. data/lib/metasm/gui/dasm_graph.rb +1354 -0
  58. data/lib/metasm/gui/dasm_hex.rb +543 -0
  59. data/lib/metasm/gui/dasm_listing.rb +599 -0
  60. data/lib/metasm/gui/dasm_main.rb +906 -0
  61. data/lib/metasm/gui/dasm_opcodes.rb +291 -0
  62. data/lib/metasm/gui/debug.rb +1228 -0
  63. data/lib/metasm/gui/gtk.rb +884 -0
  64. data/lib/metasm/gui/qt.rb +495 -0
  65. data/lib/metasm/gui/win32.rb +3004 -0
  66. data/lib/metasm/gui/x11.rb +621 -0
  67. data/lib/metasm/ia32.rb +14 -0
  68. data/lib/metasm/ia32/compile_c.rb +1523 -0
  69. data/lib/metasm/ia32/debug.rb +193 -0
  70. data/lib/metasm/ia32/decode.rb +1167 -0
  71. data/lib/metasm/ia32/decompile.rb +564 -0
  72. data/lib/metasm/ia32/encode.rb +314 -0
  73. data/lib/metasm/ia32/main.rb +233 -0
  74. data/lib/metasm/ia32/opcodes.rb +872 -0
  75. data/lib/metasm/ia32/parse.rb +327 -0
  76. data/lib/metasm/ia32/render.rb +91 -0
  77. data/lib/metasm/main.rb +1193 -0
  78. data/lib/metasm/mips.rb +11 -0
  79. data/lib/metasm/mips/compile_c.rb +7 -0
  80. data/lib/metasm/mips/decode.rb +253 -0
  81. data/lib/metasm/mips/encode.rb +51 -0
  82. data/lib/metasm/mips/main.rb +72 -0
  83. data/lib/metasm/mips/opcodes.rb +443 -0
  84. data/lib/metasm/mips/parse.rb +51 -0
  85. data/lib/metasm/mips/render.rb +43 -0
  86. data/lib/metasm/os/gnu_exports.rb +270 -0
  87. data/lib/metasm/os/linux.rb +1112 -0
  88. data/lib/metasm/os/main.rb +1686 -0
  89. data/lib/metasm/os/remote.rb +527 -0
  90. data/lib/metasm/os/windows.rb +2027 -0
  91. data/lib/metasm/os/windows_exports.rb +745 -0
  92. data/lib/metasm/parse.rb +876 -0
  93. data/lib/metasm/parse_c.rb +3938 -0
  94. data/lib/metasm/pic16c/decode.rb +42 -0
  95. data/lib/metasm/pic16c/main.rb +17 -0
  96. data/lib/metasm/pic16c/opcodes.rb +68 -0
  97. data/lib/metasm/ppc.rb +11 -0
  98. data/lib/metasm/ppc/decode.rb +264 -0
  99. data/lib/metasm/ppc/decompile.rb +251 -0
  100. data/lib/metasm/ppc/encode.rb +51 -0
  101. data/lib/metasm/ppc/main.rb +129 -0
  102. data/lib/metasm/ppc/opcodes.rb +410 -0
  103. data/lib/metasm/ppc/parse.rb +52 -0
  104. data/lib/metasm/preprocessor.rb +1277 -0
  105. data/lib/metasm/render.rb +130 -0
  106. data/lib/metasm/sh4.rb +8 -0
  107. data/lib/metasm/sh4/decode.rb +336 -0
  108. data/lib/metasm/sh4/main.rb +292 -0
  109. data/lib/metasm/sh4/opcodes.rb +381 -0
  110. data/lib/metasm/x86_64.rb +12 -0
  111. data/lib/metasm/x86_64/compile_c.rb +1025 -0
  112. data/lib/metasm/x86_64/debug.rb +59 -0
  113. data/lib/metasm/x86_64/decode.rb +268 -0
  114. data/lib/metasm/x86_64/encode.rb +264 -0
  115. data/lib/metasm/x86_64/main.rb +135 -0
  116. data/lib/metasm/x86_64/opcodes.rb +118 -0
  117. data/lib/metasm/x86_64/parse.rb +68 -0
  118. data/misc/bottleneck.rb +61 -0
  119. data/misc/cheader-findpppath.rb +58 -0
  120. data/misc/hexdiff.rb +74 -0
  121. data/misc/hexdump.rb +55 -0
  122. data/misc/metasm-all.rb +13 -0
  123. data/misc/objdiff.rb +47 -0
  124. data/misc/objscan.rb +40 -0
  125. data/misc/pdfparse.rb +661 -0
  126. data/misc/ppc_pdf2oplist.rb +192 -0
  127. data/misc/tcp_proxy_hex.rb +84 -0
  128. data/misc/txt2html.rb +440 -0
  129. data/samples/a.out.rb +31 -0
  130. data/samples/asmsyntax.rb +77 -0
  131. data/samples/bindiff.rb +555 -0
  132. data/samples/compilation-steps.rb +49 -0
  133. data/samples/cparser_makestackoffset.rb +55 -0
  134. data/samples/dasm-backtrack.rb +38 -0
  135. data/samples/dasmnavig.rb +318 -0
  136. data/samples/dbg-apihook.rb +228 -0
  137. data/samples/dbghelp.rb +143 -0
  138. data/samples/disassemble-gui.rb +102 -0
  139. data/samples/disassemble.rb +133 -0
  140. data/samples/dump_upx.rb +95 -0
  141. data/samples/dynamic_ruby.rb +1929 -0
  142. data/samples/elf_list_needed.rb +46 -0
  143. data/samples/elf_listexports.rb +33 -0
  144. data/samples/elfencode.rb +25 -0
  145. data/samples/exeencode.rb +128 -0
  146. data/samples/factorize-headers-elfimports.rb +77 -0
  147. data/samples/factorize-headers-peimports.rb +109 -0
  148. data/samples/factorize-headers.rb +43 -0
  149. data/samples/gdbclient.rb +583 -0
  150. data/samples/generate_libsigs.rb +102 -0
  151. data/samples/hotfix_gtk_dbg.rb +59 -0
  152. data/samples/install_win_env.rb +78 -0
  153. data/samples/lindebug.rb +924 -0
  154. data/samples/linux_injectsyscall.rb +95 -0
  155. data/samples/machoencode.rb +31 -0
  156. data/samples/metasm-shell.rb +91 -0
  157. data/samples/pe-hook.rb +69 -0
  158. data/samples/pe-ia32-cpuid.rb +203 -0
  159. data/samples/pe-mips.rb +35 -0
  160. data/samples/pe-shutdown.rb +78 -0
  161. data/samples/pe-testrelocs.rb +51 -0
  162. data/samples/pe-testrsrc.rb +24 -0
  163. data/samples/pe_listexports.rb +31 -0
  164. data/samples/peencode.rb +19 -0
  165. data/samples/peldr.rb +494 -0
  166. data/samples/preprocess-flatten.rb +19 -0
  167. data/samples/r0trace.rb +308 -0
  168. data/samples/rubstop.rb +399 -0
  169. data/samples/scan_pt_gnu_stack.rb +54 -0
  170. data/samples/scanpeexports.rb +62 -0
  171. data/samples/shellcode-c.rb +40 -0
  172. data/samples/shellcode-dynlink.rb +146 -0
  173. data/samples/source.asm +34 -0
  174. data/samples/struct_offset.rb +47 -0
  175. data/samples/testpe.rb +32 -0
  176. data/samples/testraw.rb +45 -0
  177. data/samples/win32genloader.rb +132 -0
  178. data/samples/win32hooker-advanced.rb +169 -0
  179. data/samples/win32hooker.rb +96 -0
  180. data/samples/win32livedasm.rb +33 -0
  181. data/samples/win32remotescan.rb +133 -0
  182. data/samples/wintrace.rb +92 -0
  183. data/tests/all.rb +8 -0
  184. data/tests/dasm.rb +39 -0
  185. data/tests/dynldr.rb +35 -0
  186. data/tests/encodeddata.rb +132 -0
  187. data/tests/ia32.rb +82 -0
  188. data/tests/mips.rb +116 -0
  189. data/tests/parse_c.rb +239 -0
  190. data/tests/preprocessor.rb +269 -0
  191. data/tests/x86_64.rb +62 -0
  192. 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