metasm 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
@@ -0,0 +1,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
|