aboutsummaryrefslogtreecommitdiffstats
path: root/tools/debugger/pdb/debugger.ml
blob: 3dd2159c57f70afb2481e4bfd1ebdaba004b2598 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
(** debugger.ml
 *
 *  main debug functionality
 *
 *  @author copyright (c) 2005 alex ho
 *  @see <www.cl.cam.ac.uk/netos/pdb> pervasive debugger
 *  @version 1
 *)

open Intel
open PDB
open Util
open Str

(** a few debugger commands such as step 's' and continue 'c' do 
 *  not immediately return a response to the debugger.  in these 
 *  cases we raise No_reply instead. 
 *)
exception No_reply

let initialize_debugger () =
  ()

let exit_debugger () =
  ()


(**
   Detach Command
   Note: response is ignored by gdb.  We leave the context in the
   hash.  It will be cleaned up with the socket is closed.
 *)
let gdb_detach ctx =
  PDB.detach_debugger ctx;
  raise No_reply

(**
   Kill Command
   Note: response is ignored by gdb.  We leave the context in the
   hash.  It will be cleaned up with the socket is closed.
 *)
let gdb_kill () =
  ""



(**
   Continue Command.
   resume the target
 *)
let gdb_continue ctx =
  PDB.continue ctx;
  raise No_reply

(**
   Step Command.
   single step the target
 *)
let gdb_step ctx =
  PDB.step ctx;
  raise No_reply


(**
   Read Registers Command.
   returns 16 4-byte registers in a particular defined by gdb.
 *)
let gdb_read_registers ctx =
  let regs = PDB.read_registers ctx in
  let str = 
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.eax)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.ecx)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.edx)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.ebx)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.esp)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.ebp)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.esi)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.edi)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.eip)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.efl)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.cs)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.ss)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.ds)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.es)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.fs)) ^
    (Printf.sprintf "%08lx" (Util.flip_int32 regs.gs)) in
  str
      
(**
   Set Thread Command
 *)
let gdb_set_thread command =
  "OK"


(**
   Read Memory Packets
 *)
let gdb_read_memory ctx command =
  let int_list_to_string i str =
    (Printf.sprintf "%02x" i) ^ str
  in
  let read_mem addr len =
    try
      let mem = PDB.read_memory ctx addr len  in
      List.fold_right int_list_to_string mem ""
    with
      Failure s -> "E02"
  in
  Scanf.sscanf command "m%lx,%d" read_mem



(**
   Write Memory Packets
 *)
let gdb_write_memory ctx command =
  let write_mem addr len =
    print_endline (Printf.sprintf "  gdb_write_memory %lx %x\n" addr len);
    print_endline (Printf.sprintf "  [[ unimplemented ]]\n")
  in
  Scanf.sscanf command "M%lx,%d" write_mem;
  "OK"



(**
   Write Register Packets
 *)
let gdb_write_register ctx command =
  let write_reg reg goofy_val =
    let new_val = Util.flip_int32 goofy_val in
    match reg with
    |  0 -> PDB.write_register ctx EAX new_val
    |  1 -> PDB.write_register ctx ECX new_val
    |  2 -> PDB.write_register ctx EDX new_val
    |  3 -> PDB.write_register ctx EBX new_val
    |  4 -> PDB.write_register ctx ESP new_val
    |  5 -> PDB.write_register ctx EBP new_val
    |  6 -> PDB.write_register ctx ESI new_val
    |  7 -> PDB.write_register ctx EDI new_val
    |  8 -> PDB.write_register ctx EIP new_val
    |  9 -> PDB.write_register ctx EFL new_val
    | 10 -> PDB.write_register ctx CS new_val
    | 11 -> PDB.write_register ctx SS new_val
    | 12 -> PDB.write_register ctx DS new_val
    | 13 -> PDB.write_register ctx ES new_val
    | 14 -> PDB.write_register ctx FS new_val
    | 15 -> PDB.write_register ctx GS new_val
    | _  -> print_endline (Printf.sprintf "write unknown register [%d]" reg)
  in
  Scanf.sscanf command "P%x=%lx" write_reg;
  "OK"


(**
   General Query Packets
 *)
let gdb_query command =
  match command with
  | "qC" -> ""
  | "qOffsets" -> ""
  | "qSymbol::" -> ""
  | _ -> 
      print_endline (Printf.sprintf "unknown gdb query packet [%s]" command);
      "E01"


(**
   Write Memory Binary Packets
 *)
let gdb_write_memory_binary ctx command =
  let write_mem addr len =
    let pos = Str.search_forward (Str.regexp ":") command 0 in
    let txt = Str.string_after command (pos + 1) in
    PDB.write_memory ctx addr (int_list_of_string txt len)
  in
  Scanf.sscanf command "X%lx,%d" write_mem;
  "OK"



(**
   Last Signal Command
 *)
let gdb_last_signal =
  "S00"




(**
   Process PDB extensions to the GDB serial protocol.
   Changes the mutable context state.
 *)
