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