metasm 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.hgtags +3 -0
  4. data/Gemfile +1 -0
  5. data/INSTALL +61 -0
  6. data/LICENCE +458 -0
  7. data/README +29 -21
  8. data/Rakefile +10 -0
  9. data/TODO +10 -12
  10. data/doc/code_organisation.txt +2 -0
  11. data/doc/core/DynLdr.txt +247 -0
  12. data/doc/core/ExeFormat.txt +43 -0
  13. data/doc/core/Expression.txt +220 -0
  14. data/doc/core/GNUExports.txt +27 -0
  15. data/doc/core/Ia32.txt +236 -0
  16. data/doc/core/SerialStruct.txt +108 -0
  17. data/doc/core/VirtualString.txt +145 -0
  18. data/doc/core/WindowsExports.txt +61 -0
  19. data/doc/core/index.txt +1 -0
  20. data/doc/style.css +6 -3
  21. data/doc/usage/debugger.txt +327 -0
  22. data/doc/usage/index.txt +1 -0
  23. data/doc/use_cases.txt +2 -2
  24. data/metasm.gemspec +22 -0
  25. data/{lib/metasm.rb → metasm.rb} +11 -3
  26. data/{lib/metasm → metasm}/compile_c.rb +13 -7
  27. data/metasm/cpu/arc.rb +8 -0
  28. data/metasm/cpu/arc/decode.rb +425 -0
  29. data/metasm/cpu/arc/main.rb +191 -0
  30. data/metasm/cpu/arc/opcodes.rb +588 -0
  31. data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
  32. data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
  33. data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
  34. data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
  35. data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
  36. data/metasm/cpu/arm/opcodes.rb +324 -0
  37. data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
  38. data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
  39. data/metasm/cpu/arm64.rb +15 -0
  40. data/metasm/cpu/arm64/debug.rb +38 -0
  41. data/metasm/cpu/arm64/decode.rb +289 -0
  42. data/metasm/cpu/arm64/encode.rb +41 -0
  43. data/metasm/cpu/arm64/main.rb +105 -0
  44. data/metasm/cpu/arm64/opcodes.rb +232 -0
  45. data/metasm/cpu/arm64/parse.rb +20 -0
  46. data/metasm/cpu/arm64/render.rb +95 -0
  47. data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
  48. data/metasm/cpu/bpf/decode.rb +142 -0
  49. data/metasm/cpu/bpf/main.rb +60 -0
  50. data/metasm/cpu/bpf/opcodes.rb +81 -0
  51. data/metasm/cpu/bpf/render.rb +41 -0
  52. data/metasm/cpu/cy16.rb +9 -0
  53. data/metasm/cpu/cy16/decode.rb +253 -0
  54. data/metasm/cpu/cy16/main.rb +63 -0
  55. data/metasm/cpu/cy16/opcodes.rb +78 -0
  56. data/metasm/cpu/cy16/render.rb +41 -0
  57. data/metasm/cpu/dalvik.rb +11 -0
  58. data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
  59. data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
  60. data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
  61. data/metasm/cpu/ia32.rb +17 -0
  62. data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
  63. data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
  64. data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
  65. data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
  66. data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
  67. data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
  68. data/metasm/cpu/ia32/opcodes.rb +1424 -0
  69. data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
  70. data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
  71. data/metasm/cpu/mips.rb +14 -0
  72. data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
  73. data/metasm/cpu/mips/debug.rb +42 -0
  74. data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
  75. data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
  76. data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
  77. data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
  78. data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
  79. data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
  80. data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
  81. data/metasm/cpu/msp430/decode.rb +247 -0
  82. data/metasm/cpu/msp430/main.rb +62 -0
  83. data/metasm/cpu/msp430/opcodes.rb +101 -0
  84. data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
  85. data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
  86. data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
  87. data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
  88. data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
  89. data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
  90. data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
  91. data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
  92. data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
  93. data/metasm/cpu/ppc/parse.rb +55 -0
  94. data/metasm/cpu/python.rb +8 -0
  95. data/metasm/cpu/python/decode.rb +136 -0
  96. data/metasm/cpu/python/main.rb +36 -0
  97. data/metasm/cpu/python/opcodes.rb +180 -0
  98. data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
  99. data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
  100. data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
  101. data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
  102. data/metasm/cpu/x86_64.rb +15 -0
  103. data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
  104. data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
  105. data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
  106. data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
  107. data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
  108. data/metasm/cpu/x86_64/opcodes.rb +136 -0
  109. data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
  110. data/metasm/cpu/x86_64/render.rb +35 -0
  111. data/metasm/cpu/z80.rb +9 -0
  112. data/metasm/cpu/z80/decode.rb +313 -0
  113. data/metasm/cpu/z80/main.rb +67 -0
  114. data/metasm/cpu/z80/opcodes.rb +224 -0
  115. data/metasm/cpu/z80/render.rb +59 -0
  116. data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
  117. data/{lib/metasm → metasm}/decode.rb +35 -4
  118. data/{lib/metasm → metasm}/decompile.rb +15 -16
  119. data/{lib/metasm → metasm}/disassemble.rb +201 -45
  120. data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
  121. data/{lib/metasm → metasm}/dynldr.rb +220 -133
  122. data/{lib/metasm → metasm}/encode.rb +10 -1
  123. data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
  124. data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
  125. data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
  126. data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
  127. data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
  128. data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
  129. data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
  130. data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
  131. data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
  132. data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
  133. data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
  134. data/metasm/exe_format/gb.rb +65 -0
  135. data/metasm/exe_format/javaclass.rb +424 -0
  136. data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
  137. data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
  138. data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
  139. data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
  140. data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
  141. data/metasm/exe_format/pyc.rb +167 -0
  142. data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
  143. data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
  144. data/metasm/exe_format/shellcode_rwx.rb +114 -0
  145. data/metasm/exe_format/swf.rb +205 -0
  146. data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
  147. data/metasm/exe_format/zip.rb +335 -0
  148. data/metasm/gui.rb +13 -0
  149. data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
  150. data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
  151. data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
  152. data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
  153. data/metasm/gui/dasm_graph.rb +1695 -0
  154. data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
  155. data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
  156. data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
  157. data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
  158. data/{lib/metasm → metasm}/gui/debug.rb +93 -27
  159. data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
  160. data/{lib/metasm → metasm}/gui/qt.rb +12 -2
  161. data/{lib/metasm → metasm}/gui/win32.rb +179 -42
  162. data/{lib/metasm → metasm}/gui/x11.rb +59 -59
  163. data/{lib/metasm → metasm}/main.rb +389 -264
  164. data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
  165. data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
  166. data/{lib/metasm → metasm}/os/linux.rb +628 -151
  167. data/metasm/os/main.rb +330 -0
  168. data/{lib/metasm → metasm}/os/windows.rb +132 -42
  169. data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
  170. data/{lib/metasm → metasm}/parse.rb +26 -24
  171. data/{lib/metasm → metasm}/parse_c.rb +221 -116
  172. data/{lib/metasm → metasm}/preprocessor.rb +55 -40
  173. data/{lib/metasm → metasm}/render.rb +14 -38
  174. data/misc/hexdump.rb +2 -1
  175. data/misc/lint.rb +58 -0
  176. data/misc/txt2html.rb +9 -7
  177. data/samples/bindiff.rb +3 -4
  178. data/samples/dasm-plugins/bindiff.rb +15 -0
  179. data/samples/dasm-plugins/bookmark.rb +133 -0
  180. data/samples/dasm-plugins/c_constants.rb +57 -0
  181. data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
  182. data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
  183. data/samples/dasm-plugins/dasm_all.rb +70 -0
  184. data/samples/dasm-plugins/demangle_cpp.rb +31 -0
  185. data/samples/dasm-plugins/deobfuscate.rb +251 -0
  186. data/samples/dasm-plugins/dump_text.rb +35 -0
  187. data/samples/dasm-plugins/export_graph_svg.rb +86 -0
  188. data/samples/dasm-plugins/findgadget.rb +75 -0
  189. data/samples/dasm-plugins/hl_opcode.rb +32 -0
  190. data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
  191. data/samples/dasm-plugins/imm2off.rb +34 -0
  192. data/samples/dasm-plugins/match_libsigs.rb +93 -0
  193. data/samples/dasm-plugins/patch_file.rb +95 -0
  194. data/samples/dasm-plugins/scanfuncstart.rb +36 -0
  195. data/samples/dasm-plugins/scanxrefs.rb +26 -0
  196. data/samples/dasm-plugins/selfmodify.rb +197 -0
  197. data/samples/dasm-plugins/stringsxrefs.rb +28 -0
  198. data/samples/dasmnavig.rb +1 -1
  199. data/samples/dbg-apihook.rb +24 -9
  200. data/samples/dbg-plugins/heapscan.rb +283 -0
  201. data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
  202. data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
  203. data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
  204. data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
  205. data/samples/dbg-plugins/heapscan/winheap.h +174 -0
  206. data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
  207. data/samples/dbg-plugins/trace_func.rb +214 -0
  208. data/samples/disassemble-gui.rb +35 -5
  209. data/samples/disassemble.rb +31 -6
  210. data/samples/dump_upx.rb +24 -12
  211. data/samples/dynamic_ruby.rb +12 -3
  212. data/samples/exeencode.rb +6 -5
  213. data/samples/factorize-headers-peimports.rb +1 -1
  214. data/samples/lindebug.rb +175 -381
  215. data/samples/metasm-shell.rb +1 -2
  216. data/samples/peldr.rb +2 -2
  217. data/tests/all.rb +1 -1
  218. data/tests/arc.rb +26 -0
  219. data/tests/dynldr.rb +22 -4
  220. data/tests/expression.rb +55 -0
  221. data/tests/graph_layout.rb +285 -0
  222. data/tests/ia32.rb +79 -26
  223. data/tests/mips.rb +9 -2
  224. data/tests/x86_64.rb +66 -18
  225. metadata +330 -218
  226. data/lib/metasm/arm/opcodes.rb +0 -177
  227. data/lib/metasm/gui.rb +0 -23
  228. data/lib/metasm/gui/dasm_graph.rb +0 -1354
  229. data/lib/metasm/ia32.rb +0 -14
  230. data/lib/metasm/ia32/opcodes.rb +0 -873
  231. data/lib/metasm/ppc/parse.rb +0 -52
  232. data/lib/metasm/x86_64.rb +0 -12
  233. data/lib/metasm/x86_64/opcodes.rb +0 -118
  234. data/samples/gdbclient.rb +0 -583
  235. data/samples/rubstop.rb +0 -399
