From 4721cf1ecb949b37c98b6fce79163541711de2e5 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Thu, 3 Dec 1998 23:54:14 +0000 Subject: Patch from Emmanuel Raguet to add remote debug server and RPC support to RTEMS. Thanks. :) Email follows: Hello, For Xmas, here is the Remote Debugger on RTEMS ! Here are 2 patches for the Remote Debugger on RTEMS for pc386 from Linux host : - one for RTEMS it self, - one for GDB-4.17. 1/ RTEMS patch -------------- This patch adds 2 libraries : - a simplified SUN RPC library - the Remote Debugger library The configuration command is the following : ../rtems4/configure --target=i386-rtemself --enable-rtemsbsp=pc386 --enable-rdbg The SUN RPC library is built only if networking is set. The RDBG library is built if networking and enable-rdbg are set. The function used to initialize the debugger is : rtems_rdbg_initialize (); A special function has been created to force a task to be in a "debug" state : enterRdbg(). The use of this function is not mandatory. 2/ GDB-4.17 patch ----------------- This patch create a new RTEMS target for GDB-4.17. The configuration command is the following : ./configure --enable-shared --target=i386RTEMS To connect to a target, use : target rtems [your_site_address] Then, attach the target using : attach 1 And... Debug ;) You can obtain the original GDB-4.17 on ftp://ftp.debian.org/debian/dists/stable/main/source/devel/gdb_4.17.orig.tar.gz This has been tested from a Debian 2.0.1 linux host. --- c/src/lib/librdbg/servbkpt.c | 587 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 587 insertions(+) create mode 100644 c/src/lib/librdbg/servbkpt.c (limited to 'c/src/lib/librdbg/servbkpt.c') diff --git a/c/src/lib/librdbg/servbkpt.c b/c/src/lib/librdbg/servbkpt.c new file mode 100644 index 0000000000..8d986751a2 --- /dev/null +++ b/c/src/lib/librdbg/servbkpt.c @@ -0,0 +1,587 @@ +/* + ********************************************************************** + * + * Component: RDB servers + * Module: servbkpt.c + * + * Synopsis: Management of breakpoints + * + ********************************************************************** + */ + +#include +#include +#include +#include + +/*----- Macros -----*/ + +#define BKPT0(plst) ((BASE_BREAK*)(plst)->break_list) +#define BKPT_INCR 5 /* how many bkpt slots alloc at a time */ + +#define BKPT_OVER(plst,idx,addr,size) \ + ((plst)->break_list[idx].ee_loc + BREAK_SIZE > (UINT32) (addr) \ + && (plst)->break_list[idx].ee_loc < (UINT32) (addr) + (size)) + +#define BKPT_SLOTS \ + (sizeof ((xdr_break*) 0)->thread_list / \ + sizeof ((xdr_break*) 0)->thread_list [0]) + + + /* + * BreakAlloc - alloc a breakpoint entry. + * + * This is a generic routine to insert an entry in the + * breakpoint list. It returns the number of entry just + * created. It returns -1 if failed. + */ + + static int +BreakAlloc (PID_LIST* plst, Boolean normal) +{ + int idx, len; + xdr_break* blst; + + if (!normal) { /* want 0 entry */ + if (plst->break_list) { + return(0); /* already there */ + } + idx = 1; /* force alloc below */ + } else { + for (idx = 1; idx < (int)plst->break_alloc; idx++) { + if (plst->break_list[idx].type == BRKT_NONE) { + /* got a free one */ + memset(&plst->break_list[idx], 0, sizeof(xdr_break)); + return(idx); /* found one */ + } + } + } + /* idx is the requested entry */ + + if (idx >= (int)plst->break_alloc) { /* need more space */ + len = plst->break_alloc + BKPT_INCR; + blst = (xdr_break*)Realloc(plst->break_list, len*sizeof(xdr_break)); + if (!blst) { + return(-1); /* failed, no space */ + } + plst->break_alloc = len; /* got more */ + plst->break_list = blst; + + /* Clear new space */ + memset(blst + len - BKPT_INCR, 0, BKPT_INCR * sizeof(xdr_break)); + idx = len - BKPT_INCR; /* next available */ + if (!idx) { + idx = 1; /* for normal cases */ + } + } + return(normal ? idx : 0); /* return it */ +} + + /* + * BreakSet - set a breakpoint in process + * + * Returns the number or -1/errno. + */ + +#ifdef DDEBUG +static const char* BreakTypes[] = { + "NONE", "INSTR", "READ", "WRITE", + "ACCESS", "EXEC", "OS_CALL", "OS_SWITCH", + "STEPEMUL" +}; + +#define BN_MAX (sizeof BreakTypes / sizeof BreakTypes[0]) +#define BREAK_NAME(t) ((unsigned) (t) < BN_MAX ? BreakTypes[t] : "???") +#endif + + int +BreakSet (PID_LIST* plst, int conn_idx, xdr_break* bkpt) +{ + int pid = plst->pid; + int type = bkpt->type; + void* addr = (void *) bkpt->ee_loc; + int idx; + int data; + + DPRINTF(("BreakSet: type %d (%s) at 0x%x th %d ee_type %d len %d " + "pass %d curr %d list %d %d %d %d\n", type, BREAK_NAME(type), + (int) addr, + bkpt->thread_spec, bkpt->ee_type, bkpt->length, bkpt->pass_count, + bkpt->curr_pass, bkpt->thread_list [0], bkpt->thread_list [1], + bkpt->thread_list [2], bkpt->thread_list [3])); + + idx = BreakAlloc(plst, True); /* get entry */ + if (idx < 0) { /* no memory */ + setErrno(ENOMEM); /* set for safety */ + return -1; /* return the error */ + } + + data = TgtPtrace(RPT_PEEKTEXT, pid, addr, 0, NULL); /* current */ + if (getErrno()) { + return -1; /* failed, return the error */ + } + if (IS_BREAK(data)) { /* There is already a break here */ + DPRINTF(("BreakSet: already have soft bkpt at %x\n", addr)); + if (type == BRKT_STEPEMUL) { + ++BKPT0 (plst)->pad1; + return 1; /* Any non-error value is OK */ + } + setErrno(EBUSY); + return -1; + } + + TgtPtrace(RPT_POKETEXT, pid, addr, SET_BREAK(data), NULL); + + if (getErrno()) { + return -1; + } + + plst->break_list[idx] = *bkpt; + plst->break_list[idx].ee_type = data; /* saved data */ + + /* Inform other owners */ + if (type != BRKT_STEPEMUL) { + TgtNotifyAll (plst - pid_list, BMSG_BREAK, 1 /*added*/, idx, + conn_idx, False); + } else { + ++BKPT0 (plst)->pad1; + } + /* Return the number */ + setErrno(0); /* Just in case */ + return idx; +} + + int +BreakSetAt (PID_LIST* plst, int conn_idx, unsigned long addr, break_type type) +{ + xdr_break xb; + + memset (&xb, 0, sizeof xb); + xb.type = type; + xb.ee_loc = addr; + return BreakSet (plst, conn_idx, &xb); +} + +/*----- Find a breakpoint by address -----*/ + + int +BreakGetIndex(PID_LIST* plst, void* addr) +{ + int idx; + int data = -1; + + if (!plst->break_alloc) { + setErrno(EFAULT); + return -1; + } + for (idx = 1; idx < (int)plst->break_alloc; idx++) { + if ((u_long) addr == plst->break_list [idx].ee_loc) { + data = idx; + break; + } + } + return data; +} + +/*----- Getting information about breakpoint -----*/ + + /* + * If data > 0, fill "bkpt" with information about breakpoint + * and return the number of the next one. + * If data == 0, return the count of breakpoints. + */ + + int +BreakGet (const PID_LIST* plst, int data, xdr_break* bkpt) +{ + int idx; + + if (!data) { /* just count them */ + for (idx = 1; idx < (int)plst->break_alloc; idx++) { + if (plst->break_list[idx].type != BRKT_NONE) { + data++; + } + } + return data; /* count */ + } + if ((unsigned) data >= plst->break_alloc) { + /* out of range */ + setErrno(EFAULT); /* closest match */ + return -1; + } + /* get it and say which is next */ + *bkpt = plst->break_list[data]; + for (idx = (int)data+1; idx < (int)plst->break_alloc; idx++) { + if (plst->break_list[idx].type != BRKT_NONE) { + return idx; + } + } + return 0; /* otherwise returns 0 for no more */ +} + +/*----- Clearing bkpts -----*/ + + /* + * BreakClear - clear one (if data != 0) or all breakpoints + * (if data == 0). Return the number of bkpts cleared. + * If (data == -1), remove step-emulation breakpoints. + */ + + int +BreakClear (PID_LIST* plst, int conn_idx, int data) +{ + int pid_idx = plst - pid_list; + int idx; + int cleared = 0; + int clearStepEmul = 0; + int terminated = PROC_TERMINATED (plst); + int stepEmulCount = 0; + + /* break handle in data */ + if (!plst->break_alloc) { /* there are no breaks */ + DPRINTF (("BreakClear: no bkpts defined.\n")); + setErrno(EFAULT); /* closest match */ + return -1; /* return error */ + } + if (!data) { /* clear all */ + idx = 1; + data = plst->break_alloc-1; + + /* Inform other owners */ + DPRINTF (("BreakClear: clearing all bkpts.\n")); + TgtNotifyAll (pid_idx, BMSG_BREAK, 0 /*clr*/, 0, conn_idx, False); + + } else if (data == -1) { /* clear all step-emul bkpts */ + DPRINTF(("BreakClear: removing %d step-emul bkpts\n", + BKPT0 (plst)->pad1)); + + stepEmulCount = BKPT0 (plst)->pad1; + BKPT0 (plst)->pad1 = 0; + + clearStepEmul = 1; + idx = 1; + data = plst->break_alloc-1; + } else if ((unsigned) data >= plst->break_alloc + || plst->break_list[data].type == BRKT_NONE) { + + /* out of range */ + DPRINTF (("BreakClear: invalid bkpt %d\n", data)); + setErrno(EFAULT); /* closest match */ + return -1; /* return error */ + } else { + idx = data; + /* Inform other owners */ + TgtNotifyAll (pid_idx, BMSG_BREAK, 0 /*clr*/, idx, conn_idx, False); + DPRINTF (("BreakClear: clearing bkpt %d\n", data)); + } + + for (; idx <= data; idx++) { /* clear each one */ + int type = plst->break_list[idx].type; + + if (clearStepEmul && type != BRKT_STEPEMUL) continue; + + if (type == BRKT_INSTR || (clearStepEmul && type == BRKT_STEPEMUL)) { + /* just patch back */ + char* addr = (char *)plst->break_list[idx].ee_loc; + int val; + + if (BKPT0 (plst)->clr_step && BKPT0 (plst)->last_break == idx) { + BKPT0 (plst)->clr_step = 0; /* not needed */ + } + /* Neighboring bytes can have breakpoints too... */ + if (! terminated) { + setErrno (0); + val = TgtPtrace(RPT_PEEKTEXT, plst->pid, addr, 0, NULL); + if (getErrno()) { + DPRINTF (("BreakClear: addr %x not readable!\n", addr)); + setErrno (0); /* Forget bkpt */ + } else { + assert (IS_BREAK (val)); + val = ORG_BREAK (val, (int)plst->break_list[idx].ee_type); + TgtPtrace(RPT_POKETEXT, plst->pid, addr, val, NULL); + if (getErrno()) { + DPRINTF (("BreakClear: addr %x not writable!\n", addr)); + setErrno (0); + } + } + } + ++cleared; /* indicate cleared */ + } + memset(&plst->break_list[idx], 0, sizeof(xdr_break)); + } + assert (!clearStepEmul || cleared <= stepEmulCount); + if (stepEmulCount && cleared == 0) { + DPRINTF (("BreakClear: all STEPEMUL bkpts were shared\n")); + return 1; + } + return cleared; +} + +/*----- Hiding of breakpoints -----*/ + + /* + * PatchBreak - patch original data from break into data buffer. + * + * Notes: + * - this routine patches the original data under a break into the data + * buffer from a ptrace read/peek. + */ + + static void +PatchBreak (char* buff, UINT32 bstart, int bsize, UINT32 dstart, char* dvalue) +{ + int size = BREAK_SIZE; /* default size */ + + /* Must deal with all sorts of unalignments + * (3 full overlaps, 3 unaligns) + */ + if (bsize < BREAK_SIZE) { + /* case where buffer is smaller than data */ + memcpy(buff, dvalue+(bstart-dstart), bsize); /* copy over */ + return; + } + /* buffer larger than data. + * we need to see where break fits in buffer and whether + * we have part of it off the end. We set bstart to be the + * buffer offset, dtart to be the break data offset, and + * size to be the amount to copy + */ + if (dstart < bstart) { + /* break before actual buffer */ + dstart = bstart-dstart; /* offset in data */ + size -= dstart; /* amount to copy */ + bstart = 0; /* offset in buffer */ + + } else if (dstart + size > bstart + bsize) { + /* off end */ + bstart += bsize; /* end of buffer */ + size -= (dstart + size) - bstart; + bstart = bsize - size; /* come back into buffer enough */ + dstart = 0; /* start of data */ + + } else { /* normal case */ + bstart = dstart - bstart; /* offset in buffer */ + dstart = 0; + } + memcpy(buff+bstart, dvalue+dstart, size); +} + + void +BreakHide (const PID_LIST* plst, void* addr, int data, void* addr2) +{ + int idx; + + if (!plst->break_list) /* no breaks exist, so skip this */ + return; + + /* if breakpoints, replace */ + + for (idx = 1; idx < (int)plst->break_alloc; idx++) { + int type = plst->break_list[idx].type; + + if (type != BRKT_INSTR && type != BRKT_STEPEMUL) { + continue; + } + /* break, see if overlaps */ + if (BKPT_OVER (plst, idx, addr, data)) { + + /* overlaps, patch in old value */ + PatchBreak((char *)addr2, (UINT32)addr, data, + plst->break_list[idx].ee_loc, + (char *)&plst->break_list[idx].ee_type); + } + } +} + +/*----- Checking of breakpoint overwrites -----*/ + + /* + * BreakOverwrite - check if memory write does not involve addresses + * having software breakpoints. + */ + + int +BreakOverwrite (const PID_LIST* plst, const char* addr, unsigned int size) +{ + int idx; + + if (!plst->break_list) { /* No breaks exist */ + return 0; + } + + for (idx = 1; idx < (int)plst->break_alloc; idx++) { + int type = plst->break_list[idx].type; + + /* Consider only breakpoints involving modified memory */ + if (type != BRKT_INSTR && type != BRKT_STEPEMUL) { + continue; + } + if (BKPT_OVER (plst, idx, addr, size)) { + return -1; /* overlaps */ + } + } + return 0; +} + +/*----- Execution support -----*/ + + /* + * BreakStepRange - Start stepping in a range. + * + * Range is saved in breakpoint 0. + */ + + int +BreakStepRange (PID_LIST* plst, void* addr, int len) +{ + if (!plst->break_list) { + /* get list */ + if (BreakAlloc (plst, False) == -1) { /* must not be any memory */ + setErrno(ENOMEM); /* to be safe */ + return -1; /* fails */ + } + } + BKPT0 (plst)->range_start = (UINT32)addr; + BKPT0 (plst)->range_end = (UINT32)addr+(len-1); + return 0; +} + + /* + * If the Program Counter is changed, consider that the + * current breakpoint has not been reached yet. + */ + + void +BreakPcChanged (PID_LIST* plst) +{ + if (plst->break_list) { + /* clear break stuff */ + BKPT0 (plst)->clr_step = False; + } +} + + /* + * BreakStepOff - prepare stepping off a breakpoint. + */ + + int +BreakStepOff (const PID_LIST* plst, void** paddr2) +{ + if (plst->break_list && BKPT0 (plst)->clr_step) { + + /* need clear then step off break */ + int last = BKPT0 (plst)->last_break; + + /* clear break, step, then do exec */ + + *paddr2 = (void*) plst->break_list[last].ee_type; + + /* Need to clr_step after TgtPtrace() when wait() returns */ + return 1; + } + return 0; +} + + /* + * BreakSteppedOff - check if just stepped off a breakpoint + * and re-insert it into the code. + */ + + void +BreakSteppedOff (PID_LIST* plst) +{ + if (plst->break_list && BKPT0 (plst)->clr_step) { + int idx = BKPT0 (plst)->last_break; + int data; + + BKPT0 (plst)->clr_step = 0; + + /* + * Re-insert the breakpoint. + */ + data = TgtPtrace (RPT_PEEKTEXT, plst->pid, + (char *)plst->break_list [idx].ee_loc, 0, NULL); + assert (! IS_BREAK (data)); + TgtPtrace (RPT_POKETEXT, plst->pid, + (char *)plst->break_list[idx].ee_loc, + (int) SET_BREAK (data), NULL); + } +} + + + /* + * Returns whether a thread matches a breakpoint. + */ + + static int +BreakThreadMatch (xdr_break* xb, int thread) +{ + int slot; + + if (thread < 0) return 1; /* Break existence check only */ + + if (xb->thread_list [0] == 0) return 1; /* Universal break */ + + for (slot = 0; slot < BKPT_SLOTS; ++slot) { + if (xb->thread_list [slot] == 0) return 0; /* End of list */ + if (xb->thread_list [slot] == thread) return 1; /* Match */ + } + return 0; /* No matches found */ +} + + +int +BreakAdjustPC (PID_LIST* plst) +{ + /* + * BREAK_ADJ is the value by which the Program Counter + * has to be decremented after a software breakpoint + * is hit. It must be defined and can be zero. + */ +#if BREAK_ADJ + /* subtract back if necessary */ + plst->regs.REG_PC -= BREAK_ADJ; /* now write back */ + TgtPtrace(RPT_SETREGS, plst->pid, (char *)&plst->regs, 0, NULL); +#else + (void) plst; +#endif + return 0; +} + + +/* + * Identify the current breakpoint. The process just stopped. + */ + + int +BreakIdentify (PID_LIST* plst, int adjust, int thread) +{ + int foreignBkpt = 0; + int bidx; + + for (bidx = 1; bidx < (int) plst->break_alloc; bidx++) { + int type = plst->break_list[bidx].type; + + if ((type == BRKT_INSTR || type == BRKT_STEPEMUL) + && plst->regs.REG_PC - BREAK_ADJ + == plst->break_list[bidx].ee_loc) { /* found matching */ + if (!BreakThreadMatch (&plst->break_list[bidx], thread)) { + if (foreignBkpt == 0) { + foreignBkpt = bidx; + } + continue; + } + if (adjust) { + BreakAdjustPC (plst); + } + return bidx; + } + } + if (foreignBkpt) { + if (adjust) { + BreakAdjustPC (plst); + } + return -foreignBkpt; + } + return 0; +} -- cgit v1.2.3