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/librdbg/include/rdbg/i386/rdbg_f.h | 53 +++ c/src/librdbg/include/rdbg/i386/reg.h | 32 ++ c/src/librdbg/include/rdbg/rdbg.h | 53 +++ c/src/librdbg/include/rdbg/servrpc.h | 268 ++++++++++++ c/src/librdbg/src/Makefile.in | 96 +++++ c/src/librdbg/src/_servtgt.c | 360 ++++++++++++++++ c/src/librdbg/src/awk.svc | 65 +++ c/src/librdbg/src/i386/any/remdeb_f.x | 58 +++ c/src/librdbg/src/i386/excep.c | 233 ++++++++++ c/src/librdbg/src/i386/pc386/remdeb_f.x | 58 +++ c/src/librdbg/src/i386/rdbg_cpu_asm.s | 70 +++ c/src/librdbg/src/i386/rdbg_f.c | 130 ++++++ c/src/librdbg/src/ptrace.c | 335 ++++++++++++++ c/src/librdbg/src/rdbg.c | 139 ++++++ c/src/librdbg/src/remdeb.x | 544 +++++++++++++++++++++++ c/src/librdbg/src/servbkpt.c | 587 +++++++++++++++++++++++++ c/src/librdbg/src/servcon.c | 136 ++++++ c/src/librdbg/src/servrpc.c | 720 +++++++++++++++++++++++++++++++ c/src/librdbg/src/servtgt.c | 550 +++++++++++++++++++++++ c/src/librdbg/src/servtsp.c | 329 ++++++++++++++ c/src/librdbg/src/servutil.c | 130 ++++++ 21 files changed, 4946 insertions(+) create mode 100644 c/src/librdbg/include/rdbg/i386/rdbg_f.h create mode 100644 c/src/librdbg/include/rdbg/i386/reg.h create mode 100644 c/src/librdbg/include/rdbg/rdbg.h create mode 100644 c/src/librdbg/include/rdbg/servrpc.h create mode 100644 c/src/librdbg/src/Makefile.in create mode 100644 c/src/librdbg/src/_servtgt.c create mode 100644 c/src/librdbg/src/awk.svc create mode 100644 c/src/librdbg/src/i386/any/remdeb_f.x create mode 100644 c/src/librdbg/src/i386/excep.c create mode 100644 c/src/librdbg/src/i386/pc386/remdeb_f.x create mode 100644 c/src/librdbg/src/i386/rdbg_cpu_asm.s create mode 100644 c/src/librdbg/src/i386/rdbg_f.c create mode 100644 c/src/librdbg/src/ptrace.c create mode 100644 c/src/librdbg/src/rdbg.c create mode 100644 c/src/librdbg/src/remdeb.x create mode 100644 c/src/librdbg/src/servbkpt.c create mode 100644 c/src/librdbg/src/servcon.c create mode 100644 c/src/librdbg/src/servrpc.c create mode 100644 c/src/librdbg/src/servtgt.c create mode 100644 c/src/librdbg/src/servtsp.c create mode 100644 c/src/librdbg/src/servutil.c (limited to 'c/src/librdbg') diff --git a/c/src/librdbg/include/rdbg/i386/rdbg_f.h b/c/src/librdbg/include/rdbg/i386/rdbg_f.h new file mode 100644 index 0000000000..3fd087f50a --- /dev/null +++ b/c/src/librdbg/include/rdbg/i386/rdbg_f.h @@ -0,0 +1,53 @@ +/* + ************************************************************************** + * + * Component = RDBG + * Module = rdbg_f.h + * + * Synopsis = Machine-dependent header file + * + ************************************************************************** + */ + +#ifndef RDBG_F_H +#define RDBG_F_H + +#include +#include + +#define EFLAGS_TF 0x00100 + +typedef struct Exception_context_struct { + struct Exception_context_struct *next; + struct Exception_context_struct *previous; + Objects_Id id; + Objects_Id semaphoreId; + CPU_Exception_frame *ctx; +} Exception_context; + +extern int PushExceptCtx (Objects_Id Id, + Objects_Id semId, + CPU_Exception_frame *ctx); +extern int PopExceptCtx (Objects_Id Id); +extern Exception_context *GetExceptCtx (Objects_Id Id); +extern int Single_Step (CPU_Exception_frame* ctx); +extern int CheckForSingleStep (CPU_Exception_frame* ctx); +extern void BreakPointExcHdl (CPU_Exception_frame *ctx); +extern void CtxToRegs (const CPU_Exception_frame*,xdr_regs*); +extern void RegsToCtx (const xdr_regs*,CPU_Exception_frame*); + +extern void enterRdbg (); +extern void get_ctx_thread (Thread_Control *thread, + CPU_Exception_frame* ctx); +extern void set_ctx_thread (Thread_Control *thread, + CPU_Exception_frame* ctx); + +void copyback_data_cache_and_invalidate_instr_cache(); + +extern int ExitForSingleStep; + + +#endif + + + diff --git a/c/src/librdbg/include/rdbg/i386/reg.h b/c/src/librdbg/include/rdbg/i386/reg.h new file mode 100644 index 0000000000..d6b34f3c28 --- /dev/null +++ b/c/src/librdbg/include/rdbg/i386/reg.h @@ -0,0 +1,32 @@ + + +#define NBREGS 19 + +#define SS 18 +#define UESP 17 +#define EFL 16 +#define CS 15 +#define EIP 14 +#define ERR 13 +#define TRAPNO 12 +#define EAX 11 +#define ECX 10 +#define EDX 9 +#define EBX 8 +#define ESP 7 +#define EBP 6 +#define ESI 5 +#define EDI 4 +#define DS 3 +#define ES 2 +#define FS 1 +#define GS 0 + +typedef unsigned int regs[NBREGS]; + +/* To be used in common code */ +typedef regs REGS; + + + + diff --git a/c/src/librdbg/include/rdbg/rdbg.h b/c/src/librdbg/include/rdbg/rdbg.h new file mode 100644 index 0000000000..737edd5575 --- /dev/null +++ b/c/src/librdbg/include/rdbg/rdbg.h @@ -0,0 +1,53 @@ +/* + ************************************************************************** + * + * Component = RDBG + * + * Synopsis = rdbg.h + * + ************************************************************************** + */ + +#ifndef RDBG_H +#define RDBG_H + +#include +#include +#include +#include +#include /* For malloc() and free() prototypes */ +#include + +#define Malloc(size) malloc (size) +#define Free(block) free (block) +#define Realloc(block,size) realloc (block, size) +#define StrDup(str) strdup(str) + +#define LIST_PID 16 /* dynamic list of processes/tasks */ +#define LIST_PID_DEB 17 /* list of processes under debug now */ +#define LIST_PID_THREAD 18 /* list of threads for specific process */ +#define LIST_CONN 19 /* dynamic list of connections */ + + /* RTEMS internals */ +extern void remotedeb_2 (struct svc_req* rqstp, SVCXPRT* transp); +extern void setErrno (int error); +extern int getErrno (); +extern int ptrace (int request, int pid, char* addr, + int data, char* addr2); + +extern int TSP_RETRIES; +extern volatile int ExitForSingleStep; +extern volatile int justSaveContext; +extern volatile Objects_Id currentTargetThread; +extern volatile int CannotRestart; +extern volatile int TotalReboot; + + /* Missing RPC prototypes */ +SVCXPRT* svcudp_create (int fd); +void svc_processrequest (SVCXPRT* xprt, + u_long prog, u_long vers, + void (*dispatch)()); +int svcudp_enablecache (SVCXPRT *transp, u_long size); + +#endif /* !RDBG_H */ + diff --git a/c/src/librdbg/include/rdbg/servrpc.h b/c/src/librdbg/include/rdbg/servrpc.h new file mode 100644 index 0000000000..71198d4d1f --- /dev/null +++ b/c/src/librdbg/include/rdbg/servrpc.h @@ -0,0 +1,268 @@ + +#ifndef SERVRPC_H +#define SERVRPC_H + + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +extern int CONN_LIST_INC; +extern int PID_LIST_INC; +extern int TSP_RETRIES; +extern int BackPort; +extern char ActName[]; +extern int getId(); + + +#ifdef DDEBUG +int rdb_debug; /* True if env var RDB_DEBUG defined */ +extern const char* PtraceNames[]; /* list of ptrace requests for debug out */ +extern const char* BmsgNames[]; /* list of BMSG_xxx names */ +extern const char* PtraceName(int req); + +# define DPRINTF(a) (rdb_debug ? printk ("%d >>> ", getId()), printk a : 0) +#else +# define DPRINTF(a) /* suppress */ +#endif + + /* Macros for analyzing/creating process status values. Presently do + not need to be separated per target. Could even use WIF... */ + +#define STS_SIGNALLED(status) (((status) & 0xFF) == 0x7F) +#define STS_TERMONSIG(status) (((status) & 0xFF) && !STS_SIGNALLED(status)) +#define STS_TERMGETSIG(status) ((status) & 0x7F) +#define STS_MAKESIG(sig) (((sig) << 8) | 0x7f) +#define STS_GETSIG(status) ((status) >> 8) +#define STS_GETCODE(status) ((status) >> 8) + + +/* now define base types */ +#ifndef UCHAR_DEFINED +#define UCHAR_DEFINED /* to handle duplicate typedes */ +typedef unsigned char UCHAR; +typedef unsigned char UINT8; +typedef char INT8; +typedef unsigned short UINT16; +typedef short INT16; +typedef unsigned long UINT32; +typedef long INT32; +#endif /* UCHAR_DEFINED */ + +typedef long PID; /* generalized process id */ + +#ifndef True +# define True 1 +# define False 0 +typedef char Boolean; +#endif + +#define MAX_FILENAME 1024 /* largest filename with path */ +#define MAX_SEND 5 /* up to 5 pended outbound messages */ + +#define SERVER_VERS 1 + +typedef enum +{ /* message types */ + BMSG_WARM=1, /* warm test for network connection */ + BMSG_WAIT, /* wait change (stopped/started) */ + BMSG_BREAK, /* breakpoint changed */ + BMSG_EXEC_FAIL, /* exec failed from spawn */ + BMSG_DETACH, /* process detached from server */ + BMSG_KILLED, /* killed by server */ + BMSG_NOT_PRIM, /* no longer the primary owner */ + BMSG_NEW_PID /* the process was restart with new pid (in + context). Same ownership rules. */ +} BACK_MSG; + +typedef struct +{ /* this is the break_list[0] entry of pid */ + UCHAR clr_step; /* true if step off break in last_break */ + UCHAR pad1; /* Set if STEPEMUL breakpoints exist */ + UINT16 last_break; /* last breakpoint we stopped on (if break) */ + UINT32 range_start; /* start address of range */ + UINT32 range_end; /* end address inclusive */ +} BASE_BREAK; + +enum +{ /* last start values */ + LAST_NONE, /* stopped already */ + LAST_STEP, /* did a step last - do not send to prim */ + LAST_CONT, /* did a continue last */ + LAST_RANGE, /* in the middle of step-in-range */ + LAST_STEPOFF, /* stepped off break, now need to cont */ + LAST_KILLED, /* was killed by ptrace */ + LAST_DETACHED /* was detached by ptrace */ +}; +#define LAST_START 0x80 /* first execed. This is to handle MiX + bug where we need to start again */ + +typedef struct +{ /* one per open process */ + PID pid; /* process id (or 0 if free) */ + int state; /* status from last wait if stopped */ + UCHAR running; /* True if running, else stopped/term */ + /* now connection control over process */ + UCHAR owners; /* count of owners for notify and term release */ + UCHAR primary_conn; /* primary owner connection or 255=none */ + + UCHAR filler; /* Preserve alignment */ + + /* now break control and support */ + UINT8 last_start; /* LAST_xx start info for wait() */ + UINT8 flags; /* PIDFLG_xxx flags */ + UINT16 break_alloc; /* number of entries in break_list */ + xdr_break *break_list; /* list of breakpoints ([0] is BASE_BREAK) */ + /* now registers and other save information */ + xdr_regs regs; /* saved registers when stopped */ + int is_step; /* Was break or step (regs ambiguous often) */ + int stop_wanted; /* Don't ignore next stop */ + UINT32 thread; /* current stopped thread or -1 if none */ + char *name; /* full pathname or NULL if not known */ + INT32 child; /* child pid that manages the pid */ + UINT32 textStart; /* for relocating breakpoints at restart */ +} PID_LIST; +PID_LIST *pid_list; /* array of processes being managed */ +int pid_list_cnt; /* number of entries allocated */ +UINT16 last_break; /* unique handle generator for breaks */ +#define NO_PRIMARY ((UCHAR)-1) + +typedef union +{ /* an opaque net address */ + unsigned long l[4]; + unsigned char c[16]; /* corresponds to IP, enough for ChIPC */ +} NET_OPAQUE; + +typedef struct +{ /* one per connection */ + UCHAR in_use; /* True if in use */ + UCHAR debug_type; /* type of connection */ + UINT16 flags; /* flags for connection (CFLG_xxx) */ + NET_OPAQUE sender; /* opaque address for transport compare */ + NET_OPAQUE back_port; /* opaque address for transport event msgs */ + NET_OPAQUE route; /* optional route address */ + UINT32 pid_map[10]; /* map of pids owned relative to pid list */ + /* this allows up to 320 pids to be managed */ + UCHAR last_msg_num; /* msg number used last to handle multi-send */ + /* next field associated with UDP send messages */ + UCHAR retry; /* count of retries. If 0, ok. If not 0, we + are in active wait for reply to an event */ + UCHAR send_idx; /* current number of send's pended */ + struct SEND_LIST + { /* holds pending msgs */ + UCHAR send_type; /* BMSG_xxx type of message */ + UCHAR retry; /* number of times to retry */ + UINT16 spec; /* spec field */ + PID pid; /* pid if applies */ + UINT32 context; /* additional context if needed */ + } send_list[MAX_SEND]; /* pended list of messages being sent */ + char user_name[NAMEMAX]; /* name of user connecting in */ + /* next fields are managed at runtime to handle lists, command upload, and + command download. */ + enum {LST_NONE, LST_SPAWN, LST_INFO, LST_CMD_DOWN} list_type; + char *list; /* curr list we are sending/getting (malloced) */ + UINT16 list_sz; /* size of current list (string len) */ + UINT16 list_num; /* number of current list or position */ + UINT16 list_alloc; /* amount allocated so far */ + UINT16 list_save; /* used internally */ +} CONN_LIST; +CONN_LIST *conn_list; /* an array of connections */ +int conn_list_cnt; /* number allocated */ + + /* Operations over the PID map. Each indexes into long and then bit */ + /* 5 is log2 of 32, the number of bits in an int */ +#define PIDMAP_TEST(conn,idx) \ + (conn_list [conn].pid_map [(idx) >> 5] & (1 << ((idx) & 31))) + +#define PIDMAP_SET(conn,idx) \ + (conn_list [conn].pid_map [(idx) >> 5] |= 1 << ((idx) & 31)) + +#define PIDMAP_CLEAR(conn,idx) \ + (conn_list [conn].pid_map [(idx) >> 5] &= ~(1 << ((idx) &31))) + +#define PROC_TERMINATED(plst) \ + (!(plst)->running && !STS_SIGNALLED ((plst)->state)) + + +/* first define the Connection routines exported from servcon.c */ + +int ConnCreate (struct svc_req *rqstp, open_in *in); +void ConnDelete (int conn_idx, struct svc_req *rqstp, close_control control); + +void TspInit (int rpc_io_channel); +Boolean TspTranslateRpcAddr (struct svc_req *rqstp, NET_OPAQUE *opaque); +Boolean TspValidateAddr (NET_OPAQUE *opaque, NET_OPAQUE *sender); +int TspConnGetIndex (struct svc_req *rqstp); + +void TspSendWaitChange (int conn_idx, BACK_MSG msg, UINT16 spec, PID pid, + UINT32 context, Boolean force); +void TspSendMessage (int conn_idx, Boolean resend); +void TspMessageReceive (int conn_idx, PID pid); +char* TspGetHostName (int conn_idx); +void TgtCreateNew (PID pid, int conn_idx, INT32 child, + char *name, Boolean spawn); +Boolean TgtAttach (int conn_idx, PID pid); +void TgtNotifyWaitChange(PID pid, int status, Boolean exclude); +void TgtNotifyAll (int pid_idx, BACK_MSG msg, UINT16 spec, + UINT32 context, int exclude_conn, Boolean force); +void TgtDelete (PID_LIST*, int conn_idx, BACK_MSG notify); +int TgtKillAndDelete (PID_LIST *plst, struct svc_req *rqstp, Boolean term); +void TgtDetachCon (int conn_idx, int pid_idx, Boolean delete); +int TgtThreadList (PID_LIST*, unsigned* buf, unsigned int size); +int TgtGetThreadName (PID_LIST*, unsigned thLi, char* name); +int TgtPtrace (int req, PID pid, char *addr, int data, void *addr2); +int TgtRealPtrace (int req, PID pid, char *addr, int data, void *addr2); +Boolean TgtHandleChildChange(PID pid, int* status, int* unexp, + CPU_Exception_frame *ctx); +#ifdef DDEBUG + /* TgtDbgPtrace is a wrapper for RealPtrace() doing traces */ +int TgtDbgPtrace (int req, PID pid, char *addr, int data, void *addr2); +#endif + + +/* Information stored in "handle" */ +#define BKPT_INACTIVE 1 /* bkpt inactive for this execution */ +#define BKPT_ACTIVE 0 /* bkpt active for this execution */ + +int BreakOverwrite (const PID_LIST* plst,const char* addr, + unsigned int size); +int BreakSet (PID_LIST*, int conn_idx, xdr_break*); +int BreakSetAt (PID_LIST*, int conn_idx, unsigned long addr,break_type); +int BreakClear (PID_LIST*, int conn_idx, int handle); +int BreakGetIndex (PID_LIST*, void* addr); +int BreakGet (const PID_LIST*, int data, xdr_break*); +void BreakHide (const PID_LIST*, void*, int, void*); +int BreakStepOff (const PID_LIST*, void** paddr2); +void BreakSteppedOff (PID_LIST*); +int BreakRespawn (PID_LIST*); +int BreakIdentify (PID_LIST*, int adjust, int thread); +void BreakPcChanged (PID_LIST*); +int BreakStepRange (PID_LIST*, void* addr, int len); +void BreaksDisable (int pid); +void BreaksEnable (int pid); + +int TgtBreakRestoreOrig (int pid, void* addr, void* addr2); +void TgtBreakCancelStep (PID_LIST* plst); + +Boolean ListAlloc (char *buff, CONN_LIST *clst); +int FindPidEntry (int pid); + +open_out* RPCGENSRVNAME(open_connex_2_svc) (open_in *in, + struct svc_req *rqstp); +signal_out* RPCGENSRVNAME(send_signal_2_svc) (signal_in *in, + struct svc_req *rqstp); +ptrace_out* RPCGENSRVNAME(ptrace_2_svc) (ptrace_in *in, + struct svc_req *rqstp); +wait_out* RPCGENSRVNAME(wait_info_2_svc) (wait_in *in, + struct svc_req *rqstp); +#endif /* !SERVRPC_H */ + diff --git a/c/src/librdbg/src/Makefile.in b/c/src/librdbg/src/Makefile.in new file mode 100644 index 0000000000..8d4a72ca96 --- /dev/null +++ b/c/src/librdbg/src/Makefile.in @@ -0,0 +1,96 @@ +# +# $Id$ +# + +@SET_MAKE@ +srcdir = @srcdir@ +VPATH = @srcdir@:@srcdir@/$(RTEMS_CPU) +RTEMS_ROOT = @top_srcdir@ +PROJECT_ROOT = @PROJECT_ROOT@ + +LIBNAME=librdbg.a +LIB=${ARCH}/${LIBNAME} + +# C and C++ source names, if any, go here -- minus the .c or .cc +C_PIECES= rdbg servcon servbkpt servrpc excep \ + servtgt servtsp servutil _servtgt rdbg_f \ + ptrace +C_FILES=$(C_PIECES:%=%.c) +C_O_FILES=$(C_PIECES:%=${ARCH}/%.o) + +# Asm source names, if any, go here -- minus the .s +ASM_PIECES= rdbg_cpu_asm +ASM_FILES=$(ASM_PIECES:%=%.s) +ASM_O_FILES=$(ASM_PIECES:%=${ARCH}/%.o) + +# Generated C source names, if any, go here -- minus the .c +C_GEN_PIECES= remdeb_xdr remdeb_svc +C_GEN_FILES=$(C_GEN_PIECES:%=%.c) +C_GEN_O_FILES=$(C_GEN_PIECES:%=${ARCH}/%.o) + +# H source names, if any, go here -- minus the .h +H_PIECES=remdeb +H_FILES=$(H_PIECES:%=%.h) + +# X source names, if any, go here -- minus the .x +X_FILE1=remdeb.x +X_FILE2=remdeb_f.x +X_FILES=$(X_FILE1) $(X_FILE2) + +SRCS=$(C_FILES) $(ASM_FILES) +OBJS=$(X_FILES) $(H_FILES) $(C_GEN_FILES) $(C_GEN_O_FILES) $(C_O_FILES) $(ASM_O_FILES) + +RPCGEN=rpcgen +AWK=awk + +include $(RTEMS_ROOT)/make/custom/$(RTEMS_BSP).cfg +include $(RTEMS_ROOT)/make/lib.cfg + +# +# Add local stuff here using += +# + +DEFINES += +CPPFLAGS += +CFLAGS += + +# +# Add your list of files to delete here. The config files +# already know how to delete some stuff, so you may want +# to just run 'make clean' first to see what gets missed. +# 'make clobber' already includes 'make clean' +# + +CLEAN_ADDITIONS += $(LIB) $(H_FILES) $(X_FILES) $(C_GEN_FILES) +CLOBBER_ADDITIONS += + +all: ${ARCH} $(LIB) + $(INSTALL_VARIANT) -m 644 $(LIB) ${PROJECT_RELEASE}/lib + +$(LIB): $(SRCS) ${OBJS} + $(make-library) + +remdeb.h: $(X_FILES) + @rm -f $@ + $(RPCGEN) -h remdeb.x -o $@ + @rm -f $(PROJECT_INCLUDE)/rdbg/$@ + $(INSTALL) -m 444 $@ $(PROJECT_INCLUDE)/rdbg + +remdeb_xdr.c: $(X_FILES) + @rm -f $@ + $(RPCGEN) -c remdeb.x -o $@ + +remdeb_svc.c: $(X_FILES) + @rm -f $@ tmpSvc.c + $(RPCGEN) -s udp remdeb.x -o tmpSvc.c + $(AWK) -f @srcdir@/awk.svc THEPROG="remdeb.h" tmpSvc.c >$@ + @rm -f tmpSvc.c + +preinstall: + @rm -f $(X_FILES) + @cp @srcdir@/$(X_FILE1) . + @cp @srcdir@/$(RTEMS_CPU)/$(RTEMS_BSP)/$(X_FILE2) . + + + + diff --git a/c/src/librdbg/src/_servtgt.c b/c/src/librdbg/src/_servtgt.c new file mode 100644 index 0000000000..184415a1e2 --- /dev/null +++ b/c/src/librdbg/src/_servtgt.c @@ -0,0 +1,360 @@ +/* + ============================================================================ + _SERVTGT + ============================================================================ +*/ + + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +extern void rtems_exception_prologue_50(); + + +#ifdef DDEBUG +#define Ptrace TgtDbgPtrace +#else +#define Ptrace TgtRealPtrace +#endif + +extern int errno; + +rtems_id eventTaskId; +rtems_id serializeSemId; +rtems_id wakeupEventSemId; + +CPU_Exception_frame Idle_frame; + +cpuExcHandlerType old_currentExcHandler; + +/* ----------------------------------------------------------------- + TgtRealPtrace - lowest level ptrace() wrapper + ----------------------------------------------------------------- */ + + int +TgtRealPtrace(int req, PID aid, char* addr, int d, void* addr2) +{ + return ptrace(req, aid, addr, d, addr2); +} + + +/* ----------------------------------------------------------------- + Maping of hardware exceptions into Unix-like signal numbers. + It is identical to the one used by the PM and the AM. + ----------------------------------------------------------------- */ + + int +ExcepToSig (int excep) +{ + switch (excep) { + + case I386_EXCEPTION_MATH_COPROC_UNAVAIL: + case I386_EXCEPTION_I386_COPROC_SEG_ERR: + case I386_EXCEPTION_FLOAT_ERROR: + case I386_EXCEPTION_BOUND: + return SIGFPE; + + case I386_EXCEPTION_DEBUG: + case I386_EXCEPTION_BREAKPOINT: + case I386_EXCEPTION_ENTER_RDBG: + return SIGTRAP; + + case I386_EXCEPTION_OVERFLOW: + case I386_EXCEPTION_DIVIDE_BY_ZERO: + case I386_EXCEPTION_ILLEGAL_INSTR: + return SIGILL; + + case I386_EXCEPTION_SEGMENT_NOT_PRESENT: + case I386_EXCEPTION_STACK_SEGMENT_FAULT: + case I386_EXCEPTION_GENERAL_PROT_ERR: + case I386_EXCEPTION_PAGE_FAULT: + return SIGSEGV; + + default: + break; + } + return SIGKILL; +} + +/* ----------------------------------------------------------------------- + TgtChange() is called when the system stops. + It informs the generic layers must be informed of + that fact. + ----------------------------------------------------------------------- */ + + static int +TgtChange (PID pid, CPU_Exception_frame* ctx, int status) +{ + + if (TgtHandleChildChange (pid, &status, NULL, ctx)) { + TgtNotifyWaitChange (pid, status, -1); + } + + return 0; +} + +/* ----------------------------------------------------------------------- + eventTask + ----------------------------------------------------------------------- */ + +rtems_task eventTask( rtems_task_argument pid) +{ + Exception_context *ctx; + + DPRINTF (("event task: pid %d\n", pid)); + + + /* + * we spend all our time waiting for a semaphore. + * If wait change, we send info + */ + + for (;;){ + DPRINTF (("Event Task: wait event\n")); + rtems_semaphore_obtain(wakeupEventSemId, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + DPRINTF (("Event Task: wake up !!!!!!!!!!!!!\n")); + + errno = 0; + ctx = GetExceptCtx(currentTargetThread); + + CheckForSingleStep(ctx->ctx); + + TgtChange(pid, ctx->ctx,STS_MAKESIG(ExcepToSig(ctx->ctx->idtIndex))); + + } +} + +/* ------------------------------------------------------------------- + MyThreadIdle - + + This task is used to initiate the exception mechanism: + It calls the enterDebug function with justSaveContext=1 + only to push a first valid context in the list + ---------------------------------------------------------------------*/ + +rtems_task MyThreadIdle(rtems_task_argument argument) +{ + enterRdbg(); + rtems_task_delete( RTEMS_SELF ); +} + +/* ----------------------------------------------------------------------- + TgtAttach - attach to a process that is running without control. + + Notes: + - this function performs a ptrace ATTACH equivalent (attaching to a + process that we do not control now). + ----------------------------------------------------------------------- */ + +Boolean TgtAttach( + int conn_idx, /* client that is requesting */ + PID pid) /* process to attach to */ +{ + rtems_name task_name; + rtems_status_code status; + rtems_id debugId; + interrupt_gate_descriptor *currentIdtEntry; + unsigned limit; + unsigned level; + + errno = 0; + + DPRINTF (("TgtAttach pid=%d\n",pid)); + + Ptrace(RPT_ATTACH, pid, NULL, 0, NULL); + if (errno) + return(False); /* failed */ + + TgtCreateNew(pid, conn_idx, 0, NULL, False); + + + /* + * Connect the Exception used to debug + */ + i386_get_info_from_IDTR (¤tIdtEntry, &limit); + + _CPU_ISR_Disable(level); + create_interrupt_gate_descriptor (¤tIdtEntry[50], rtems_exception_prologue_50); + _CPU_ISR_Enable(level); + + old_currentExcHandler = _currentExcHandler; + _currentExcHandler = BreakPointExcHdl ; + + + /* + * Create the attach debuger task + */ + task_name = rtems_build_name( 'E', 'v', 'n', 't' ); + if ((status = rtems_task_create( task_name, 10, 24576, + RTEMS_INTERRUPT_LEVEL(0), + RTEMS_DEFAULT_ATTRIBUTES | RTEMS_SYSTEM_TASK, + &eventTaskId )) + != RTEMS_SUCCESSFUL){ + printf("status = %d\n",status); + rtems_panic ("Can't create task.\n"); + } + + status = rtems_task_start(eventTaskId, eventTask, pid); + + status = rtems_semaphore_create (rtems_build_name('D', 'B', 'G', 's'), + 1, + RTEMS_FIFO | + RTEMS_COUNTING_SEMAPHORE | + RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | + RTEMS_LOCAL, + 0, + &serializeSemId); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't create serialize semaphore: `%s'\n",rtems_status_text(status)); + + status = rtems_semaphore_create (rtems_build_name('D', 'B', 'G', 'w'), + 0, + RTEMS_FIFO | + RTEMS_COUNTING_SEMAPHORE | + RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | + RTEMS_LOCAL, + 0, + &wakeupEventSemId); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't create wakeup semaphore: `%s'\n",rtems_status_text(status)); + + /* + * Create the MyThreadIdle task to init Exception mechanism + */ + task_name = rtems_build_name( 'R', 'i', 'n', 'i' ); + if ((status = rtems_task_create( task_name, 10, 24576, + RTEMS_INTERRUPT_LEVEL(0), + RTEMS_DEFAULT_ATTRIBUTES, + &debugId )) + != RTEMS_SUCCESSFUL){ + printf("status = %d\n",status); + rtems_panic ("Can't create task.\n"); + } + + status = rtems_task_start(debugId, MyThreadIdle, pid); + + return(True); +} + +/* ----------------------------------------------------------------------- + TgtPtrace - handle ptrace requests for server. + ----------------------------------------------------------------------- */ + +int TgtPtrace( + int req, + PID pid, + char *addr, + int data, + void *addr2) +{ + if ((req == RPT_SINGLESTEP || req == RPT_CONT) + && addr2) /* clear then step */ + { /* addr2 is the old value */ + int ret; + + errno = 0; + TgtBreakRestoreOrig (pid, addr, addr2); + ret = Ptrace(RPT_SINGLESTEP, pid, addr, data, NULL); /* step over */ + if (ret) /* error, cannot single-step */ + { + int pid_idx = FindPidEntry (pid); + TgtBreakCancelStep (&pid_list [pid_idx]); + } + return(ret); /* failed or done */ + } + else + return(Ptrace(req, pid, addr, data, addr2)); /* normal call */ +} + +/* ----------------------------------------------------------------- + TgtGetThreadName - get thread name + --------------------------------------------------------------- */ + +int TgtGetThreadName ( + PID_LIST *plst, /* Process entry */ + unsigned Id, /* Thread ID */ + char *ThrName) /* Thread name */ +{ + int index; + unsigned name; + + if ( Id <_Objects_Information_table[OBJECTS_RTEMS_TASKS]->maximum_id && + Id >_Objects_Information_table[OBJECTS_RTEMS_TASKS]->minimum_id) { + + index = Id - _Objects_Information_table[OBJECTS_RTEMS_TASKS]->minimum_id; + name = *(unsigned*)(_Objects_Information_table[OBJECTS_RTEMS_TASKS]->local_table[1+index]->name); + ThrName[0] = (char)((name >> 24) & 0xFF ); + ThrName[1] = (char)((name >> 16) & 0xFF ); + ThrName[2] = (char)((name >> 8) & 0xFF ); + ThrName[3] = (char)( name & 0xFF ); + ThrName[4] = 0x0; + return 0; + } + + if ( Id <_Objects_Information_table[OBJECTS_POSIX_THREADS]->maximum_id && + Id >_Objects_Information_table[OBJECTS_POSIX_THREADS]->minimum_id) { + + index = Id - _Objects_Information_table[OBJECTS_POSIX_THREADS]->minimum_id; + name = *(unsigned*)(_Objects_Information_table[OBJECTS_POSIX_THREADS]->local_table[1+index]->name); + ThrName[0] = (char)((name >> 24) & 0xFF ); + ThrName[1] = (char)((name >> 16) & 0xFF ); + ThrName[2] = (char)((name >> 8) & 0xFF ); + ThrName[3] = (char)( name & 0xFF ); + ThrName[4] = 0x0; + return 0; + } + + + return -1; + +} + +/* ----------------------------------------------------------------- + TgtThreadList - return all the threads in the system + ----------------------------------------------------------------- */ + + int +TgtThreadList ( + PID_LIST* plst, /* Process entry */ + unsigned* threads, /* Output buffer */ + unsigned size) /* Output buffer size */ +{ + int curr = 0; + Objects_Id id; + unsigned index; + + id = _Objects_Information_table[OBJECTS_RTEMS_TASKS]->minimum_id; + + while (id < _Objects_Information_table[OBJECTS_RTEMS_TASKS]->maximum_id){ + index = id - _Objects_Information_table[OBJECTS_RTEMS_TASKS]->minimum_id; + if ( _Objects_Information_table[OBJECTS_RTEMS_TASKS]->local_table[1+index] != NULL){ + threads[curr] = (unsigned) id; + curr++; + } + id ++; + } + + id = _Objects_Information_table[OBJECTS_POSIX_THREADS]->minimum_id; + + while (id < _Objects_Information_table[OBJECTS_POSIX_THREADS]->maximum_id){ + index = id - _Objects_Information_table[OBJECTS_POSIX_THREADS]->minimum_id; + if ( _Objects_Information_table[OBJECTS_POSIX_THREADS]->local_table[1+index] != NULL){ + threads[curr] = (unsigned) id; + curr++; + } + id ++; + } + + return curr; +} diff --git a/c/src/librdbg/src/awk.svc b/c/src/librdbg/src/awk.svc new file mode 100644 index 0000000000..79e24a9d90 --- /dev/null +++ b/c/src/librdbg/src/awk.svc @@ -0,0 +1,65 @@ +######################################################################### +# +# Component: RDBG +# Module: awk.svc +# +# Synopsis: AWK script which transforms the server skeleton produced +# by rpcgen(1) into something suitable for RDB servers. +# +######################################################################### + +BEGIN { + headerstarted = 0 + withinproc = 0 + brack = 0 +} + +$1 ~ /^\/\*HEADER_START\*\/$/ { + headerstarted = 1 + printf("#include \n"); + printf("#include \n"); + printf("#include \n"); + printf("#include \n"); + printf("#include \n"); + printf("#include \n"); + printf("#include \n"); + printf("#include \n", THEPROG); + printf("#define fprintf(a,b) printf(b)\n"); + printf("#define msgout(a) printf(a)\n") + printf("#define _msgout(a) fprintf(stderr,a)\n"); +} + +$1 ~ /^\/\*HEADER_END\*\/$/ { + headerstarted = 0 +} + +{ + if (headerstarted == 1) { + print $0 + } else if ($1 ~ /.*_2.*/) { + withinproc = 1 + printf("void\n"); + print $0 + } else if (withinproc == 1) { + if ($1 == "switch") { + print "\tDPRINTF ((\"remotedeb_2: %s (%d)\\n\", " + print "\t\t(unsigned) rqstp->rq_proc < " + print "\t\t(unsigned) (sizeof names / sizeof names[0]) ?" + print "\t\tnames [rqstp->rq_proc] : \"???\", " + print "\t\t(int) rqstp->rq_proc));\n" + } + for (i = 1; i <= NF; i++) { + if ($i == "{") { + brack++; + } else if ($i == "}") { + brack--; + if (brack == 0) { + withinproc = 0; + } + } + } + if ($1 != "_rpcsvcdirty" ) { + print $0 + } + } +} diff --git a/c/src/librdbg/src/i386/any/remdeb_f.x b/c/src/librdbg/src/i386/any/remdeb_f.x new file mode 100644 index 0000000000..0ff52d1307 --- /dev/null +++ b/c/src/librdbg/src/i386/any/remdeb_f.x @@ -0,0 +1,58 @@ +/* + ************************************************************************** + * + * Component = rdblib + * + * Synopsis = remdeb_f.x + * + * + ************************************************************************** + */ + +struct xdr_regs +{ + unsigned int tabreg[19]; +}; + +#ifdef RPC_HDR + +%/* now define register macros to apply to xdr_reg struct */ +% +%#define GS 0 +%#define FS 1 +%#define ES 2 +%#define DS 3 +%#define EDI 4 +%#define ESI 5 +%#define EBP 6 +%#define ESP 7 +%#define EBX 8 +%#define EDX 9 +%#define ECX 10 +%#define EAX 11 +%#define TRAPNO 12 +%#define ERR 13 +%#define EIP 14 +%#define CS 15 +%#define EFL 16 +%#define UESP 17 +%#define SS 18 +% +%#define REG_PC tabreg[EIP] /* PC (eip) register offset */ +%#define REG_SP tabreg[UESP] /* SP (uesp) register offset */ +%#define REG_FP tabreg[EBP] /* FP (ebp) register offset */ + +%/* now define the BREAKPOINT mask technique to a long word */ +%#define SET_BREAK(l) ((l&0xFFFFFF00) | 0xCC) +%#define IS_BREAK(l) (((l) & 0xFF) == 0xCC) +%#define ORG_BREAK(c,p) (((c) & 0xFFFFFF00) | ((p) & 0xFF)) +%#define IS_STEP(regs) (regs.tabreg[TRAPNO] == 1) /* was step and not break */ +%#define BREAK_ADJ 1 /* must subtract one from address after bp */ +%#define BREAK_SIZE 1 /* Breakpoint occupies one byte */ + +%#define TARGET_PROC_TYPE 0 + +#endif + + + diff --git a/c/src/librdbg/src/i386/excep.c b/c/src/librdbg/src/i386/excep.c new file mode 100644 index 0000000000..5ca1fd8df6 --- /dev/null +++ b/c/src/librdbg/src/i386/excep.c @@ -0,0 +1,233 @@ +/* + ************************************************************************** + * + * Component = + * + * Synopsis = rdbg/i386/excep.c + * + ************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include + + +extern rtems_id serializeSemId; +extern rtems_id wakeupEventSemId; + + +unsigned int NbExceptCtx; +volatile unsigned int NbSerializedCtx; +Exception_context *FirstCtx = NULL; +Exception_context *LastCtx = NULL; + +CPU_Exception_frame SavedContext; + + +/********* Save an exception context at the end of a list *****/ + +int PushExceptCtx ( Objects_Id Id, Objects_Id semId, CPU_Exception_frame *ctx ) { + + Exception_context *SaveCtx; + + SaveCtx = (Exception_context *)malloc(sizeof(Exception_context)); + if (SaveCtx == NULL) + rtems_panic("Can't allocate memory to save Exception context"); + + SaveCtx->id = Id; + SaveCtx->ctx = ctx; + SaveCtx->semaphoreId = semId; + SaveCtx->previous = NULL; + SaveCtx->next = NULL; + + if (FirstCtx == NULL){ /* initialization */ + FirstCtx = SaveCtx; + LastCtx = SaveCtx; + NbExceptCtx = 1; + } + else { + NbExceptCtx ++; + LastCtx->next = SaveCtx; + SaveCtx->previous = LastCtx; + LastCtx = SaveCtx; + } + return 0; +} + +/********* Save an temporary exception context in a ******/ +/********* global variable ******/ + +int PushSavedExceptCtx ( Objects_Id Id, CPU_Exception_frame *ctx ) { + + memcpy (&(SavedContext), ctx, sizeof(CPU_Exception_frame)); + return 0; +} + + +/****** Remove the context of the specified Id thread *********/ +/****** If Id = -1, then return the first context *********/ + + +int PopExceptCtx ( Objects_Id Id ) { + + Exception_context *ExtractCtx; + + if (FirstCtx == NULL) return -1; + + if (Id == -1) { + ExtractCtx = LastCtx; + LastCtx = LastCtx->previous; + free(ExtractCtx); + NbExceptCtx --; + return 0; + } + + ExtractCtx = LastCtx; + + while (ExtractCtx->id != Id && ExtractCtx != NULL) { + ExtractCtx = ExtractCtx->previous; + } + + if (ExtractCtx == NULL) + return -1; + + if ( ExtractCtx->previous != NULL) + (ExtractCtx->previous)->next = ExtractCtx->next; + + if ( ExtractCtx->next != NULL) + (ExtractCtx->next)->previous = ExtractCtx->previous; + + if (ExtractCtx == FirstCtx) + FirstCtx = FirstCtx->next; + else + if (ExtractCtx == LastCtx) + LastCtx = LastCtx->previous; + + free(ExtractCtx); + NbExceptCtx --; + return 0; +} + +/****** Return the context of the specified Id thread *********/ +/****** If Id = -1, then return the first context *********/ + + +Exception_context *GetExceptCtx ( Objects_Id Id ) { + + Exception_context *ExtractCtx; + + if (FirstCtx == NULL) return NULL; + + if (Id == -1) { + return LastCtx; + } + + ExtractCtx = LastCtx; + + while (ExtractCtx->id != Id && ExtractCtx != NULL) { + ExtractCtx = ExtractCtx->previous; + } + + if (ExtractCtx == NULL) + return NULL; + + return ExtractCtx; +} + +/*----- Breakpoint Exception management -----*/ + + /* + * Handler for Breakpoint Exceptions : + * software breakpoints. + */ + +void +BreakPointExcHdl(CPU_Exception_frame *ctx) +{ + rtems_status_code status; + rtems_id continueSemId; + + if ( (justSaveContext) && (ctx->idtIndex == I386_EXCEPTION_ENTER_RDBG) ) { + PushSavedExceptCtx (_Thread_Executing->Object.id, ctx); + justSaveContext = 0; + } + else { + if (ctx->idtIndex != DEBUG){ + NbSerializedCtx++; + rtems_semaphore_obtain(serializeSemId, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + NbSerializedCtx--; + } + + currentTargetThread = _Thread_Executing->Object.id; + +#ifdef DDEBUG + printk("----------------------------------------------------------\n"); + printk("Exception %d caught at PC %x by thread %d\n", + ctx->idtIndex, + ctx->eip, + _Thread_Executing->Object.id); + printk("----------------------------------------------------------\n"); + printk("Processor execution context at time of the fault was :\n"); + printk("----------------------------------------------------------\n"); + printk(" EAX = %x EBX = %x ECX = %x EDX = %x\n", + ctx->eax, ctx->ebx, ctx->ecx, ctx->edx); + printk(" ESI = %x EDI = %x EBP = %x ESP = %x\n", + ctx->esi, ctx->edi, ctx->ebp, ctx->esp0); + printk("----------------------------------------------------------\n"); + printk("Error code pushed by processor itself (if not 0) = %x\n", + ctx->faultCode); + printk("----------------------------------------------------------\n\n"); +#endif + + status = rtems_semaphore_create (rtems_build_name('D', 'B', 'G', 'c'), + 0, + RTEMS_FIFO | + RTEMS_COUNTING_SEMAPHORE | + RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | + RTEMS_LOCAL, + 0, + &continueSemId); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't create continue semaphore: `%s'\n",rtems_status_text(status)); + + PushExceptCtx (_Thread_Executing->Object.id, continueSemId, ctx); + + switch (ctx->idtIndex){ + case I386_EXCEPTION_DEBUG: + DPRINTF((" DEBUG EXCEPTION !!!\n")); + ctx->eflags &= ~EFLAGS_TF; + ExitForSingleStep-- ; + rtems_semaphore_release( wakeupEventSemId ); + break; + + case I386_EXCEPTION_BREAKPOINT: + DPRINTF((" BREAKPOINT EXCEPTION !!!\n")); + rtems_semaphore_release( wakeupEventSemId ); + break; + + case I386_EXCEPTION_ENTER_RDBG: + DPRINTF((" ENTER RDBG !!!\n")); + rtems_semaphore_release( wakeupEventSemId ); + break; + + default: + DPRINTF((" OTHER EXCEPTION !!!\n")); + rtems_semaphore_release( wakeupEventSemId ); + break; + } + + rtems_semaphore_obtain(continueSemId, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + + PopExceptCtx (_Thread_Executing->Object.id); + rtems_semaphore_delete(continueSemId); + } +} + + + diff --git a/c/src/librdbg/src/i386/pc386/remdeb_f.x b/c/src/librdbg/src/i386/pc386/remdeb_f.x new file mode 100644 index 0000000000..0ff52d1307 --- /dev/null +++ b/c/src/librdbg/src/i386/pc386/remdeb_f.x @@ -0,0 +1,58 @@ +/* + ************************************************************************** + * + * Component = rdblib + * + * Synopsis = remdeb_f.x + * + * + ************************************************************************** + */ + +struct xdr_regs +{ + unsigned int tabreg[19]; +}; + +#ifdef RPC_HDR + +%/* now define register macros to apply to xdr_reg struct */ +% +%#define GS 0 +%#define FS 1 +%#define ES 2 +%#define DS 3 +%#define EDI 4 +%#define ESI 5 +%#define EBP 6 +%#define ESP 7 +%#define EBX 8 +%#define EDX 9 +%#define ECX 10 +%#define EAX 11 +%#define TRAPNO 12 +%#define ERR 13 +%#define EIP 14 +%#define CS 15 +%#define EFL 16 +%#define UESP 17 +%#define SS 18 +% +%#define REG_PC tabreg[EIP] /* PC (eip) register offset */ +%#define REG_SP tabreg[UESP] /* SP (uesp) register offset */ +%#define REG_FP tabreg[EBP] /* FP (ebp) register offset */ + +%/* now define the BREAKPOINT mask technique to a long word */ +%#define SET_BREAK(l) ((l&0xFFFFFF00) | 0xCC) +%#define IS_BREAK(l) (((l) & 0xFF) == 0xCC) +%#define ORG_BREAK(c,p) (((c) & 0xFFFFFF00) | ((p) & 0xFF)) +%#define IS_STEP(regs) (regs.tabreg[TRAPNO] == 1) /* was step and not break */ +%#define BREAK_ADJ 1 /* must subtract one from address after bp */ +%#define BREAK_SIZE 1 /* Breakpoint occupies one byte */ + +%#define TARGET_PROC_TYPE 0 + +#endif + + + diff --git a/c/src/librdbg/src/i386/rdbg_cpu_asm.s b/c/src/librdbg/src/i386/rdbg_cpu_asm.s new file mode 100644 index 0000000000..f0942310ce --- /dev/null +++ b/c/src/librdbg/src/i386/rdbg_cpu_asm.s @@ -0,0 +1,70 @@ +/* cpu_asm.s + * + * This file contains all assembly code for the Intel i386 implementation + * of RDBG. + * + */ + +#include + + BEGIN_CODE + +/* + * void copyback_data_cache_and_invalidate_instr_cache() + * + * This routine performs a copy of the data cache + * and invalidate the instruction cache + */ + + .p2align 1 + PUBLIC (copyback_data_cache_and_invalidate_instr_cache) + +SYM (copyback_data_cache_and_invalidate_instr_cache): + wbinvd + ret + + + +/* + * void enterRdbg(void) + * + * This function perform a call to the exception 19 + * It is used : + * 1 - in the user code, to simulate a Breakpoint. + * (with justSaveContext = 0) + * 2 - in the RDBG code, to push a ctx in the list. + * (with justSaveContext = 1) + * + * In most of case, it will be use as described in 1. + * The 2nd possibility will be used by RDBG to obtain + * its own ctx + */ + + PUBLIC (enterRdbg) + +SYM (enterRdbg): + int $50 + ret + + +/* + * void rtems_exception_prologue_50(void) + * + * Exception 50 is used to enter Rdbg + * + */ + + .p2align 4 + + PUBLIC (rtems_exception_prologue_50) + PUBLIC (_Exception_Handler) + +SYM (rtems_exception_prologue_50): + pushl $ 0 + pushl $ 50 + jmp SYM(_Exception_Handler) ; + + +END_CODE + +END diff --git a/c/src/librdbg/src/i386/rdbg_f.c b/c/src/librdbg/src/i386/rdbg_f.c new file mode 100644 index 0000000000..aeb07fcbe8 --- /dev/null +++ b/c/src/librdbg/src/i386/rdbg_f.c @@ -0,0 +1,130 @@ +/* + ************************************************************************** + * + * Component = + * + * Synopsis = rdbg/i386/rdbg_f.c + * + ************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include + + +void +CtxToRegs (const CPU_Exception_frame* ctx, xdr_regs* regs) +{ + regs->tabreg [GS] = 0; + regs->tabreg [FS] = 0; + regs->tabreg [ES] = 0; + regs->tabreg [DS] = 0; + regs->tabreg [EDI] = ctx->edi; + regs->tabreg [ESI] = ctx->esi; + regs->tabreg [EBP] = ctx->ebp; + regs->tabreg [ESP] = ctx->esp0; + regs->tabreg [EBX] = ctx->ebx; + regs->tabreg [EDX] = ctx->edx; + regs->tabreg [ECX] = ctx->ecx; + regs->tabreg [EAX] = ctx->eax; + regs->tabreg [TRAPNO] = ctx->idtIndex; + regs->tabreg [ERR] = ctx->faultCode; + regs->tabreg [EIP] = ctx->eip; + regs->tabreg [CS] = ctx->cs & 0xFFFF; + regs->tabreg [EFL] = ctx->eflags; +} + + + void +RegsToCtx (const xdr_regs* regs, CPU_Exception_frame* ctx) +{ + ctx->edi = regs->tabreg [EDI]; + ctx->esi = regs->tabreg [ESI]; + ctx->ebp = regs->tabreg [EBP]; + ctx->esp0 = regs->tabreg [ESP]; + ctx->ebx = regs->tabreg [EBX]; + ctx->edx = regs->tabreg [EDX]; + ctx->ecx = regs->tabreg [ECX]; + ctx->eax = regs->tabreg [EAX]; + ctx->idtIndex = regs->tabreg [TRAPNO]; + ctx->faultCode = regs->tabreg [ERR]; + ctx->eip = regs->tabreg [EIP]; + ctx->cs = regs->tabreg [CS]; + ctx->eflags = regs->tabreg [EFL]; +} + +void +get_ctx_thread( Thread_Control *thread, CPU_Exception_frame* ctx) +{ + ctx->edi = thread->Registers.edi; + ctx->esi = thread->Registers.esi; + ctx->ebp = (unsigned32)(thread->Registers.ebp); + ctx->esp0 = (unsigned32)(thread->Registers.esp); + ctx->ebx = thread->Registers.ebx; + ctx->edx = 0; + ctx->ecx = 0; + ctx->eax = 0; + ctx->idtIndex = 0; + ctx->faultCode = 0; + ctx->eip = *(unsigned int*)(thread->Registers.esp); + ctx->cs = 0; + ctx->eflags = thread->Registers.eflags; +} + +void +set_ctx_thread( Thread_Control *thread, CPU_Exception_frame* ctx) +{ + thread->Registers.edi = ctx->edi; + thread->Registers.esi = ctx->esi; + thread->Registers.ebp = (void*)(ctx->ebp); + thread->Registers.esp = (void*)(ctx->esp0); + thread->Registers.ebx = ctx->ebx; + thread->Registers.eflags = ctx->eflags; +} + + + +int +Single_Step(CPU_Exception_frame* ctx) +{ + /* Check if not already set */ + if ((ctx->eflags & EFLAGS_TF) != 0 || ExitForSingleStep != 0) { + /* Check coherency */ + assert ((ctx->eflags & EFLAGS_TF) != 0); + assert (ExitForSingleStep != 0); + return 0; + } + ctx->eflags |= EFLAGS_TF; /* eflags */ + ++ExitForSingleStep; + + return 0; +} + + int +CheckForSingleStep (CPU_Exception_frame* ctx) +{ + if (ExitForSingleStep) { + /* + * This functions can be called both from + * INT1 and INT3 handlers. In case it is + * called from INT3, need to clear TF. + */ + ctx->eflags &= ~EFLAGS_TF; + ExitForSingleStep = 0; + return 1; + } + return 0; +} + +void +CancelSingleStep (CPU_Exception_frame* ctx) +{ + /* Cancel scheduled SS */ + ctx->eflags &= ~EFLAGS_TF; + ExitForSingleStep-- ; +} diff --git a/c/src/librdbg/src/ptrace.c b/c/src/librdbg/src/ptrace.c new file mode 100644 index 0000000000..158bd57cbb --- /dev/null +++ b/c/src/librdbg/src/ptrace.c @@ -0,0 +1,335 @@ +/* + ************************************************************************** + * + * Component = + * + * Synopsis = rkdb/rkdb.c + * + ************************************************************************** + */ + +#include +#include +#include +#include +#include + +extern rtems_id serializeSemId; +extern rtems_id wakeupEventSemId; +extern rtems_id eventTaskId; +extern Exception_context *FirstCtx; +extern Exception_context *LastCtx; +extern CPU_Exception_frame SavedContext; +extern unsigned int NbExceptCtx; +extern unsigned int NbSerializedCtx; + + + +/* -------------------------------------------------------------------- + return a pointeur to the Tread Control structure of the specified + Id + -------------------------------------------------------------------- */ + +Thread_Control *Thread_Get_RDBG ( + Objects_Id Id +) +{ + unsigned index; + + if ( Id <_Objects_Information_table[OBJECTS_RTEMS_TASKS]->maximum_id && + Id >_Objects_Information_table[OBJECTS_RTEMS_TASKS]->minimum_id) { + + index = Id - _Objects_Information_table[OBJECTS_RTEMS_TASKS]->minimum_id; + if ( _Objects_Information_table[OBJECTS_RTEMS_TASKS]->local_table[1+index] != NULL) { + return (Thread_Control *)(_Objects_Information_table[OBJECTS_RTEMS_TASKS]->local_table[1+index]); + } + } + + if ( Id <_Objects_Information_table[OBJECTS_POSIX_THREADS]->maximum_id && + Id >_Objects_Information_table[OBJECTS_POSIX_THREADS]->minimum_id) { + + index = Id - _Objects_Information_table[OBJECTS_POSIX_THREADS]->minimum_id; + if ( _Objects_Information_table[OBJECTS_POSIX_THREADS]->local_table[1+index] != NULL) + return (Thread_Control *)(_Objects_Information_table[OBJECTS_POSIX_THREADS]->local_table[1+index]); + } + + return 0; + +} + + +/* -------------------------------------------------------------------- + Memory read + -------------------------------------------------------------------- */ + +int +safeMemRead(void *src, void *dest, int nbBytes){ + + /* + * safe because if it generates an exception, + * it must return normally + * TBD + */ + + memcpy(dest, src, nbBytes); + return 0; +} + +/* -------------------------------------------------------------------- + Memory write + -------------------------------------------------------------------- */ +int +safeMemWrite(void *src, void * dest, int nbBytes){ + + /* + * safe because if it generates an exception, + * it must return normally + * TBD + */ + + memcpy(dest, src, nbBytes); + return 0; +} + +/* -------------------------------------------------------------------- + Ptrace + -------------------------------------------------------------------- */ + +int +ptrace (int request, int pid, char* addr, int data, char* addr2) + { + int diag; + errno = 0 ; + if (pid != 1) { + errno = ESRCH; + return -1; + } + switch (request) { + + case RPT_SINGLESTEP:{ + Exception_context *ctx; + + if (CannotRestart == 1){ + setErrno(EIO); + return -1; + } + + if ((ctx = GetExceptCtx (currentTargetThread)) != NULL) { + Single_Step(ctx->ctx); + rtems_semaphore_release( ctx->semaphoreId ); + return 0; + } + break; + } + + case RPT_PEEKTEXT: + case RPT_PEEKDATA: { + diag = safeMemRead(addr, &data, sizeof data); + if (diag == 0) return data; + mem_error: + return -1; + } + + case RPT_POKETEXT: { + diag = safeMemWrite(&data, addr, sizeof data); + + /* + * We must flush the INSTR and DATA cache to be sure the + * opcode modification is taken into account, because + * the breakpoint opcode is written via the data cache + * while execution code is fetched via the instruction + * cache + */ + + if (diag == 0) { + copyback_data_cache_and_invalidate_instr_cache(); + return 0; + } + goto mem_error; + } + case RPT_POKEDATA: { + diag = safeMemWrite(&data, addr, sizeof data); + if (diag == 0) return 0; + goto mem_error; + } + case RPT_CONT: { + Exception_context *ctx; + + if (CannotRestart == 1){ + setErrno (EIO); + return -1; + } + + ctx = GetExceptCtx (currentTargetThread); + + if ( + ctx->ctx->idtIndex != I386_EXCEPTION_DEBUG && + ctx->ctx->idtIndex != I386_EXCEPTION_BREAKPOINT && + ctx->ctx->idtIndex != I386_EXCEPTION_ENTER_RDBG + ) { + CannotRestart = 1; + setErrno (EIO); + return -1; + } + + assert (data == 0); + assert (ExitForSingleStep == 0); + + rtems_semaphore_release( serializeSemId ); + + if ((ctx = GetExceptCtx (currentTargetThread)) != NULL) { + rtems_semaphore_release( ctx->semaphoreId ); + } + return 0; + } + + case RPT_ATTACH: + return 0; + + case RPT_DETACH:{ + Exception_context *ctx; + + if (NbExceptCtx || NbSerializedCtx) { + ctx = FirstCtx; + rtems_task_delete(eventTaskId); + rtems_semaphore_delete(serializeSemId); + rtems_semaphore_delete(wakeupEventSemId); + } + return 0; + } + + case RPT_GETREGS:{ + Exception_context *ctx; + + if ((ctx = GetExceptCtx (currentTargetThread)) != NULL) { + CtxToRegs (ctx->ctx, (xdr_regs*) addr); + return 0; + } + break; + } + + case RPT_SETREGS:{ + Exception_context *ctx; + + if ((ctx = GetExceptCtx (currentTargetThread)) != NULL) { + RegsToCtx ((xdr_regs*) addr, ctx->ctx); + return 0; + } + break; + } + + case RPT_READTEXT: + case RPT_READDATA: { + diag = safeMemRead(addr, addr2, data); + if (diag == 0) return 0; + goto mem_error; + } + case RPT_WRITETEXT: + case RPT_WRITEDATA: { + diag = safeMemWrite(addr2, addr, data); + if (diag == 0) return 0; + goto mem_error; + } + + case RPT_GETTARGETTHREAD: + if (!NbExceptCtx) { + errno = EBUSY; + return -1; + } + return currentTargetThread; + + case RPT_SETTARGETTHREAD: + if (!NbExceptCtx) { + errno = EBUSY; + return -1; + } + currentTargetThread = data; + return 0; + + case RPT_GETTHREADNAME: { + return TgtGetThreadName (NULL, (unsigned)(data), (char *) addr); + } + + case RPT_THREADLIST: { + int count = TgtThreadList (NULL, (unsigned*) addr, UTHREAD_MAX + * sizeof (unsigned)); + if (count < 0) { + errno = EINVAL; + return -1; + } + return count; + } + + case RPT_SETTHREADREGS: { + Exception_context *ctx; + CPU_Exception_frame Ectx; + Thread_Control *thread; + rtems_id id; + + rtems_task_ident(RTEMS_SELF, RTEMS_SEARCH_ALL_NODES, &id); + if (data == (unsigned)id) + break; + + if ((ctx = GetExceptCtx (data)) != NULL) { + RegsToCtx ((xdr_regs*) addr, ctx->ctx); + return 0; + } + thread = Thread_Get_RDBG ((Objects_Id)(data)); + if (thread != NULL) { + RegsToCtx ((xdr_regs*) addr, &Ectx); + set_ctx_thread (thread, &Ectx); + return 0; + } + break; + } + + case RPT_GETTHREADREGS: { + Exception_context *ctx; + CPU_Exception_frame Ectx; + Thread_Control *thread; + rtems_id id; + + rtems_task_ident(RTEMS_SELF, RTEMS_SEARCH_ALL_NODES, &id); + if (data == (unsigned)id){ + justSaveContext = 1; + enterRdbg(); + CtxToRegs (&(SavedContext), (xdr_regs*) addr); + return 0; + } + + if ((ctx = GetExceptCtx (data)) != NULL) { + CtxToRegs (ctx->ctx, (xdr_regs*) addr); + return 0; + } + thread = Thread_Get_RDBG ((Objects_Id)(data)); + if (thread != NULL) { + get_ctx_thread (thread, &Ectx); + CtxToRegs (&Ectx, (xdr_regs*) addr); + return 0; + } + break; + } + + case RPT_KILL: + TotalReboot = 1; + return 0; + + case RPT_TRACEME: + case RPT_PEEKUSER: + case RPT_POKEUSER: + case RPT_GETFPREGS: + case RPT_SETFPREGS: + case RPT_GETFPAREGS: + case RPT_SETFPAREGS: + case RPT_SYSCALL: + case RPT_DUMPCORE: + case RPT_GETUCODE: + case RPT_THREADSUSPEND: + case RPT_THREADRESUME: + case RPT_SETTHREADNAME: + default: + break; + } + errno = EINVAL; + return -1; +} diff --git a/c/src/librdbg/src/rdbg.c b/c/src/librdbg/src/rdbg.c new file mode 100644 index 0000000000..1f04119e8e --- /dev/null +++ b/c/src/librdbg/src/rdbg.c @@ -0,0 +1,139 @@ +/* + ************************************************************************** + * + * Component = + * + * Synopsis = rkdb/rkdb.c + * + ************************************************************************** + */ + +#include +#include +#include +#include +#include +#include + +u_short rtemsPort = RTEMS_PORT; +int BackPort = RTEMS_BACK_PORT; +int rtemsActive = 0; +SVCXPRT* rtemsXprt; +int rtemsSock; +char ActName[] = "RTEMS"; +volatile int ExitForSingleStep = 0 ; +volatile int Continue; +volatile int justSaveContext; +volatile Objects_Id currentTargetThread; +volatile int CannotRestart = 0; +volatile int TotalReboot = 0; +int CONN_LIST_INC = 3; +int PID_LIST_INC = 1; +int TSP_RETRIES = 10; + + + int +getId() +{ + rtems_id id; + + rtems_task_ident(RTEMS_SELF, RTEMS_SEARCH_ALL_NODES, &id); + return (int)(id) ; +} + + static int +rdbgInit (void) +{ + int sock; + struct sockaddr_in addr; + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) { + printf("%s: rkdbInit: cannot allocate socket\n", ActName); + return -1; + } + + bzero( (void *)&addr, sizeof(struct sockaddr_in)); + addr.sin_port = htons(rtemsPort); + if ((bind(sock, (struct sockaddr*) &addr, sizeof(addr))) == -1) { + printf("%s: rkdbInit: cannot bind socket\n", ActName); + return -2; + } + rtemsXprt = svcudp_create(sock); + if (svcudp_enablecache(rtemsXprt, 1) == 0) { + printf("%s: rkdbInit: cannot enable rpc cache\n", ActName); + return -3; + } + rtemsSock = sock; + return 0; +} + + rtems_task +rdbgDaemon (rtems_task_argument argument) +{ + for (;;){ + + if (TotalReboot == 1){ + rtemsReboot(); + } + + svc_processrequest( rtemsXprt, REMOTEDEB, REMOTEVERS, remotedeb_2); + } +} + + void +rtems_rdbg_initialize (void) +{ + rtems_name task_name; + rtems_id tid; + rtems_status_code status; + +#ifdef DDEBUG + rdb_debug = 1; /* DPRINTF now will display */ +#endif + + DPRINTF (("%s init starting\n", ActName)); + + /* Print version string */ +#ifdef DDEBUG + printk ("RDBG v.%d built on [%s %s]\n", SERVER_VERS, __DATE__, __TIME__); +#else + printk ("RDBG v.%d\n", SERVER_VERS); +#endif + + /* Create socket and init UDP RPC server */ + if (rdbgInit() != 0) goto error; + + Continue = 1; + justSaveContext = 0; + currentTargetThread = 0; + + task_name = rtems_build_name( 'R', 'D', 'B', 'G' ); + if ((status = rtems_task_create( task_name, 5, 24576, + RTEMS_INTERRUPT_LEVEL(0), + RTEMS_DEFAULT_ATTRIBUTES | RTEMS_SYSTEM_TASK + , &tid )) + != RTEMS_SUCCESSFUL){ + printf("status = %d\n",status); + rtems_panic ("Can't create task.\n"); + } + + status = rtems_task_start(tid, rdbgDaemon, 0); + + return; + +error: + printf ("initialization failed.\n"); +} + + void +setErrno (int error) +{ + errno = error; +} + + int +getErrno() +{ + return errno; +} diff --git a/c/src/librdbg/src/remdeb.x b/c/src/librdbg/src/remdeb.x new file mode 100644 index 0000000000..e4cb998ebc --- /dev/null +++ b/c/src/librdbg/src/remdeb.x @@ -0,0 +1,544 @@ +/* + ********************************************************************** + * + * Component: RDBG servers + * Module: remdeb.x + * + * Synopsis: XDR definitions for remote debug server RPC calls. + * XDR definitions for RPCGEN to build remote debug server. + * + ********************************************************************** + */ + +#ifdef RPC_SVC +%/*HEADER_START*/ +#endif + +%#define RTEMS_PORT 2071 +%#define RTEMS_BACK_PORT 2073 + +#ifdef RPC_HDR +%#ifndef REMDEB_H +%#define RPCGENSRVNAME(a) a +#endif + +enum rpc_type { + SUNRPC = 0, + BADRPCTYPE = 25 +}; + + +const NET_SAFE = 1400; /* this is safe for UDP messages */ + +struct UDP_MSG +{ /* format of UDP messages (should be in .h) */ + unsigned char type; /* type of message (BMSG_xx) */ + unsigned char msg_num; /* ringed number for resend detect */ + unsigned short spec; /* specific information for type */ + long pid; /* process this affects */ + unsigned long context; /* specific information to request */ +}; + + /* First we support the overhead structures and types needed for RPC + requests. Then, we have all RPC routines input/output args. */ + +%/* +% * Sun request values for the remote ptrace system call +% */ +% +enum ptracereq +{ /* these match PTRACE_xxx numbers */ + RPT_TRACEME = 0, /* 0, by tracee to begin tracing */ + RPT_CHILDDONE = 0, /* 0, tracee is done with his half */ + RPT_PEEKTEXT, /* 1, read word from text segment */ + RPT_PEEKDATA, /* 2, read word from data segment */ + RPT_PEEKUSER, /* 3, read word from user struct */ + RPT_POKETEXT, /* 4, write word into text segment */ + RPT_POKEDATA, /* 5, write word into data segment */ + RPT_POKEUSER, /* 6, write word into user struct */ + RPT_CONT, /* 7, continue process */ + RPT_KILL, /* 8, terminate process */ + RPT_SINGLESTEP, /* 9, single step process */ + RPT_ATTACH, /* 10, attach to an existing process (returns 2 if not primary)*/ + RPT_DETACH, /* 11, detach from a process */ + RPT_GETREGS, /* 12, get all registers */ + RPT_SETREGS, /* 13, set all registers */ + RPT_GETFPREGS, /* 14, get all floating point regs */ + RPT_SETFPREGS, /* 15, set all floating point regs */ + RPT_READDATA, /* 16, read data segment */ + RPT_WRITEDATA, /* 17, write data segment */ + RPT_READTEXT, /* 18, read text segment */ + RPT_WRITETEXT, /* 19, write text segment */ + RPT_GETFPAREGS, /* 20, get all fpa regs */ + RPT_SETFPAREGS, /* 21, set all fpa regs */ + RPT_22, /* 22, filler */ + RPT_23, /* 23, filler */ + RPT_SYSCALL, /* 24, trap next sys call */ + RPT_DUMPCORE, /* 25, dump process core */ + RPT_26, /* 26, filler */ + RPT_27, /* 27, filler */ + RPT_28, /* 28, filler */ + RPT_GETUCODE, /* 29, get u.u_code */ + /* Begin specific ptrace options */ + RPT_GETTARGETTHREAD = 50, /* get PM target thread identifier */ + RPT_SETTARGETTHREAD = 51, /* set PM target thread identifier */ + RPT_THREADSUSPEND = 52, /* suspend a thread */ + RPT_THREADRESUME = 53, /* resume a thread */ + RPT_THREADLIST = 54, /* get list of process's threads */ + RPT_GETTHREADNAME = 55, /* get the name of the thread */ + RPT_SETTHREADNAME = 56, /* set the name of the thread */ + RPT_SETTHREADREGS = 57, /* set all registers for a specific thread*/ + RPT_GETTHREADREGS = 58, /* get all registers for a specific thread*/ + /* Begin extended ptrace options for remote debug server */ + RPT_STEPRANGE = 75, /* step while in range (addr=start, data=len) */ + RPT_CONTTO = 76, /* cont from PC to temp break in addr */ + RPT_SETBREAK = 77, /* set a breakpoint (addr=break) */ + RPT_CLRBREAK = 78, /* clear a breakpoint (data=handle or 0 for all) */ + RPT_GETBREAK = 79, /* get breakpoint (data=handle, addr=buffer to + fill). Returns next break. If data=0, + returns number of breaks. */ + RPT_GETNAME = 80, /* get name of process (data 0=name, 1=path + as started, 2=fullpath). Return in addr + as mem) */ + RPT_STOP = 81, /* (C-actors) Stop the C-actor */ + RPT_PGETREGS = 82, /* portable version */ + RPT_PSETREGS = 83, /* portable version */ + RPT_PSETTHREADREGS = 84, /* portable version */ + RPT_PGETTHREADREGS = 85 /* portable version */ +}; + +#include "remdeb_f.x" + +const MAXDEBUGGEE= 150; +const NAMEMAX = 17; + +% /* +% * Memory data for read/write text or data. The size is in data. The target +% * addr is in the addr field. +% * Be careful before modifying because this value goes into internal +% * pipes and is allocated on stack too. Pipes and/or the stack could +% * become too small if this value gets incremented. +% */ + +const MEM_DATA_MAX = 256; + +#ifndef RPC_XDR + +struct xdr_mem { + u_long addr; + u_int dataNb; + unsigned char data[MEM_DATA_MAX]; +}; + +#else +/* manually define best XDR function for this */ +%bool_t xdr_xdr_mem(xdrs, objp) +% XDR *xdrs; +% struct xdr_mem *objp; +%{ +% if (!xdr_u_long(xdrs, &objp->addr)) { +% return (FALSE); +% } +% if (!xdr_u_int(xdrs, &objp->dataNb)) { +% return(FALSE); +% } +% return (xdr_opaque(xdrs, objp->data, objp->dataNb)); +%} + +#endif + +/* Breakpoint structure maps to same structure on host. Do not change one + without changing the other. */ + +enum break_type +{ /* types of breakpoints */ + BRKT_NONE, /* unused entry */ + BRKT_INSTR, /* general instruction break */ + BRKT_READ, /* read break */ + BRKT_WRITE, /* write breakpoint */ + BRKT_ACCESS, /* read-or-write break */ + BRKT_EXEC, /* execution HW breakpoint */ + BRKT_OS_CALL, /* break on OS call, addr is call number */ + BRKT_OS_SWITCH, /* dispatch breakpoint */ + BRKT_STEPEMUL /* emulate hardware single-step */ +}; +const MAX_THRD_BRK = 4; /* enough for 128 threads per process */ +struct xdr_break +{ /* one per process local breakpoint */ + u_char type; /* BRKT_xxx type of break */ + u_char thread_spec; /* 0=all, else count of threads it affects */ + u_short handle; /* handle of breakpoint returned */ + u_long ee_loc; /* address of start */ + u_long ee_type; /* type/method of address */ + u_short length; /* length of break if range, else 0 */ + u_char pass_count; /* pass count to initialize to (0=none) */ + u_char curr_pass; /* pass count current value */ + u_long thread_list[MAX_THRD_BRK]; /* bit map for thread list */ +}; /* 20 bytes+4 per thread_list (4x4=16) = 36 */ + +const UTHREAD_MAX = 64; + +const THREADNAMEMAX = 16; +typedef string thread_name ; + +struct KernThread { + unsigned int threadLi; +}; + +#ifndef RPC_XDR + +#ifdef RPC_HDR +%typedef KernThread *ptThreadList; +#endif + +struct thread_list { + unsigned int nbThread; + ptThreadList threads; +}; + +#else /* RPC_XDR */ + +/* must write this function by hand */ + +%bool_t xdr_thread_list(xdrs, objp) +% XDR *xdrs; +% struct thread_list *objp; +%{ +% return (xdr_array(xdrs, (char**)&objp->threads, &objp->nbThread, +% UTHREAD_MAX, sizeof(KernThread), xdr_KernThread)); +%} + +#endif /* not RPC_XDR */ + + +union ptrace_addr_data_in switch (ptracereq req) { + /* + * due to rpcgen poor features, we cannot put RPC_SETREGS + * AND RPC_SETTHREADREGS in the case list. So we use a hack (FIX rpcgen). + */ +#ifndef RPC_HDR + case RPT_SETTHREADREGS : + xdr_regs regs; +#endif + case RPT_SETREGS: + + xdr_regs regs; + +#ifndef RPC_HDR + case RPT_PSETTHREADREGS: + u_int pregs<>; +#endif + case RPT_PSETREGS: + u_int pregs<>; + +#ifdef LATER + case RPT_SETFPREGS: + xdr_fp_status fpregs; +#endif + case RPT_SETTHREADNAME: + thread_name name; +#ifndef RPC_HDR + case RPT_WRITETEXT: + xdr_mem mem; +#endif + case RPT_WRITEDATA: + xdr_mem mem; + case RPT_SETBREAK: + xdr_break breakp; + default: + u_int address; +}; + +union ptrace_addr_data_out switch (ptracereq req) { + case RPT_GETREGS: + xdr_regs regs; +#ifndef RPC_HDR + case RPT_GETTHREADREGS: + xdr_regs regs; +#endif + + case RPT_PGETREGS: + u_int pregs<>; + +#ifndef RPC_HDR + case RPT_PGETTHREADREGS: + u_int pregs<>; +#endif + +#ifdef LATER + case RPT_GETFPREGS: + xdr_fp_status fpregs; +#endif + case RPT_THREADLIST: + thread_list threads; + case RPT_GETTHREADNAME: + thread_name name; +#ifndef RPC_HDR + case RPT_READTEXT: + xdr_mem mem; + case RPT_GETNAME: + xdr_mem mem; +#endif + case RPT_READDATA: + xdr_mem mem; + case RPT_GETBREAK: + xdr_break breakp; + default: + u_int addr; +}; + +typedef opaque CHAR_DATA ; /* variable sized data */ + +const XRY_MAX_INST_BUFF = 128; +const XRY_MAX_INSTANCES = 16; +%#ifndef XRY_MAX_CMD_STR +const XRY_MAX_CMD_STR = 320; /* XRY_MAX_INST_BUFF+(XRY_MAX_INSTANCES*12) */ +%#endif /* REMDEB_H */ + + +struct xry_inst +{ + unsigned char flags; /* value2 interp, etc. INFL_xxx */ + unsigned char type; /* base type of data (str, val, etc) INST_xxx */ + unsigned char sub_type; /* specific type (task, res, etc). This is + set and defined by the user defined instance + processor and not the auto-processor */ + unsigned char res_type; + u_long value; /* pointer to value or value itself */ + u_long value2; /* second value (optional - based on flags) */ +}; + +struct instance +{ + struct xry_inst instances[XRY_MAX_INSTANCES]; + unsigned char buffer[XRY_MAX_INST_BUFF]; +}; + +union instance_union switch (bool instances) +{ + case TRUE: + instance inst; + case FALSE: + string buffer ; +}; + +typedef string one_arg ; + +const XRY_MAX_OBJ_NAME = 32; /* objname in some commands */ + +% /* now open_connex() routine which establishes a connection to server */ + +enum debug_type +{ /* type of connection requested */ + DEBTYP_PROCESS = 0, /* process connection */ + DEBTYP_C_ACTOR = 1, /* C-Actor connection */ + DEBTYP_KERNEL = 2, /* kernel debug connection */ + DEBTYP_OTHER = 3 /* other subsystem */ +}; + +%#define DEBUGGER_IS_GDB 0x2 /* */ + +struct open_in +{ /* input args to open a connection */ + u_char back_port[16]; /* opaque NET address format */ + u_short debug_type; /* type of process DEBTYP_xxx */ + u_short flags; /* connection information OPNFLG_xxx */ + u_char destination[16];/* opaque address if to router */ + one_arg user_name; /* name of user on host */ +}; + +struct open_out +{ /* return from open_connex */ + u_long port; /* connection number to server or -1 if error */ + u_int pad[4]; /* Planned to be KnIpcDest. Never used */ + u_int fp; /* True if floating point processor. If error, + set to errno for open error. */ + u_char cmd_table_num; /* command table used */ + u_char cmd_table_vers; /* version of command table */ + u_short server_vers; /* version number of server itself */ +}; + +% /* now close_connex() routine which detaches from server */ + +enum close_control +{ /* choice of how to handle owned processes */ + CLOSE_IGNORE = 0, /* ignore all controlled pids on close */ + CLOSE_KILL = 1, /* kill all controlled pids on close */ + CLOSE_DETACH = 2 /* detach free running all controlled pids */ +}; + +struct close_in +{ /* arg to close connection */ + close_control control; /* shutdown of owned processes control */ +}; + +% /* now send_signal() routine which sends signals to processes like kill(2) */ + +struct signal_in +{ /* input to send_signal */ + int pid; /* process/actor to send signal to */ + int sig; /* signal to send (from /usr/include/signal.h) */ +}; + +struct signal_out +{ /* return from send_signal */ + int kill_return; /* return code from kill(2) call */ + int errNo; /* error code if failed */ +}; + + +% /* now wait_info() routine which returns results of polling the wait status +% of a process/actor. It may return 0 if running, else pid or -1 */ + +enum stop_code +{ /* stop code information */ + STOP_ERROR = 0, /* error, errno set */ + STOP_NONE = 1, /* not stopped */ + STOP_UNKNOWN = 2, /* unknown stop reason */ + STOP_BREAK = 3, /* stopped on breakpoint */ + STOP_STEP = 4, /* stopped on step */ + STOP_SIGNAL = 5, /* stopped on signal receieve */ + STOP_TERM_EXIT = 6, /* terminated normally */ + STOP_TERM_SIG = 7, /* terminated by signal */ + STOP_DETACHED = 8, /* detached from server */ + STOP_KILLED = 9, /* killed by ptrace KILL */ + STOP_SPAWN_FAILED = 10 /* spawn failed in exec part, handle=errno */ +}; + +struct wait_in +{ /* input arg to wait is process */ + int pid; /* process/actor id */ +}; + +struct wait_out +{ /* result of wait_info call */ + int wait_return; /* -1=error,0=running,pid=stopped */ + int errNo; /* error code if error */ + int status; /* wait(2) status if stopped */ + stop_code reason; /* reason in more abstracted terms */ + int handle; /* handle of break if stopped on break, + or signal number or exit code */ + u_long PC; /* program counter if stopped */ + u_long SP; /* stack pointer if stopped */ + u_long FP; /* frame pointer if stopped */ + u_long thread; /* thread that stopped if applies (else -1) */ +}; + +% /* now ptrace() routine. This matches the Sun UNIX ptrace as well as +% some additions */ + +const PTRFLG_FORCE = 1; /* when set and process running, forces process + to stop, make the request, then start again. + This is used for breakpoints and the like */ +const PTRFLG_NON_OWNER = 2; /* do request even if not primary owner (will + notify all owners including caller if owns) */ +const PTRFLG_FREE = 4; /* free pid_list after KILL/DETACH */ + +const PTRDET_UNOWN = 0x100; /* data value in RPT_DETACH just disconnects + caller as an owner of process. */ + +struct ptrace_in +{ /* input args matches ptrace but for XDR */ + int pid; /* process to act on */ + ptrace_addr_data_in addr; /* mappings for addr and addr2 */ + u_int data; /* simple data arg of ptrace */ + u_int flags; /* mask of PTRFLG_xxx flags. */ +}; + +struct ptrace_out +{ /* return information from ptrace */ + ptrace_addr_data_out addr; /* return through addr/addr2 */ + int result; /* result of ptrace call (return value) */ + int errNo; /* error code if error */ +}; + + /* Data for GET_GLOBAL_SYMBOLS */ +struct one_symbol { /* Must match common/src/lib/ctx/ctx.h */ + string symbolName<>; + long symbolValue; +}; + +typedef one_symbol all_symbols<>; + +struct get_global_symbols_out { + all_symbols symbols; +}; + + /* Data for GET_TEXT_DATA */ +struct get_text_data_in { + int pid; /* process/actor id if non-zero */ + string actorName<16>; /* actor name for system mode */ +}; + +struct get_text_data_out { + int result; + int errNo; + u_long textStart; + u_long textSize; + u_long dataStart; + u_long dataSize; +}; + + /* Data for GET_SIGNAL_NAMES */ +struct one_signal { + u_int number; + string name<>; +}; + +typedef one_signal all_signals<>; + +struct get_signal_names_out { + all_signals signals; +}; + +% /* now define the actual calls we support */ + +program REMOTEDEB { + version REMOTEVERS { + + /* open a connection to server or router */ + open_out + OPEN_CONNEX(open_in) = 1; + + /* send a signal to a process */ + signal_out + SEND_SIGNAL(signal_in) = 2; + + /* all routines below require a connection first */ + + /* close the connection to the server */ + void + CLOSE_CONNEX(close_in) = 10; + + /* process ptrace request */ + ptrace_out + PTRACE(ptrace_in) = 11; + + /* poll for status of process */ + wait_out + WAIT_INFO(wait_in) = 13; + + get_signal_names_out + GET_SIGNAL_NAMES(void) = 17; + + } = 2; /* now version 2 */ +} = 0x20000fff; + +#ifdef RPC_HDR +%#define REMDEB_H +%#endif +#endif + +#ifdef RPC_SVC + +%const char* names [] = { +% "NULLPROC", "OPEN_CONNEX", "SEND_SIGNAL", "name3", +% "name4", "name5", "name6", "name7", +% "name8", "name9", "CLOSE_CONNEX", "PTRACE", +% "name12", "WAIT_INFO", "name14", "name15", +% "name16", "GET_SIGNAL_NAMES", "name18" +%}; +% + +%/*HEADER_END*/ +#endif diff --git a/c/src/librdbg/src/servbkpt.c b/c/src/librdbg/src/servbkpt.c new file mode 100644 index 0000000000..8d986751a2 --- /dev/null +++ b/c/src/librdbg/src/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; +} diff --git a/c/src/librdbg/src/servcon.c b/c/src/librdbg/src/servcon.c new file mode 100644 index 0000000000..a26bfd2c7b --- /dev/null +++ b/c/src/librdbg/src/servcon.c @@ -0,0 +1,136 @@ +/* + ************************************************************************** + * + * Component: RDBG + * Module: servcon.c + * + * Synopsis: Management of RPC client connections. + * + ************************************************************************** + */ + +#include +#include +#include + + /* + * ConnCreate - create a new connection entry for a client. + * + * This function finds an empty entry in the connection array + * or makes space. It fills in the fields that are passed to it. + * It does not do any validation on net addresses nor does it + * start a validation cycle on other clients. This is done by + * the caller. + */ + + int +ConnCreate (struct svc_req* rqstp, open_in* in) +{ + NET_OPAQUE sender; + int idx; + CONN_LIST* clst; + + setErrno (0); + + /* Convert to valid Net address */ + if (! TspTranslateRpcAddr (rqstp, &sender)) { + DPRINTF (("ConnCreate: TspTranslateRpcAddr failed\n")); + return -1; + } + if (! TspValidateAddr ((NET_OPAQUE*) in->back_port, &sender)) { + DPRINTF (("ConnCreate: TspValidateAddr failed\n")); + return -1; /* errno now setup with error */ + } + + /* look for an empty connection entry */ + for (idx = 0; idx < conn_list_cnt; idx++) { + if (!conn_list[idx].in_use) + break; /* an empty one found */ + } + + if (idx >= conn_list_cnt) { /* no empties, create space */ + CONN_LIST* tmp_conn_list = conn_list; + + conn_list_cnt += CONN_LIST_INC; + if (conn_list) { + conn_list = (CONN_LIST *) Realloc (conn_list, /* extend */ + conn_list_cnt * sizeof (CONN_LIST)); + } else { + conn_list = (CONN_LIST *)Malloc(conn_list_cnt * sizeof(CONN_LIST)); + } + + if (!conn_list) { /* unable to get space */ + if ((conn_list_cnt -= CONN_LIST_INC)) { + /* was realloc, restore space */ + conn_list = tmp_conn_list; + } + return -1; /* errno set by failed alloc */ + } + /* clear newly created memory */ + memset (conn_list + idx, 0, CONN_LIST_INC * sizeof (CONN_LIST)); + } else { /* clear new entry */ + memset (conn_list + idx, 0, sizeof (CONN_LIST)); + } + clst = conn_list + idx; + + clst->in_use = True; /* now in use */ + clst->sender = sender; + memcpy (&clst->back_port, &in->back_port, sizeof (NET_OPAQUE)); + memcpy (&clst->route, &in->destination, sizeof (NET_OPAQUE)); + clst->debug_type = (UCHAR) in->debug_type; + clst->flags = in->flags; + strncpy (clst->user_name, in->user_name, NAMEMAX-1); + clst->user_name [NAMEMAX-1] = 0; + + return idx; +} + + /* + * ConnDelete - remove connection entry when shutdown. + * + */ + + void +ConnDelete (int conn, struct svc_req* rqstp, close_control control) +{ + CONN_LIST* clst = conn_list + conn; + int idx; + Boolean prim; + + if (! clst->in_use) return; /* not active */ + + for (idx = 0; idx < pid_list_cnt; idx++) { + PID_LIST* plst = pid_list + idx; + + if (! PIDMAP_TEST (conn, idx)) continue; + + /* found a controlled pid */ + prim = (plst->primary_conn == conn) ? True : False; + TgtDetachCon (conn, idx, True); + + /* if still running or alive, we use close control on it */ + if (! plst->pid) + continue; /* entry gone */ + + if (prim && control == CLOSE_KILL) { + /* kill off process */ + TgtKillAndDelete (plst, rqstp, True); + } else if (! plst->owners) { + /* no owners left */ + if (control == CLOSE_DETACH) { + TgtKillAndDelete (plst, rqstp, False); + } + if (control == CLOSE_DETACH || PROC_TERMINATED (plst)) { + TgtDelete (plst, conn, (control==CLOSE_DETACH) ? + BMSG_DETACH : 0); + } + } + } + if (clst->list) { + Free (clst->list); /* free allocated memory */ + } + DPRINTF (("ConnDelete: Connection closed for port %u\n", + HL_W(*((UINT16*) &clst->back_port.c[2])))); + + clst->in_use = False; /* free it back */ +} diff --git a/c/src/librdbg/src/servrpc.c b/c/src/librdbg/src/servrpc.c new file mode 100644 index 0000000000..f62a2ecbb8 --- /dev/null +++ b/c/src/librdbg/src/servrpc.c @@ -0,0 +1,720 @@ +/* + ********************************************************************** + * + * Component: RDBG + * Module: servrpc.c + * + * Synopsis: support routines for RPC dispatch for remote debug server. + * Main server dispatch routines from RPC to support remote debug. + * + ********************************************************************** + */ + +#include +#include +#include +#include +#include + +/************************************************************************/ + +/* ----------------------------------------------------------------------- + open_connex_2_svc - setup a new connection from a client. + + Notes: + - this function creates a new connection to a client. It allocates + an entry in the connection structure and fills in the information + sent and implied by the message. + - a client connection entry is needed for all further messages to work + properly. + ----------------------------------------------------------------------- */ + +open_out* RPCGENSRVNAME(open_connex_2_svc) (open_in *in, struct svc_req *rqstp) +{ + static open_out out; /* output response. This could be heap local */ + int idx; + static int one_time = 0; /* we do one-time setup on back port */ + + /* need to support in->debug_type, in->flags, and in->destination!!! */ + + if (!one_time) + { /* only setup one backport socket */ + /* now setup signals and the like for handling process changes */ + setErrno(0); + TspInit(rqstp->rq_xprt->xp_sock); /* init transport system */ + if (getErrno()) + { /* failed in setup */ + out.port = (u_long)-1; + out.fp = getErrno(); /* error causing to fail */ + return(&out); /* fail */ + } + one_time = True; /* disable doing this again */ + } + + DPRINTF(("open_connex_2_svc: Opening connection from '%s'\n", + in->user_name)); + + /* now setup a validation of all other connections */ + for (idx = 0; idx < conn_list_cnt; idx++) + if (conn_list[idx].in_use) + { /* setup retry timer */ + DPRINTF(("open_connex_2_svc: Still have connection %d with port %d\n", + idx, HL_W(*((UINT16*)&conn_list[idx].back_port.c[2])))); + } + + idx = ConnCreate(rqstp, in); /* setup the connection */ + out.port = idx; /* connection number */ + if (idx == -1) + out.fp = getErrno(); /* error causing to fail */ + else + out.fp = TARGET_PROC_TYPE; + + out.server_vers = SERVER_VERS; + return(&out); +} + +/* ----------------------------------------------------------------------- + send_signal_2_svc - send a kill/signal to the specified process. + + Notes: + - this function sends a signal to the process specified. This process + does not have to be under debug nor attached by this server. The kill + may be refused on other grounds though. + - kill(pid, 0) can be used to validate the process still exists if + needed. + ----------------------------------------------------------------------- */ + +signal_out *RPCGENSRVNAME(send_signal_2_svc) (signal_in *in, struct svc_req *rqstp) +{ + static signal_out out; /* return code from kill */ + + /* we do not care if connected */ + setErrno(0); + out.kill_return = 0; + out.errNo = 0; + TotalReboot = 1; + return(&out); +} + +/* ----------------------------------------------------------------------- + close_connex_2_svc - close a connection from a client. + ----------------------------------------------------------------------- */ + +void *RPCGENSRVNAME(close_connex_2_svc) (close_in *in, struct svc_req *rqstp) +{ + int conn_idx = TspConnGetIndex(rqstp); + + if (conn_idx != -1) /* found it, clear out */ + ConnDelete(conn_idx, rqstp, in->control); + + return (void*) ""; /* need to return something */ +} + +/* ----------------------------------------------------------------------- + ptrace_2_svc - control process under debug. + ----------------------------------------------------------------------- */ + +#define REG_COUNT \ + (sizeof (xdr_regs) / sizeof (int)) + +ptrace_out *RPCGENSRVNAME(ptrace_2_svc) (ptrace_in *in, struct svc_req *rqstp) +{ + int conn_idx = rqstp ? TspConnGetIndex(rqstp) : -1; + static ptrace_out out; /* outut response (error or data) */ + void *addr, *addr2; /* used for actual ptrace call */ + unsigned int data; + int req, pid, ret, pid_idx, idx; + static union + { /* local buffer for returned data */ + Objects_Id t_list[UTHREAD_MAX]; /* thread_list return */ + char t_name[THREADNAMEMAX]; /* thread name return */ + } local_buff; /* for return handling of strings and the like */ + PID_LIST *plst = NULL; /* current pid_list entry */ + + DPRINTF (("ptrace_2_svc: entered (%s (%d), %d, XXXX, %d, XXXX)\n", + PtraceName (in->addr.req), in->addr.req, in->pid, + in->data)); + + out.addr.ptrace_addr_data_out_u.addr = 0; + + /* validate the connection */ + if (conn_idx == -1 && rqstp != NULL) + { /* no connection, error */ + DPRINTF(("ptrace_2_svc: msg from unknown debugger!\n")); + out.result = -1; + out.errNo = ECHILD; /* closest error */ + out.addr.req = 0; /* to avoid copies that should not occur */ + return(&out); + } + /* Consider that the last back-message is acknowledged */ + if (conn_idx >= 0 && conn_list[conn_idx].retry) { + TspMessageReceive(conn_idx, in->pid); + } + + req = in->addr.req; + out.addr.req = req; /* needed for RPC */ + pid = in->pid; + addr = addr2 = NULL; + data = in->data; + setErrno(0); /* assume works */ + out.result = 0; /* assume worked ok */ + out.errNo = 0; + + /* lookup process to make sure we have under control */ + pid_idx = FindPidEntry (in->pid); + if (pid_idx >= 0) /* found it */ + { + plst = &pid_list[pid_idx]; + if (conn_idx < 0) + conn_idx = plst->primary_conn; + } + + /* now we handle the special case of ATTACH to a pid we already control */ + if (req == RPT_ATTACH) + { /* look it up first */ + if (plst) + { /* we have controlled , so return ok+show conn */ + ret = 2; /* normally secondary connection */ + if (! PIDMAP_TEST (conn_idx, pid_idx)) + { /* mark as an owner if not already */ + plst->owners++; + PIDMAP_SET (conn_idx, pid_idx); /* mask in */ + } + else if (plst->primary_conn != NO_PRIMARY) + { /* regrab makes primary */ + /* Only if not primary already */ + if (plst->primary_conn != conn_idx) { + TspSendWaitChange(plst->primary_conn, BMSG_NOT_PRIM, + conn_idx, plst->pid, 0, False); /* tell old owner */ + } + plst->primary_conn = NO_PRIMARY; + } + + if (plst->primary_conn == NO_PRIMARY) + { /* none now, so take over */ + plst->primary_conn = conn_idx; /* new primary */ + ret = 1; /* primary */ + } + out.result = ret; /* primary or secondary owner */ + return(&out); + } + /* else attach process using target code */ + setErrno(ESRCH); /* assume the worst */ + if (!TgtAttach(conn_idx, pid)) + { /* failed */ + out.errNo = getErrno(); + out.result = 0; + } + return(&out); + } + else if (req == RPT_DETACH) + { /* see which kind of detach */ + if (data == PTRDET_UNOWN) + { /* only want to disconnect from */ + TgtDetachCon(conn_idx, pid_idx, True); /* remove from control */ + return(&out); /* done */ + } + } + else if (plst && (req == RPT_GETNAME || req == RPT_GETBREAK)) + { + /* do nothing */ + } + + else if (plst && req == RPT_CLRBREAK) { + /* To be able to remove breakpoints from a "running" system */ + DPRINTF (("ptrace_2_svc: allowing RPT_CLRBREAK %d\n", data)); + /* do nothing */ + } + + else if (plst && plst->running) + { /* error, process is running and not detach */ + out.result = -1; + out.errNo = ETXTBSY; /* closest error */ + DPRINTF (("ptrace_2_svc: failed, still running.\n")); + return(&out); + } + if (plst == NULL) { + out.result = -1; + out.errNo = ESRCH; + DPRINTF (("ptrace_2_svc: No such process.\n")); + return (&out); + } + + /* now make sure secondary owner is not trying to modify */ + if (!(in->flags & PTRFLG_NON_OWNER)) /* if not overriden */ + if (conn_idx != plst->primary_conn + && ( (req >= RPT_POKETEXT && req <= RPT_SINGLESTEP) + || (req >= RPT_SETREGS && req <= RPT_SETFPAREGS && (req & 1)) + || (req >= RPT_SYSCALL && req <= RPT_DUMPCORE) + || (req >= RPT_SETTARGETTHREAD && req <= RPT_THREADRESUME) + || (req >= RPT_SETTHREADNAME && req <= RPT_SETTHREADREGS) + || (req >= RPT_STEPRANGE && req <= RPT_CLRBREAK) + || (req == RPT_STOP) + || (req >= RPT_PSETREGS && req <= RPT_PSETTHREADREGS))) + { /* not owner */ + out.result = -1; + out.errNo = EPERM; /* cannot alter as not primary */ + DPRINTF (("ptrace_2_svc: refused, not owner, flags %d conn_idx %d primary_conn %d\n", in->flags, conn_idx, + plst->primary_conn)); + return(&out); + } + + addr = (void *)in->addr.ptrace_addr_data_in_u.address; /* default */ + /* now setup normal ptrace request by unpacking. May execute here. */ + switch (req) + { /* handle unpacking or setup for real call */ + /* first the ones where addr points to input data */ + case RPT_SETREGS: + case RPT_SETTHREADREGS: + addr = (void *)&in->addr.ptrace_addr_data_in_u.regs; /* reg list */ + break; + + case RPT_PSETREGS: + case RPT_PSETTHREADREGS: + if (in->addr.ptrace_addr_data_in_u.pregs.pregs_len != REG_COUNT) { + DPRINTF(("ptrace_2_svc: pid %d got %d expected %d\n", pid, + in->addr.ptrace_addr_data_in_u.pregs.pregs_len, REG_COUNT)); + setErrno(EINVAL); + break; + } + req = req == RPT_PSETREGS ? RPT_SETREGS : RPT_SETTHREADREGS; + addr = (void *) in->addr.ptrace_addr_data_in_u.pregs.pregs_val; + break; + + case RPT_SETTHREADNAME: + addr = (void *)in->addr.ptrace_addr_data_in_u.name; + break; + case RPT_WRITETEXT: + case RPT_WRITEDATA: + if ((int) data < 0) { + setErrno(EINVAL); + break; + } + addr = (void *)in->addr.ptrace_addr_data_in_u.mem.addr; /* targ addr */ + addr2 = (void *)in->addr.ptrace_addr_data_in_u.mem.data; /* buff */ + + /* Forbid writing over breakpoints */ + if (BreakOverwrite (plst, addr, data)) { + setErrno(EBUSY); + } + break; + + case RPT_POKETEXT: + case RPT_POKEDATA: + /* Forbid writing over breakpoints */ + if (BreakOverwrite (plst, addr, sizeof (int))) { + setErrno(EBUSY); + } + break; + + /* now ones where we handle locally */ + case RPT_GETTARGETTHREAD: + out.result = plst->thread; + req = 0; /* force exit */ + break; + + case RPT_PGETREGS: /* return from our buffer */ + out.addr.ptrace_addr_data_out_u.pregs.pregs_len = REG_COUNT; + out.addr.ptrace_addr_data_out_u.pregs.pregs_val = (u_int*) &plst->regs; + req = 0; /* force exit */ + break; + + case RPT_GETREGS: + /* return directly from our buffer */ + /* this buffer is refreshed when changing target thread */ + out.addr.ptrace_addr_data_out_u.regs = plst->regs; + req = 0; /* force exit */ + break; + + case RPT_SETBREAK: + idx = BreakSet (plst, conn_idx, &in->addr.ptrace_addr_data_in_u.breakp); + if (idx < 0) break; + req = 0; /* force exit */ + out.result = idx; /* return break index (>0) */ + break; + + case RPT_CLRBREAK: + if (conn_list[conn_idx].flags & DEBUGGER_IS_GDB) { + data = BreakGetIndex (plst, addr); + } + out.result = BreakClear (plst, conn_idx, data); + /* if errored, errno will still be set */ + req = 0; + break; + + case RPT_GETBREAK: + /* data=handle, addr=in_buffer, returns next break. Data=0, returns cnt */ + out.result = BreakGet (plst, data, &out.addr. + ptrace_addr_data_out_u.breakp); + req = 0; /* handle locally */ + break; + + case RPT_GETNAME: /* get the name of the process */ + if (!plst->name) + out.addr.ptrace_addr_data_out_u.mem.dataNb = 0; + else + { + int maxLen = sizeof out.addr.ptrace_addr_data_out_u.mem.data - 1; + data = strlen(plst->name); + if (data > maxLen) + data = maxLen; + out.addr.ptrace_addr_data_out_u.mem.dataNb = data+1; + memcpy(out.addr.ptrace_addr_data_out_u.mem.data, plst->name, data+1); + out.addr.ptrace_addr_data_out_u.mem.data [maxLen] = '\0'; + } + req = 0; + break; + + case RPT_CONTTO: + if (BreakSetAt (plst, conn_idx, (u_long) addr, BRKT_STEPEMUL) < 0) + { + DPRINTF(("ptrace_2_svc: BreakSet failed at %x", addr)); + break; + } + req = RPT_CONT; + /* data can contain a signal number, addr2 is unused */ + goto case_RPT_CONT; + + case RPT_STEPRANGE: + /* convert to step */ + if (!data) + data = 1; /* should we give an error?? */ + BreakStepRange (plst, addr, data); + if (getErrno()) break; + + req = RPT_SINGLESTEP; /* do by stepping */ + addr = (void*) 1; /* start from current PC */ + data = -2; /* want non-atomic stepping */ + /* fall through to other exec cases */ + + case RPT_CONT: + case_RPT_CONT: + case RPT_SINGLESTEP: + + if (BreakStepOff (plst, &addr2)) + { /* need clear then step off break */ + /* clear break, step, then do exec */ + if (addr == (void*) 1) + addr = (void*) plst->regs.REG_PC;/* need for patch */ + + /* data is always 0, so atomic single-step */ + } else if (req == RPT_SINGLESTEP) { + data = -2; /* want non-atomic stepping */ + } + break; + + /* now ones where addr points to an output area */ + case RPT_PGETTHREADREGS: + addr = (void*) out.addr.ptrace_addr_data_out_u.mem.data; + if (sizeof out.addr.ptrace_addr_data_out_u.mem.data < + REG_COUNT * sizeof(int)) { + setErrno(EINVAL); + break; + } + if (data == plst->thread) { + out.addr.ptrace_addr_data_out_u.pregs.pregs_len = REG_COUNT; + out.addr.ptrace_addr_data_out_u.pregs.pregs_val = (u_int*) &plst->regs; + req = 0; /* force exit */ + break; + } + req = RPT_GETTHREADREGS; + break; + + case RPT_GETTHREADREGS: + addr = (void*) &out.addr.ptrace_addr_data_out_u.regs; + break; + case RPT_GETTHREADNAME: + out.addr.ptrace_addr_data_out_u.name = local_buff.t_name; + addr = (void*) out.addr.ptrace_addr_data_out_u.name; + break; + case RPT_THREADLIST: + out.addr.ptrace_addr_data_out_u.threads.threads =(ptThreadList) local_buff.t_list; + addr = (void*) out.addr.ptrace_addr_data_out_u.threads.threads; + break; + case RPT_READTEXT: + case RPT_READDATA: + if ((int) data < 0) { + setErrno(EINVAL); + break; + } + addr = (void *)in->addr.ptrace_addr_data_in_u.address; + addr2 = (void *)out.addr.ptrace_addr_data_out_u.mem.data; + out.addr.ptrace_addr_data_out_u.mem.dataNb = data; + break; + case RPT_DETACH: + /* Do not allow detaching if breakpoints still there */ + if (BreakGet (plst, 0, NULL)) + { /* some bkpts still set */ + setErrno(EINVAL); /* cannot detach safely */ + break; + } + /* fall through */ + case RPT_KILL: + /* in the event they are trying to detach or kill a terminated process, + we just delete the entry. */ + if (PROC_TERMINATED (plst)) + { + TgtDelete(plst, -1, BMSG_KILLED); /* just blow off */ + req = 0; /* now exit */ + } + break; + } + + if (getErrno()) + { /* failed in code above */ + out.result = -1; + out.errNo = getErrno(); + DPRINTF(("ptrace_2_svc: result %d errNo %d\n", out.result, out.errNo)); + return(&out); + } + else if (!req) + { /* bail out now */ + DPRINTF(("ptrace_2_svc: result %d errNo %d\n", out.result, out.errNo)); + return(&out); + } + + /* OK, make the call */ + out.result = TgtPtrace(req, pid, addr, data, addr2); + out.errNo = getErrno(); + + /* if no error, cleanup afterwards */ + if (getErrno()) + { + /* Remove step-emul breakpoints if any */ + if (req == RPT_SINGLESTEP || req == RPT_CONT) { + BreakClear (plst, -1, -1); + } + DPRINTF(("ptrace_2_svc: result %d errNo %d\n", out.result, out.errNo)); + return(&out); /* return error */ + } + + switch (in->addr.req) + { /* handle some special calls that affect state */ + case RPT_CONT: + case RPT_STEPRANGE: + /* change to running */ + if (in->addr.req == RPT_STEPRANGE) + plst->last_start = LAST_RANGE; /* so range steps */ + else if (addr2) + plst->last_start = LAST_STEPOFF; /* now continue after wait */ + else + plst->last_start = LAST_CONT; + plst->running = 1; /* mark as running */ + if (!rqstp) /* Called internally to restart bkpt, no msg to anybody */ + break; + TgtNotifyAll(pid_idx, BMSG_WAIT, 0, 0, (in->flags & PTRFLG_NON_OWNER) + ? -1 : conn_idx, True); + break; + case RPT_SINGLESTEP: + /* mark as step */ + plst->last_start = LAST_STEP; /* so we know how started */ + plst->running = 1; /* mark as running (wait should catch fast) */ + break; + case RPT_DETACH: /* mark as disconnected */ + case RPT_KILL: /* mark as killed */ + if (in->flags & PTRFLG_FREE) /* notify and delete entry */ + TgtDelete(plst, -1, (in->addr.req==RPT_KILL) ? BMSG_KILLED : BMSG_DETACH); + else + { /* notify and mark */ + plst->last_start = (in->addr.req==RPT_KILL) ? + LAST_KILLED : LAST_DETACHED; + plst->state = -1; + plst->running = False; + TgtNotifyAll(pid_idx, (in->addr.req==RPT_KILL) ? + BMSG_KILLED : BMSG_DETACH, 0, 0, -1, True); + } + break; + case RPT_SETTHREADREGS: + case RPT_PSETTHREADREGS: + if (data != plst->thread) + break; + DPRINTF(("ptrace_2_svc: pid %d target thread regs changed!\n", pid)); + + case RPT_SETREGS: + case RPT_PSETREGS: + /* change our buffer as well */ + if (plst->regs.REG_PC != ((xdr_regs*)addr)->REG_PC) + BreakPcChanged (plst); + plst->regs = *(xdr_regs*) addr; /* copy in */ + break; + + /* case RPT_PGETREGS has been handled locally above */ + case RPT_PGETTHREADREGS: + /* We need to update pointer so that XDR works on return */ + out.addr.ptrace_addr_data_out_u.pregs.pregs_len = REG_COUNT; + out.addr.ptrace_addr_data_out_u.pregs.pregs_val = + (void*) out.addr.ptrace_addr_data_out_u.mem.data; + break; + + case RPT_PEEKTEXT: + case RPT_PEEKDATA: + case RPT_READDATA: + case RPT_READTEXT: + if (req < RPT_READDATA) + { /* peek */ + /* addr is start */ + data = sizeof(int); + addr2 = &out.result; /* data buffer */ + /* Like read: addr is start, data is length, addr2 is buffer */ + } + BreakHide (plst, addr, data, addr2); + break; + + case RPT_SETTARGETTHREAD: + DPRINTF(("ptrace_2_svc: pid %d new target thread %d\n", pid, data)); + TgtPtrace (RPT_GETREGS, pid, (char*) &plst->regs, 0, NULL); + plst->thread = data; + if (plst->break_list) { /* Forget we had to step off breakpoint */ + BASE_BREAK* base = (BASE_BREAK*) plst->break_list; + DPRINTF(("ptrace_2_svc: clr_step %d last_break %d\n", base->clr_step, + base->last_break)); + base->clr_step = 0; /* Not stopped on break */ + base->last_break = 0; + } + break; + + case RPT_THREADLIST: + out.addr.ptrace_addr_data_out_u.threads.nbThread = out.result; + break; + + default: + break; + } /* end switch */ + DPRINTF(("ptrace_2_svc 2: result %d errNo %d\n", out.result, out.errNo)); + return(&out); +} + +/* ----------------------------------------------------------------------- + wait_info_2_svc - non-blocking wait request to check status. + ----------------------------------------------------------------------- */ + +wait_out *RPCGENSRVNAME(wait_info_2_svc) (in, rqstp) + wait_in *in; + struct svc_req *rqstp; /* server info */ +{ + int conn_idx = TspConnGetIndex(rqstp); + static wait_out out; /* output of pid and status */ + int idx; + PID_LIST *plst; + + memset(&out, 0, sizeof(out)); /* zero for safety */ + out.reason = STOP_ERROR; /* assume the worst */ + + if (conn_idx == -1) + { /* no connection, error */ + DPRINTF(("wait_info_2_svc: msg from unknown debugger!\n")); + out.wait_return = -1; + out.errNo = ECHILD; /* closest error */ + return(&out); + } + else + { /* see if confirming message received */ + if (conn_list[conn_idx].retry) + TspMessageReceive(conn_idx, in->pid); + } + + if (!in->pid) + { /* warm test verify only */ + /* this call (pid==0) is made to confirm that that connection is still + active. */ + /* we let it fall through as an error since any use other than connection + reset would be an error (there is no pid0). */ + } + else + { /* normal request */ + idx = FindPidEntry (in->pid); + if (idx >= 0) + { /* found process they requested on */ + plst = &pid_list[idx]; + out.wait_return = plst->running ? 0 : in->pid; + /* return: 0 is running, pid is stopped/term */ + out.errNo = 0; + out.status = plst->state; /* last stopped reason if stopped */ + out.thread = plst->thread;/* current thread (or -1 if none) from stop */ + if (!out.wait_return) + out.reason = STOP_NONE; /* running, no action */ + else if (STS_SIGNALLED (out.status)) + { /* stopped on signal */ + out.handle = STS_GETSIG (out.status); /* signal number */ + if (out.handle == SIGTRAP) + if (plst->is_step) + { /* single step with hitting a break */ + out.reason = STOP_STEP; + out.handle = 0; /* no information */ + } + else + { /* stopped on break */ + out.reason = STOP_BREAK; + if (plst->break_list) + out.handle = ((BASE_BREAK*)plst->break_list)->last_break; + else + out.handle = 0; /* no break */ + } + else + out.reason = STOP_SIGNAL; + out.PC = plst->regs.REG_PC; /* copy standard regs */ + out.SP = plst->regs.REG_SP; + out.FP = plst->regs.REG_FP; + } + else + { /* terminated, so lower use count */ + if (plst->last_start == LAST_KILLED) + out.reason = STOP_KILLED; + else if (plst->last_start == LAST_DETACHED) + out.reason = STOP_DETACHED; + else if (plst->last_start == LAST_START) + { /* failed in exec */ + out.reason = STOP_SPAWN_FAILED; + out.handle = STS_GETCODE (out.status); /* errno reason */ + } + else if (STS_TERMONSIG (out.status)) + { /* terminated on signal */ + out.reason = STOP_TERM_SIG; + /* mask off the core-dumped bit 7 */ + out.handle = (int)(unsigned)(u_char) STS_TERMGETSIG (out.status); + } + else + { /* exit(2)ed */ + out.reason = STOP_TERM_EXIT; + out.handle = STS_GETCODE (out.status); /* code */ + } + } + DPRINTF(("wait_info_2_svc: pid %d return %d status %x errNo %d" + " reason %d handle %d pc %x sp %x fp %x thread %d\n", + in->pid, out.wait_return, out.status, out.errNo, out.reason, + out.handle, out.PC, out.SP, out.FP, out.thread)); + return(&out); + } + } + /* if not found in list, we return error: no such process */ + out.wait_return = -1; + out.errNo = ESRCH; /* no process */ + out.status = 0; + return(&out); +} + +/* ----------------------------------------------------------------------- + get_signal_names_2_svc - return names for signals + ----------------------------------------------------------------------- */ + +static one_signal SignalNames[] = { + {SIGILL, "SIGILL/EVT_ILL"}, + {SIGTRAP, "SIGTRAP/EVT_BKPT"}, + {SIGFPE, "SIGFPE/EVT_FPE"}, + {SIGKILL, "SIGKILL/EVT_AKILL"}, + {SIGSEGV, "SIGSEGV/EVT_SEGV"}, + {17, "SIGSTOP"}, + {23, "SIGSTOP"} +}; + +get_signal_names_out* RPCGENSRVNAME(get_signal_names_2_svc) (in, rqstp) + void* in; + struct svc_req *rqstp; /* server info */ +{ + static get_signal_names_out out; + + out.signals.all_signals_len = sizeof SignalNames / sizeof SignalNames[0]; + out.signals.all_signals_val = SignalNames; + + return(&out); +} diff --git a/c/src/librdbg/src/servtgt.c b/c/src/librdbg/src/servtgt.c new file mode 100644 index 0000000000..7a8471e3e2 --- /dev/null +++ b/c/src/librdbg/src/servtgt.c @@ -0,0 +1,550 @@ +/* + ************************************************************************** + * + * Component: RDB servers + * Module: servtgt.c + * + ************************************************************************** + */ + + +#include +#include +#include +#include +#include +#include + +#ifdef DDEBUG +#define Ptrace TgtDbgPtrace +#else +#define Ptrace TgtRealPtrace +#endif + +/* ---------------------------------------------------------------- + TgtBreakRestoreOrig - Restore original instruction at "addr" + just before single-stepping it. + ---------------------------------------------------------------- */ + +int TgtBreakRestoreOrig (int pid, void *addr, void *addr2) + /* Process identifier */ + /* Breakpoint address */ + /* Original instruction or bkpt number */ +{ + int ret; + int l; + + l = (long)Ptrace(RPT_PEEKTEXT, pid, addr, 0, NULL); /* assume ok */ + ret = ORG_BREAK (l, (UINT32) addr2); /* reconstruct old instr */ + ret = Ptrace(RPT_POKETEXT, pid, addr, ret, NULL); /* poke back old */ + return ret; +} + +/* ----------------------------------------------------------------------- + TgtBreakCancelStep - Restore the breakpoint at "addr" if the single-step + has failed at the ptrace level. + ----------------------------------------------------------------------- */ + +#define BKPT0(plst) ((BASE_BREAK*)(plst)->break_list) + +void TgtBreakCancelStep (PID_LIST* plst) +{ + assert (plst->break_list); + assert (BKPT0 (plst)->clr_step); + + if (plst->break_list && BKPT0 (plst)->clr_step) { + int idx = BKPT0 (plst)->last_break; + int data; + + data = Ptrace (RPT_PEEKTEXT, plst->pid, + (char *)plst->break_list [idx].ee_loc, 0, NULL); + assert (! IS_BREAK (data)); + Ptrace (RPT_POKETEXT, plst->pid, + (char *)plst->break_list[idx].ee_loc, + (int) SET_BREAK (data), NULL); + } +} + +/* ----------------------------------------------------------------------- + TgtCreateNew - add a new process into the process management lists. + ----------------------------------------------------------------------- */ + + void +TgtCreateNew(PID pid, int conn, INT32 child, char *name, Boolean spawn) +{ + int idx; + + for (idx = 0; idx < pid_list_cnt; idx++) + if (!pid_list[idx].pid) + break; /* find empty */ + + if (idx >= pid_list_cnt) + { /* no empties, add more */ + PID_LIST *tmp_pid_list = pid_list; + + pid_list_cnt += PID_LIST_INC; + pid_list = (PID_LIST*) Realloc(pid_list, /* get new or extend */ + pid_list_cnt * sizeof(PID_LIST)); + if (!pid_list) + { /* out of memory */ + pid_list_cnt -= PID_LIST_INC; + if (pid_list_cnt) + { /* realloc failed - malloc again */ + pid_list = tmp_pid_list; + /* above relies on old pointer being valid after failed realloc */ + } + return; /* failed */ + } + /* now clear newly added space */ + memset(pid_list+pid_list_cnt-PID_LIST_INC, 0, + PID_LIST_INC * sizeof(PID_LIST)); + idx = pid_list_cnt - PID_LIST_INC; + } + else /* clear entry we found */ + memset(&pid_list[idx], 0, sizeof(PID_LIST)); + + /* now fill in empty entry */ + pid_list[idx].pid = pid; + pid_list[idx].running = 1; /* we have not called wait yet */ + pid_list[idx].primary_conn = (UCHAR)conn; /* primary owner */ + if (conn != -1) + { /* found caller */ + pid_list[idx].owners = 1; + PIDMAP_SET (conn, idx); /* mask in */ + } + pid_list[idx].thread = (UINT32)-1; /* no thread for now */ + pid_list[idx].last_start = LAST_START; /* handle MiX bug */ + + pid_list[idx].name = name ? (char *)StrDup(name) : (char *)NULL; + +} + +/* ----------------------------------------------------------------------- + TgtNotifyWaitChange - send event to clients indicating child changed state. + ----------------------------------------------------------------------- */ + + void +TgtNotifyWaitChange( PID pid, int status, Boolean exclude) +{ + int conn, idx; + + idx = FindPidEntry (pid); /* locate the pid that changed */ + if (idx < 0) + { + DPRINTF(("TgtNotifyWaitChange: pid %d not in our list\n", + (int) pid)); + return; /* not in our list */ + } + pid_list[idx].running = 0; /* not running */ + pid_list[idx].state = status; /* save status of stop/term */ + if (!pid_list[idx].owners && !STS_SIGNALLED(status)) + TgtDelete(&pid_list[idx], -1, 0); /* terminated and no owners */ + else + { /* normal cases */ + for (conn = 0; conn < conn_list_cnt; conn++) + { /* now find all interested clients */ + if (!conn_list[conn].in_use /* free entry */ + || ! PIDMAP_TEST (conn, idx)) + continue; /* not using this pid */ + if (conn == exclude) + continue; /* do not do this one */ + TspSendWaitChange(conn, BMSG_WAIT, 1, pid, 0, False);/* notify of change */ + } + } +} + +/* ----------------------------------------------------------------------- + TgtNotifyAll - send a message to all clients interested in process. + ----------------------------------------------------------------------- */ + + void + TgtNotifyAll( int pid_idx, BACK_MSG msg, UINT16 spec, + UINT32 context, int exclude, Boolean force) +{ + int conn; + + DPRINTF(("TgtNotifyAll: msg %d (%s) for pid_idx=%d (%d,%d)\n", + msg, BmsgNames [msg], pid_idx, exclude, force)); + for (conn = 0; conn < conn_list_cnt; conn++) + if (conn_list[conn].in_use /* not free */ + && PIDMAP_TEST (conn, pid_idx)) + { + if (conn != exclude) + TspSendWaitChange(conn, msg, spec, pid_list[pid_idx].pid, context, + force); + } +} + +/* ----------------------------------------------------------------------- + TgtDelete - mark process as now uncontrolled. + + Notes: + - this function removes a process from the process list. + - the notify argument indicates a message to send if needed. + ----------------------------------------------------------------------- */ + +void TgtDelete(PID_LIST *plst, int conn_idx, BACK_MSG notify) +{ + int idx = plst - pid_list, cnt, conn; + + /* found */ + cnt = pid_list[idx].owners; + if (cnt) + { /* some connections to break */ + for (conn = 0; cnt && conn < conn_list_cnt; conn++) + if (conn_list[conn].in_use /* not free */ + && PIDMAP_TEST (conn, idx)) + { /* found one that uses it */ + PIDMAP_CLEAR (conn, idx); + if (notify && conn != conn_idx) + TspSendWaitChange(conn, notify, 0, plst->pid, 0, True); + if (!--cnt) + break; + } + } + if (pid_list[idx].name) + Free(pid_list[idx].name); /* free string name back */ + /* Free breakpoint list */ + if (pid_list [idx].break_list != NULL) { + Free (pid_list [idx].break_list); + } + pid_list[idx].pid = 0; /* gone */ +} + + +/* ----------------------------------------------------------------------- + TgtKillAndDelete - kill or detach process and remove entry. + ----------------------------------------------------------------------- */ + + int +TgtKillAndDelete( PID_LIST *plst, struct svc_req *rqstp, Boolean term) +{ + ptrace_in pin; /* used for ptrace call */ + ptrace_out *pout; + + /* Remove breakpoints */ + if (plst->break_alloc > 0) { + pin.pid = plst->pid; + pin.addr.req = RPT_CLRBREAK; + pin.data = 0; /* clear all */ + pin.flags = PTRFLG_NON_OWNER; + pout = RPCGENSRVNAME(ptrace_2_svc) (&pin, rqstp); + if (pout->result < 0) { + DPRINTF (("TgtKillAndDelete: RPT_CLRBREAK failed %d\n", + getErrno())); + return -1; + } + } + + if (term) + { /* kill */ + pin.addr.ptrace_addr_data_in_u.address = 0; + pin.data = -1; /* Don't want notification from slave */ + pin.addr.req = RPT_KILL; + } + else + { /* detach */ + pin.addr.ptrace_addr_data_in_u.address = 1; + pin.data = 0; + pin.addr.req = RPT_DETACH; + } + pin.pid = plst->pid; + pin.flags = PTRFLG_FREE | PTRFLG_NON_OWNER; + + DPRINTF (("TgtKillAndDelete: ptrace_2_svc (%s (%d), %d)\n", + PtraceName (pin.addr.req), pin.addr.req, pin.pid)); + + pout = RPCGENSRVNAME(ptrace_2_svc) (&pin, rqstp);/* start it */ + if (pout->errNo == ESRCH && plst->pid) + TgtDelete(plst, -1, BMSG_KILLED); /* only entry remains */ + return 0; +} + +/* ----------------------------------------------------------------------- + TgtDetachCon - detach a connection's ownership of a process. + ----------------------------------------------------------------------- */ + + void +TgtDetachCon( int conn_idx, int pid_idx, Boolean delete) +{ + if ((unsigned) pid_idx >= pid_list_cnt + || !pid_list[pid_idx].pid) + return; /* not valid */ + if (PIDMAP_TEST (conn_idx, pid_idx)) + { /* if an owner, release control */ + PIDMAP_CLEAR (conn_idx, pid_idx); + + if (pid_list[pid_idx].owners) + pid_list[pid_idx].owners--; + if (pid_list[pid_idx].primary_conn == conn_idx) + pid_list[pid_idx].primary_conn = NO_PRIMARY; + if (delete + && !pid_list[pid_idx].owners + && PROC_TERMINATED (pid_list + pid_idx)) + TgtDelete(&pid_list[pid_idx], -1, 0); /* remove entry */ + } +} + +/* ----------------------------------------------------------------------- + TgtHandleChildChange - decide what action to take after wait() returns. + Used in the master only. + ----------------------------------------------------------------------- */ + +#ifdef DDEBUG +static char* LastStartNames[] = { + "NONE", "STEP", "CONT", "RANGE", + "STEPOFF", "KILLED", "DETACHED" +}; + +char* GetLastStartName (int last_start) +{ + static char buf [32]; + + strcpy (buf, LastStartNames [last_start & ~LAST_START]); + if (last_start & LAST_START) { + strcat (buf, "+START"); + } + return buf; +} +#endif + +Boolean TgtHandleChildChange(PID pid, int* status, int *unexp, + CPU_Exception_frame* ctx) +{ /* return False if continue, else stop */ + int idx, sig; + int bidx = 0; + PID_LIST *plst; + unsigned long PC; + BASE_BREAK *base = NULL; /* break_list[0] is really BASE_BREAK */ + int hadStepEmul; + int origHadStepEmul; + int stopWanted; + + DPRINTF (("TgtHandleChildChange: pid %d status %x cap\n", + (int) pid, *status)); + if (unexp) + *unexp = 0; /* initialize to ok */ + + /* first, find pid in question */ + idx = FindPidEntry (pid); + if (idx < 0) + { /* cannot locate this process */ + DPRINTF (("TgtHandleChildChange: unknown process (%s pid)\n", + FindPidEntry (pid) >= 0 ? "stale" : "unknown")); + if (unexp) + *unexp = 1; /* Unexpected change */ + return(False); /* unknown: ignore (used to stop and notify) */ + } + + /* found */ + plst = &pid_list[idx]; /* pointer to entry */ + /* first we see if just stopped */ + + /* copy ctxt */ + CtxToRegs(ctx, &(plst->regs)); + + stopWanted = plst->stop_wanted; + plst->stop_wanted = 0; /* For the next time */ + + hadStepEmul = BreakClear (plst, -1, -1) > 0; + origHadStepEmul = hadStepEmul; /* hadStepEmul is cleared if real bkpt met */ + + if (STS_SIGNALLED (*status)) + { /* stopped, not terminated */ + sig = STS_GETSIG (*status); /* signal that stopped us */ + + /* now, we read the registers and see what to do next */ + if (TgtPtrace(RPT_GETREGS, pid, (void *)&plst->regs, 0, NULL) < 0) { + memset (&plst->regs, 0, sizeof plst->regs); + } + + /* Get current thread */ + plst->thread = TgtPtrace(RPT_GETTARGETTHREAD, pid, NULL, 0, NULL); + + if (sig == SIGTRAP) + { /* stopped from break/step */ + PC = plst->regs.REG_PC; + /* Must check PC to see whether in situations where we had + step emulation we are on a breakpoint or just + have returned from an emulated single-step */ + if (BreakIdentify (plst, 0 /*no adjust*/, -1 /*no thread*/) > 0) { + hadStepEmul = 0; + } + plst->is_step = hadStepEmul || IS_STEP(plst->regs) + || plst->last_start == LAST_START; + DPRINTF (("TgtHandleChildChange: %s last_start %s\n", plst->is_step + ? "step": "break", GetLastStartName (plst->last_start))); + + if ((plst->is_step || origHadStepEmul || stopWanted) + && (plst->last_start == LAST_STEP + || plst->last_start == LAST_STEPOFF + || plst->last_start == LAST_RANGE)) + { + DPRINTF (("TgtHandleChildChange: restoring stepped-off bkpt\n")); + BreakSteppedOff (plst); + } + + if (plst->last_start == LAST_STEPOFF && (plst->is_step||origHadStepEmul)) + { /* stepped off break and now need cont */ + DPRINTF (("TgtHandleChildChange: auto-resuming after step-off\n")); + plst->last_start = LAST_CONT; /* convert to normal cont */ + if (!stopWanted) { + if (TgtPtrace(RPT_CONT, pid, (char *)1, 0, NULL)) + return True; /* tell people */ + return(False); /* wait for change */ + } + DPRINTF (("TgtHandleChildChange: stop_wanted %d in step-off\n", + stopWanted)); + *status = STS_MAKESIG (stopWanted); + return True; /* Stop and notify */ + } + + base = plst->break_list ? ((BASE_BREAK*)plst->break_list) : + ((BASE_BREAK*)NULL); + /* now see if step in range */ + + if (plst->last_start == LAST_RANGE /* step in range */ + && (plst->is_step || origHadStepEmul) /* not a breakpoint */ + && PC >= base->range_start + && PC <= base->range_end) + { /* still in range, keep going */ + if (stopWanted) { + DPRINTF (("TgtHandleChildChange: stop_wanted %d in step-range\n", + stopWanted)); + } else { + DPRINTF (("TgtHandleChildChange: Reservation at %x\n", + plst->regs.REG_PC)); + } + } + if (!plst->is_step) /* was break */ + { + bidx = BreakIdentify (plst, 1 /*adjust*/, plst->thread); + if (bidx == 0) { + DPRINTF (("TgtHandleChildChange: forwarding bkpt to kernel\n")); + if (unexp) { + *unexp = 1; + } + return False; + } + if (bidx < 0) { /* Unwanted breakpoint, must step it off */ + ptrace_in pin; + ptrace_out* out; + if (origHadStepEmul) + { + DPRINTF (("TgtHandleChildChange: bkpt %x becomes step\n", + plst->regs.REG_PC)); + bidx = -bidx; + plst->is_step = 1; + base->clr_step = plst->break_list [bidx].type == BRKT_INSTR; + base->last_break = bidx; + return True; + } + if (stopWanted) { + DPRINTF (("TgtHandleChildChange: stop_wanted %d at bkpt %x\n", + stopWanted, plst->regs.REG_PC)); + /* The PC has already been adjusted by BreakIdentify */ + *status = STS_MAKESIG (stopWanted); + return True; + } + /* All the handling is done in ptrace_2_svc() so call it */ + bidx = -bidx; + DPRINTF (("TgtHandleChildChange: last %d (%s) restarting bkpt %d\n", + plst->last_start, GetLastStartName (plst->last_start), bidx)); + base->clr_step = 1; + base->last_break = bidx; /* remember which one */ + plst->running = 0; /* So that ptrace is accepted */ + pin.pid = plst->pid; + + if (plst->last_start == LAST_STEP) { + pin.addr.req = RPT_SINGLESTEP; + } else { + pin.addr.req = RPT_CONT; + } + pin.addr.ptrace_addr_data_in_u.address = 1; + pin.data = 0; + pin.flags = PTRFLG_NON_OWNER; + out = RPCGENSRVNAME(ptrace_2_svc) (&pin, NULL); + if (out->result == 0) return False; /* Continue waiting */ + DPRINTF(("TgtHandleChildChange: failed to restart bkpt!\n")); + /* If something went wrong, just stop on breakpoint */ + } + } + } /* else sig != SIGTRAP */ + + /* finally, fill in stop info in break point array base */ + if (bidx > 0) + { /* store break info */ + /* will need to get off the break for SW breakpoints only */ + base->clr_step = plst->break_list [bidx].type == BRKT_INSTR; + base->last_break = bidx; /* remember which one */ + } + else if (base) + { /* clear break info */ + base->clr_step = False; /* not stopped on break */ + base->last_break = 0; + } + /* decision to notify owner based on last_start */ + } /* stopped */ + else /* terminated */ + { + if (plst->last_start == LAST_START) + { /* spawn failed */ + TgtNotifyAll(idx, BMSG_EXEC_FAIL, 0, 0, -1, True); + plst->running = False; /* not running - dead */ + plst->state = *status; /* contains errno in high word */ + return(False); + } + + else if ((UCHAR)(plst->last_start & ~LAST_START) < (UCHAR)LAST_KILLED) + plst->last_start = LAST_NONE; /* doesn't matter anymore */ + else + return(False); /* killed and detach already notified */ + } + return(True); /* stop and notify */ +} + +#ifdef DDEBUG + +/* ----------------------------------------------------------------------- + TgtDbgPtrace - debug version of ptrace. + ----------------------------------------------------------------------- */ + +int TgtDbgPtrace(int request, PID pid, char *addr, int data, void *addr2) +{ + int diag; + + DPRINTF (("TgtDbgPtrace: entered (%s (%d), %d, %x, %d, %x)\n", + PtraceName (request), request, pid, (int) addr, data, + (int) addr2)); + + if (request == RPT_WRITETEXT || request == RPT_WRITEDATA) { + int i; + + DPRINTF (("TgtDbgPtrace:")); + if (rdb_debug) { + for (i = 0; i < data && i < 16; ++i) { + printf (" %02x", ((char*) addr2) [i] & 0xFF); + } + printf ("\n"); + } + } + + diag = TgtRealPtrace (request, pid, addr, data, addr2); + + DPRINTF (("TgtDbgPtrace: returned %d (%x) errno %d\n", + diag, diag, getErrno())); + + if (request == RPT_GETREGS || request == RPT_GETTHREADREGS + || request == RPT_SETREGS || request == RPT_SETTHREADREGS) + { + /* Use DPRINTF() so as to have the id prefix */ + DPRINTF (("TgtDbgPtrace: (%s) PC = %x, SP = %x, FP = %x\n", + PtraceName (request), + ((xdr_regs*)addr)->REG_PC, + ((xdr_regs*)addr)->REG_SP, + ((xdr_regs*)addr)->REG_FP)); + } + + return(diag); +} +#endif /* DDEBUG */ diff --git a/c/src/librdbg/src/servtsp.c b/c/src/librdbg/src/servtsp.c new file mode 100644 index 0000000000..6a83c6caf5 --- /dev/null +++ b/c/src/librdbg/src/servtsp.c @@ -0,0 +1,329 @@ +/* + ************************************************************************** + * + * Component: RDBG + * Module: servtsp.c + * + * Synopsis: Transport management for remote debug server. + * + ************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int out_sock; +static int warm_test; + +static void TimeTestHandler(); + + /* + * TspInit - Initialize the transport system. + * + */ + + void +TspInit (int id) +{ + struct sigaction sa; + + /* setup a socket to send event messages back through */ + out_sock = socket (PF_INET, SOCK_DGRAM, 0); + if (out_sock < 0) { + DPRINTF (("TspInit: socket() failed %d errno %d\n", + out_sock, getErrno())); + return; /* failed to open socket, let caller deal with */ + } + { + struct sockaddr_in addr; + + bzero ((void *)(&addr), sizeof addr); + addr.sin_family = AF_INET; + addr.sin_port = htons (BackPort); + if (bind (out_sock, (struct sockaddr*) &addr, sizeof addr) < 0) { + DPRINTF (("TspInit: bind() failed\n")); + } + } + /* setup alarm timer for warm testing */ + memset (&sa, 0, sizeof (sa)); + sa.sa_handler = TimeTestHandler; + sigaction (SIGALRM, &sa, 0); +} + + /* + * TspTranslateRpcAddr - translate from an RPC handle to an + * opaque address. + * + * Converts the sender's address into the opaque data structure + * used for network addresses. This is used to look up the sender + * on each call. + */ + + Boolean +TspTranslateRpcAddr (struct svc_req* rqstp, NET_OPAQUE* opaque) +{ + struct sockaddr_in* addr; /* used as template to extract net info */ + unsigned char* up; + + memset (opaque, 0, sizeof (NET_OPAQUE)); + /* + * We interpret the remote address as a standard netbuf name. + * The format is 2 bytes of address family (normally AF_INET) + * and then a length (5) and then the IP address. + */ + if (rqstp->rq_xprt->xp_addrlen != 16) + { + DPRINTF (("TspTranslateRpcAddr: Unknown remote address!!!\n")); + setErrno (EPROTONOSUPPORT); + return False; /* invalid, so fails */ + } + /* addr = &rqstp->rq_xprt->xp_raddr; */ + addr = svc_getcaller (rqstp->rq_xprt); + /* verify it is AF_INET */ + if (addr->sin_family != AF_INET) { /* no, error */ + DPRINTF (("TspTranslateRpcAddr: Not an internet address!!\n")); + setErrno (EAFNOSUPPORT);/* invalid addr family */ + return False; + } + /* good address type */ + up = (unsigned char *) &addr->sin_addr.s_addr; + DPRINTF (("TspTranslateRpcAddr: Sent by %u.%u.%u.%u port #%u\n", + up[0], up[1], up[2], up[3], htons (addr->sin_port))); + memcpy (opaque, addr, sizeof (struct sockaddr_in)); + return True; +} + + /* + * TspValidateAddr - validate a passed in opaque address. + * + * Checks that the passed in address is in the format + * expected. + */ + + Boolean +TspValidateAddr (NET_OPAQUE* opaque, NET_OPAQUE* sender) +{ + struct sockaddr_in* addr; /* used as template to extract net info */ + + addr = (struct sockaddr_in*) opaque; + /* Verify it is AF_INET. Should check against sender IP address too */ + if (addr->sin_family != AF_INET) { + DPRINTF (("TspValidateAddr: Back port invalid: %d\n", + htons (addr->sin_port))); + return False; /* not valid */ + } + /* otherwise, we copy in the IP address, since client may not know it */ + addr->sin_addr.s_addr = ((struct sockaddr_in*) sender)->sin_addr.s_addr; + DPRINTF (("TspValidateAddr: Back port is %d\n", htons (addr->sin_port))); + return True; +} + + /* + * TspConnGetIndex - lookup an rpc caller's address as a connection entry. + * + * Looks up an ip address of a caller to locate the + * connection index in our connection array. + */ + + int +TspConnGetIndex (struct svc_req* rqstp) +{ + int conn; + /* &rqstp->rq_xprt->xp_raddr; */ + struct sockaddr_in *addr = svc_getcaller (rqstp->rq_xprt); + + for (conn = 0; conn < conn_list_cnt; conn++) { + if (!conn_list[conn].in_use) + continue; /* not used */ + + if (addr->sin_addr.s_addr == ((struct sockaddr_in *) + &conn_list [conn].sender)->sin_addr.s_addr + && addr->sin_port == ((struct sockaddr_in *) + &conn_list[conn].sender)->sin_port) { + return conn; + } + } + return -1; +} + + + /* + * TspSendWaitChange - send wait-change message to clients to + * notify change. + */ + + void +TspSendWaitChange( + int conn, /* connection to send to */ + BACK_MSG msg, /* BMSG type */ + UINT16 spec, /* special information */ + PID pid, /* pid it refers to */ + UINT32 context, /* additional context for message */ + Boolean force) /* force into being only message */ +{ + int idx; + struct SEND_LIST* snd_ptr; + + if (force) { + /* force to top, which means others gone */ + idx = 0; + conn_list [conn].send_idx = 1; + conn_list[conn].retry = 0; + } else { + for (idx = 0; idx < (int) conn_list[conn].send_idx; idx++) { + if (conn_list[conn].send_list[idx].send_type == msg + && conn_list[conn].send_list[idx].pid == pid) + return; /* already pended for this pid */ + } + idx = conn_list[conn].send_idx; + if (idx+1 > MAX_SEND) + return; /* we lose it, what should we do??? */ + conn_list[conn].send_idx++; + } + snd_ptr = &conn_list[conn].send_list[idx]; + snd_ptr->send_type = msg; /* message to send */ + snd_ptr->retry = TSP_RETRIES; /* about 1 minute of retries */ + snd_ptr->spec = htons ((u_short) spec); + snd_ptr->pid = htonl (pid); + snd_ptr->context = htonl (context); + TspSendMessage (conn, False); /* now do the send */ +} + + /* + * TspSendMessage - send message at top of send list for connection. + */ + + void +TspSendMessage( int conn, Boolean resend) +{ + struct sockaddr_in addr; + struct UDP_MSG msg; + int cnt; + + if (!resend && conn_list[conn].retry) + return; /* already waiting for reply */ + + /* + * Note on above: if no back port we can't remove unless + * someone blows off. + */ + if (!resend) { + /* first time, setup. Set retry count: */ + conn_list[conn].retry = conn_list[conn].send_list[0].retry; + conn_list[conn].last_msg_num++; /* new sequence number */ + if (!warm_test++) { /* starting, so enable timer */ + alarm (2); /* resend every 2 seconds as needed */ + } + } + + msg.type = conn_list[conn].send_list[0].send_type; + msg.msg_num = conn_list[conn].last_msg_num; + msg.spec = conn_list[conn].send_list[0].spec; + msg.pid = conn_list[conn].send_list[0].pid; + msg.context = conn_list[conn].send_list[0].context; + + memset (&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_port = ((struct sockaddr_in*)&conn_list[conn].back_port)->sin_port; + addr.sin_addr.s_addr = + ((struct sockaddr_in*)&conn_list[conn].back_port)->sin_addr.s_addr; + + DPRINTF (("TspSendMessage: Sending msg %d (%s) to port %d\n", + msg.type, BmsgNames [msg.type], HL_W (addr.sin_port))); + + cnt = sendto (out_sock, &msg, sizeof msg, 0, (struct sockaddr*) &addr, + sizeof addr); + if (cnt != sizeof msg) { /* failed on send */ + printf ("%s: Failed to send msg %d to conn %d (%d vs. %d)\n", + ActName, msg.type, conn, cnt, sizeof msg); + } +} + + /* + * TspMessageReceive - confirmation received, now send next if any. + * + * - since UDP is connectionless, we batch up the sends and use + * one at a time until we get a message indicating ready for + * next (from ack). + */ + + void +TspMessageReceive (int conn, PID pid) +{ + /* We remove the send list entry and use next if any */ + conn_list[conn].retry = 0; /* reset */ + if (!warm_test || !--warm_test) { + alarm (0); /* reset timer if not used */ + } +#ifdef DDEBUG + if (conn_list[conn].send_list[0].send_type == BMSG_WARM) { + DPRINTF (("TspMessageReceive: Connection reset for conn %d\n", conn)); + } +#endif + /* Move up by one if needed */ + if (!--conn_list[conn].send_idx) + return; /* no more to do */ + + memcpy (conn_list[conn].send_list, conn_list[conn].send_list+1, + conn_list[conn].send_idx * sizeof(struct SEND_LIST)); /* copy down */ + TspSendMessage (conn, 0); +} + + /* + * TspGetHostName - return client's host name. + * + * - this routine returns the name of the client's host or the net + * number of unknown. + */ + + char* +TspGetHostName (conn_idx) + int conn_idx; /* client connection number */ +{ + static char buff [30]; /* largest net num */ + unsigned char* cp; + + cp = conn_list[conn_idx].sender.c+4; + sprintf (buff, "%u.%u.%u.%u", cp[0], cp[1], cp[2], cp[3]); + return buff; +} + + /* + * TimeTestHandler - alarm timer handler to resend warm/wait test. + */ + + static void +TimeTestHandler() +{ + int conn; + + if (!warm_test) + return; /* no longer enabled */ + + for (conn = 0; conn < conn_list_cnt; conn++) { + /* locate all that are using this */ + if (!conn_list[conn].in_use) + continue; /* not used */ + + if (!conn_list[conn].retry) continue; + /* found one that we are testing */ + if (!--conn_list[conn].retry) { + /* + * Counted down the retries: blow off. + * Need to have connection flag to indicate not blowing + * off for cases where client is stopped due to being + * debugged. + */ + ConnDelete (conn, NULL, CLOSE_IGNORE); + continue; + } + TspSendMessage (conn, True); /* send another message */ + } + alarm (2); /* setup for 2 seconds from now */ +} diff --git a/c/src/librdbg/src/servutil.c b/c/src/librdbg/src/servutil.c new file mode 100644 index 0000000000..40187faead --- /dev/null +++ b/c/src/librdbg/src/servutil.c @@ -0,0 +1,130 @@ +/* + ********************************************************************** + * + * Component: RDB + * Module: servutil.c + * + * Synopsis: Various utility routines + * + ********************************************************************** + */ + +#include +#include +#include + +/*----- Management of per-process list ----*/ + + /* + * ListAlloc - build up list entry. + * + * Notes: + * - this is a generic routine to build up entries in the per-connection + * list. The fields list, list_sz and list_alloc are affected. + */ + + Boolean +ListAlloc(buff, clst) + char *buff; + CONN_LIST *clst; /* place to copy it */ +{ + int tmp; + char* name; + int new_len; + int len; + + tmp = strlen(buff); + new_len = (int)clst->list_sz + 1 + tmp; + if (clst->list_alloc < (unsigned)new_len) { + /* need more space */ + name = (char *)Realloc(clst->list, len = new_len + MAX_FILENAME); + if (name == NULL) { + return(False); /* failed, no space */ + } + clst->list_alloc = len; + clst->list = name; + } + strcpy(clst->list + clst->list_sz, buff); + clst->list_sz += tmp; + return(True); +} + +/*----- Management of processes -----*/ + + /* + * FindPidEntry - locate pid_list entry from pid + */ + + int +FindPidEntry (pid) + int pid; /* process identifier */ +{ + int idx; + + /* pid 0 is invalid, and signals a free slot */ + if (pid_list == NULL || pid == 0) { + return -1; + } + for (idx = 0; idx < pid_list_cnt; idx++) { + if (pid_list [idx].pid == pid ) + return idx; + } + return -1; +} + +/*----- Debug suport -----*/ + +#ifdef DDEBUG + + /* + * Names of debug primitives + */ + +const char* PtraceNames [] = { + +"RPT_TRACEME", "RPT_PEEKTEXT", "RPT_PEEKDATA", "RPT_PEEKUSER", +"RPT_POKETEXT", "RPT_POKEDATA", "RPT_POKEUSER", "RPT_CONT", +"RPT_KILL", "RPT_SINGLESTEP", "RPT_ATTACH", "RPT_DETACH", +"RPT_GETREGS", "RPT_SETREGS", "RPT_GETFPREGS", "RPT_SETFPREGS", +"RPT_READDATA", "RPT_WRITEDATA", "RPT_READTEXT", "RPT_WRITETEXT", +"RPT_GETFPAREGS", "RPT_SETFPAREGS", "RPT_22", "RPT_23", +"RPT_SYSCALL", "RPT_DUMPCORE", "RPT_26", "RPT_27", +"RPT_28", "RPT_GETUCODE", "RPT_30", "RPT_31", +"RPT_32", "RPT_33", "RPT_34", "RPT_35", +"RPT_36", "RPT_37", "RPT_38", "RPT_39", +"RPT_40", "RPT_41", "RPT_42", "RPT_43", +"RPT_44", "RPT_45", "RPT_46", "RPT_47", +"RPT_48", "RPT_49", "RPT_GETTARGETTHREAD", "RPT_SETTARGETTHREAD", +"RPT_THREADSUSPEND", "RPT_THREADRESUME", "RPT_THREADLIST", "RPT_GETTHREADNAME", +"RPT_SETTHREADNAME", "RPT_SETTHREADREGS", "RPT_GETTHREADREGS", + "RPT_59", +"RPT_60", "RPT_61", "RPT_62", "RPT_63", +"RPT_64", "RPT_65", "RPT_66", "RPT_67", +"RPT_68", "RPT_69", "RPT_70", "RPT_71", +"RPT_72", "RPT_73", "RPT_74", "RPT_STEPRANGE", +"RPT_CONTTO", "RPT_SETBREAK", "RPT_CLRBREAK", "RPT_GETBREAK", +"RPT_GETNAME", "RPT_STOP", +"RPT_PGETREGS", "RPT_PSETREGS", +"RPT_PSETTHREADREGS", "RPT_PGETTHREADREGS" +}; + +const char* +PtraceName(req) + int req; +{ + static char bufret[40]; + + if ((req < 0) || (req >= sizeof(PtraceNames)/sizeof(char*))) { + sprintf(bufret, "BAD_REQ_%d", req); + return bufret; + } + return PtraceNames[req]; +} + +const char* BmsgNames [] = { + "?", "WARM", "WAIT", "BREAK", + "EXEC_FAIL", "DETACH", "KILLED", "NOT_PRIM", + "NEW_PID" +}; + +#endif /* DDEBUG */ -- cgit v1.2.3