metasm 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
@@ -0,0 +1,291 @@
|
|
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 AsmOpcodeWidget < DrawableWidget
|
9
|
+
attr_accessor :dasm
|
10
|
+
# nr of raw data bytes to display next to decoded instructions
|
11
|
+
attr_accessor :raw_data_length
|
12
|
+
|
13
|
+
def initialize_widget(dasm, parent_widget)
|
14
|
+
@dasm = dasm
|
15
|
+
@parent_widget = parent_widget
|
16
|
+
|
17
|
+
@raw_data_length = 5
|
18
|
+
|
19
|
+
@line_text = {}
|
20
|
+
@line_address = {}
|
21
|
+
@view_min = @dasm.sections.keys.min rescue nil
|
22
|
+
@view_max = @dasm.sections.map { |s, e| s + e.length }.max rescue nil
|
23
|
+
@view_addr = @dasm.prog_binding['entrypoint'] || @view_min || 0
|
24
|
+
|
25
|
+
@default_color_association = { :comment => :darkblue, :label => :darkgreen, :text => :black,
|
26
|
+
:instruction => :black, :address => :blue, :caret => :black, :raw_data => :black,
|
27
|
+
:background => :white, :cursorline_bg => :paleyellow, :hl_word => :palered }
|
28
|
+
end
|
29
|
+
|
30
|
+
def resized(w, h)
|
31
|
+
w /= @font_width
|
32
|
+
h /= @font_height
|
33
|
+
@caret_x = w-1 if @caret_x >= w
|
34
|
+
@caret_y = h-1 if @caret_y >= h
|
35
|
+
end
|
36
|
+
|
37
|
+
def click(x, y)
|
38
|
+
@caret_x = (x-1).to_i / @font_width
|
39
|
+
@caret_y = y.to_i / @font_height
|
40
|
+
update_caret
|
41
|
+
end
|
42
|
+
|
43
|
+
def rightclick(x, y)
|
44
|
+
click(x, y)
|
45
|
+
@parent_widget.clone_window(@hl_word, :opcodes)
|
46
|
+
end
|
47
|
+
|
48
|
+
def doubleclick(x, y)
|
49
|
+
click(x, y)
|
50
|
+
@parent_widget.focus_addr(@hl_word)
|
51
|
+
end
|
52
|
+
|
53
|
+
def mouse_wheel(dir, x, y)
|
54
|
+
case dir
|
55
|
+
when :up; (height/@font_height/4).times { scrollup }
|
56
|
+
when :down; (height/@font_height/4).times { scrolldown }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def di_at(addr)
|
61
|
+
s = @dasm.get_section_at(addr) and s[0].ptr < s[0].length and update_di_args(@dasm.cpu.decode_instruction(s[0], addr))
|
62
|
+
end
|
63
|
+
|
64
|
+
def update_di_args(di)
|
65
|
+
if di
|
66
|
+
di.instruction.args.map! { |e|
|
67
|
+
next e if not e.kind_of? Expression
|
68
|
+
@dasm.get_label_at(e) || e
|
69
|
+
}
|
70
|
+
end
|
71
|
+
di
|
72
|
+
end
|
73
|
+
|
74
|
+
def scrollup
|
75
|
+
return if @view_min and @view_addr < @view_min
|
76
|
+
# keep current instrs in sync
|
77
|
+
16.times { |o|
|
78
|
+
o += 1
|
79
|
+
if di = di_at(@view_addr-o) and di.bin_length == o
|
80
|
+
@view_addr -= o
|
81
|
+
@line_address = {}
|
82
|
+
redraw
|
83
|
+
return
|
84
|
+
end
|
85
|
+
}
|
86
|
+
@view_addr -= 1
|
87
|
+
@line_address = {}
|
88
|
+
redraw
|
89
|
+
end
|
90
|
+
|
91
|
+
def scrolldown
|
92
|
+
return if @view_max and @view_addr >= @view_max
|
93
|
+
if di = di_at(@view_addr)
|
94
|
+
@view_addr += di.bin_length
|
95
|
+
else
|
96
|
+
@view_addr += 1
|
97
|
+
end
|
98
|
+
@line_address = {}
|
99
|
+
redraw
|
100
|
+
end
|
101
|
+
|
102
|
+
def paint
|
103
|
+
# draw caret line background
|
104
|
+
draw_rectangle_color(:cursorline_bg, 0, @caret_y*@font_height, width, @font_height)
|
105
|
+
|
106
|
+
want_update_caret = true if @line_address == {}
|
107
|
+
|
108
|
+
# map lineno => address shown
|
109
|
+
@line_address = Hash.new(-1)
|
110
|
+
# map lineno => raw text
|
111
|
+
@line_text = Hash.new('')
|
112
|
+
|
113
|
+
# current address drawing
|
114
|
+
curaddr = @view_addr
|
115
|
+
# current line text buffer
|
116
|
+
fullstr = ''
|
117
|
+
# current line number
|
118
|
+
line = 0
|
119
|
+
# current window position
|
120
|
+
x = 1
|
121
|
+
y = 0
|
122
|
+
|
123
|
+
# renders a string at current cursor position with a color
|
124
|
+
# must not include newline
|
125
|
+
render = lambda { |str, color|
|
126
|
+
fullstr << str
|
127
|
+
if @hl_word
|
128
|
+
stmp = str
|
129
|
+
pre_x = 0
|
130
|
+
while stmp =~ /^(.*?)(\b#{Regexp.escape @hl_word}\b)/
|
131
|
+
s1, s2 = $1, $2
|
132
|
+
pre_x += s1.length * @font_width
|
133
|
+
hl_x = s2.length * @font_width
|
134
|
+
draw_rectangle_color(:hl_word, x+pre_x, y, hl_x, @font_height)
|
135
|
+
pre_x += hl_x
|
136
|
+
stmp = stmp[s1.length+s2.length..-1]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
draw_string_color(color, x, y, str)
|
140
|
+
x += str.length * @font_width
|
141
|
+
}
|
142
|
+
|
143
|
+
# newline: current line is fully rendered, update @line_address/@line_text etc
|
144
|
+
nl = lambda {
|
145
|
+
@line_text[line] = fullstr
|
146
|
+
@line_address[line] = curaddr
|
147
|
+
fullstr = ''
|
148
|
+
line += 1
|
149
|
+
x = 1
|
150
|
+
y += @font_height
|
151
|
+
}
|
152
|
+
|
153
|
+
invb = @dasm.prog_binding.invert
|
154
|
+
|
155
|
+
# draw text until screen is full
|
156
|
+
while y < height
|
157
|
+
if label = invb[curaddr]
|
158
|
+
nl[]
|
159
|
+
@dasm.label_alias[curaddr].to_a.each { |name|
|
160
|
+
render["#{name}:", :label]
|
161
|
+
nl[]
|
162
|
+
}
|
163
|
+
end
|
164
|
+
render["#{Expression[curaddr]} ", :address]
|
165
|
+
|
166
|
+
if di = di_at(curaddr)
|
167
|
+
if @raw_data_length.to_i > 0
|
168
|
+
if s = @dasm.get_section_at(curaddr)
|
169
|
+
raw = s[0].read(di.bin_length)
|
170
|
+
raw = raw.unpack('H*').first
|
171
|
+
else
|
172
|
+
raw = ''
|
173
|
+
end
|
174
|
+
raw = raw.ljust(@raw_data_length*2)[0, @raw_data_length*2]
|
175
|
+
raw += (di.bin_length > @raw_data_length ? '- ' : ' ')
|
176
|
+
render[raw, :raw_data]
|
177
|
+
end
|
178
|
+
render["#{di.instruction} ", :instruction]
|
179
|
+
else
|
180
|
+
if s = @dasm.get_section_at(curaddr) and s[0].ptr < s[0].length
|
181
|
+
render["db #{Expression[s[0].read(1).unpack('C')]} ", :instruction]
|
182
|
+
end
|
183
|
+
end
|
184
|
+
nl[]
|
185
|
+
curaddr += di ? di.bin_length : 1
|
186
|
+
end
|
187
|
+
|
188
|
+
if focus?
|
189
|
+
# draw caret
|
190
|
+
cx = @caret_x*@font_width+1
|
191
|
+
cy = @caret_y*@font_height
|
192
|
+
draw_line_color(:caret, cx, cy, cx, cy+@font_height-1)
|
193
|
+
end
|
194
|
+
|
195
|
+
update_caret if want_update_caret
|
196
|
+
end
|
197
|
+
|
198
|
+
def keypress(key)
|
199
|
+
case key
|
200
|
+
when :left
|
201
|
+
if @caret_x >= 1
|
202
|
+
@caret_x -= 1
|
203
|
+
update_caret
|
204
|
+
end
|
205
|
+
when :up
|
206
|
+
if @caret_y >= 1
|
207
|
+
@caret_y -= 1
|
208
|
+
else
|
209
|
+
scrollup
|
210
|
+
end
|
211
|
+
update_caret
|
212
|
+
when :right
|
213
|
+
if @caret_x <= @line_text.values.map { |s| s.length }.max
|
214
|
+
@caret_x += 1
|
215
|
+
update_caret
|
216
|
+
end
|
217
|
+
when :down
|
218
|
+
if @caret_y < @line_text.length-3
|
219
|
+
@caret_y += 1
|
220
|
+
else
|
221
|
+
scrolldown
|
222
|
+
end
|
223
|
+
update_caret
|
224
|
+
when :pgup
|
225
|
+
(height/@font_height/2).times { scrollup }
|
226
|
+
when :pgdown
|
227
|
+
@view_addr = @line_address.fetch(@line_address.length/2, @view_addr+15)
|
228
|
+
redraw
|
229
|
+
when :home
|
230
|
+
@caret_x = 0
|
231
|
+
update_caret
|
232
|
+
when :end
|
233
|
+
@caret_x = @line_text[@caret_y].length
|
234
|
+
update_caret
|
235
|
+
else return false
|
236
|
+
end
|
237
|
+
true
|
238
|
+
end
|
239
|
+
|
240
|
+
def get_cursor_pos
|
241
|
+
[@view_addr, @caret_x, @caret_y]
|
242
|
+
end
|
243
|
+
|
244
|
+
def set_cursor_pos(p)
|
245
|
+
@view_addr, @caret_x, @caret_y = p
|
246
|
+
redraw
|
247
|
+
update_caret
|
248
|
+
end
|
249
|
+
|
250
|
+
# hint that the caret moved
|
251
|
+
# redraws the caret, change the hilighted word, redraw if needed
|
252
|
+
def update_caret
|
253
|
+
if update_hl_word(@line_text[@caret_y], @caret_x) or @caret_y != @oldcaret_y
|
254
|
+
redraw
|
255
|
+
elsif @oldcaret_x != @caret_x
|
256
|
+
invalidate_caret(@oldcaret_x, @oldcaret_y)
|
257
|
+
invalidate_caret(@caret_x, @caret_y)
|
258
|
+
end
|
259
|
+
|
260
|
+
@oldcaret_x, @oldcaret_y = @caret_x, @caret_y
|
261
|
+
end
|
262
|
+
|
263
|
+
# focus on addr
|
264
|
+
# returns true on success (address exists)
|
265
|
+
def focus_addr(addr)
|
266
|
+
return if not addr = @parent_widget.normalize(addr)
|
267
|
+
if l = @line_address.index(addr) and l < @line_address.keys.max - 4
|
268
|
+
@caret_y, @caret_x = @line_address.keys.find_all { |k| @line_address[k] == addr }.max, 0
|
269
|
+
elsif @dasm.get_section_at(addr)
|
270
|
+
@view_addr, @caret_x, @caret_y = addr, 0, 0
|
271
|
+
redraw
|
272
|
+
else
|
273
|
+
return
|
274
|
+
end
|
275
|
+
update_caret
|
276
|
+
true
|
277
|
+
end
|
278
|
+
|
279
|
+
# returns the address of the data under the cursor
|
280
|
+
def current_address
|
281
|
+
@line_address[@caret_y]
|
282
|
+
end
|
283
|
+
|
284
|
+
def gui_update
|
285
|
+
@view_min = @dasm.sections.keys.min rescue nil
|
286
|
+
@view_max = @dasm.sections.map { |s, e| s + e.length }.max rescue nil
|
287
|
+
redraw
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
@@ -0,0 +1,1228 @@
|
|
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
|
+
require 'metasm/gui/dasm_main'
|
7
|
+
|
8
|
+
module Metasm
|
9
|
+
module Gui
|
10
|
+
|
11
|
+
# TODO invalidate dbg.disassembler on selfmodifying code
|
12
|
+
class DbgWidget < ContainerVBoxWidget
|
13
|
+
attr_accessor :dbg, :console, :regs, :code, :mem, :win
|
14
|
+
attr_accessor :watchpoint
|
15
|
+
attr_accessor :parent_widget, :keyboard_callback, :keyboard_callback_ctrl
|
16
|
+
def initialize_widget(dbg)
|
17
|
+
@dbg = dbg
|
18
|
+
@keyboard_callback = {}
|
19
|
+
@keyboard_callback_ctrl = {}
|
20
|
+
@parent_widget = nil
|
21
|
+
|
22
|
+
@regs = DbgRegWidget.new(dbg, self)
|
23
|
+
@mem = DisasmWidget.new(dbg.disassembler)
|
24
|
+
@code = DisasmWidget.new(dbg.disassembler) # after mem so that dasm.gui == @code
|
25
|
+
@console = DbgConsoleWidget.new(dbg, self)
|
26
|
+
@code.parent_widget = self
|
27
|
+
@mem.parent_widget = self
|
28
|
+
@dbg.disassembler.disassemble_fast(@dbg.pc)
|
29
|
+
|
30
|
+
oldcb = @code.bg_color_callback
|
31
|
+
@code.bg_color_callback = lambda { |a|
|
32
|
+
if a == @dbg.pc
|
33
|
+
'f88'
|
34
|
+
# TODO breakpoints & stuff
|
35
|
+
elsif oldcb; oldcb[a]
|
36
|
+
end
|
37
|
+
}
|
38
|
+
# TODO popup menu, set bp, goto here, show arg in memdump..
|
39
|
+
|
40
|
+
@children = [@code, @mem, @regs]
|
41
|
+
|
42
|
+
add @regs, 'expand' => false # XXX
|
43
|
+
add @mem
|
44
|
+
add @code
|
45
|
+
add @console
|
46
|
+
|
47
|
+
@watchpoint = { @code => @dbg.register_pc }
|
48
|
+
|
49
|
+
pc = @dbg.resolve_expr(@watchpoint[@code])
|
50
|
+
graph = :graph if @dbg.disassembler.function_blocks(pc).to_a.length < 100
|
51
|
+
@code.focus_addr(pc, graph)
|
52
|
+
@mem.focus_addr(0, :hex)
|
53
|
+
end
|
54
|
+
|
55
|
+
def swapin_tid
|
56
|
+
@regs.swapin_tid
|
57
|
+
@dbg.disassembler.disassemble_fast(@dbg.pc)
|
58
|
+
@children.each { |c|
|
59
|
+
if wp = @watchpoint[c]
|
60
|
+
c.focus_addr @dbg.resolve_expr(wp), nil, true
|
61
|
+
end
|
62
|
+
}
|
63
|
+
redraw
|
64
|
+
end
|
65
|
+
|
66
|
+
def swapin_pid
|
67
|
+
@mem.dasm = @dbg.disassembler
|
68
|
+
@code.dasm = @dbg.disassembler
|
69
|
+
swapin_tid
|
70
|
+
gui_update
|
71
|
+
end
|
72
|
+
|
73
|
+
def keypress(key)
|
74
|
+
return true if @keyboard_callback[key] and @keyboard_callback[key][key]
|
75
|
+
case key
|
76
|
+
when :f5; protect { dbg_continue }
|
77
|
+
when :f10; protect { dbg_stepover }
|
78
|
+
when :f11; protect { dbg_singlestep }
|
79
|
+
when :f12; protect { dbg_stepout }
|
80
|
+
when ?.; @console.grab_focus
|
81
|
+
else return @parent_widget ? @parent_widget.keypress(key) : false
|
82
|
+
end
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
def keypress_ctrl(key)
|
87
|
+
return true if @keyboard_callback_ctrl[key] and @keyboard_callback_ctrl[key][key]
|
88
|
+
case key
|
89
|
+
when :f5; protect { @dbg.pass_current_exception ; dbg.continue }
|
90
|
+
else return @parent_widget ? @parent_widget.keypress_ctrl(key) : false
|
91
|
+
end
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
def pre_dbg_run
|
96
|
+
@regs.pre_dbg_run
|
97
|
+
end
|
98
|
+
|
99
|
+
# TODO check_target always, incl when :stopped
|
100
|
+
def post_dbg_run
|
101
|
+
want_redraw = true
|
102
|
+
return if @idle_checking ||= nil # load only one bg proc
|
103
|
+
@idle_checking = true
|
104
|
+
Gui.idle_add {
|
105
|
+
@dbg.check_target
|
106
|
+
if @dbg.state == :running
|
107
|
+
redraw if want_redraw # redraw once if the target is running (less flicker with singlestep)
|
108
|
+
want_redraw = false
|
109
|
+
sleep 0.01
|
110
|
+
next true
|
111
|
+
end
|
112
|
+
@idle_checking = false
|
113
|
+
@dbg.dasm_invalidate
|
114
|
+
@mem.gui_update
|
115
|
+
@dbg.disassembler.sections.clear if @dbg.state == :dead
|
116
|
+
@dbg.disassembler.disassemble_fast(@dbg.pc)
|
117
|
+
@children.each { |c|
|
118
|
+
if wp = @watchpoint[c]
|
119
|
+
c.focus_addr @dbg.resolve_expr(wp), nil, true
|
120
|
+
end
|
121
|
+
}
|
122
|
+
redraw
|
123
|
+
false
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
def wrap_run
|
128
|
+
pre_dbg_run
|
129
|
+
yield
|
130
|
+
post_dbg_run
|
131
|
+
end
|
132
|
+
|
133
|
+
def dbg_continue(*a) wrap_run { @dbg.continue(*a) } end
|
134
|
+
def dbg_singlestep(*a) wrap_run { @dbg.singlestep(*a) } end
|
135
|
+
def dbg_stepover(*a) wrap_run { @dbg.stepover(*a) } end
|
136
|
+
def dbg_stepout(*a) wrap_run { @dbg.stepout(*a) } end
|
137
|
+
|
138
|
+
def redraw
|
139
|
+
super
|
140
|
+
@console.redraw
|
141
|
+
@regs.gui_update
|
142
|
+
@children.each { |c| c.redraw }
|
143
|
+
end
|
144
|
+
|
145
|
+
def gui_update
|
146
|
+
@console.redraw
|
147
|
+
@children.each { |c| c.gui_update }
|
148
|
+
end
|
149
|
+
|
150
|
+
def prompt_attach(caption='chose target')
|
151
|
+
l = nil
|
152
|
+
i = inputbox(caption) { |name|
|
153
|
+
i = nil ; l.destroy if l and not l.destroyed?
|
154
|
+
@dbg.attach(name)
|
155
|
+
}
|
156
|
+
|
157
|
+
# build process list in bg (exe name resolution takes a few seconds)
|
158
|
+
list = [['pid', 'name']]
|
159
|
+
list_pr = OS.current.list_processes
|
160
|
+
Gui.idle_add {
|
161
|
+
if pr = list_pr.shift
|
162
|
+
list << [pr.pid, pr.path] if pr.path
|
163
|
+
true
|
164
|
+
elsif i
|
165
|
+
me = ::Process.pid.to_s
|
166
|
+
l = listwindow('running processes', list,
|
167
|
+
:noshow => true,
|
168
|
+
:color_callback => lambda { |le| [:grey, :palegrey] if le[0] == me }
|
169
|
+
) { |e| i.text = e[0] }
|
170
|
+
l.x += l.width
|
171
|
+
l.show
|
172
|
+
false
|
173
|
+
end
|
174
|
+
} if not list_pr.empty?
|
175
|
+
end
|
176
|
+
|
177
|
+
def prompt_createprocess(caption='chose path')
|
178
|
+
openfile(caption) { |path|
|
179
|
+
path = '"' + path + '"' if @dbg.shortname == 'windbg' and path =~ /\s/
|
180
|
+
inputbox('target args?', :text => path) { |pa|
|
181
|
+
@dbg.create_process(pa)
|
182
|
+
}
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
def prompt_datawatch
|
187
|
+
inputbox('data watch', :text => @watchpoint[@mem].to_s) { |e|
|
188
|
+
case e
|
189
|
+
when '', 'nil', 'none', 'delete'
|
190
|
+
@watchpoint.delete @mem
|
191
|
+
else
|
192
|
+
@watchpoint[@mem] = @console.parse_expr(e)
|
193
|
+
end
|
194
|
+
}
|
195
|
+
end
|
196
|
+
|
197
|
+
def dragdropfile(f)
|
198
|
+
case f
|
199
|
+
when /\.(c|h|cpp)$/; @dbg.disassembler.parse_c_file(f)
|
200
|
+
when /\.map$/; @dbg.load_map(f)
|
201
|
+
when /\.rb$/; @dbg.load_plugin(f)
|
202
|
+
else messagebox("unsupported file extension #{f}")
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
# a widget that displays values of registers of a Debugger
|
209
|
+
# also controls the Debugger and commands slave windows (showing listing & memory)
|
210
|
+
class DbgRegWidget < DrawableWidget
|
211
|
+
attr_accessor :dbg
|
212
|
+
|
213
|
+
def initialize_widget(dbg, parent_widget)
|
214
|
+
@dbg = dbg
|
215
|
+
@parent_widget = parent_widget
|
216
|
+
|
217
|
+
@caret_x = @caret_reg = 0
|
218
|
+
@oldcaret_x = @oldcaret_reg = 42
|
219
|
+
|
220
|
+
@tid_stuff = {}
|
221
|
+
swapin_tid
|
222
|
+
|
223
|
+
@reg_pos = [] # list of x y w h vx of the reg drawing on widget, vx is x of value
|
224
|
+
|
225
|
+
@default_color_association = { :label => :black, :data => :blue, :write_pending => :darkred,
|
226
|
+
:changed => :darkgreen, :caret => :black, :background => :white,
|
227
|
+
:inactive => :palegrey }
|
228
|
+
end
|
229
|
+
|
230
|
+
def swapin_tid
|
231
|
+
stf = @tid_stuff[[@dbg.pid, @dbg.tid]] ||= {}
|
232
|
+
return if not @dbg.cpu
|
233
|
+
@write_pending = stf[:write_pending] ||= {} # addr -> newvalue (bytewise)
|
234
|
+
@registers = stf[:registers] ||= @dbg.register_list
|
235
|
+
@flags = stf[:flags] ||= @dbg.flag_list
|
236
|
+
@register_size = stf[:reg_sz] ||= @registers.inject(Hash.new(1)) { |h, r| h.update r => @dbg.register_size[r]/4 }
|
237
|
+
@reg_cache = stf[:reg_cache] ||= Hash.new(0)
|
238
|
+
@reg_cache_old = stf[:reg_cache_old] ||= {}
|
239
|
+
end
|
240
|
+
|
241
|
+
def initialize_visible
|
242
|
+
gui_update
|
243
|
+
end
|
244
|
+
|
245
|
+
def click(ex, ey)
|
246
|
+
if p = @reg_pos.find { |x, y, w, h, vx| x <= ex and x+w >= ex and y <= ey and y+h >= ey }
|
247
|
+
@caret_reg = @reg_pos.index(p)
|
248
|
+
@caret_x = ((ex - p[4]) / @font_width).to_i
|
249
|
+
rs = @register_size[@registers[@caret_reg]]
|
250
|
+
@caret_x = rs-1 if @caret_x > rs-1
|
251
|
+
@caret_x = 0 if @caret_x < 0
|
252
|
+
update_caret
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def rightclick(x, y)
|
257
|
+
doubleclick(x, y) # XXX
|
258
|
+
end
|
259
|
+
|
260
|
+
def doubleclick(x, y)
|
261
|
+
gui_update # XXX
|
262
|
+
end
|
263
|
+
|
264
|
+
def paint
|
265
|
+
curaddr = 0
|
266
|
+
x = 1
|
267
|
+
y = 0
|
268
|
+
|
269
|
+
w_w = width
|
270
|
+
|
271
|
+
render = lambda { |str, color|
|
272
|
+
draw_string_color(color, x, y, str)
|
273
|
+
x += str.length * @font_width
|
274
|
+
}
|
275
|
+
|
276
|
+
@reg_pos = []
|
277
|
+
running = (@dbg.state != :stopped)
|
278
|
+
regstrlen = @registers.map { |reg| reg.to_s.length + 1 }.max
|
279
|
+
@registers.each { |reg|
|
280
|
+
strlen = regstrlen + @register_size[reg]
|
281
|
+
if x + strlen*@font_width >= w_w
|
282
|
+
x = 1
|
283
|
+
y += @font_height
|
284
|
+
end
|
285
|
+
@reg_pos << [x, y, (strlen+1)*@font_width, @font_height, x+regstrlen*@font_width]
|
286
|
+
|
287
|
+
render["#{reg}=".ljust(regstrlen), :label]
|
288
|
+
v = @write_pending[reg] || @reg_cache[reg]
|
289
|
+
col = running ? :inactive : @write_pending[reg] ? :write_pending : @reg_cache_old.fetch(reg, v) != v ? :changed : :data
|
290
|
+
render["%0#{@register_size[reg]}x " % v, col]
|
291
|
+
x += @font_width # space
|
292
|
+
}
|
293
|
+
|
294
|
+
@flags.each { |reg|
|
295
|
+
if x + @font_width >= w_w # XXX nowrap flags ?
|
296
|
+
x = 1
|
297
|
+
y += @font_height
|
298
|
+
end
|
299
|
+
@reg_pos << [x, y, @font_width, @font_height, x]
|
300
|
+
|
301
|
+
v = @write_pending[reg] || @reg_cache[reg]
|
302
|
+
col = running ? :inactive : @write_pending[reg] ? :write_pending : @reg_cache_old.fetch(reg, v) != v ? :changed : :data
|
303
|
+
v = v == 0 ? reg.to_s.downcase : reg.to_s.upcase
|
304
|
+
render[v, col]
|
305
|
+
x += @font_width # space
|
306
|
+
}
|
307
|
+
|
308
|
+
if focus?
|
309
|
+
# draw caret
|
310
|
+
cx = @reg_pos[@caret_reg][4] + @caret_x*@font_width
|
311
|
+
cy = @reg_pos[@caret_reg][1]
|
312
|
+
draw_line_color(:caret, cx, cy, cx, cy+@font_height-1)
|
313
|
+
end
|
314
|
+
|
315
|
+
@oldcaret_x, @oldcaret_reg = @caret_x, @caret_reg
|
316
|
+
|
317
|
+
@parent_widget.resize_child(self, width, y+@font_height)
|
318
|
+
end
|
319
|
+
|
320
|
+
# keyboard binding
|
321
|
+
# basic navigation (arrows, pgup etc)
|
322
|
+
def keypress(key)
|
323
|
+
case key
|
324
|
+
when :left
|
325
|
+
if @caret_x > 0
|
326
|
+
@caret_x -= 1
|
327
|
+
update_caret
|
328
|
+
end
|
329
|
+
when :right
|
330
|
+
if @caret_x < @register_size[@registers[@caret_reg]]-1
|
331
|
+
@caret_x += 1
|
332
|
+
update_caret
|
333
|
+
end
|
334
|
+
when :up
|
335
|
+
if @caret_reg > 0
|
336
|
+
@caret_reg -= 1
|
337
|
+
else
|
338
|
+
@caret_reg = @registers.length+@flags.length-1
|
339
|
+
end
|
340
|
+
@caret_x = 0
|
341
|
+
update_caret
|
342
|
+
when :down
|
343
|
+
if @caret_reg < @registers.length+@flags.length-1
|
344
|
+
@caret_reg += 1
|
345
|
+
else
|
346
|
+
@caret_reg = 0
|
347
|
+
end
|
348
|
+
@caret_x = 0
|
349
|
+
update_caret
|
350
|
+
when :home
|
351
|
+
@caret_x = 0
|
352
|
+
update_caret
|
353
|
+
when :end
|
354
|
+
@caret_x = @register_size[@registers[@caret_reg]]-1
|
355
|
+
update_caret
|
356
|
+
when :tab
|
357
|
+
if @caret_reg < @registers.length+@flags.length-1
|
358
|
+
@caret_reg += 1
|
359
|
+
else
|
360
|
+
@caret_reg = 0
|
361
|
+
end
|
362
|
+
@caret_x = 0
|
363
|
+
update_caret
|
364
|
+
when :backspace
|
365
|
+
# TODO
|
366
|
+
when :enter
|
367
|
+
commit_writes
|
368
|
+
redraw
|
369
|
+
when :esc
|
370
|
+
@write_pending.clear
|
371
|
+
redraw
|
372
|
+
|
373
|
+
when ?\x20..?\x7e
|
374
|
+
if ?a.kind_of?(String)
|
375
|
+
v = key.ord
|
376
|
+
case key
|
377
|
+
when ?\x20; v = nil # keep current value
|
378
|
+
when ?0..?9; v -= ?0.ord
|
379
|
+
when ?a..?f; v -= ?a.ord-10
|
380
|
+
when ?A..?F; v -= ?A.ord-10
|
381
|
+
else return false
|
382
|
+
end
|
383
|
+
else
|
384
|
+
case v = key
|
385
|
+
when ?\x20; v = nil
|
386
|
+
when ?0..?9; v -= ?0
|
387
|
+
when ?a..?f; v -= ?a-10
|
388
|
+
when ?A..?F; v -= ?A-10
|
389
|
+
else return false
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
reg = @registers[@caret_reg] || @flags[@caret_reg-@registers.length]
|
394
|
+
rsz = @register_size[reg]
|
395
|
+
if v and rsz != 1
|
396
|
+
oo = 4*(rsz-@caret_x-1)
|
397
|
+
ov = @write_pending[reg] || @reg_cache[reg]
|
398
|
+
ov &= ~(0xf << oo)
|
399
|
+
ov |= v << oo
|
400
|
+
@write_pending[reg] = ov
|
401
|
+
elsif v and (v == 0 or v == 1) # TODO change z flag by typing 'z' or 'Z'
|
402
|
+
@write_pending[reg] = v
|
403
|
+
rsz = 1
|
404
|
+
end
|
405
|
+
|
406
|
+
if rsz == 1
|
407
|
+
@caret_reg += 1
|
408
|
+
@caret_reg = @registers.length if @caret_reg >= @registers.length + @flags.length
|
409
|
+
elsif @caret_x < rsz-1
|
410
|
+
@caret_x += 1
|
411
|
+
else
|
412
|
+
@caret_x = 0
|
413
|
+
end
|
414
|
+
redraw
|
415
|
+
else return false
|
416
|
+
end
|
417
|
+
true
|
418
|
+
end
|
419
|
+
|
420
|
+
def pre_dbg_run
|
421
|
+
@reg_cache_old.replace @reg_cache if @reg_cache
|
422
|
+
end
|
423
|
+
|
424
|
+
def commit_writes
|
425
|
+
@write_pending.each { |k, v|
|
426
|
+
if @registers.index(k)
|
427
|
+
@dbg.set_reg_value(k, v)
|
428
|
+
else
|
429
|
+
@dbg.set_flag_value(k, v)
|
430
|
+
end
|
431
|
+
@reg_cache[k] = v
|
432
|
+
}
|
433
|
+
@write_pending.clear
|
434
|
+
end
|
435
|
+
|
436
|
+
def gui_update
|
437
|
+
@reg_cache.replace @registers.inject({}) { |h, r| h.update r => @dbg.get_reg_value(r) }
|
438
|
+
@flags.each { |f| @reg_cache[f] = @dbg.get_flag_value(f) }
|
439
|
+
redraw
|
440
|
+
end
|
441
|
+
|
442
|
+
# hint that the caret moved
|
443
|
+
def update_caret
|
444
|
+
return if @oldcaret_x == @caret_x and @oldcaret_reg == @caret_reg
|
445
|
+
|
446
|
+
invalidate_caret(@oldcaret_x, 0, *@reg_pos[@oldcaret_reg].values_at(4, 1))
|
447
|
+
invalidate_caret(@caret_x, 0, *@reg_pos[@caret_reg].values_at(4, 1))
|
448
|
+
|
449
|
+
@oldcaret_x, @oldcaret_reg = @caret_x, @caret_reg
|
450
|
+
end
|
451
|
+
|
452
|
+
end
|
453
|
+
|
454
|
+
|
455
|
+
# a widget that displays logs of the debugger, and a cli interface to the dbg
|
456
|
+
class DbgConsoleWidget < DrawableWidget
|
457
|
+
attr_accessor :dbg, :cmd_history, :log, :statusline, :commands, :cmd_help
|
458
|
+
|
459
|
+
def initialize_widget(dbg, parent_widget)
|
460
|
+
@dbg = dbg
|
461
|
+
@parent_widget = parent_widget
|
462
|
+
@dbg.gui = self
|
463
|
+
|
464
|
+
@log = []
|
465
|
+
@log_length = 4000
|
466
|
+
@log_offset = 0
|
467
|
+
@curline = ''
|
468
|
+
@statusline = 'type \'help\' for help'
|
469
|
+
@cmd_history = ['']
|
470
|
+
@cmd_history_length = 200 # number of past commands to remember
|
471
|
+
@cmd_histptr = nil
|
472
|
+
|
473
|
+
@dbg.set_log_proc { |l| add_log l }
|
474
|
+
|
475
|
+
@default_color_association = { :log => :palegrey, :curline => :white, :caret => :yellow,
|
476
|
+
:background => :black, :status => :black, :status_bg => '088' }
|
477
|
+
|
478
|
+
init_commands
|
479
|
+
end
|
480
|
+
|
481
|
+
def initialize_visible
|
482
|
+
grab_focus
|
483
|
+
gui_update
|
484
|
+
end
|
485
|
+
|
486
|
+
def swapin_tid
|
487
|
+
@parent_widget.swapin_tid
|
488
|
+
end
|
489
|
+
|
490
|
+
def swapin_pid
|
491
|
+
@parent_widget.swapin_pid
|
492
|
+
end
|
493
|
+
|
494
|
+
def click(x, y)
|
495
|
+
@caret_x = (x-1).to_i / @font_width - 1
|
496
|
+
@caret_x = [[@caret_x, 0].max, @curline.length].min
|
497
|
+
update_caret
|
498
|
+
end
|
499
|
+
|
500
|
+
def doubleclick(x, y)
|
501
|
+
# TODO real copy/paste
|
502
|
+
# for now, copy the line under the dblclick
|
503
|
+
y -= height % @font_height
|
504
|
+
y = y.to_i / @font_height
|
505
|
+
hc = height / @font_height
|
506
|
+
if y == hc - 1
|
507
|
+
txt = @statusline
|
508
|
+
elsif y == hc - 2
|
509
|
+
txt = @curline
|
510
|
+
else
|
511
|
+
txt = @log.reverse[@log_offset + hc - y - 3].to_s
|
512
|
+
end
|
513
|
+
clipboard_copy(txt)
|
514
|
+
end
|
515
|
+
|
516
|
+
def mouse_wheel(dir, x, y)
|
517
|
+
case dir
|
518
|
+
when :up; @log_offset += 3
|
519
|
+
when :down; @log_offset -= 3
|
520
|
+
end
|
521
|
+
redraw
|
522
|
+
end
|
523
|
+
|
524
|
+
def paint
|
525
|
+
y = height
|
526
|
+
|
527
|
+
render = lambda { |str, color|
|
528
|
+
draw_string_color(color, 1, y, str)
|
529
|
+
y -= @font_height
|
530
|
+
}
|
531
|
+
|
532
|
+
w_w = width
|
533
|
+
|
534
|
+
y -= @font_height
|
535
|
+
draw_rectangle_color(:status_bg, 0, y, w_w, @font_height)
|
536
|
+
str = "#{@dbg.pid}:#{@dbg.tid} #{@dbg.state} #{@dbg.info}"
|
537
|
+
draw_string_color(:status, w_w-str.length*@font_width-1, y, str)
|
538
|
+
draw_string_color(:status, 1+@font_width, y, @statusline)
|
539
|
+
y -= @font_height
|
540
|
+
|
541
|
+
w_w_c = w_w/@font_width
|
542
|
+
@caret_y = y
|
543
|
+
if @caret_x < w_w_c-1
|
544
|
+
render[':' + @curline, :curline]
|
545
|
+
else
|
546
|
+
render['~' + @curline[@caret_x-w_w_c+2, w_w_c], :curline]
|
547
|
+
end
|
548
|
+
|
549
|
+
l_nr = -1
|
550
|
+
lastline = nil
|
551
|
+
@log_offset = 0 if @log_offset < 0
|
552
|
+
@log.reverse.each { |l|
|
553
|
+
l.scan(/.{1,#{w_w/@font_width}}/).reverse_each { |l_|
|
554
|
+
lastline = l_
|
555
|
+
l_nr += 1
|
556
|
+
next if l_nr < @log_offset
|
557
|
+
render[l_, :log]
|
558
|
+
}
|
559
|
+
break if y < 0
|
560
|
+
}
|
561
|
+
if lastline and l_nr < @log_offset
|
562
|
+
render[lastline, :log]
|
563
|
+
@log_offset = l_nr-1
|
564
|
+
end
|
565
|
+
|
566
|
+
if focus?
|
567
|
+
cx = [@caret_x+1, w_w_c-1].min*@font_width+1
|
568
|
+
cy = @caret_y
|
569
|
+
draw_line_color(:caret, cx, cy, cx, cy+@font_height-1)
|
570
|
+
end
|
571
|
+
|
572
|
+
@oldcaret_x = @caret_x
|
573
|
+
end
|
574
|
+
|
575
|
+
def keypress(key)
|
576
|
+
case key
|
577
|
+
when :left
|
578
|
+
if @caret_x > 0
|
579
|
+
@caret_x -= 1
|
580
|
+
update_caret
|
581
|
+
end
|
582
|
+
when :right
|
583
|
+
if @caret_x < @curline.length
|
584
|
+
@caret_x += 1
|
585
|
+
update_caret
|
586
|
+
end
|
587
|
+
when :up
|
588
|
+
if not @cmd_histptr
|
589
|
+
if @curline != ''
|
590
|
+
@cmd_history << @curline
|
591
|
+
@cmd_histptr = 2
|
592
|
+
else
|
593
|
+
@cmd_histptr = 1
|
594
|
+
end
|
595
|
+
else
|
596
|
+
@cmd_histptr += 1
|
597
|
+
@cmd_histptr = 1 if @cmd_histptr > @cmd_history.length
|
598
|
+
end
|
599
|
+
@curline = @cmd_history[-@cmd_histptr].dup
|
600
|
+
@caret_x = @curline.length
|
601
|
+
update_status_cmd
|
602
|
+
redraw
|
603
|
+
|
604
|
+
when :down
|
605
|
+
if not @cmd_histptr
|
606
|
+
@cmd_history << @curline if @curline != ''
|
607
|
+
@cmd_histptr = @cmd_history.length
|
608
|
+
else
|
609
|
+
@cmd_histptr -= 1
|
610
|
+
@cmd_histptr = @cmd_history.length if @cmd_histptr < 1
|
611
|
+
end
|
612
|
+
@curline = @cmd_history[-@cmd_histptr].dup
|
613
|
+
@caret_x = @curline.length
|
614
|
+
update_status_cmd
|
615
|
+
redraw
|
616
|
+
|
617
|
+
when :home
|
618
|
+
@caret_x = 0
|
619
|
+
update_caret
|
620
|
+
when :end
|
621
|
+
@caret_x = @curline.length
|
622
|
+
update_caret
|
623
|
+
|
624
|
+
when :pgup
|
625
|
+
@log_offset += height/@font_height - 3
|
626
|
+
redraw
|
627
|
+
when :pgdown
|
628
|
+
@log_offset -= height/@font_height - 3
|
629
|
+
redraw
|
630
|
+
|
631
|
+
when :tab
|
632
|
+
# autocomplete
|
633
|
+
if @caret_x > 0 and not @curline[0, @caret_x].index(?\ ) and st = @curline[0, @caret_x] and not @commands[st]
|
634
|
+
keys = @commands.keys.find_all { |k| k[0, st.length] == st }
|
635
|
+
while st.length < keys.first.to_s.length and keys.all? { |k| k[0, st.length+1] == keys.first[0, st.length+1] }
|
636
|
+
st << keys.first[st.length]
|
637
|
+
@curline[@caret_x, 0] = st[-1, 1]
|
638
|
+
@caret_x += 1
|
639
|
+
end
|
640
|
+
update_status_cmd
|
641
|
+
redraw
|
642
|
+
end
|
643
|
+
|
644
|
+
when :enter
|
645
|
+
@cmd_histptr = nil
|
646
|
+
handle_command
|
647
|
+
update_status_cmd
|
648
|
+
when :esc
|
649
|
+
when :delete
|
650
|
+
if @caret_x < @curline.length
|
651
|
+
@curline[@caret_x, 1] = ''
|
652
|
+
update_status_cmd
|
653
|
+
redraw
|
654
|
+
end
|
655
|
+
when :backspace
|
656
|
+
if @caret_x > 0
|
657
|
+
@caret_x -= 1
|
658
|
+
@curline[@caret_x, 1] = ''
|
659
|
+
update_status_cmd
|
660
|
+
redraw
|
661
|
+
end
|
662
|
+
|
663
|
+
when :insert
|
664
|
+
if keyboard_state(:shift)
|
665
|
+
txt = clipboard_paste.to_s
|
666
|
+
@curline[@caret_x, 0] = txt
|
667
|
+
@caret_x += txt.length
|
668
|
+
update_status_cmd
|
669
|
+
redraw
|
670
|
+
end
|
671
|
+
|
672
|
+
when Symbol; return false # avoid :shift cannot coerce to Int warning
|
673
|
+
when ?\x20..?\x7e
|
674
|
+
@curline[@caret_x, 0] = key.chr
|
675
|
+
@caret_x += 1
|
676
|
+
update_status_cmd
|
677
|
+
redraw
|
678
|
+
|
679
|
+
else return false
|
680
|
+
end
|
681
|
+
true
|
682
|
+
end
|
683
|
+
|
684
|
+
def keypress_ctrl(key)
|
685
|
+
case key
|
686
|
+
when ?v
|
687
|
+
txt = clipboard_paste.to_s
|
688
|
+
@curline[@caret_x, 0] = txt
|
689
|
+
@caret_x += txt.length
|
690
|
+
update_status_cmd
|
691
|
+
redraw
|
692
|
+
else return false
|
693
|
+
end
|
694
|
+
true
|
695
|
+
end
|
696
|
+
|
697
|
+
def update_status_cmd
|
698
|
+
st = @curline.split.first
|
699
|
+
if @commands[st]
|
700
|
+
@statusline = "#{st}: #{@cmd_help[st]}"
|
701
|
+
else
|
702
|
+
keys = @commands.keys.find_all { |k| k[0, st.length] == st } if st
|
703
|
+
if keys and not keys.empty?
|
704
|
+
@statusline = keys.sort.join(' ')
|
705
|
+
else
|
706
|
+
@statusline = 'type \'help\' for help'
|
707
|
+
end
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
def new_command(*cmd, &b)
|
712
|
+
hlp = cmd.pop if cmd.last.include? ' '
|
713
|
+
cmd.each { |c|
|
714
|
+
@cmd_help[c] = hlp || 'nodoc'
|
715
|
+
@commands[c] = lambda { |*a| protect { b.call(*a) } }
|
716
|
+
}
|
717
|
+
end
|
718
|
+
|
719
|
+
# arg str -> expr value, with special codeptr/dataptr = code/data.curaddr
|
720
|
+
def parse_expr(arg)
|
721
|
+
parse_expr!(arg.dup)
|
722
|
+
end
|
723
|
+
|
724
|
+
def parse_expr!(arg)
|
725
|
+
@dbg.parse_expr!(arg) { |e|
|
726
|
+
case e.downcase
|
727
|
+
when 'code_addr', 'codeptr'; @parent_widget.code.curaddr
|
728
|
+
when 'data_addr', 'dataptr'; @parent_widget.mem.curaddr
|
729
|
+
end
|
730
|
+
}
|
731
|
+
end
|
732
|
+
|
733
|
+
def solve_expr(arg)
|
734
|
+
return arg if arg.kind_of? Integer
|
735
|
+
solve_expr!(arg.dup)
|
736
|
+
end
|
737
|
+
|
738
|
+
def solve_expr!(arg)
|
739
|
+
return if not e = parse_expr!(arg)
|
740
|
+
@dbg.resolve_expr(e)
|
741
|
+
end
|
742
|
+
|
743
|
+
# update the data window, or dump data to console if len given
|
744
|
+
def cmd_dd(addr, dlen=nil, len=nil)
|
745
|
+
if addr.kind_of? String
|
746
|
+
s = addr.strip
|
747
|
+
addr = solve_expr!(s)
|
748
|
+
if not s.empty?
|
749
|
+
s = s[1..-1] if s[0] == ?,
|
750
|
+
len ||= solve_expr(s)
|
751
|
+
end
|
752
|
+
end
|
753
|
+
|
754
|
+
if len
|
755
|
+
while len > 0
|
756
|
+
data = @dbg.memory[addr, [len, 16].min]
|
757
|
+
le = (@dbg.cpu.endianness == :little)
|
758
|
+
data = '' if @dbg.memory.page_invalid?(addr)
|
759
|
+
case dlen
|
760
|
+
when nil; add_log "#{Expression[addr]} #{data.unpack('C*').map { |c| '%02X' % c }.join(' ').ljust(2*16+15)} #{data.tr("\0-\x1f\x7f-\xff", '.')}"
|
761
|
+
when 1; add_log "#{Expression[addr]} #{data.unpack('C*').map { |c| '%02X' % c }.join(' ')}"
|
762
|
+
when 2; add_log "#{Expression[addr]} #{data.unpack(le ? 'v*' : 'n*').map { |c| '%04X' % c }.join(' ')}"
|
763
|
+
when 4; add_log "#{Expression[addr]} #{data.unpack(le ? 'V*' : 'N*').map { |c| '%08X' % c }.join(' ')}"
|
764
|
+
when 8; add_log "#{Expression[addr]} #{data.unpack('Q*').map { |c| '%016X' % c }.join(' ')}"
|
765
|
+
end
|
766
|
+
addr += 16
|
767
|
+
len -= 16
|
768
|
+
end
|
769
|
+
else
|
770
|
+
@parent_widget.mem.view(:hex).data_size = dlen if dlen
|
771
|
+
@parent_widget.mem.focus_addr(solve_expr(addr)) if addr and addr != ''
|
772
|
+
@parent_widget.mem.gui_update
|
773
|
+
end
|
774
|
+
end
|
775
|
+
|
776
|
+
def init_commands
|
777
|
+
@commands = {}
|
778
|
+
@cmd_help = {}
|
779
|
+
p = @parent_widget
|
780
|
+
new_command('help') { add_log @commands.keys.sort.join(' ') } # TODO help <subject>
|
781
|
+
new_command('d', 'focus data window on an address') { |arg| cmd_dd(arg) }
|
782
|
+
new_command('db', 'dump/focus bytes in data window') { |arg| cmd_dd(arg, 1) }
|
783
|
+
new_command('dw', 'dump/focus words in data window') { |arg| cmd_dd(arg, 2) }
|
784
|
+
new_command('dd', 'dump/focus dwords in data window') { |arg| cmd_dd(arg, 4) }
|
785
|
+
new_command('dq', 'dump/focus qwords in data window') { |arg| cmd_dd(arg, 8) }
|
786
|
+
new_command('u', 'focus code window on an address') { |arg| p.code.focus_addr(solve_expr(arg)) }
|
787
|
+
new_command('.', 'focus code window on current address') { p.code.focus_addr(solve_expr(@dbg.register_pc.to_s)) }
|
788
|
+
new_command('wc', 'set code window height') { |arg|
|
789
|
+
if arg == ''
|
790
|
+
p.code.curview.grab_focus
|
791
|
+
else
|
792
|
+
p.resize_child(p.code, width, arg.to_i*@font_height)
|
793
|
+
end
|
794
|
+
}
|
795
|
+
new_command('wd', 'set data window height') { |arg|
|
796
|
+
if arg == ''
|
797
|
+
p.mem.curview.grab_focus
|
798
|
+
else
|
799
|
+
p.resize_child(p.mem, width, arg.to_i*@font_height)
|
800
|
+
end
|
801
|
+
}
|
802
|
+
new_command('wp', 'set console window height') { |arg|
|
803
|
+
if arg == ''
|
804
|
+
grab_focus
|
805
|
+
else
|
806
|
+
p.resize_child(self, width, arg.to_i*@font_height)
|
807
|
+
end
|
808
|
+
}
|
809
|
+
new_command('width', 'set window width (chars)') { |arg|
|
810
|
+
if a = solve_expr(arg); p.win.width = a*@font_width
|
811
|
+
else add_log "width #{p.win.width/@font_width}"
|
812
|
+
end
|
813
|
+
}
|
814
|
+
new_command('height', 'set window height (chars)') { |arg|
|
815
|
+
if a = solve_expr(arg); p.win.height = a*@font_height
|
816
|
+
else add_log "height #{p.win.height/@font_height}"
|
817
|
+
end
|
818
|
+
}
|
819
|
+
new_command('continue', 'run', 'let the target run until something occurs') { p.dbg_continue }
|
820
|
+
new_command('stepinto', 'singlestep', 'run a single instruction of the target') { p.dbg_singlestep }
|
821
|
+
new_command('stepover', 'run a single instruction of the target, do not enter into subfunctions') { p.dbg_stepover }
|
822
|
+
new_command('stepout', 'stepover until getting out of the current function') { p.dbg_stepout }
|
823
|
+
new_command('bpx', 'set a breakpoint') { |arg|
|
824
|
+
arg =~ /^(.*?)( once)?(?: if (.*?))?(?: do (.*?))?(?: if (.*?))?$/i
|
825
|
+
e, o, c, a = $1, $2, ($3 || $5), $4
|
826
|
+
o = o ? true : false
|
827
|
+
cd = parse_expr(c) if c
|
828
|
+
cb = lambda { a.split(';').each { |aaa| run_command(aaa) } } if a
|
829
|
+
@dbg.bpx(solve_expr(e), o, cd, &cb)
|
830
|
+
}
|
831
|
+
new_command('hwbp', 'set a hardware breakpoint') { |arg|
|
832
|
+
arg =~ /^(.*?)(?: if (.*?))?(?: do (.*?))?(?: if (.*?))?$/i
|
833
|
+
e, c, a = $1, ($2 || $4), $3
|
834
|
+
cd = parse_expr(c) if c
|
835
|
+
cb = lambda { a.split(';').each { |aaa| run_command(aaa) } } if a
|
836
|
+
@dbg.hwbp(solve_expr(e), :x, 1, false, cd, &cb)
|
837
|
+
}
|
838
|
+
new_command('bpm', 'set a hardware memory breakpoint: bpm r 0x4800ff 16') { |arg|
|
839
|
+
arg =~ /^(.*?)(?: if (.*?))?(?: do (.*?))?(?: if (.*?))?$/i
|
840
|
+
e, c, a = $1, ($2 || $4), $3
|
841
|
+
cd = parse_expr(c) if c
|
842
|
+
cb = lambda { a.split(';').each { |aaa| run_command(aaa) } } if a
|
843
|
+
raise 'bad syntax: bpm r|w|x addr [len]' unless e =~ /^([rwx]) (.*)/i
|
844
|
+
mode = $1.downcase.to_sym
|
845
|
+
e = $2
|
846
|
+
exp = solve_expr!(e)
|
847
|
+
len = solve_expr(e) if e != ''
|
848
|
+
len ||= 1
|
849
|
+
@dbg.hwbp(exp, mode, len, false, cd, &cb)
|
850
|
+
}
|
851
|
+
new_command('g', 'wait until target reaches the specified address') { |arg|
|
852
|
+
arg =~ /^(.*?)(?: if (.*?))?(?: do (.*?))?(?: if (.*?))?$/i
|
853
|
+
e, c, a = $1, ($2 || $4), $3
|
854
|
+
cd = parse_expr(c) if c
|
855
|
+
cb = lambda { a.split(';').each { |aaa| run_command(aaa) } } if a
|
856
|
+
@dbg.bpx(solve_expr(e), true, cd, &cb) if arg
|
857
|
+
p.dbg_continue
|
858
|
+
}
|
859
|
+
new_command('refresh', 'redraw', 'update', 'update the target memory/register cache') {
|
860
|
+
@dbg.invalidate
|
861
|
+
@dbg.dasm_invalidate
|
862
|
+
p.gui_update
|
863
|
+
}
|
864
|
+
new_command('bl', 'list breakpoints') {
|
865
|
+
@bl = []
|
866
|
+
@dbg.all_breakpoints.each { |b|
|
867
|
+
add_log "#{@bl.length} #{@dbg.addrname!(b.address)} #{b.type} #{b.state}#{" if #{b.condition}" if b.condition}"
|
868
|
+
@bl << b
|
869
|
+
}
|
870
|
+
}
|
871
|
+
new_command('bc', 'clear breakpoints') { |arg|
|
872
|
+
@bl ||= @dbg.all_breakpoints
|
873
|
+
if arg == '*'
|
874
|
+
@bl.each { |b| @dbg.del_bp(b) }
|
875
|
+
else
|
876
|
+
next if not i = solve_expr(arg)
|
877
|
+
if b = @bl[i]
|
878
|
+
@dbg.del_bp(b)
|
879
|
+
end
|
880
|
+
end
|
881
|
+
}
|
882
|
+
new_command('break', 'interrupt a running target') { |arg| @dbg.break ; p.post_dbg_run }
|
883
|
+
new_command('kill', 'kill the target') { |arg| @dbg.kill(arg) ; p.post_dbg_run }
|
884
|
+
new_command('detach', 'detach from the target') { @dbg.detach ; p.post_dbg_run }
|
885
|
+
new_command('r', 'read/write the content of a register') { |arg|
|
886
|
+
reg, val = arg.split(/\s+|\s*=\s*/, 2)
|
887
|
+
if reg == 'fl'
|
888
|
+
@dbg.toggle_flag(val.to_sym)
|
889
|
+
elsif not reg
|
890
|
+
@dbg.register_list.each { |r|
|
891
|
+
add_log "#{r} = #{Expression[@dbg.get_reg_value(r)]}"
|
892
|
+
}
|
893
|
+
elsif not val
|
894
|
+
add_log "#{reg} = #{Expression[@dbg.get_reg_value(reg.to_sym)]}"
|
895
|
+
else
|
896
|
+
@dbg.set_reg_value(reg.to_sym, solve_expr(val))
|
897
|
+
end
|
898
|
+
p.regs.gui_update
|
899
|
+
}
|
900
|
+
new_command('ma', 'memory_ascii', 'write memory (ascii) - ma <addr> foo bar') { |arg|
|
901
|
+
next if not addr = solve_expr!(arg)
|
902
|
+
data = arg.strip
|
903
|
+
@dbg.memory[addr, data.length] = data
|
904
|
+
@dbg.invalidate
|
905
|
+
@dbg.dasm_invalidate
|
906
|
+
p.gui_update
|
907
|
+
}
|
908
|
+
new_command('mx', 'memory_hex', 'write memory (hex) - mx <addr> 0011223344') { |arg|
|
909
|
+
next if not addr = solve_expr!(arg)
|
910
|
+
data = [arg.delete(' ')].pack('H*')
|
911
|
+
@dbg.memory[addr, data.length] = data
|
912
|
+
@dbg.invalidate
|
913
|
+
@dbg.dasm_invalidate
|
914
|
+
p.gui_update
|
915
|
+
}
|
916
|
+
new_command('?', 'display a value') { |arg|
|
917
|
+
next if not v = solve_expr(arg)
|
918
|
+
add_log "#{v} 0x#{v.to_s(16)} #{[v & 0xffff_ffff].pack('L').inspect} #{@dbg.addrname!(v)}"
|
919
|
+
}
|
920
|
+
new_command('exit', 'quit', 'quit the debugger interface') { p.win.destroy }
|
921
|
+
new_command('ruby', 'execute arbitrary ruby code') { |arg|
|
922
|
+
case ret = eval(arg)
|
923
|
+
when nil, true, false, Symbol; add_log ret.inspect
|
924
|
+
when String; add_log ret[0, 64].inspect
|
925
|
+
when Integer, Expression; add_log Expression[ret].to_s
|
926
|
+
else add_log "#<#{ret.class}>"
|
927
|
+
end
|
928
|
+
}
|
929
|
+
new_command('loadsyms', 'load symbols from a mapped module') { |arg|
|
930
|
+
if not arg.empty? and arg = (solve_expr(arg) rescue arg)
|
931
|
+
@dbg.loadsyms(arg)
|
932
|
+
else
|
933
|
+
@dbg.loadallsyms { |a|
|
934
|
+
@statusline = "loading symbols from #{Expression[a]}"
|
935
|
+
redraw
|
936
|
+
Gui.main_iter
|
937
|
+
}
|
938
|
+
end
|
939
|
+
p.gui_update
|
940
|
+
}
|
941
|
+
new_command('scansyms', 'scan target memory for loaded modules') {
|
942
|
+
if defined? @scan_addr and @scan_addr
|
943
|
+
add_log 'scanning @%08x' % @scan_addr
|
944
|
+
next
|
945
|
+
end
|
946
|
+
@scan_addr = 0
|
947
|
+
Gui.idle_add {
|
948
|
+
if @scan_addr <= 0xffff_f000 # cpu.size?
|
949
|
+
protect { @dbg.loadsyms(@scan_addr) }
|
950
|
+
@scan_addr += 0x1000
|
951
|
+
true
|
952
|
+
else
|
953
|
+
add_log 'scansyms finished'
|
954
|
+
@scan_addr = nil
|
955
|
+
p.gui_update
|
956
|
+
nil
|
957
|
+
end
|
958
|
+
}
|
959
|
+
}
|
960
|
+
new_command('symbol', 'display information on symbols') { |arg|
|
961
|
+
arg = arg.to_s.downcase
|
962
|
+
@dbg.symbols.map { |k, v| an = @dbg.addrname(k) ; [k, an] if an.downcase.include? arg }.compact.sort_by { |k, v| v.downcase }.each { |k, v|
|
963
|
+
add_log "#{Expression[k]} #{@dbg.addrname(k)}"
|
964
|
+
}
|
965
|
+
}
|
966
|
+
new_command('maps', 'show file mappings from parsed modules') { |arg|
|
967
|
+
want = arg.to_s.downcase
|
968
|
+
want = nil if want == ''
|
969
|
+
@dbg.modulemap.map { |n, (a_b, a_e)|
|
970
|
+
[a_b, "#{Expression[a_b]}-#{Expression[a_e]} #{n}"] if not want or n.downcase.include?(want)
|
971
|
+
}.compact.sort.each { |s1, s2|
|
972
|
+
add_log s2
|
973
|
+
}
|
974
|
+
}
|
975
|
+
new_command('rawmaps', 'show OS file mappings') { |arg|
|
976
|
+
# XXX listwindow
|
977
|
+
@dbg.mappings.sort.each { |a, l, *i|
|
978
|
+
foo = i*' '
|
979
|
+
next if arg.to_s != '' and foo !~ /#{arg}/i
|
980
|
+
add_log "%08x %06x %s" % [a, l, i*' ']
|
981
|
+
}
|
982
|
+
}
|
983
|
+
new_command('add_symbol', 'add a symbol name') { |arg|
|
984
|
+
name, val = arg.to_s.split(/\s+/, 2)
|
985
|
+
val = solve_expr(val)
|
986
|
+
if val.kind_of? Integer
|
987
|
+
@dbg.symbols[val] = name
|
988
|
+
@dbg.disassembler.set_label_at(val, name)
|
989
|
+
p.gui_update
|
990
|
+
end
|
991
|
+
}
|
992
|
+
new_command('bt', 'backtrace', 'stacktrace', 'bt [limit] - show a stack trace from current pc') { |arg|
|
993
|
+
arg = solve_expr(arg) if arg
|
994
|
+
arg = 500 if not arg.kind_of? ::Integer
|
995
|
+
@dbg.stacktrace(arg) { |a, s| add_log "#{Expression[a]} #{s}" }
|
996
|
+
}
|
997
|
+
new_command('dasm', 'disassemble_fast', 'disassembles from an address') { |arg|
|
998
|
+
addr = solve_expr(arg)
|
999
|
+
dasm = @dbg.disassembler
|
1000
|
+
dasm.disassemble_fast(addr)
|
1001
|
+
dasm.function_blocks(addr).keys.sort.each { |a|
|
1002
|
+
next if not di = dasm.di_at(a)
|
1003
|
+
dasm.dump_block(di.block) { |l| add_log l }
|
1004
|
+
}
|
1005
|
+
p.gui_update
|
1006
|
+
}
|
1007
|
+
new_command('save_hist', 'save the command buffer to a file') { |arg|
|
1008
|
+
File.open(arg, 'w') { |fd| fd.puts @log }
|
1009
|
+
}
|
1010
|
+
|
1011
|
+
new_command('watch', 'follow an expression in the data view (none to delete)') { |arg|
|
1012
|
+
if not arg
|
1013
|
+
add_log p.watchpoint[p.mem].to_s
|
1014
|
+
elsif arg == 'nil' or arg == 'none' or arg == 'delete'
|
1015
|
+
p.watchpoint.delete p.mem
|
1016
|
+
else
|
1017
|
+
p.watchpoint[p.mem] = parse_expr(arg)
|
1018
|
+
end
|
1019
|
+
}
|
1020
|
+
|
1021
|
+
new_command('list_pid', 'list pids currently debugged') { |arg|
|
1022
|
+
add_log @dbg.list_debug_pids.sort.map { |pp| pp == @dbg.pid ? "*#{pp}" : pp }.join(' ')
|
1023
|
+
}
|
1024
|
+
new_command('list_tid', 'list tids currently debugged') { |arg|
|
1025
|
+
add_log @dbg.list_debug_tids.sort.map { |tt| tt == @dbg.tid ? "*#{tt}" : tt }.join(' ')
|
1026
|
+
}
|
1027
|
+
|
1028
|
+
new_command('list_processes', 'list processes available for debugging') { |arg|
|
1029
|
+
@dbg.list_processes.each { |pp|
|
1030
|
+
add_log "#{pp.pid} #{pp.path}"
|
1031
|
+
}
|
1032
|
+
}
|
1033
|
+
new_command('list_threads', 'list thread ids of the current process') { |arg|
|
1034
|
+
@dbg.list_threads.each { |t|
|
1035
|
+
stf = { :state => @dbg.state, :info => @dbg.info } if t == @dbg.tid
|
1036
|
+
stf ||= @dbg.tid_stuff[t]
|
1037
|
+
stf ||= {}
|
1038
|
+
add_log "#{t} #{stf[:state]} #{stf[:info]}"
|
1039
|
+
}
|
1040
|
+
}
|
1041
|
+
|
1042
|
+
new_command('pid', 'select a pid') { |arg|
|
1043
|
+
if pid = solve_expr(arg)
|
1044
|
+
@dbg.pid = pid
|
1045
|
+
else
|
1046
|
+
add_log "pid #{@dbg.pid}"
|
1047
|
+
end
|
1048
|
+
}
|
1049
|
+
new_command('tid', 'select a tid') { |arg|
|
1050
|
+
if tid = solve_expr(arg)
|
1051
|
+
@dbg.tid = tid
|
1052
|
+
else
|
1053
|
+
add_log "tid #{@dbg.tid} #{@dbg.state} #{@dbg.info}"
|
1054
|
+
end
|
1055
|
+
}
|
1056
|
+
|
1057
|
+
new_command('exception_pass', 'pass the exception unhandled to the target on next continue') {
|
1058
|
+
@dbg.pass_current_exception
|
1059
|
+
}
|
1060
|
+
new_command('exception_handle', 'handle the exception, hide it from the target on next continue') {
|
1061
|
+
@dbg.pass_current_exception false
|
1062
|
+
}
|
1063
|
+
|
1064
|
+
new_command('exception_pass_all', 'ignore all target exceptions') {
|
1065
|
+
@dbg.pass_all_exceptions = true
|
1066
|
+
}
|
1067
|
+
new_command('exception_handle_all', 'break on target exceptions') {
|
1068
|
+
@dbg.pass_all_exceptions = false
|
1069
|
+
}
|
1070
|
+
|
1071
|
+
new_command('thread_events_break', 'break on thread creation/termination') {
|
1072
|
+
@dbg.ignore_newthread = false
|
1073
|
+
@dbg.ignore_endthread = false
|
1074
|
+
}
|
1075
|
+
new_command('thread_event_ignore', 'ignore thread creation/termination') {
|
1076
|
+
@dbg.ignore_newthread = true
|
1077
|
+
@dbg.ignore_endthread = true
|
1078
|
+
}
|
1079
|
+
|
1080
|
+
new_command('trace_children', 'trace children of debuggee (0|1)') { |arg|
|
1081
|
+
arg = case arg.to_s.strip.downcase
|
1082
|
+
when '0', 'no', 'false'; false
|
1083
|
+
else true
|
1084
|
+
end
|
1085
|
+
add_log "trace children #{arg ? 'active' : 'inactive'}"
|
1086
|
+
# update the flag for future debugee
|
1087
|
+
@dbg.trace_children = arg
|
1088
|
+
# change current debugee setting if supported
|
1089
|
+
@dbg.do_trace_children if @dbg.respond_to?(:do_trace_children)
|
1090
|
+
}
|
1091
|
+
|
1092
|
+
new_command('attach', 'attach to a running process') { |arg|
|
1093
|
+
if pr = @dbg.list_processes.find { |pp| pp.path.to_s.downcase.include?(arg.downcase) }
|
1094
|
+
pid = pr.pid
|
1095
|
+
else
|
1096
|
+
pid = solve_expr(arg)
|
1097
|
+
end
|
1098
|
+
@dbg.attach(pid)
|
1099
|
+
}
|
1100
|
+
new_command('create_process', 'create a new process and debug it') { |arg|
|
1101
|
+
@dbg.create_process(arg)
|
1102
|
+
}
|
1103
|
+
|
1104
|
+
@dbg.ui_command_setup(self) if @dbg.respond_to? :ui_command_setup
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
def wrap_run(&b) @parent_widget.wrap_run(&b) end
|
1108
|
+
def keyboard_callback; @parent_widget.keyboard_callback end
|
1109
|
+
def keyboard_callback_ctrl; @parent_widget.keyboard_callback_ctrl end
|
1110
|
+
|
1111
|
+
def handle_command
|
1112
|
+
add_log(":#@curline")
|
1113
|
+
return if @curline == ''
|
1114
|
+
@cmd_history << @curline
|
1115
|
+
@cmd_history.shift if @cmd_history.length > @cmd_history_length
|
1116
|
+
@log_offset = 0
|
1117
|
+
cmd = @curline
|
1118
|
+
@curline = ''
|
1119
|
+
@caret_x = 0
|
1120
|
+
|
1121
|
+
run_command(cmd)
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
def run_command(cmd)
|
1125
|
+
cn = cmd.split.first
|
1126
|
+
if not @commands[cn]
|
1127
|
+
a = @commands.keys.find_all { |k| k[0, cn.length] == cn }
|
1128
|
+
cn = a.first if a.length == 1
|
1129
|
+
end
|
1130
|
+
if pc = @commands[cn]
|
1131
|
+
pc[cmd.split(/\s+/, 2)[1].to_s]
|
1132
|
+
else
|
1133
|
+
add_log 'unknown command'
|
1134
|
+
end
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
def add_log(l)
|
1138
|
+
@log << l.to_s
|
1139
|
+
@log.shift if log.length > @log_length
|
1140
|
+
redraw
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
def gui_update
|
1144
|
+
redraw
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
# hint that the caret moved
|
1148
|
+
def update_caret
|
1149
|
+
return if @oldcaret_x == @caret_x
|
1150
|
+
w_w = width - @font_width
|
1151
|
+
x1 = (@oldcaret_x+1) * @font_width + 1
|
1152
|
+
x2 = (@caret_x+1) * @font_width + 1
|
1153
|
+
y = @caret_y
|
1154
|
+
|
1155
|
+
if x1 > w_w or x2 > w_w
|
1156
|
+
invalidate(0, y, 100000, @font_height)
|
1157
|
+
else
|
1158
|
+
invalidate(x1-1, y, 2, @font_height)
|
1159
|
+
invalidate(x2-1, y, 2, @font_height)
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
@oldcaret_x = @caret_x
|
1163
|
+
end
|
1164
|
+
end
|
1165
|
+
|
1166
|
+
class DbgWindow < Window
|
1167
|
+
attr_accessor :dbg_widget
|
1168
|
+
def initialize_window(dbg = nil, title='metasm debugger')
|
1169
|
+
self.title = title
|
1170
|
+
display(dbg) if dbg
|
1171
|
+
end
|
1172
|
+
|
1173
|
+
# show a new DbgWidget
|
1174
|
+
def display(dbg)
|
1175
|
+
@dbg_widget = DbgWidget.new(dbg)
|
1176
|
+
@dbg_widget.win = self
|
1177
|
+
self.widget = @dbg_widget
|
1178
|
+
@dbg_widget
|
1179
|
+
end
|
1180
|
+
|
1181
|
+
def build_menu
|
1182
|
+
filemenu = new_menu
|
1183
|
+
addsubmenu(filemenu, '_attach process') { @dbg_widget.prompt_attach }
|
1184
|
+
addsubmenu(filemenu, 'create _process') { @dbg_widget.prompt_createprocess }
|
1185
|
+
addsubmenu(filemenu, 'open _dasm window') { DasmWindow.new }
|
1186
|
+
addsubmenu(filemenu)
|
1187
|
+
addsubmenu(filemenu, 'QUIT') { destroy }
|
1188
|
+
|
1189
|
+
addsubmenu(@menu, filemenu, '_File')
|
1190
|
+
|
1191
|
+
dbgmenu = new_menu
|
1192
|
+
addsubmenu(dbgmenu, 'continue', '<f5>') { @dbg_widget.dbg_continue }
|
1193
|
+
addsubmenu(dbgmenu, 'step over', '<f10>') { @dbg_widget.dbg_stepover }
|
1194
|
+
addsubmenu(dbgmenu, 'step into', '<f11>') { @dbg_widget.dbg_singlestep }
|
1195
|
+
addsubmenu(dbgmenu, '_kill target') { @dbg_widget.dbg.kill }
|
1196
|
+
addsubmenu(dbgmenu, '_detach target') { @dbg_widget.dbg.detach }
|
1197
|
+
addsubmenu(dbgmenu)
|
1198
|
+
addsubmenu(dbgmenu, 'QUIT') { destroy }
|
1199
|
+
|
1200
|
+
addsubmenu(@menu, dbgmenu, '_Debug')
|
1201
|
+
|
1202
|
+
codeviewmenu = new_menu
|
1203
|
+
addsubmenu(codeviewmenu, '_listing') { @dbg_widget.code.focus_addr(@dbg_widget.code.curaddr, :listing) }
|
1204
|
+
addsubmenu(codeviewmenu, '_graph') { @dbg_widget.code.focus_addr(@dbg_widget.code.curaddr, :graph) }
|
1205
|
+
addsubmenu(codeviewmenu, 'raw _opcodes') { @dbg_widget.code.focus_addr(@dbg_widget.code.curaddr, :opcodes) }
|
1206
|
+
|
1207
|
+
dataviewmenu = new_menu
|
1208
|
+
addsubmenu(dataviewmenu, '_hexa') { @dbg_widget.mem.focus_addr(@dbg_widget.mem.curaddr, :hex) }
|
1209
|
+
addsubmenu(dataviewmenu, 'raw _opcodes') { @dbg_widget.mem.focus_addr(@dbg_widget.mem.curaddr, :opcodes) }
|
1210
|
+
addsubmenu(dataviewmenu, '_c struct') { @dbg_widget.mem.focus_addr(@dbg_widget.mem.curaddr, :cstruct) }
|
1211
|
+
|
1212
|
+
focusmenu = new_menu
|
1213
|
+
addsubmenu(focusmenu, '_regs') { @dbg_widget.regs.grab_focus ; @dbg_widget.redraw }
|
1214
|
+
addsubmenu(focusmenu, '_data') { @dbg_widget.mem.grab_focus ; @dbg_widget.redraw }
|
1215
|
+
addsubmenu(focusmenu, '_code') { @dbg_widget.code.grab_focus ; @dbg_widget.redraw }
|
1216
|
+
addsubmenu(focusmenu, 'conso_le', '.') { @dbg_widget.console.grab_focus ; @dbg_widget.redraw }
|
1217
|
+
|
1218
|
+
viewmenu = new_menu
|
1219
|
+
addsubmenu(viewmenu, codeviewmenu, '_code display')
|
1220
|
+
addsubmenu(viewmenu, dataviewmenu, '_data display')
|
1221
|
+
addsubmenu(viewmenu, focusmenu, '_focus')
|
1222
|
+
addsubmenu(viewmenu, 'data _watch') { @dbg_widget.prompt_datawatch }
|
1223
|
+
addsubmenu(@menu, viewmenu, '_Views')
|
1224
|
+
end
|
1225
|
+
end
|
1226
|
+
|
1227
|
+
end
|
1228
|
+
end
|