let pdb_extensions command sock =
  let process_extension key value =
    (* since this command can change the context, 
       we need to grab it again each time *)
    let ctx = PDB.find_context sock in
    match key with
    | "status" ->
	PDB.debug_contexts ();
	(* print_endline ("debugger status");
	   debugger_status () 
	*)
    | "context" ->
        PDB.add_context sock (List.hd value) 
                             (int_list_of_string_list (List.tl value))
    | _ -> failwith (Printf.sprintf "unknown pdb extension command [%s:%s]" 
		                    key (List.hd value))
  in
  try
    Util.little_parser process_extension 
                       (String.sub command 1 ((String.length command) - 1));
    "OK"
  with
  | Unknown_context s -> 
      print_endline (Printf.sprintf "unknown context [%s]" s);
      "E01"
  | Unknown_domain -> "E01"
  | Failure s -> "E01"


(**
   Insert Breakpoint or Watchpoint Packet
 *)
let gdb_insert_bwcpoint ctx command =
  let insert cmd addr length =
    try
      match cmd with
      | 0 -> PDB.insert_memory_breakpoint ctx addr length; "OK"
      | _ -> ""
    with
      Failure s -> "E03"
  in
  Scanf.sscanf command "Z%d,%lx,%d" insert

(**
   Remove Breakpoint or Watchpoint Packet
 *)
let gdb_remove_bwcpoint ctx command =
  let insert cmd addr length =
    try
      match cmd with
      | 0 -> PDB.remove_memory_breakpoint ctx addr length; "OK"
      | _ -> ""
    with
      Failure s -> "E04"
  in
  Scanf.sscanf command "z%d,%lx,%d" insert

(**
   Do Work!

   @param command  char list
 *)

let process_command command sock =
  let ctx = PDB.find_context sock in
  try
    match command.[0] with
    | 'c' -> gdb_continue ctx
    | 'D' -> gdb_detach ctx
    | 'g' -> gdb_read_registers ctx
    | 'H' -> gdb_set_thread command
    | 'k' -> gdb_kill ()
    | 'm' -> gdb_read_memory ctx command
    | 'M' -> gdb_write_memory ctx command
    | 'P' -> gdb_write_register ctx command
    | 'q' -> gdb_query command
    | 's' -> gdb_step ctx
    | 'x' -> pdb_extensions command sock
    | 'X' -> gdb_write_memory_binary ctx command
    | '?' -> gdb_last_signal
    | 'z' -> gdb_remove_bwcpoint ctx command
    | 'Z' -> gdb_insert_bwcpoint ctx command
    | _ -> 
	print_endline (Printf.sprintf "unknown gdb command [%s]" command);
	"E02"
  with
    Unimplemented s ->
      print_endline (Printf.sprintf "loser. unimplemented command [%s][%s]" 
		                    command s);
      "E03"

(**
   process_xen_domain

   This is called whenever a domain debug assist responds to a
   pdb packet.
*)

let process_xen_domain fd =
  let channel = Evtchn.read fd in
  let ctx = find_context fd in
  
  begin
    match ctx with
      | Xen_domain d -> Xen_domain.process_response (Xen_domain.get_ring d)
      | _ -> failwith ("process_xen_domain called without Xen_domain context")
  end;
    
  Evtchn.unmask fd channel                                (* allow next virq *)
  

(**
   process_xen_virq

   This is called each time a virq_pdb is sent from xen to dom 0.
   It is sent by Xen when a domain hits a breakpoint. 

   Think of this as the continuation function for a "c" or "s" command
   issued to a domain.
*)

external query_domain_stop : unit -> (int * int) list = "query_domain_stop"
(* returns a list of paused domains : () -> (domain, vcpu) list *)

let process_xen_virq fd =
  let channel = Evtchn.read fd in
  let find_pair (dom, vcpu) =
    print_endline (Printf.sprintf "checking %d.%d" dom vcpu);
    try
      let sock = PDB.find_domain dom vcpu in
      true
    with
      Unknown_domain -> false
  in
  let dom_list = query_domain_stop () in
  let (dom, vcpu) = List.find find_pair dom_list in
  let vec = 3 in
  let sock = PDB.find_domain dom vcpu in
  print_endline (Printf.sprintf "handle bkpt d:%d ed:%d v:%d  %s" 
		   dom vcpu vec (Util.get_connection_info sock));
  Util.send_reply sock "S05";
  Evtchn.unmask fd channel                                (* allow next virq *)
  

(**
   process_xen_xcs

   This is called each time the software assist residing in a backend 
   domain starts up.  The control message includes the address of a 
   shared ring page and our end of an event channel (which indicates
   when data is available on the ring).
*)

let process_xen_xcs xcs_fd =
  let (local_evtchn_fd, evtchn, dom, ring) = Xcs.read xcs_fd in
  add_xen_domain_context local_evtchn_fd dom evtchn ring;
  local_evtchn_fd