@@ -1,177 +0,0 @@
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/arm/main'
8
-
9
- module Metasm
10
- class ARM
11
- private
12
- def addop(name, bin, *args)
13
- args << :cond if not args.delete :uncond
14
-
15
- o = Opcode.new name, bin
16
- o.args.concat(args & @valid_args)
17
- (args & @valid_props).each { |p| o.props[p] = true }
18
- args.grep(Hash).each { |h| o.props.update h }
19
-
20
- # special args -> multiple fields
21
- case (o.args & [:i8_r, :rm_is, :rm_rs, :mem_rn_rm, :mem_rn_i8_12, :mem_rn_rms, :mem_rn_i12]).first
22
- when :i8_r; args << :i8 << :rotate
23
- when :rm_is; args << :rm << :stype << :shifti
24
- when :rm_rs; args << :rm << :stype << :rs
25
- when :mem_rn_rm; args << :rn << :rm << :rsx << :u
26
- when :mem_rn_i8_12; args << :rn << :i8_12 << :u
27
- when :mem_rn_rms; args << :rn << :rm << :stype << :shifti << :u
28
- when :mem_rn_i12; args << :rn << :i12 << :u
29
- end
30
-
31
- (args & @fields_mask.keys).each { |f|
32
- o.fields[f] = [@fields_mask[f], @fields_shift[f]]
33
- }
34
-
35
- @opcode_list << o
36
- end
37
-
38
- def addop_data_s(name, op, a1, a2, *h)
39
- addop name, op | (1 << 25), a1, a2, :i8_r, :rotate, *h
40
- addop name, op, a1, a2, :rm_is, *h
41
- addop name, op | (1 << 4), a1, a2, :rm_rs, *h
42
- end
43
- def addop_data(name, op, a1, a2)
44
- addop_data_s name, op << 21, a1, a2
45
- addop_data_s name+'s', (op << 21) | (1 << 20), a1, a2, :cond_name_off => name.length
46
- end
47
-
48
- def addop_load_puw(name, op, *a)
49
- addop name, op, {:baseincr => :post}, :rd, :u, *a
50
- addop name, op | (1 << 24), :rd, :u, *a
51
- addop name, op | (1 << 24) | (1 << 21), {:baseincr => :pre}, :rd, :u, *a
52
- end
53
- def addop_load_lsh_o(name, op)
54
- addop_load_puw name, op, :rsz, :mem_rn_rm, {:cond_name_off => 3}
55
- addop_load_puw name, op | (1 << 22), :mem_rn_i8_12, {:cond_name_off => 3}
56
- end
57
- def addop_load_lsh
58
- op = 9 << 4
59
- addop_load_lsh_o 'strh', op | (1 << 5)
60
- addop_load_lsh_o 'ldrd', op | (1 << 6)
61
- addop_load_lsh_o 'strd', op | (1 << 6) | (1 << 5)
62
- addop_load_lsh_o 'ldrh', op | (1 << 20) | (1 << 5)
63
- addop_load_lsh_o 'ldrsb', op | (1 << 20) | (1 << 6)
64
- addop_load_lsh_o 'ldrsh', op | (1 << 20) | (1 << 6) | (1 << 5)
65
- end
66
-
67
- def addop_load_puwt(name, op, *a)
68
- addop_load_puw name, op, *a
69
- addop name+'t', op | (1 << 21), {:baseincr => :post, :cond_name_off => name.length}, :rd, :u, *a
70
- end
71
- def addop_load_o(name, op, *a)
72
- addop_load_puwt name, op, :mem_rn_i12, *a
73
- addop_load_puwt name, op | (1 << 25), :mem_rn_rms, *a
74
- end
75
- def addop_load(name, op)
76
- addop_load_o name, op
77
- addop_load_o name+'b', op | (1 << 22), :cond_name_off => name.length
78
- end
79
-
80
- def addop_ldm_go(name, op, *a)
81
- addop name, op, :rn, :reglist, {:cond_name_off => 3}, *a
82
- end
83
- def addop_ldm_w(name, op, *a)
84
- addop_ldm_go name, op, *a # base reg untouched
85
- addop_ldm_go name, op | (1 << 21), {:baseincr => :post}, *a # base updated
86
- end
87
- def addop_ldm_s(name, op)
88
- addop_ldm_w name, op # transfer regs
89
- addop_ldm_w name, op | (1 << 22), :usermoderegs # transfer usermode regs
90
- end
91
- def addop_ldm_p(name, op)
92
- addop_ldm_s name+'a', op # target memory included
93
- addop_ldm_s name+'b', op | (1 << 24) # target memory excluded, transfer starts at next addr
94
- end
95
- def addop_ldm_u(name, op)
96
- addop_ldm_p name+'d', op # transfer made downward
97
- addop_ldm_p name+'i', op | (1 << 23) # transfer made upward
98
- end
99
- def addop_ldm(name, op)
100
- addop_ldm_u name, op
101
- end
102
-
103
- # ARMv6 instruction set, aka arm7/arm9
104
- def init_arm_v6
105
- @opcode_list = []
106
- @valid_props << :baseincr << :cond << :cond_name_off << :usermoderegs <<
107
- :tothumb << :tojazelle
108
- @valid_args.concat [:rn, :rd, :rm, :crn, :crd, :crm, :cpn, :reglist, :i24,
109
- :rm_rs, :rm_is, :i8_r, :mem_rn_rm, :mem_rn_i8_12, :mem_rn_rms, :mem_rn_i12]
110
- @fields_mask.update :rn => 0xf, :rd => 0xf, :rs => 0xf, :rm => 0xf,
111
- :crn => 0xf, :crd => 0xf, :crm => 0xf, :cpn => 0xf,
112
- :rnx => 0xf, :rdx => 0xf, :rsx => 0xf,
113
- :shifti => 0x1f, :stype => 3, :rotate => 0xf, :reglist => 0xffff,
114
- :i8 => 0xff, :i12 => 0xfff, :i24 => 0xff_ffff, :i8_12 => 0xf0f,
115
- :u => 1, :mask => 0xf, :sbo => 0xf, :cond => 0xf
116
-
117
- @fields_shift.update :rn => 16, :rd => 12, :rs => 8, :rm => 0,
118
- :crn => 16, :crd => 12, :crm => 0, :cpn => 8,
119
- :rnx => 16, :rdx => 12, :rsx => 8,
120
- :shifti => 7, :stype => 5, :rotate => 8, :reglist => 0,
121
- :i8 => 0, :i12 => 0, :i24 => 0, :i8_12 => 0,
122
- :u => 23, :mask => 16, :sbo => 12, :cond => 28
123
-
124
- addop_data 'and', 0, :rd, :rn
125
- addop_data 'eor', 1, :rd, :rn
126
- addop_data 'xor', 1, :rd, :rn
127
- addop_data 'sub', 2, :rd, :rn
128
- addop_data 'rsb', 3, :rd, :rn
129
- addop_data 'add', 4, :rd, :rn
130
- addop_data 'adc', 5, :rd, :rn
131
- addop_data 'sbc', 6, :rd, :rn
132
- addop_data 'rsc', 7, :rd, :rn
133
- addop_data 'tst', 8, :rdx, :rn
134
- addop_data 'teq', 9, :rdx, :rn
135
- addop_data 'cmp', 10, :rdx, :rn
136
- addop_data 'cmn', 11, :rdx, :rn
137
- addop_data 'orr', 12, :rd, :rn
138
- addop_data 'or', 12, :rd, :rn
139
- addop_data 'mov', 13, :rd, :rnx
140
- addop_data 'bic', 14, :rd, :rn
141
- addop_data 'mvn', 15, :rd, :rnx
142
-
143
- addop 'b', 0b1010 << 24, :setip, :stopexec, :i24
144
- addop 'bl', 0b1011 << 24, :setip, :stopexec, :i24, :saveip
145
- addop 'bkpt', (0b00010010 << 20) | (0b0111 << 4) # other fields are available&unused, also cnd != AL is undef
146
- addop 'blx', 0b1111101 << 25, :setip, :stopexec, :saveip, :tothumb, :h, :nocond, :i24
147
- addop 'blx', (0b00010010 << 20) | (0b0011 << 4), :setip, :stopexec, :saveip, :tothumb, :rm
148
- addop 'bx', (0b00010010 << 20) | (0b0001 << 4), :setip, :stopexec, :rm
149
- addop 'bxj', (0b00010010 << 20) | (0b0010 << 4), :setip, :stopexec, :rm, :tojazelle
150
-
151
- addop_load 'str', (1 << 26)
152
- addop_load 'ldr', (1 << 26) | (1 << 20)
153
- addop_load_lsh
154
- addop_ldm 'stm', (1 << 27)
155
- addop_ldm 'ldm', (1 << 27) | (1 << 20)
156
- end
157
- alias init_latest init_arm_v6
158
- end
159
- end
160
-
161
- __END__
162
- addop_cond 'mrs', 0b0001000011110000000000000000, :rd
163
- addop_cond 'msr', 0b0001001010011111000000000000, :rd
164
- addop_cond 'msrf', 0b0001001010001111000000000000, :rd
165
-
166
- addop_cond 'mul', 0b000000000000001001 << 4, :rd, :rn, :rs, :rm
167
- addop_cond 'mla', 0b100000000000001001 << 4, :rd, :rn, :rs, :rm
168
-
169
- addop_cond 'swp', 0b0001000000000000000010010000, :rd, :rn, :rs, :rm
170
- addop_cond 'swpb', 0b0001010000000000000010010000, :rd, :rn, :rs, :rm
171
-
172
- addop_cond 'undef', 0b00000110000000000000000000010000
173
-
174
- addop_cond 'swi', 0b00001111 << 24
175
-
176
- addop_cond 'bkpt', 0b1001000000000000001110000
177
- addop_cond 'movw', 0b0011 << 24, :movwimm
@@ -1,23 +0,0 @@
1
- backend = case ENV['METASM_GUI']
2
- when 'gtk'; 'gtk'
3
- when 'qt'; 'qt'
4
- when 'win32'; 'win32'
5
- else
6
- puts "Unsupported METASM_GUI #{ENV['METASM_GUI'].inspect}" if $VERBOSE and ENV['METASM_GUI']
7
- if RUBY_PLATFORM =~ /(i.86|x(86_)?64)-(mswin|mingw|cygwin)/i
8
- 'win32'
9
- else
10
- begin
11
- require 'gtk2'
12
- 'gtk'
13
- rescue LoadError
14
- #begin
15
- # require 'Qt4'
16
- # 'qt'
17
- #rescue LoadError
18
- raise LoadError, 'No GUI ruby binding installed - please install libgtk2-ruby'
19
- #end
20
- end
21
- end
22
- end
23
- require "metasm/gui/#{backend}"
@@ -1,1354 +0,0 @@
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
- module Metasm
7
- module Gui
8
- class Graph
9
- # one box, has a text, an id, and a list of other boxes to/from
10
- class Box
11
- attr_accessor :id, :x, :y, :w, :h
12
- attr_accessor :to, :from # other boxes linked (arrays)
13
- attr_accessor :content
14
- attr_accessor :direct_to
15
- def initialize(id, content=nil)
16
- @id = id
17
- @x = @y = @w = @h = 0
18
- @to, @from = [], []
19
- @content = content
20
- end
21
- def [](a) @content[a] end
22
- #def inspect ; puts caller ; "#{Expression[@id] rescue @id.inspect}" end
23
- end
24
-
25
- # TODO
26
- class MergedBox
27
- attr_accessor :id, :text, :x, :y, :w, :h
28
- attr_accessor :to, :from
29
- end
30
-
31
- attr_accessor :id, :box, :root_addrs, :view_x, :view_y, :keep_split
32
- def initialize(id)
33
- @id = id
34
- @root_addrs = []
35
- @view_x = @view_y = -0xfff_ffff
36
- clear
37
- end
38
-
39
- # empty @box
40
- def clear
41
- @box = []
42
- end
43
-
44
- # link the two boxes (by id)
45
- def link_boxes(id1, id2)
46
- raise "unknown index 1 #{id1}" if not b1 = @box.find { |b| b.id == id1 }
47
- raise "unknown index 2 #{id2}" if not b2 = @box.find { |b| b.id == id2 }
48
- b1.to |= [b2]
49
- b2.from |= [b1]
50
- end
51
-
52
- # creates a new box, ensures id is not already taken
53
- def new_box(id, content=nil)
54
- raise "duplicate id #{id}" if @box.find { |b| b.id == id }
55
- b = Box.new(id, content)
56
- @box << b
57
- b
58
- end
59
-
60
- # place boxes in a good-looking layout
61
- def auto_arrange_init(list=@box)
62
- # groups is an array of box groups
63
- # all groups are centered on the origin
64
- @groups = list.map { |b|
65
- b.x = -b.w/2
66
- b.y = -b.h/2
67
- g = Box.new(nil, [b])
68
- g.x = b.x - 8
69
- g.y = b.y - 9
70
- g.w = b.w + 16
71
- g.h = b.h + 18
72
- g
73
- }
74
-
75
- # init group.to/from
76
- # must always point to something that is in the 'groups' array
77
- # no self references
78
- # a box is in one and only one group in 'groups'
79
- @groups.each { |g|
80
- g.to = g.content.first.to.map { |t| next if not t = list.index(t) ; @groups[t] }.compact - [g]
81
- g.from = g.content.first.from.map { |f| next if not f = list.index(f) ; @groups[f] }.compact - [g]
82
- }
83
-
84
- # walk from a box, fork at each multiple to, chop links to a previous box (loops etc)
85
- @madetree = false
86
- end
87
-
88
- # gives a text representation of the current graph state
89
- def dump_layout(groups=@groups)
90
- groups.map { |g| "#{groups.index(g)} -> #{g.to.map { |t| groups.index(t) }.sort.inspect}" }
91
- end
92
-
93
- def auto_arrange_step
94
- # TODO fix
95
- # 0->[1, 2] 1->[3] 2->[3, 4] 3->[] 4->[1]
96
- # push 0 jz l3 push 1 jz l4 push 2 l3: push 3 l4: hlt
97
- # and more generally all non-looping graphs where this algo creates backward links
98
-
99
- groups = @groups
100
- return if groups.length <= 1
101
-
102
- maketree = lambda { |roots|
103
- next if @madetree
104
- @madetree = true
105
-
106
- maxdepth = {} # max arc count to reach this box from graph start (excl loop)
107
-
108
- trim = lambda { |g, from|
109
- # unlink g from (part of) its from
110
- from.each { |gg| gg.to.delete g }
111
- g.from -= from
112
- }
113
-
114
- walk = lambda { |g|
115
- # score
116
- parentdepth = g.from.map { |gg| maxdepth[gg] }
117
- if parentdepth.empty?
118
- # root
119
- maxdepth[g] = 0
120
- elsif parentdepth.include? nil
121
- # not farthest parent found / loop
122
- next
123
- # elsif maxdepth[g] => ?
124
- else
125
- maxdepth[g] = parentdepth.max + 1
126
- end
127
- g.to.each { |gg| walk[gg] }
128
- }
129
-
130
- roots.each { |g| trim[g, g.from] unless g.from.empty? }
131
- roots.each { |g| walk[g] }
132
-
133
- # handle loops now (unmarked nodes)
134
- while unmarked = groups - maxdepth.keys and not unmarked.empty?
135
- if g = unmarked.find { |g_| g_.from.find { |gg| maxdepth[gg] } }
136
- # loop head
137
- trim[g, g.from.find_all { |gg| not maxdepth[gg] }] # XXX not quite sure for this
138
- walk[g]
139
- else
140
- # disconnected subgraph
141
- g = unmarked.find { |g_| g_.from.empty? } || unmarked.first
142
- trim[g, g.from]
143
- maxdepth[g] = 0
144
- walk[g]
145
- end
146
- end
147
- }
148
-
149
- # concat all ary boxes into its 1st element, remove trailing groups from 'groups'
150
- # updates from/to
151
- merge_groups = lambda { |ary|
152
- bg = Box.new(nil, [])
153
- bg.x, bg.y = ary.map { |g| g.x }.min, ary.map { |g| g.y }.min
154
- bg.w, bg.h = ary.map { |g| g.x+g.w }.max - bg.x, ary.map { |g| g.y+g.h }.max - bg.y
155
- ary.each { |g|
156
- bg.content.concat g.content
157
- bg.to |= g.to
158
- bg.from |= g.from
159
- }
160
- bg.to -= ary
161
- bg.to.each { |t| t.from = t.from - ary + [bg] }
162
- bg.from -= ary
163
- bg.from.each { |f| f.to = f.to - ary + [bg] }
164
- idx = ary.map { |g| groups.index(g) }.min
165
- groups = @groups = groups - ary
166
- groups.insert(idx, bg)
167
- bg
168
- }
169
-
170
- # move all boxes within group of dx, dy
171
- move_group = lambda { |g, dx, dy|
172
- g.content.each { |b| b.x += dx ; b.y += dy }
173
- g.x += dx ; g.y += dy
174
- }
175
-
176
- align_hz = lambda { |ary|
177
- # if we have one of the block much bigger than the others, put it on the far right
178
- big = ary.sort_by { |g| g.h }.last
179
- if (ary-[big]).all? { |g| g.h < big.h/3 }
180
- ary -= [big]
181
- else
182
- big = nil
183
- end
184
- nx = ary.map { |g| g.w }.inject(0) { |a, b| a+b } / -2
185
- nx *= 2 if big and ary.length == 1 # just put the parent on the separation of the 2 child
186
- ary.each { |g|
187
- move_group[g, nx-g.x, 0]
188
- nx += g.w
189
- }
190
- move_group[big, nx-big.x, 0] if big
191
- }
192
- align_vt = lambda { |ary|
193
- ny = ary.map { |g| g.h }.inject(0) { |a, b| a+b } / -2
194
- ary.each { |g|
195
- move_group[g, 0, ny-g.y]
196
- ny += g.h
197
- }
198
- }
199
-
200
- # scan groups for a column pattern (head has 1 'to' which from == [head])
201
- group_columns = lambda {
202
- groups.find { |g|
203
- next if g.from.length == 1 and g.from.first.to.length == 1
204
- ary = [g]
205
- ary << (g = g.to.first) while g.to.length == 1 and g.to.first.from.length == 1
206
- next if ary.length <= 1
207
- align_vt[ary]
208
- merge_groups[ary]
209
- true
210
- }
211
- }
212
-
213
- # scan groups for a line pattern (multiple groups with same to & same from)
214
- group_lines = lambda { |strict|
215
- if groups.all? { |g1| g1.from.empty? and g1.to.empty? }
216
- # disjoint subgraphs
217
- align_hz[groups]
218
- merge_groups[groups]
219
- next true
220
- end
221
-
222
- groups.find { |g1|
223
- ary = g1.from.map { |gg| gg.to }.flatten.uniq.find_all { |gg|
224
- gg != g1 and
225
- (gg.from - g1.from).empty? and (g1.from - gg.from).empty? and
226
- (strict ? ((gg.to - g1.to).empty? and (g1.to - gg.to).empty?) : (g1.to & gg.to).first)
227
- }
228
- ary = g1.to.map { |gg| gg.from }.flatten.uniq.find_all { |gg|
229
- gg != g1 and
230
- (gg.to - g1.to).empty? and (g1.to - gg.to).empty? and
231
- (strict ? ((gg.from - g1.from).empty? and (g1.from - gg.from).empty?) : (g1.from & gg.from).first)
232
- } if ary.empty?
233
- next if ary.empty?
234
- ary << g1
235
- dy = 16*ary.map { |g| g.to.length + g.from.length }.inject { |a, b| a+b }
236
- ary.each { |g| g.h += dy ; g.y -= dy/2 }
237
- align_hz[ary]
238
- if ary.first.to.empty? # shrink graph if highly dissymetric and to.empty?
239
- ah = ary.map { |g| g.h }.max
240
- ary.each { |g|
241
- move_group[g, 0, (g.h-ah)/2] # move up
242
- next if not p = ary[ary.index(g)-1]
243
- y = [g.y, p.y].min # shrink width
244
- h = [g.h, p.h].min
245
- xp = p.content.map { |b| b.x+b.w if b.y+b.h+8 >= y and b.y-8 <= y+h }.compact.max || p.x+p.w/2
246
- xg = g.content.map { |b| b.x if b.y+b.h+8 >= y and b.y-8 <= y+h }.compact.min || g.x+g.w/2
247
- dx = xg-xp-24
248
- next if dx <= 0
249
- ary.each { |gg|
250
- dx = -dx if gg == g
251
- move_group[gg, dx/2, 0]
252
- }
253
- if p.x+p.w > ary.last.x+ary.last.w or ary.first.x > g.x # fix broken centerism
254
- x = [g.x, ary.first.x].min
255
- xm = [p.x+p.w, ary.last.x+ary.last.w].max
256
- ary.each { |gg| move_group[gg, (x+xm)/-2, 0] }
257
- end
258
- }
259
- end
260
- merge_groups[ary]
261
- true
262
- }
263
- }
264
-
265
- group_inv_if = {}
266
-
267
- # scan groups for a if/then pattern (1 -> 2 -> 3 & 1 -> 3)
268
- group_ifthen = lambda { |strict|
269
- groups.reverse.find { |g|
270
- next if not g2 = g.to.find { |g2_| (g2_.to.length == 1 and g.to.include?(g2_.to.first)) or
271
- (not strict and g2_.to.empty?) }
272
- next if strict and g2.from != [g] or g.to.length != 2
273
- g2.h += 16 ; g2.y -= 8
274
- align_vt[[g, g2]]
275
- dx = -g2.x+8
276
- dx -= g2.w+16 if group_inv_if[g]
277
- move_group[g2, dx, 0]
278
- merge_groups[[g, g2]]
279
- true
280
- }
281
- }
282
-
283
- # if (a || b) c;
284
- # the 'else' case handles '&& else', and && is two if/then nested
285
- group_or = lambda { |strict|
286
- groups.find { |g|
287
- next if g.to.length != 2
288
- g2 = g.to[0]
289
- g2 = g.to[1] if not g2.to.include? g.to[1]
290
- thn = (g.to & g2.to).first
291
- next if g2.to.length != 2 or not thn or thn.to.length != 1
292
- els = (g2.to - [thn]).first
293
- if thn.to == [els]
294
- els = nil
295
- elsif els.to != thn.to
296
- next if strict
297
- align_vt[[g, g2]]
298
- merge_groups[[g, g2]]
299
- break true
300
- else
301
- align_hz[[thn, els]]
302
- thn = merge_groups[[thn, els]]
303
- end
304
- thn.h += 16 ; thn.y -= 8
305
- align_vt[[g, g2, thn]]
306
- move_group[g2, -g2.x, 0]
307
- move_group[thn, thn.x-8, 0] if not els
308
- merge_groups[[g, g2, thn]]
309
- true
310
- }
311
- }
312
-
313
-
314
- # loop with exit 1 -> 2, 3 & 2 -> 1
315
- group_loop = lambda {
316
- groups.find { |g|
317
- next if not g2 = g.to.sort_by { |g2_| g2_.h }.find { |g2_| g2_.to == [g] or (g2_.to.empty? and g2_.from == [g]) }
318
- g2.h += 16
319
- align_vt[[g, g2]]
320
- move_group[g2, g2.x-8, 0]
321
- merge_groups[[g, g2]]
322
- true
323
- }
324
- }
325
-
326
- # same single from or to
327
- group_halflines = lambda {
328
- ary = nil
329
- if groups.find { |g| ary = g.from.find_all { |gg| gg.to == [g] } and ary.length > 1 } or
330
- groups.find { |g| ary = g.to.find_all { |gg| gg.from == [g] } and ary.length > 1 }
331
- align_hz[ary]
332
- merge_groups[ary]
333
- true
334
- end
335
- }
336
-
337
-
338
- # unknown pattern, group as we can..
339
- group_other = lambda {
340
- puts 'graph arrange: unknown configuration', dump_layout
341
- g1 = groups.find_all { |g| g.from.empty? }
342
- g1 << groups[rand(groups.length)] if g1.empty?
343
- g2 = g1.map { |g| g.to }.flatten.uniq - g1
344
- align_vt[g1]
345
- g1 = merge_groups[g1]
346
- g1.w += 128 ; g1.x -= 64
347
- next if g2.empty?
348
- align_vt[g2]
349
- g2 = merge_groups[g2]
350
- g2.w += 128 ; g2.x -= 64
351
-
352
- align_hz[[g1, g2]]
353
- merge_groups[[g1, g2]]
354
- true
355
- }
356
-
357
- # check constructs with multiple blocks with to to end block (a la break;)
358
- ign_break = lambda {
359
- can_reach = lambda { |b1, b2, term|
360
- next if b1 == term
361
- done = [term]
362
- todo = b1.to.dup
363
- while t = todo.pop
364
- next if done.include? t
365
- done << t
366
- break true if t == b2
367
- todo.concat t.to
368
- end
369
- }
370
- can_reach_unidir = lambda { |b1, b2, term| can_reach[b1, b2, term] and not can_reach[b2, b1, term] }
371
- groups.find { |g|
372
- f2 = nil
373
- if (g.from.length > 2 and f3 = g.from.find { |f| f.to == [g] } and f1 = g.from.find { |f|
374
- f2 = g.from.find { |ff| can_reach_unidir[ff, f3, g] and can_reach_unidir[f, ff, g] }}) or
375
- (g.to.length > 2 and f3 = g.to.find { |f| f.from == [g] } and f1 = g.to.find { |f|
376
- f2 = g.to.find { |ff| can_reach_unidir[f3, ff, g] and can_reach_unidir[ff, f, g] }})
377
- group_inv_if[f1] = true
378
- if f3.to == [g]
379
- g.from.delete f2
380
- f2.to.delete g
381
- else
382
- g.to.delete f2
383
- f2.from.delete g
384
- end
385
- true
386
- end
387
- }
388
- }
389
-
390
- # walk graph from roots, cut backward links
391
- trim_graph = lambda {
392
- next true if ign_break[]
393
- g1 = groups.find_all { |g| g.from.empty? }
394
- g1 << groups.first if g1.empty?
395
- cntpre = groups.inject(0) { |cntpre_, g| cntpre_ + g.to.length }
396
- g1.each { |g| maketree[[g]] }
397
- cntpost = groups.inject(0) { |cntpre_, g| cntpre_ + g.to.length }
398
- true if cntpre != cntpost
399
- }
400
-
401
- # known, clean patterns
402
- group_clean = lambda {
403
- group_columns[] or group_lines[true] or group_ifthen[true] or group_loop[] or group_or[true]
404
- }
405
- # approximations
406
- group_unclean = lambda {
407
- group_lines[false] or group_or[false] or group_halflines[] or group_ifthen[false] or group_other[]
408
- }
409
-
410
- group_clean[] or trim_graph[] or group_unclean[]
411
- end
412
-
413
- # the boxes have been almost put in place, here we soften a little the result & arrange some qwirks
414
- def auto_arrange_post
415
- # entrypoint should be above other boxes, same for exitpoints
416
- @box.each { |b|
417
- if b.from == []
418
- chld = b.to
419
- chld = @box - [b] if not @box.find { |bb| bb != b and bb.from == [] }
420
- chld.each { |t| b.y = t.y - b.h - 16 if t.y < b.y+b.h }
421
- end
422
- if b.to == []
423
- chld = b.from
424
- chld = @box - [b] if not @box.find { |bb| bb != b and bb.to == [] }
425
- chld.each { |f| b.y = f.y + f.h + 16 if f.y+f.h > b.y }
426
- end
427
- }
428
-
429
- boxxy = @box.sort_by { |bb| bb.y }
430
- # fill gaps that we created
431
- @box.each { |b|
432
- bottom = b.y+b.h
433
- next if not follower = boxxy.find { |bb| bb.y+bb.h > bottom }
434
-
435
- # preserve line[] constructs margins
436
- gap = follower.y-16*follower.from.length - (bottom+16*b.to.length)
437
- next if gap <= 0
438
-
439
- @box.each { |bb|
440
- if bb.y+bb.h <= bottom
441
- bb.y += gap/2
442
- else
443
- bb.y -= gap/2
444
- end
445
- }
446
- boxxy = @box.sort_by { |bb| bb.y }
447
- }
448
-
449
- @box[0,0].each { |b|
450
- # TODO elastic positionning (ignore up arrows ?) & collision detection (box/box + box/arrow)
451
- f = b.from[0]
452
- t = b.to[0]
453
- if b.to.length == 1 and b.from.length == 1 and b.y+b.h<t.y and b.y>f.y+f.h
454
- wx = (t.x+t.w/2 + f.x+f.w/2)/2 - b.w/2
455
- wy = (t.y + f.y+f.h)/2 - b.h/2
456
- b.x += (wx-b.x)/5
457
- b.y += (wy-b.y)/5
458
- end
459
- }
460
-
461
- end
462
-
463
- def auto_arrange_boxes
464
- auto_arrange_init
465
- nil while @groups.length > 1 and auto_arrange_step
466
- @groups = []
467
- auto_arrange_post
468
- end
469
- end
470
-
471
-
472
-
473
-
474
-
475
- class GraphViewWidget < DrawableWidget
476
- attr_accessor :dasm, :caret_box, :curcontext, :zoom, :margin
477
- # bool, specifies if we should display addresses before instrs
478
- attr_accessor :show_addresses
479
-
480
- def initialize_widget(dasm, parent_widget)
481
- @dasm = dasm
482
- @parent_widget = parent_widget
483
-
484
- @show_addresses = false
485
-
486
- @caret_box = nil
487
- @selected_boxes = []
488
- @shown_boxes = []
489
- @mousemove_origin = @mousemove_origin_ctrl = nil
490
- @curcontext = Graph.new(nil)
491
- @margin = 8
492
- @zoom = 1.0
493
- @default_color_association = { :background => :paleblue, :hlbox_bg => :palegrey, :box_bg => :white,
494
- :text => :black, :arrow_hl => :red, :comment => :darkblue, :address => :darkblue,
495
- :instruction => :black, :label => :darkgreen, :caret => :black, :hl_word => :palered,
496
- :cursorline_bg => :paleyellow, :arrow_cond => :darkgreen, :arrow_uncond => :darkblue,
497
- :arrow_direct => :darkred }
498
- # @othergraphs = ? (to keep user-specified formatting)
499
- end
500
-
501
- def resized(w, h)
502
- redraw
503
- end
504
-
505
- def find_box_xy(x, y)
506
- x = @curcontext.view_x+x/@zoom
507
- y = @curcontext.view_y+y/@zoom
508
- @shown_boxes.to_a.reverse.find { |b| b.x <= x and b.x+b.w > x and b.y <= y-1 and b.y+b.h > y+1 }
509
- end
510
-
511
- def mouse_wheel_ctrl(dir, x, y)
512
- case dir
513
- when :up
514
- if @zoom < 100
515
- oldzoom = @zoom
516
- @zoom *= 1.1
517
- @zoom = 1.0 if (@zoom-1.0).abs < 0.05
518
- @curcontext.view_x += (x / oldzoom - x / @zoom)
519
- @curcontext.view_y += (y / oldzoom - y / @zoom)
520
- end
521
- when :down
522
- if @zoom > 1.0/100
523
- oldzoom = @zoom
524
- @zoom /= 1.1
525
- @zoom = 1.0 if (@zoom-1.0).abs < 0.05
526
- @curcontext.view_x += (x / oldzoom - x / @zoom)
527
- @curcontext.view_y += (y / oldzoom - y / @zoom)
528
- end
529
- end
530
- redraw
531
- end
532
-
533
- def mouse_wheel(dir, x, y)
534
- case dir
535
- when :up; @curcontext.view_y -= height/4 / @zoom
536
- when :down; @curcontext.view_y += height/4 / @zoom
537
- end
538
- redraw
539
- end
540
-
541
- def mousemove(x, y)
542
- return if not @mousemove_origin
543
-
544
- dx = (x - @mousemove_origin[0])/@zoom
545
- dy = (y - @mousemove_origin[1])/@zoom
546
- @mousemove_origin = [x, y]
547
- if @selected_boxes.empty?
548
- @curcontext.view_x -= dx ; @curcontext.view_y -= dy
549
- else
550
- @selected_boxes.each { |b| b.x += dx ; b.y += dy }
551
- end
552
- redraw
553
- end
554
-
555
- def mouserelease(x, y)
556
- mousemove(x, y)
557
- @mousemove_origin = nil
558
-
559
- if @mousemove_origin_ctrl
560
- x1 = @curcontext.view_x + @mousemove_origin_ctrl[0]/@zoom
561
- x2 = x1 + (x - @mousemove_origin_ctrl[0])/@zoom
562
- x1, x2 = x2, x1 if x1 > x2
563
- y1 = @curcontext.view_y + @mousemove_origin_ctrl[1]/@zoom
564
- y2 = y1 + (y - @mousemove_origin_ctrl[1])/@zoom
565
- y1, y2 = y2, y1 if y1 > y2
566
- @selected_boxes |= @curcontext.box.find_all { |b| b.x >= x1 and b.x + b.w <= x2 and b.y >= y1 and b.y + b.h <= y2 }
567
- redraw
568
- @mousemove_origin_ctrl = nil
569
- end
570
- end
571
-
572
- def click_ctrl(x, y)
573
- if b = find_box_xy(x, y)
574
- if @selected_boxes.include? b
575
- @selected_boxes.delete b
576
- else
577
- @selected_boxes << b
578
- end
579
- redraw
580
- else
581
- @mousemove_origin_ctrl = [x, y]
582
- end
583
- end
584
-
585
- def click(x, y)
586
- @mousemove_origin = [x, y]
587
- if b = find_box_xy(x, y)
588
- @selected_boxes = [b] if not @selected_boxes.include? b
589
- @caret_box = b
590
- @caret_x = (@curcontext.view_x+x/@zoom-b.x-1).to_i / @font_width
591
- @caret_y = (@curcontext.view_y+y/@zoom-b.y-1).to_i / @font_height
592
- update_caret
593
- else
594
- @selected_boxes = []
595
- @caret_box = nil
596
- end
597
- redraw
598
- end
599
-
600
- # if the target is a call to a subfunction, open a new window with the graph of this function (popup)
601
- def rightclick(x, y)
602
- if b = find_box_xy(x, y) and @zoom >= 0.90 and @zoom <= 1.1
603
- click(x, y)
604
- @mousemove_origin = nil
605
- @parent_widget.clone_window(@hl_word, :graph)
606
- end
607
- end
608
-
609
- def doubleclick(x, y)
610
- if b = find_box_xy(x, y)
611
- @mousemove_origin = nil
612
- if @hl_word and @zoom >= 0.90 and @zoom <= 1.1
613
- @parent_widget.focus_addr(@hl_word)
614
- else
615
- @parent_widget.focus_addr b[:addresses].first
616
- end
617
- elsif doubleclick_check_arrow(x, y)
618
- elsif @zoom == 1.0
619
- zoom_all
620
- else
621
- @curcontext.view_x += (x/@zoom - x)
622
- @curcontext.view_y += (y/@zoom - y)
623
- @zoom = 1.0
624
- end
625
- redraw
626
- end
627
-
628
- # check if the user clicked on the beginning/end of an arrow, if so focus on the other end
629
- def doubleclick_check_arrow(x, y)
630
- return if @margin*@zoom < 2
631
- x = @curcontext.view_x+x/@zoom
632
- y = @curcontext.view_y+y/@zoom
633
- sx = nil
634
- if bt = @shown_boxes.to_a.reverse.find { |b|
635
- y >= b.y+b.h-1 and y <= b.y+b.h-1+@margin+2 and
636
- sx = b.x+b.w/2 - b.to.length/2 * @margin/2 and
637
- x >= sx-@margin/2 and x <= sx+b.to.length*@margin/2 # should be margin/4, but add a little comfort margin
638
- }
639
- idx = (x-sx+@margin/4).to_i / (@margin/2)
640
- idx = 0 if idx < 0
641
- idx = bt.to.length-1 if idx >= bt.to.length
642
- if bt.to[idx]
643
- if @parent_widget
644
- @parent_widget.focus_addr bt.to[idx][:line_address][0]
645
- else
646
- focus_xy(bt.to[idx].x, bt.to[idx].y)
647
- end
648
- end
649
- true
650
- elsif bf = @shown_boxes.to_a.reverse.find { |b|
651
- y >= b.y-@margin-2 and y <= b.y and
652
- sx = b.x+b.w/2 - b.from.length/2 * @margin/2 and
653
- x >= sx-@margin/2 and x <= sx+b.from.length*@margin/2
654
- }
655
- idx = (x-sx+@margin/4).to_i / (@margin/2)
656
- idx = 0 if idx < 0
657
- idx = bf.from.length-1 if idx >= bf.from.length
658
- if bf.from[idx]
659
- if @parent_widget
660
- @parent_widget.focus_addr bf.from[idx][:line_address][-1]
661
- else
662
- focus_xy(bt.from[idx].x, bt.from[idx].y)
663
- end
664
- end
665
- true
666
- end
667
- end
668
-
669
- # update the zoom & view_xy to show the whole graph in the window
670
- def zoom_all
671
- minx = @curcontext.box.map { |b| b.x }.min.to_i - 10
672
- miny = @curcontext.box.map { |b| b.y }.min.to_i - 10
673
- maxx = @curcontext.box.map { |b| b.x + b.w }.max.to_i + 10
674
- maxy = @curcontext.box.map { |b| b.y + b.h }.max.to_i + 10
675
- @zoom = [width.to_f/(maxx-minx), height.to_f/(maxy-miny)].min
676
- @zoom = 1.0 if @zoom > 1.0 or (@zoom-1.0).abs < 0.1
677
- @curcontext.view_x = minx + (maxx-minx-width/@zoom)/2
678
- @curcontext.view_y = miny + (maxy-miny-height/@zoom)/2
679
- redraw
680
- end
681
-
682
- def paint
683
- update_graph if @want_update_graph
684
- if @want_focus_addr and @curcontext.box.find { |b_| b_[:line_address].index(@want_focus_addr) }
685
- focus_addr(@want_focus_addr, false)
686
- @want_focus_addr = nil
687
- #zoom_all
688
- end
689
-
690
- @curcontext.box.each { |b|
691
- # reorder arrows so that endings do not overlap
692
- b.to = b.to.sort_by { |bt| bt.x+bt.w/2 }
693
- b.from = b.from.sort_by { |bt| bt.x+bt.w/2 }
694
- }
695
- # arrows drawn first to stay under the boxes
696
- # XXX precalc ?
697
- @curcontext.box.each { |b|
698
- b.to.each { |bt| paint_arrow(b, bt) }
699
- }
700
-
701
- @shown_boxes = []
702
- w_w, w_h = width, height
703
- @curcontext.box.each { |b|
704
- next if b.x >= @curcontext.view_x+w_w/@zoom or b.y >= @curcontext.view_y+w_h/@zoom or b.x+b.w <= @curcontext.view_x or b.y+b.h <= @curcontext.view_y
705
- @shown_boxes << b
706
- paint_box(b)
707
- }
708
- end
709
-
710
- def set_color_arrow(b1, b2)
711
- if b1 == @caret_box or b2 == @caret_box
712
- draw_color :arrow_hl
713
- elsif b1.to.length == 1
714
- draw_color :arrow_uncond
715
- elsif b1.direct_to == b2.id
716
- draw_color :arrow_direct
717
- else
718
- draw_color :arrow_cond
719
- end
720
- end
721
-
722
- def paint_arrow(b1, b2)
723
- x1, y1 = b1.x+b1.w/2-@curcontext.view_x, b1.y+b1.h-@curcontext.view_y
724
- x2, y2 = b2.x+b2.w/2-@curcontext.view_x, b2.y-1-@curcontext.view_y
725
- x1o, x2o = x1, x2
726
- margin = @margin
727
- x1 += (-(b1.to.length-1)/2 + b1.to.index(b2)) * margin/2
728
- x2 += (-(b2.from.length-1)/2 + b2.from.index(b1)) * margin/2
729
- return if (y1+margin < 0 and y2 < 0) or (y1 > height/@zoom and y2-margin > height/@zoom) # just clip on y
730
- margin, x1, y1, x2, y2, b1w, b2w, x1o, x2o = [margin, x1, y1, x2, y2, b1.w, b2.w, x1o, x2o].map { |v| v*@zoom }
731
-
732
-
733
- # XXX gtk wraps coords around 0x8000
734
- if x1.abs > 0x7000 ; y1 /= x1.abs/0x7000 ; x1 /= x1.abs/0x7000 ; end
735
- if y1.abs > 0x7000 ; x1 /= y1.abs/0x7000 ; y1 /= y1.abs/0x7000 ; end
736
- if x2.abs > 0x7000 ; y2 /= x2.abs/0x7000 ; x2 /= x2.abs/0x7000 ; end
737
- if y2.abs > 0x7000 ; x2 /= y2.abs/0x7000 ; y2 /= y2.abs/0x7000 ; end
738
-
739
- # straighten vertical arrows if possible
740
- if y2 > y1 and (x1-x2).abs <= margin
741
- if b1.to.length == 1
742
- x1 = x2
743
- elsif b2.from.length == 1
744
- x2 = x1
745
- end
746
- end
747
-
748
- set_color_arrow(b1, b2)
749
- if margin > 1
750
- # draw arrow tip
751
- draw_line(x1, y1, x1, y1+margin)
752
- draw_line(x2, y2-margin+1, x2, y2)
753
- draw_line(x2-margin/2, y2-margin/2, x2, y2)
754
- draw_line(x2+margin/2, y2-margin/2, x2, y2)
755
- y1 += margin
756
- y2 -= margin-1
757
- end
758
- if y2+margin >= y1-margin-1
759
- # straight vertical down arrow
760
- draw_line(x1, y1, x2, y2) if x1 != y1 or x2 != y2
761
-
762
- # else arrow up, need to sneak around boxes
763
- elsif x1o-b1w/2-margin >= x2o+b2w/2+margin # z
764
- draw_line(x1, y1, x1o-b1w/2-margin, y1)
765
- draw_line(x1o-b1w/2-margin, y1, x2o+b2w/2+margin, y2)
766
- draw_line(x2o+b2w/2+margin, y2, x2, y2)
767
- draw_line(x1, y1+1, x1o-b1w/2-margin, y1+1) # double
768
- draw_line(x1o-b1w/2-margin+1, y1, x2o+b2w/2+margin+1, y2)
769
- draw_line(x2o+b2w/2+margin, y2+1, x2, y2+1)
770
- elsif x1+b1w/2+margin <= x2-b2w/2-margin # invert z
771
- draw_line(x1, y1, x1o+b1w/2+margin, y1)
772
- draw_line(x1o+b1w/2+margin, y1, x2o-b2w/2-margin, y2)
773
- draw_line(x2o-b2w/2-margin, y2, x2, y2)
774
- draw_line(x1, y1+1, x1+b1w/2+margin, y1+1) # double
775
- draw_line(x1o+b1w/2+margin+1, y1, x2o-b2w/2-margin+1, y2)
776
- draw_line(x2o-b2w/2-margin, y2+1, x2, y2+1)
777
- else # turn around
778
- x = (x1 <= x2 ? [x1o-b1w/2-margin, x2o-b2w/2-margin].min : [x1o+b1w/2+margin, x2o+b2w/2+margin].max)
779
- draw_line(x1, y1, x, y1)
780
- draw_line(x, y1, x, y2)
781
- draw_line(x, y2, x2, y2)
782
- draw_line(x1, y1+1, x, y1+1) # double
783
- draw_line(x+1, y1, x+1, y2)
784
- draw_line(x, y2+1, x2, y2+1)
785
- end
786
- end
787
-
788
- def set_color_boxshadow(b)
789
- draw_color :black
790
- end
791
-
792
- def set_color_box(b)
793
- if @selected_boxes.include? b
794
- draw_color :hlbox_bg
795
- else
796
- draw_color :box_bg
797
- end
798
- end
799
-
800
- def paint_box(b)
801
- set_color_boxshadow(b)
802
- draw_rectangle((b.x-@curcontext.view_x+3)*@zoom, (b.y-@curcontext.view_y+4)*@zoom, b.w*@zoom, b.h*@zoom)
803
- set_color_box(b)
804
- draw_rectangle((b.x-@curcontext.view_x)*@zoom, (b.y-@curcontext.view_y+1)*@zoom, b.w*@zoom, b.h*@zoom)
805
-
806
- # current text position
807
- x = (b.x - @curcontext.view_x + 1)*@zoom
808
- y = (b.y - @curcontext.view_y + 1)*@zoom
809
- w_w = (b.x - @curcontext.view_x + b.w - @font_width)*@zoom
810
- w_h = (b.y - @curcontext.view_y + b.h - @font_height)*@zoom
811
-
812
- if @parent_widget and @parent_widget.bg_color_callback
813
- ly = 0
814
- b[:line_address].each { |a|
815
- if c = @parent_widget.bg_color_callback[a]
816
- draw_rectangle_color(c, (b.x-@curcontext.view_x)*@zoom, (1+b.y-@curcontext.view_y+ly*@font_height)*@zoom, b.w*@zoom, (@font_height*@zoom).ceil)
817
- end
818
- ly += 1
819
- }
820
- end
821
-
822
- if @caret_box == b
823
- draw_rectangle_color(:cursorline_bg, (b.x-@curcontext.view_x)*@zoom, (1+b.y-@curcontext.view_y+@caret_y*@font_height)*@zoom, b.w*@zoom, @font_height*@zoom)
824
- end
825
-
826
- return if @zoom < 0.99 or @zoom > 1.1
827
- # TODO dynamic font size ?
828
-
829
- # renders a string at current cursor position with a color
830
- # must not include newline
831
- render = lambda { |str, color|
832
- # function ends when we write under the bottom of the listing
833
- next if y >= w_h+2 or x >= w_w
834
- if @hl_word
835
- stmp = str
836
- pre_x = 0
837
- while stmp =~ /^(.*?)(\b#{Regexp.escape @hl_word}\b)/
838
- s1, s2 = $1, $2
839
- pre_x += s1.length * @font_width
840
- hl_x = s2.length * @font_width
841
- draw_rectangle_color(:hl_word, x+pre_x, y, hl_x, @font_height*@zoom)
842
- pre_x += hl_x
843
- stmp = stmp[s1.length+s2.length..-1]
844
- end
845
- end
846
- draw_string_color(color, x, y, str)
847
- x += str.length * @font_width
848
- }
849
-
850
- b[:line_text_col].each { |list|
851
- list.each { |s, c| render[s, c] }
852
- x = (b.x - @curcontext.view_x + 1)*@zoom
853
- y += @font_height*@zoom
854
- }
855
-
856
- if b == @caret_box and focus?
857
- cx = (b.x - @curcontext.view_x + 1 + @caret_x*@font_width)*@zoom
858
- cy = (b.y - @curcontext.view_y + 1 + @caret_y*@font_height)*@zoom
859
- draw_line_color(:caret, cx, cy, cx, cy+(@font_height-1)*@zoom)
860
- end
861
- end
862
-
863
- def gui_update
864
- @want_update_graph = true
865
- redraw
866
- end
867
-
868
- #
869
- # rebuild the code flow graph from @curcontext.roots
870
- # recalc the boxes w/h
871
- #
872
- def update_graph
873
- @want_update_graph = false
874
-
875
- ctx = @curcontext
876
-
877
- boxcnt = ctx.box.length
878
- arrcnt = ctx.box.inject(0) { |s, b| s + b.to.length + b.from.length }
879
- ctx.clear
880
-
881
- build_ctx(ctx)
882
-
883
- ctx.auto_arrange_boxes
884
-
885
- return if ctx != @curcontext
886
-
887
- if boxcnt != ctx.box.length or arrcnt != ctx.box.inject(0) { |s, b| s + b.to.length + b.from.length }
888
- zoom_all
889
- elsif @caret_box # update @caret_box with a box at the same place
890
- bx = @caret_box.x + @caret_box.w/2
891
- by = @caret_box.y + @caret_box.h/2
892
- @caret_box = ctx.box.find { |cb| cb.x < bx and cb.x+cb.w > bx and cb.y < by and cb.y+cb.h > by }
893
- end
894
- end
895
-
896
- def load_dotfile(path)
897
- @want_update_graph = false
898
- @curcontext.clear
899
- boxes = {}
900
- new_box = lambda { |text|
901
- b = @curcontext.new_box(text, :line_text_col => [[[text, :text]]])
902
- b.w = text.length * @font_width
903
- b.h = @font_height
904
- b
905
- }
906
- max = File.size(path)
907
- i = 0
908
- File.open(path) { |fd|
909
- while l = fd.gets
910
- case l.strip
911
- when /^"?(\w+)"?\s*->\s*"?(\w+)"?;?$/
912
- b1 = boxes[$1] ||= new_box[$1]
913
- b2 = boxes[$2] ||= new_box[$2]
914
- b1.to |= [b2]
915
- b2.from |= [b1]
916
- end
917
- $stderr.printf("%.02f\r" % (fd.pos*100.0/max)) if (i += 1) & 0xff == 0
918
- end
919
- }
920
- p boxes.length
921
- redraw
922
- rescue Interrupt
923
- p boxes.length
924
- end
925
-
926
- # create the graph objects in ctx
927
- def build_ctx(ctx)
928
- # graph : block -> following blocks in same function
929
- block_rel = {}
930
-
931
- todo = ctx.root_addrs.dup
932
- done = [:default, Expression::Unknown]
933
- while a = todo.shift
934
- a = @dasm.normalize a
935
- next if done.include? a
936
- done << a
937
- next if not di = @dasm.di_at(a)
938
- if not di.block_head?
939
- block_rel[di.block.address] = [a]
940
- @dasm.split_block(a)
941
- end
942
- block_rel[a] = []
943
- di.block.each_to_samefunc(@dasm) { |t|
944
- t = @dasm.normalize t
945
- next if not @dasm.di_at(t)
946
- todo << t
947
- block_rel[a] << t
948
- }
949
- block_rel[a].uniq!
950
- end
951
-
952
- # populate boxes
953
- addr2box = {}
954
- todo = ctx.root_addrs.dup
955
- todo.delete_if { |t| not @dasm.di_at(t) } # undefined func start
956
- done = []
957
- while a = todo.shift
958
- next if done.include? a
959
- done << a
960
- if not ctx.keep_split.to_a.include?(a) and from = block_rel.keys.find_all { |ba| block_rel[ba].include? a } and
961
- from.length == 1 and block_rel[from.first].length == 1 and
962
- addr2box[from.first] and lst = @dasm.decoded[from.first].block.list.last and
963
- lst.next_addr == a and (not lst.opcode.props[:saveip] or lst.block.to_subfuncret)
964
- box = addr2box[from.first]
965
- else
966
- box = ctx.new_box a, :addresses => [], :line_text_col => [], :line_address => []
967
- end
968
- @dasm.decoded[a].block.list.each { |di_|
969
- box[:addresses] << di_.address
970
- addr2box[di_.address] = box
971
- }
972
- todo.concat block_rel[a]
973
- end
974
-
975
- # link boxes
976
- ctx.box.each { |b|
977
- next if not di = @dasm.decoded[b[:addresses].last]
978
- a = di.block.address
979
- next if not block_rel[a]
980
- block_rel[a].each { |t|
981
- ctx.link_boxes(b.id, t)
982
- b.direct_to = t if t == di.next_addr
983
- }
984
- }
985
-
986
- # calc box dimensions/text
987
- ctx.box.each { |b|
988
- colstr = []
989
- curaddr = nil
990
- line = 0
991
- render = lambda { |str, col| colstr << [str, col] }
992
- nl = lambda {
993
- b[:line_address][line] = curaddr
994
- b[:line_text_col][line] = colstr
995
- colstr = []
996
- line += 1
997
- }
998
- b[:addresses].each { |addr|
999
- curaddr = addr
1000
- if di = @dasm.di_at(curaddr)
1001
- if di.block_head?
1002
- # render dump_block_header, add a few colors
1003
- b_header = '' ; @dasm.dump_block_header(di.block) { |l| b_header << l ; b_header << ?\n if b_header[-1] != ?\n }
1004
- b_header.strip.each_line { |l| l.chomp!
1005
- col = :comment
1006
- col = :label if l[0, 2] != '//' and l[-1] == ?:
1007
- render[l, col]
1008
- nl[]
1009
- }
1010
- end
1011
- render["#{Expression[curaddr]} ", :address] if @show_addresses
1012
- render[di.instruction.to_s.ljust(di.comment ? 24 : 0), :instruction]
1013
- render[' ; ' + di.comment.join(' ')[0, 64], :comment] if di.comment
1014
- nl[]
1015
- else
1016
- # TODO real data display (dwords, xrefs, strings..)
1017
- if label = @dasm.get_label_at(curaddr)
1018
- render[label + ' ', :label]
1019
- end
1020
- s = @dasm.get_section_at(curaddr)
1021
- render['db '+((s and s[0].data.length > s[0].ptr) ? Expression[s[0].read(1)[0]].to_s : '?'), :text]
1022
- nl[]
1023
- end
1024
- }
1025
- b.w = b[:line_text_col].map { |strc| strc.map { |s, c| s }.join.length }.max.to_i * @font_width + 2
1026
- b.w += 1 if b.w % 2 == 0 # ensure boxes have odd width -> vertical arrows are straight
1027
- b.h = line * @font_height
1028
- }
1029
- end
1030
-
1031
- def keypress_ctrl(key)
1032
- case key
1033
- when ?F
1034
- @parent_widget.inputbox('text to search in curview (regex)', :text => @hl_word) { |pat|
1035
- re = /#{pat}/i
1036
- list = [['addr', 'instr']]
1037
- @curcontext.box.each { |b|
1038
- b[:line_text_col].zip(b[:line_address]) { |l, a|
1039
- str = l.map { |s, c| s }.join
1040
- list << [Expression[a], str] if str =~ re
1041
- }
1042
- }
1043
- @parent_widget.list_bghilight("search result for /#{pat}/i", list) { |i| @parent_widget.focus_addr i[0] }
1044
- }
1045
- else return false
1046
- end
1047
- true
1048
- end
1049
-
1050
- def keypress(key)
1051
- case key
1052
- when :left
1053
- if @caret_box
1054
- if @caret_x > 0
1055
- @caret_x -= 1
1056
- update_caret
1057
- elsif b = @curcontext.box.sort_by { |b_| -b_.x }.find { |b_| b_.x < @caret_box.x and
1058
- b_.y < @caret_box.y+@caret_y*@font_height and
1059
- b_.y+b_.h > @caret_box.y+(@caret_y+1)*@font_height }
1060
- @caret_x = (b.w/@font_width).to_i
1061
- @caret_y += ((@caret_box.y-b.y)/@font_height).to_i
1062
- @caret_box = b
1063
- update_caret
1064
- redraw
1065
- else
1066
- @curcontext.view_x -= 20/@zoom
1067
- redraw
1068
- end
1069
- else
1070
- @curcontext.view_x -= 20/@zoom
1071
- redraw
1072
- end
1073
- when :up
1074
- if @caret_box
1075
- if @caret_y > 0
1076
- @caret_y -= 1
1077
- update_caret
1078
- elsif b = @curcontext.box.sort_by { |b_| -b_.y }.find { |b_| b_.y < @caret_box.y and
1079
- b_.x < @caret_box.x+@caret_x*@font_width and
1080
- b_.x+b_.w > @caret_box.x+(@caret_x+1)*@font_width }
1081
- @caret_x += ((@caret_box.x-b.x)/@font_width).to_i
1082
- @caret_y = b[:line_address].length-1
1083
- @caret_box = b
1084
- update_caret
1085
- redraw
1086
- else
1087
- @curcontext.view_y -= 20/@zoom
1088
- redraw
1089
- end
1090
- else
1091
- @curcontext.view_y -= 20/@zoom
1092
- redraw
1093
- end
1094
- when :right
1095
- if @caret_box
1096
- if @caret_x <= @caret_box[:line_text_col].map { |s| s.map { |ss, cc| ss }.join.length }.max
1097
- @caret_x += 1
1098
- update_caret
1099
- elsif b = @curcontext.box.sort_by { |b_| b_.x }.find { |b_| b_.x > @caret_box.x and
1100
- b_.y < @caret_box.y+@caret_y*@font_height and
1101
- b_.y+b_.h > @caret_box.y+(@caret_y+1)*@font_height }
1102
- @caret_x = 0
1103
- @caret_y += ((@caret_box.y-b.y)/@font_height).to_i
1104
- @caret_box = b
1105
- update_caret
1106
- redraw
1107
- else
1108
- @curcontext.view_x += 20/@zoom
1109
- redraw
1110
- end
1111
- else
1112
- @curcontext.view_x += 20/@zoom
1113
- redraw
1114
- end
1115
- when :down
1116
- if @caret_box
1117
- if @caret_y < @caret_box[:line_address].length-1
1118
- @caret_y += 1
1119
- update_caret
1120
- elsif b = @curcontext.box.sort_by { |b_| b_.y }.find { |b_| b_.y > @caret_box.y and
1121
- b_.x < @caret_box.x+@caret_x*@font_width and
1122
- b_.x+b_.w > @caret_box.x+(@caret_x+1)*@font_width }
1123
- @caret_x += ((@caret_box.x-b.x)/@font_width).to_i
1124
- @caret_y = 0
1125
- @caret_box = b
1126
- update_caret
1127
- redraw
1128
- else
1129
- @curcontext.view_y += 20/@zoom
1130
- redraw
1131
- end
1132
- else
1133
- @curcontext.view_y += 20/@zoom
1134
- redraw
1135
- end
1136
- when :pgup
1137
- if @caret_box
1138
- @caret_y = 0
1139
- update_caret
1140
- else
1141
- @curcontext.view_y -= height/4/@zoom
1142
- redraw
1143
- end
1144
- when :pgdown
1145
- if @caret_box
1146
- @caret_y = @caret_box[:line_address].length-1
1147
- update_caret
1148
- else
1149
- @curcontext.view_y += height/4/@zoom
1150
- redraw
1151
- end
1152
- when :home
1153
- if @caret_box
1154
- @caret_x = 0
1155
- update_caret
1156
- else
1157
- @curcontext.view_x = @curcontext.box.map { |b_| b_.x }.min-10
1158
- @curcontext.view_y = @curcontext.box.map { |b_| b_.y }.min-10
1159
- redraw
1160
- end
1161
- when :end
1162
- if @caret_box
1163
- @caret_x = @caret_box[:line_text_col][@caret_y].to_a.map { |ss, cc| ss }.join.length
1164
- update_caret
1165
- else
1166
- @curcontext.view_x = [@curcontext.box.map { |b_| b_.x+b_.w }.max-width/@zoom+10, @curcontext.box.map { |b_| b_.x }.min-10].max
1167
- @curcontext.view_y = [@curcontext.box.map { |b_| b_.y+b_.h }.max-height/@zoom+10, @curcontext.box.map { |b_| b_.y }.min-10].max
1168
- redraw
1169
- end
1170
-
1171
- when :delete
1172
- @selected_boxes.each { |b_|
1173
- @curcontext.box.delete b_
1174
- b_.from.each { |bb| bb.to.delete b_ }
1175
- b_.to.each { |bb| bb.from.delete b_ }
1176
- }
1177
- redraw
1178
-
1179
- when ?a
1180
- puts 'autoarrange'
1181
- @curcontext.auto_arrange_boxes
1182
- redraw
1183
- puts 'autoarrange done'
1184
- when ?u
1185
- gui_update
1186
-
1187
- when ?R
1188
- load __FILE__
1189
- when ?S # reset
1190
- @curcontext.auto_arrange_init(@selected_boxes.empty? ? @curcontext.box : @selected_boxes)
1191
- puts 'reset', @curcontext.dump_layout, ''
1192
- zoom_all
1193
- redraw
1194
- when ?T # step auto_arrange
1195
- @curcontext.auto_arrange_step
1196
- puts @curcontext.dump_layout, ''
1197
- zoom_all
1198
- redraw
1199
- when ?L # post auto_arrange
1200
- @curcontext.auto_arrange_post
1201
- zoom_all
1202
- redraw
1203
- when ?V # shrink
1204
- @selected_boxes.each { |b_|
1205
- dx = (b_.from + b_.to).map { |bb| bb.x+bb.w/2 - b_.x-b_.w/2 }
1206
- dx = dx.inject(0) { |s, xx| s+xx }/dx.length
1207
- b_.x += dx
1208
- }
1209
- redraw
1210
- when ?I # create arbitrary boxes/links
1211
- if @selected_boxes.empty?
1212
- @fakebox ||= 0
1213
- b = @curcontext.new_box "id_#@fakebox",
1214
- :addresses => [], :line_address => [],
1215
- :line_text_col => [[[" blublu #@fakebox", :text]]]
1216
- b.w = @font_width * 15
1217
- b.h = @font_height * 2
1218
- b.x = rand(200) - 100
1219
- b.y = rand(200) - 100
1220
-
1221
- @fakebox += 1
1222
- else
1223
- b1, *bl = @selected_boxes
1224
- bl = [b1] if bl.empty? # loop
1225
- bl.each { |b2|
1226
- if b1.to.include? b2
1227
- b1.to.delete b2
1228
- b2.from.delete b1
1229
- else
1230
- b1.to << b2
1231
- b2.from << b1
1232
- end
1233
- }
1234
- end
1235
- redraw
1236
-
1237
- when ?1 # (numeric) zoom to 1:1
1238
- if @zoom == 1.0
1239
- zoom_all
1240
- else
1241
- @curcontext.view_x += (width/2 / @zoom - width/2)
1242
- @curcontext.view_y += (height/2 / @zoom - height/2)
1243
- @zoom = 1.0
1244
- end
1245
- redraw
1246
- when :insert # split curbox at @caret_y
1247
- if @caret_box and a = @caret_box[:line_address][@caret_y] and @dasm.decoded[a]
1248
- @dasm.split_block(a)
1249
- @curcontext.keep_split ||= []
1250
- @curcontext.keep_split |= [a]
1251
- gui_update
1252
- focus_addr a
1253
- end
1254
- else return false
1255
- end
1256
- true
1257
- end
1258
-
1259
- # find a suitable array of graph roots, walking up from a block (function start/entrypoint)
1260
- def dasm_find_roots(addr)
1261
- todo = [addr]
1262
- done = []
1263
- roots = []
1264
- default_root = nil
1265
- while a = todo.shift
1266
- next if not di = @dasm.di_at(a)
1267
- b = di.block
1268
- a = b.address
1269
- if done.include? a
1270
- default_root ||= a
1271
- next
1272
- end
1273
- done << a
1274
- newf = []
1275
- b.each_from_samefunc(@dasm) { |f| newf << f }
1276
- if newf.empty?
1277
- roots << b.address
1278
- else
1279
- todo.concat newf
1280
- end
1281
- end
1282
- roots << default_root if roots.empty? and default_root
1283
-
1284
- roots
1285
- end
1286
-
1287
- def set_cursor_pos(p)
1288
- addr, x = p
1289
- focus_addr(addr)
1290
- @caret_x = x
1291
- update_caret
1292
- end
1293
-
1294
- def get_cursor_pos
1295
- [current_address, @caret_x]
1296
- end
1297
-
1298
- # focus on addr
1299
- # addr may be a dasm label, dasm address, dasm address in string form (eg "0DEADBEEFh")
1300
- # addr must point to a decodedinstruction
1301
- # if the addr is not found in curcontext, the code flow is walked up until a function
1302
- # start or an entrypoint is found, then the graph is created from there
1303
- # will call gui_update then
1304
- def focus_addr(addr, can_update_context=true)
1305
- return if @parent_widget and not addr = @parent_widget.normalize(addr)
1306
- return if not @dasm.di_at(addr)
1307
-
1308
- # move window / change curcontext
1309
- if b = @curcontext.box.find { |b_| b_[:line_address].index(addr) }
1310
- @caret_box, @caret_x, @caret_y = b, 0, b[:line_address].rindex(addr)
1311
- @curcontext.view_x += (width/2 / @zoom - width/2)
1312
- @curcontext.view_y += (height/2 / @zoom - height/2)
1313
- @zoom = 1.0
1314
-
1315
- focus_xy(b.x, b.y + @caret_y*@font_height)
1316
- update_caret
1317
- elsif can_update_context
1318
- @curcontext = Graph.new 'testic'
1319
- @curcontext.root_addrs = dasm_find_roots(addr)
1320
- @want_focus_addr = addr
1321
- gui_update
1322
- else
1323
- return
1324
- end
1325
- true
1326
- end
1327
-
1328
- def focus_xy(x, y)
1329
- if not @curcontext.view_x or @curcontext.view_x*@zoom + width*3/4 < x or @curcontext.view_x*@zoom > x
1330
- @curcontext.view_x = (x - width/5)/@zoom
1331
- redraw
1332
- end
1333
- if not @curcontext.view_y or @curcontext.view_y*@zoom + height*3/4 < y or @curcontext.view_y*@zoom > y
1334
- @curcontext.view_y = (y - height/5)/@zoom
1335
- redraw
1336
- end
1337
- end
1338
-
1339
- # hint that the caret moved
1340
- # redraw, change the hilighted word
1341
- def update_caret
1342
- return if not @caret_box or not @caret_x or not l = @caret_box[:line_text_col][@caret_y]
1343
- l = l.map { |s, c| s }.join
1344
- @parent_widget.focus_changed_callback[] if @parent_widget and @parent_widget.focus_changed_callback and @oldcaret_y != @caret_y
1345
- update_hl_word(l, @caret_x)
1346
- redraw
1347
- end
1348
-
1349
- def current_address
1350
- @caret_box ? @caret_box[:line_address][@caret_y] : @curcontext.root_addrs.first
1351
- end
1352
- end
1353
- end
1354
- end