mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
544 lines
13 KiB
C
544 lines
13 KiB
C
/*
|
|
* pdb_caml_process.c
|
|
*
|
|
* http://www.cl.cam.ac.uk/netos/pdb
|
|
*
|
|
* PDB's OCaml interface library for debugging processes
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <caml/alloc.h>
|
|
#include <caml/fail.h>
|
|
#include <caml/memory.h>
|
|
#include <caml/mlvalues.h>
|
|
|
|
#include <xc.h>
|
|
#include <xen/xen.h>
|
|
#include <xen/io/domain_controller.h>
|
|
#include <xen/linux/privcmd.h>
|
|
#include "pdb_module.h"
|
|
#include "pdb_caml_xen.h"
|
|
|
|
/* this order comes from linux-2.6.11/include/asm-i386/ptrace.h */
|
|
enum x86_registers { LINUX_EBX, LINUX_ECX, LINUX_EDX, LINUX_ESI, LINUX_EDI,
|
|
LINUX_EBP, LINUX_EAX, LINUX_DS, LINUX_ES, LINUX_FS,
|
|
LINUX_GS, LINUX_ORIG_EAX, LINUX_EIP, LINUX_CS, LINUX_EFL,
|
|
LINUX_ESP, LINUX_SS };
|
|
#define FRAME_SIZE 17
|
|
|
|
typedef struct
|
|
{
|
|
int domain;
|
|
int process;
|
|
int evtchn;
|
|
pdb_front_ring_t *ring;
|
|
} context_t;
|
|
|
|
#define decode_context(_ctx, _ocaml) \
|
|
{ \
|
|
(_ctx)->domain = Int_val(Field((_ocaml),0)); \
|
|
(_ctx)->process = Int_val(Field((_ocaml),1)); \
|
|
(_ctx)->evtchn = Int_val(Field((_ocaml),2)); \
|
|
(_ctx)->ring = (pdb_front_ring_t *)Int32_val(Field((_ocaml),3)); \
|
|
}
|
|
|
|
#define encode_context(_ctx, _ocaml) \
|
|
{ \
|
|
(_ocaml) = caml_alloc_tuple(2); \
|
|
Store_field((_ocaml), 0, Val_int((_ctx)->domain)); \
|
|
Store_field((_ocaml), 1, Val_int((_ctx)->process)); \
|
|
}
|
|
|
|
/*
|
|
* send a request to a pdb domain backend.
|
|
*
|
|
* puts the request on a ring and kicks the backend using an event channel.
|
|
*/
|
|
static void
|
|
send_request (pdb_front_ring_t *pdb_ring, int evtchn, pdb_request_t *request)
|
|
{
|
|
pdb_request_t *req;
|
|
|
|
req = RING_GET_REQUEST(pdb_ring, pdb_ring->req_prod_pvt);
|
|
|
|
memcpy(req, request, sizeof(pdb_request_t));
|
|
|
|
pdb_ring->req_prod_pvt++;
|
|
|
|
RING_PUSH_REQUESTS(pdb_ring);
|
|
xc_evtchn_send(xc_handle, evtchn);
|
|
}
|
|
|
|
/*
|
|
* read a response from a pdb domain backend.
|
|
*
|
|
* grabs the response off a ring.
|
|
*/
|
|
static void
|
|
read_response (pdb_front_ring_t *pdb_ring, pdb_response_p response)
|
|
{
|
|
RING_IDX loop, rp;
|
|
|
|
rp = pdb_ring->sring->rsp_prod;
|
|
rmb(); /* Ensure we see queued responses up to 'rp'. */
|
|
|
|
for ( loop = pdb_ring->rsp_cons; loop != rp; loop++ )
|
|
{
|
|
pdb_response_p resp;
|
|
|
|
resp = RING_GET_RESPONSE(pdb_ring, loop);
|
|
memcpy(response, resp, sizeof(pdb_response_t));
|
|
|
|
/*
|
|
printf ("got response %x %x %x\n", response->operation,
|
|
response->status, response->value);
|
|
*/
|
|
}
|
|
pdb_ring->rsp_cons = loop;
|
|
}
|
|
|
|
/*
|
|
* process_handle_response : int32 -> unit
|
|
*/
|
|
|
|
value
|
|
process_handle_response (value ring)
|
|
{
|
|
CAMLparam1(ring);
|
|
|
|
pdb_front_ring_t *my_ring = (pdb_front_ring_t *)Int32_val(ring);
|
|
pdb_response_t resp;
|
|
|
|
if ( my_ring )
|
|
read_response(my_ring, &resp);
|
|
|
|
CAMLreturn(Val_unit);
|
|
}
|
|
|
|
/*
|
|
* proc_attach_debugger : context_t -> unit
|
|
*/
|
|
value
|
|
proc_attach_debugger (value context)
|
|
{
|
|
CAMLparam1(context);
|
|
context_t ctx;
|
|
pdb_request_t req;
|
|
pdb_response_t resp;
|
|
|
|
decode_context(&ctx, context);
|
|
|
|
printf("(pdb) attach process [%d.%d] %d %p\n", ctx.domain, ctx.process,
|
|
ctx.evtchn, ctx.ring);
|
|
fflush(stdout);
|
|
|
|
req.operation = PDB_OPCODE_ATTACH;
|
|
req.domain = ctx.domain;
|
|
req.process = ctx.process;
|
|
|
|
send_request (ctx.ring, ctx.evtchn, &req);
|
|
|
|
printf("awaiting response\n");
|
|
fflush(stdout);
|
|
|
|
read_response (ctx.ring, &resp);
|
|
|
|
printf("response %d %d\n", resp.operation, resp.status);
|
|
fflush(stdout);
|
|
|
|
CAMLreturn(Val_unit);
|
|
}
|
|
|
|
|
|
/*
|
|
* proc_detach_debugger : context_t -> unit
|
|
*/
|
|
value
|
|
proc_detach_debugger (value context)
|
|
{
|
|
CAMLparam1(context);
|
|
context_t ctx;
|
|
pdb_request_t req;
|
|
|
|
decode_context(&ctx, context);
|
|
|
|
printf("(pdb) detach process [%d.%d] %d %p\n", ctx.domain, ctx.process,
|
|
ctx.evtchn, ctx.ring);
|
|
fflush(stdout);
|
|
|
|
req.operation = PDB_OPCODE_DETACH;
|
|
req.domain = ctx.domain;
|
|
req.process = ctx.process;
|
|
|
|
send_request (ctx.ring, ctx.evtchn, &req);
|
|
|
|
CAMLreturn(Val_unit);
|
|
}
|
|
|
|
|
|
/*
|
|
* proc_pause_target : int -> unit
|
|
*/
|
|
value
|
|
proc_pause_target (value context)
|
|
{
|
|
CAMLparam1(context);
|
|
context_t ctx;
|
|
|
|
decode_context(&ctx, context);
|
|
|
|
printf("(pdb) pause target %d %d\n", ctx.domain, ctx.process);
|
|
fflush(stdout);
|
|
|
|
CAMLreturn(Val_unit);
|
|
}
|
|
|
|
|
|
/*
|
|
* proc_read_registers : context_t -> int32
|
|
*/
|
|
value
|
|
proc_read_registers (value context)
|
|
{
|
|
CAMLparam1(context);
|
|
CAMLlocal1(result);
|
|
|
|
u32 regs[FRAME_SIZE];
|
|
|
|
pdb_request_t req;
|
|
context_t ctx;
|
|
int loop;
|
|
|
|
decode_context(&ctx, context);
|
|
|
|
req.operation = PDB_OPCODE_RD_REG;
|
|
req.domain = ctx.domain;
|
|
req.process = ctx.process;
|
|
|
|
for (loop = 0; loop < FRAME_SIZE; loop++)
|
|
{
|
|
pdb_response_t resp;
|
|
|
|
req.u.rd_reg.reg = loop;
|
|
send_request(ctx.ring, ctx.evtchn, &req);
|
|
read_response(ctx.ring, &resp);
|
|
regs[loop] = resp.value;
|
|
}
|
|
|
|
result = caml_alloc_tuple(16);
|
|
|
|
Store_field(result, 0, caml_copy_int32(regs[LINUX_EAX]));
|
|
Store_field(result, 1, caml_copy_int32(regs[LINUX_ECX]));
|
|
Store_field(result, 2, caml_copy_int32(regs[LINUX_EDX]));
|
|
Store_field(result, 3, caml_copy_int32(regs[LINUX_EBX]));
|
|
Store_field(result, 4, caml_copy_int32(regs[LINUX_ESP]));
|
|
Store_field(result, 5, caml_copy_int32(regs[LINUX_EBP]));
|
|
Store_field(result, 6, caml_copy_int32(regs[LINUX_ESI]));
|
|
Store_field(result, 7, caml_copy_int32(regs[LINUX_EDI]));
|
|
Store_field(result, 8, caml_copy_int32(regs[LINUX_EIP]));
|
|
Store_field(result, 9, caml_copy_int32(regs[LINUX_EFL]));
|
|
Store_field(result, 10, caml_copy_int32(regs[LINUX_CS])); /* 16 */
|
|
Store_field(result, 11, caml_copy_int32(regs[LINUX_SS])); /* 16 */
|
|
Store_field(result, 12, caml_copy_int32(regs[LINUX_DS])); /* 16 */
|
|
Store_field(result, 13, caml_copy_int32(regs[LINUX_ES])); /* 16 */
|
|
Store_field(result, 14, caml_copy_int32(regs[LINUX_FS])); /* 16 */
|
|
Store_field(result, 15, caml_copy_int32(regs[LINUX_GS])); /* 16 */
|
|
|
|
CAMLreturn(result);
|
|
}
|
|
|
|
|
|
/*
|
|
* proc_write_register : context_t -> register -> int32 -> unit
|
|
*/
|
|
value
|
|
proc_write_register (value context, value reg, value newval)
|
|
{
|
|
CAMLparam3(context, reg, newval);
|
|
|
|
int my_reg = Int_val(reg);
|
|
unsigned long my_newval = Int32_val(newval);
|
|
|
|
context_t ctx;
|
|
pdb_request_t req;
|
|
pdb_response_t resp;
|
|
|
|
decode_context(&ctx, context);
|
|
|
|
req.operation = PDB_OPCODE_WR_REG;
|
|
req.domain = ctx.domain;
|
|
req.process = ctx.process;
|
|
req.u.wr_reg.value = my_newval;
|
|
|
|
switch (my_reg)
|
|
{
|
|
case GDB_EAX: req.u.wr_reg.reg = LINUX_EAX; break;
|
|
case GDB_ECX: req.u.wr_reg.reg = LINUX_ECX; break;
|
|
case GDB_EDX: req.u.wr_reg.reg = LINUX_EDX; break;
|
|
case GDB_EBX: req.u.wr_reg.reg = LINUX_EBX; break;
|
|
|
|
case GDB_ESP: req.u.wr_reg.reg = LINUX_ESP; break;
|
|
case GDB_EBP: req.u.wr_reg.reg = LINUX_EBP; break;
|
|
case GDB_ESI: req.u.wr_reg.reg = LINUX_ESI; break;
|
|
case GDB_EDI: req.u.wr_reg.reg = LINUX_EDI; break;
|
|
|
|
case GDB_EIP: req.u.wr_reg.reg = LINUX_EIP; break;
|
|
case GDB_EFL: req.u.wr_reg.reg = LINUX_EFL; break;
|
|
|
|
case GDB_CS: req.u.wr_reg.reg = LINUX_CS; break;
|
|
case GDB_SS: req.u.wr_reg.reg = LINUX_SS; break;
|
|
case GDB_DS: req.u.wr_reg.reg = LINUX_DS; break;
|
|
case GDB_ES: req.u.wr_reg.reg = LINUX_ES; break;
|
|
case GDB_FS: req.u.wr_reg.reg = LINUX_FS; break;
|
|
case GDB_GS: req.u.wr_reg.reg = LINUX_GS; break;
|
|
}
|
|
|
|
send_request(ctx.ring, ctx.evtchn, &req);
|
|
read_response(ctx.ring, &resp);
|
|
|
|
CAMLreturn(Val_unit);
|
|
}
|
|
|
|
|
|
/*
|
|
* proc_read_memory : context_t -> int32 -> int -> int
|
|
*/
|
|
value
|
|
proc_read_memory (value context, value address, value length)
|
|
{
|
|
CAMLparam3(context, address, length);
|
|
CAMLlocal2(result, temp);
|
|
|
|
context_t ctx;
|
|
int loop;
|
|
char *buffer;
|
|
/* memory_t my_address = Int32_val(address); */
|
|
u32 my_length = Int_val(length);
|
|
|
|
printf ("(pdb) read memory\n");
|
|
|
|
decode_context(&ctx, context);
|
|
|
|
buffer = malloc(my_length);
|
|
if ( buffer == NULL )
|
|
{
|
|
printf("(pdb) read memory: malloc failed.\n"); fflush(stdout);
|
|
failwith("read memory error");
|
|
}
|
|
|
|
/*
|
|
if ( xendebug_read_memory(xc_handle, ctx.domain, ctx.vcpu,
|
|
my_address, my_length, buffer) )
|
|
{
|
|
printf("(pdb) read memory error!\n"); fflush(stdout);
|
|
failwith("read memory error");
|
|
}
|
|
*/
|
|
|
|
memset(buffer, 0xff, my_length);
|
|
|
|
result = caml_alloc(2,0);
|
|
if ( my_length > 0 ) /* car */
|
|
{
|
|
Store_field(result, 0, Val_int(buffer[my_length - 1] & 0xff));
|
|
}
|
|
else
|
|
|
|
{
|
|
Store_field(result, 0, Val_int(0));
|
|
}
|
|
Store_field(result, 1, Val_int(0)); /* cdr */
|
|
|
|
for (loop = 1; loop < my_length; loop++)
|
|
{
|
|
temp = result;
|
|
result = caml_alloc(2,0);
|
|
Store_field(result, 0, Val_int(buffer[my_length - loop - 1] & 0xff));
|
|
Store_field(result, 1, temp);
|
|
}
|
|
|
|
CAMLreturn(result);
|
|
}
|
|
|
|
/*
|
|
* proc_write_memory : context_t -> int32 -> int list -> unit
|
|
*/
|
|
value
|
|
proc_write_memory (value context, value address, value val_list)
|
|
{
|
|
CAMLparam3(context, address, val_list);
|
|
CAMLlocal1(node);
|
|
|
|
context_t ctx;
|
|
|
|
char buffer[4096]; /* a big buffer */
|
|
memory_t my_address;
|
|
u32 length = 0;
|
|
|
|
printf ("(pdb) write memory\n");
|
|
|
|
decode_context(&ctx, context);
|
|
|
|
node = val_list;
|
|
if ( Int_val(node) == 0 ) /* gdb functionalty test uses empty list */
|
|
{
|
|
CAMLreturn(Val_unit);
|
|
}
|
|
|
|
while ( Int_val(Field(node,1)) != 0 )
|
|
{
|
|
buffer[length++] = Int_val(Field(node, 0));
|
|
node = Field(node,1);
|
|
}
|
|
buffer[length++] = Int_val(Field(node, 0));
|
|
|
|
my_address = (memory_t) Int32_val(address);
|
|
|
|
/*
|
|
if ( xendebug_write_memory(xc_handle, ctx.domain, ctx.vcpu,
|
|
my_address, length, buffer) )
|
|
{
|
|
printf("(pdb) write memory error!\n"); fflush(stdout);
|
|
failwith("write memory error");
|
|
}
|
|
*/
|
|
{
|
|
int loop;
|
|
for (loop = 0; loop < length; loop++)
|
|
{
|
|
printf (" %02x", buffer[loop]);
|
|
}
|
|
printf ("\n");
|
|
}
|
|
|
|
CAMLreturn(Val_unit);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* proc_continue_target : context_t -> unit
|
|
*/
|
|
value
|
|
proc_continue_target (value context)
|
|
{
|
|
CAMLparam1(context);
|
|
|
|
context_t ctx;
|
|
|
|
decode_context(&ctx, context);
|
|
|
|
/*
|
|
if ( xendebug_continue(xc_handle, ctx.domain, ctx.vcpu) )
|
|
{
|
|
printf("(pdb) continue\n"); fflush(stdout);
|
|
failwith("continue");
|
|
}
|
|
*/
|
|
printf ("CONTINUE\n");
|
|
|
|
CAMLreturn(Val_unit);
|
|
}
|
|
|
|
/*
|
|
* proc_step_target : context_t -> unit
|
|
*/
|
|
value
|
|
proc_step_target (value context)
|
|
{
|
|
CAMLparam1(context);
|
|
|
|
context_t ctx;
|
|
|
|
decode_context(&ctx, context);
|
|
|
|
/*
|
|
if ( xendebug_step(xc_handle, ctx.domain, ctx.vcpu) )
|
|
{
|
|
printf("(pdb) step\n"); fflush(stdout);
|
|
failwith("step");
|
|
}
|
|
*/
|
|
printf ("STEP\n");
|
|
|
|
CAMLreturn(Val_unit);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* proc_insert_memory_breakpoint : context_t -> int32 -> int list -> unit
|
|
*/
|
|
value
|
|
proc_insert_memory_breakpoint (value context, value address, value length)
|
|
{
|
|
CAMLparam3(context, address, length);
|
|
|
|
context_t ctx;
|
|
memory_t my_address = (memory_t) Int32_val(address);
|
|
int my_length = Int_val(length);
|
|
|
|
decode_context(&ctx, context);
|
|
|
|
printf ("(pdb) insert memory breakpoint 0x%lx %d\n",
|
|
my_address, my_length);
|
|
|
|
/*
|
|
if ( xendebug_insert_memory_breakpoint(xc_handle, ctx.domain, ctx.vcpu,
|
|
my_address, my_length) )
|
|
{
|
|
printf("(pdb) error: insert memory breakpoint\n"); fflush(stdout);
|
|
failwith("insert memory breakpoint");
|
|
}
|
|
*/
|
|
|
|
CAMLreturn(Val_unit);
|
|
}
|
|
|
|
/*
|
|
* proc_remove_memory_breakpoint : context_t -> int32 -> int list -> unit
|
|
*/
|
|
value
|
|
proc_remove_memory_breakpoint (value context, value address, value length)
|
|
{
|
|
CAMLparam3(context, address, length);
|
|
|
|
context_t ctx;
|
|
|
|
memory_t my_address = (memory_t) Int32_val(address);
|
|
int my_length = Int_val(length);
|
|
|
|
printf ("(pdb) remove memory breakpoint 0x%lx %d\n",
|
|
my_address, my_length);
|
|
|
|
decode_context(&ctx, context);
|
|
|
|
/*
|
|
if ( xendebug_remove_memory_breakpoint(xc_handle,
|
|
ctx.domain, ctx.vcpu,
|
|
my_address, my_length) )
|
|
{
|
|
printf("(pdb) error: remove memory breakpoint\n"); fflush(stdout);
|
|
failwith("remove memory breakpoint");
|
|
}
|
|
*/
|
|
|
|
CAMLreturn(Val_unit);
|
|
}
|
|
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-set-style: "BSD"
|
|
* c-basic-offset: 4
|
|
* tab-width: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|
|
|
|
|