diff options
Diffstat (limited to 'gdb/remote-vx.c')
-rw-r--r-- | gdb/remote-vx.c | 1467 |
1 files changed, 1467 insertions, 0 deletions
diff --git a/gdb/remote-vx.c b/gdb/remote-vx.c new file mode 100644 index 0000000..9ad86d4 --- /dev/null +++ b/gdb/remote-vx.c @@ -0,0 +1,1467 @@ +/* Memory-access and commands for remote VxWorks processes, for GDB. + Copyright (C) 1990-95, 1997-98, 1999 Free Software Foundation, Inc. + Contributed by Wind River Systems and Cygnus Support. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "frame.h" +#include "inferior.h" +#include "wait.h" +#include "target.h" +#include "gdbcore.h" +#include "command.h" +#include "symtab.h" +#include "complaints.h" +#include "gdbcmd.h" +#include "bfd.h" /* Required by objfiles.h. */ +#include "symfile.h" /* Required by objfiles.h. */ +#include "objfiles.h" +#include "gdb-stabs.h" + +#include "gdb_string.h" +#include <errno.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#define malloc bogon_malloc /* Sun claims "char *malloc()" not void * */ +#define free bogon_free /* Sun claims "int free()" not void */ +#define realloc bogon_realloc /* Sun claims "char *realloc()", not void * */ +#include <rpc/rpc.h> +#undef malloc +#undef free +#undef realloc +#include <sys/time.h> /* UTek's <rpc/rpc.h> doesn't #incl this */ +#include <netdb.h> +#include "vx-share/ptrace.h" +#include "vx-share/xdr_ptrace.h" +#include "vx-share/xdr_ld.h" +#include "vx-share/xdr_rdb.h" +#include "vx-share/dbgRpcLib.h" + +#include <symtab.h> + +/* Maximum number of bytes to transfer in a single + PTRACE_{READ,WRITE}DATA request. */ +#define VX_MEMXFER_MAX 4096 + +extern void vx_read_register (); +extern void vx_write_register (); +extern void symbol_file_command (); +extern int stop_soon_quietly; /* for wait_for_inferior */ + +static int net_step (); +static int net_ptrace_clnt_call (); /* Forward decl */ +static enum clnt_stat net_clnt_call (); /* Forward decl */ + +/* Target ops structure for accessing memory and such over the net */ + +static struct target_ops vx_ops; + +/* Target ops structure for accessing VxWorks child processes over the net */ + +static struct target_ops vx_run_ops; + +/* Saved name of target host and called function for "info files". + Both malloc'd. */ + +static char *vx_host; +static char *vx_running; /* Called function */ + +/* Nonzero means target that is being debugged remotely has a floating + point processor. */ + +int target_has_fp; + +/* Default error message when the network is forking up. */ + +static const char rpcerr[] = "network target debugging: rpc error"; + +CLIENT *pClient; /* client used in net debugging */ +static int ptraceSock = RPC_ANYSOCK; + +enum clnt_stat net_clnt_call(); +static void parse_args (); + +static struct timeval rpcTimeout = { 10, 0 }; + +static char *skip_white_space (); +static char *find_white_space (); + +/* Tell the VxWorks target system to download a file. + The load addresses of the text, data, and bss segments are + stored in *pTextAddr, *pDataAddr, and *pBssAddr (respectively). + Returns 0 for success, -1 for failure. */ + +static int +net_load (filename, pTextAddr, pDataAddr, pBssAddr) + char *filename; + CORE_ADDR *pTextAddr; + CORE_ADDR *pDataAddr; + CORE_ADDR *pBssAddr; +{ + enum clnt_stat status; + struct ldfile ldstruct; + struct timeval load_timeout; + + memset ((char *) &ldstruct, '\0', sizeof (ldstruct)); + + /* We invoke clnt_call () here directly, instead of through + net_clnt_call (), because we need to set a large timeout value. + The load on the target side can take quite a while, easily + more than 10 seconds. The user can kill this call by typing + CTRL-C if there really is a problem with the load. + + Do not change the tv_sec value without checking -- select() imposes + a limit of 10**8 on it for no good reason that I can see... */ + + load_timeout.tv_sec = 99999999; /* A large number, effectively inf. */ + load_timeout.tv_usec = 0; + + status = clnt_call (pClient, VX_LOAD, xdr_wrapstring, &filename, xdr_ldfile, + &ldstruct, load_timeout); + + if (status == RPC_SUCCESS) + { + if (*ldstruct.name == 0) /* load failed on VxWorks side */ + return -1; + *pTextAddr = ldstruct.txt_addr; + *pDataAddr = ldstruct.data_addr; + *pBssAddr = ldstruct.bss_addr; + return 0; + } + else + return -1; +} + +/* returns 0 if successful, errno if RPC failed or VxWorks complains. */ + +static int +net_break (addr, procnum) + int addr; + u_long procnum; +{ + enum clnt_stat status; + int break_status; + Rptrace ptrace_in; /* XXX This is stupid. It doesn't need to be a ptrace + structure. How about something smaller? */ + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + break_status = 0; + + ptrace_in.addr = addr; + ptrace_in.pid = inferior_pid; + + status = net_clnt_call (procnum, xdr_rptrace, &ptrace_in, xdr_int, + &break_status); + + if (status != RPC_SUCCESS) + return errno; + + if (break_status == -1) + return ENOMEM; + return break_status; /* probably (FIXME) zero */ +} + +/* returns 0 if successful, errno otherwise */ + +static int +vx_insert_breakpoint (addr) + int addr; +{ + return net_break (addr, VX_BREAK_ADD); +} + +/* returns 0 if successful, errno otherwise */ + +static int +vx_remove_breakpoint (addr) + int addr; +{ + return net_break (addr, VX_BREAK_DELETE); +} + +/* Start an inferior process and sets inferior_pid to its pid. + EXEC_FILE is the file to run. + ALLARGS is a string containing the arguments to the program. + ENV is the environment vector to pass. + Returns process id. Errors reported with error(). + On VxWorks, we ignore exec_file. */ + +static void +vx_create_inferior (exec_file, args, env) + char *exec_file; + char *args; + char **env; +{ + enum clnt_stat status; + arg_array passArgs; + TASK_START taskStart; + + memset ((char *) &passArgs, '\0', sizeof (passArgs)); + memset ((char *) &taskStart, '\0', sizeof (taskStart)); + + /* parse arguments, put them in passArgs */ + + parse_args (args, &passArgs); + + if (passArgs.arg_array_len == 0) + error ("You must specify a function name to run, and arguments if any"); + + status = net_clnt_call (PROCESS_START, xdr_arg_array, &passArgs, + xdr_TASK_START, &taskStart); + + if ((status != RPC_SUCCESS) || (taskStart.status == -1)) + error ("Can't create process on remote target machine"); + + /* Save the name of the running function */ + vx_running = savestring (passArgs.arg_array_val[0], + strlen (passArgs.arg_array_val[0])); + + push_target (&vx_run_ops); + inferior_pid = taskStart.pid; + + /* We will get a trace trap after one instruction. + Insert breakpoints and continue. */ + + init_wait_for_inferior (); + + /* Set up the "saved terminal modes" of the inferior + based on what modes we are starting it with. */ + target_terminal_init (); + + /* Install inferior's terminal modes. */ + target_terminal_inferior (); + + stop_soon_quietly = 1; + wait_for_inferior (); /* Get the task spawn event */ + stop_soon_quietly = 0; + + /* insert_step_breakpoint (); FIXME, do we need this? */ + proceed (-1, TARGET_SIGNAL_DEFAULT, 0); +} + +/* Fill ARGSTRUCT in argc/argv form with the arguments from the + argument string ARGSTRING. */ + +static void +parse_args (arg_string, arg_struct) + register char *arg_string; + arg_array *arg_struct; +{ + register int arg_count = 0; /* number of arguments */ + register int arg_index = 0; + register char *p0; + + memset ((char *) arg_struct, '\0', sizeof (arg_array)); + + /* first count how many arguments there are */ + + p0 = arg_string; + while (*p0 != '\0') + { + if (*(p0 = skip_white_space (p0)) == '\0') + break; + p0 = find_white_space (p0); + arg_count++; + } + + arg_struct->arg_array_len = arg_count; + arg_struct->arg_array_val = (char **) xmalloc ((arg_count + 1) + * sizeof (char *)); + + /* now copy argument strings into arg_struct. */ + + while (*(arg_string = skip_white_space (arg_string))) + { + p0 = find_white_space (arg_string); + arg_struct->arg_array_val[arg_index++] = savestring (arg_string, + p0 - arg_string); + arg_string = p0; + } + + arg_struct->arg_array_val[arg_count] = NULL; +} + +/* Advance a string pointer across whitespace and return a pointer + to the first non-white character. */ + +static char * +skip_white_space (p) + register char *p; +{ + while (*p == ' ' || *p == '\t') + p++; + return p; +} + +/* Search for the first unquoted whitespace character in a string. + Returns a pointer to the character, or to the null terminator + if no whitespace is found. */ + +static char * +find_white_space (p) + register char *p; +{ + register int c; + + while ((c = *p) != ' ' && c != '\t' && c) + { + if (c == '\'' || c == '"') + { + while (*++p != c && *p) + { + if (*p == '\\') + p++; + } + if (!*p) + break; + } + p++; + } + return p; +} + +/* Poll the VxWorks target system for an event related + to the debugged task. + Returns -1 if remote wait failed, task status otherwise. */ + +static int +net_wait (pEvent) + RDB_EVENT *pEvent; +{ + int pid; + enum clnt_stat status; + + memset ((char *) pEvent, '\0', sizeof (RDB_EVENT)); + + pid = inferior_pid; + status = net_clnt_call (PROCESS_WAIT, xdr_int, &pid, xdr_RDB_EVENT, + pEvent); + + /* return (status == RPC_SUCCESS)? pEvent->status: -1; */ + if (status == RPC_SUCCESS) + return ((pEvent->status) ? 1 : 0); + else if (status == RPC_TIMEDOUT) + return (1); + else + return (-1); +} + +/* Suspend the remote task. + Returns -1 if suspend fails on target system, 0 otherwise. */ + +static int +net_quit () +{ + int pid; + int quit_status; + enum clnt_stat status; + + quit_status = 0; + + /* don't let rdbTask suspend itself by passing a pid of 0 */ + + if ((pid = inferior_pid) == 0) + return -1; + + status = net_clnt_call (VX_TASK_SUSPEND, xdr_int, &pid, xdr_int, + &quit_status); + + return (status == RPC_SUCCESS)? quit_status: -1; +} + +/* Read a register or registers from the remote system. */ + +void +net_read_registers (reg_buf, len, procnum) + char *reg_buf; + int len; + u_long procnum; +{ + int status; + Rptrace ptrace_in; + Ptrace_return ptrace_out; + C_bytes out_data; + char message[100]; + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out)); + + /* Initialize RPC input argument structure. */ + + ptrace_in.pid = inferior_pid; + ptrace_in.info.ttype = NOINFO; + + /* Initialize RPC return value structure. */ + + out_data.bytes = reg_buf; + out_data.len = len; + ptrace_out.info.more_data = (caddr_t) &out_data; + + /* Call RPC; take an error exit if appropriate. */ + + status = net_ptrace_clnt_call (procnum, &ptrace_in, &ptrace_out); + if (status) + error (rpcerr); + if (ptrace_out.status == -1) + { + errno = ptrace_out.errno_num; + sprintf (message, "reading %s registers", (procnum == PTRACE_GETREGS) + ? "general-purpose" + : "floating-point"); + perror_with_name (message); + } +} + +/* Write register values to a VxWorks target. REG_BUF points to a buffer + containing the raw register values, LEN is the length of REG_BUF in + bytes, and PROCNUM is the RPC procedure number (PTRACE_SETREGS or + PTRACE_SETFPREGS). An error exit is taken if the RPC call fails or + if an error status is returned by the remote debug server. This is + a utility routine used by vx_write_register (). */ + +void +net_write_registers (reg_buf, len, procnum) + char *reg_buf; + int len; + u_long procnum; +{ + int status; + Rptrace ptrace_in; + Ptrace_return ptrace_out; + C_bytes in_data; + char message[100]; + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out)); + + /* Initialize RPC input argument structure. */ + + in_data.bytes = reg_buf; + in_data.len = len; + + ptrace_in.pid = inferior_pid; + ptrace_in.info.ttype = DATA; + ptrace_in.info.more_data = (caddr_t) &in_data; + + /* Call RPC; take an error exit if appropriate. */ + + status = net_ptrace_clnt_call (procnum, &ptrace_in, &ptrace_out); + if (status) + error (rpcerr); + if (ptrace_out.status == -1) + { + errno = ptrace_out.errno_num; + sprintf (message, "writing %s registers", (procnum == PTRACE_SETREGS) + ? "general-purpose" + : "floating-point"); + perror_with_name (message); + } +} + +/* Prepare to store registers. Since we will store all of them, + read out their current values now. */ + +static void +vx_prepare_to_store () +{ + /* Fetch all registers, if any of them are not yet fetched. */ + read_register_bytes (0, NULL, REGISTER_BYTES); +} + +/* Copy LEN bytes to or from remote inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. WRITE is true if writing to the + inferior. + Result is the number of bytes written or read (zero if error). The + protocol allows us to return a negative count, indicating that we can't + handle the current address but can handle one N bytes further, but + vxworks doesn't give us that information. */ + +static int +vx_xfer_memory (memaddr, myaddr, len, write, target) + CORE_ADDR memaddr; + char *myaddr; + int len; + int write; + struct target_ops *target; /* ignored */ +{ + int status; + Rptrace ptrace_in; + Ptrace_return ptrace_out; + C_bytes data; + enum ptracereq request; + int nleft, nxfer; + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out)); + + ptrace_in.pid = inferior_pid; /* XXX pid unnecessary for READDATA */ + ptrace_in.addr = (int) memaddr; /* Where from */ + ptrace_in.data = len; /* How many bytes */ + + if (write) + { + ptrace_in.info.ttype = DATA; + ptrace_in.info.more_data = (caddr_t) &data; + + data.bytes = (caddr_t) myaddr; /* Where from */ + data.len = len; /* How many bytes (again, for XDR) */ + request = PTRACE_WRITEDATA; + } + else + { + ptrace_out.info.more_data = (caddr_t) &data; + request = PTRACE_READDATA; + } + /* Loop until the entire request has been satisfied, transferring + at most VX_MEMXFER_MAX bytes per iteration. Break from the loop + if an error status is returned by the remote debug server. */ + + nleft = len; + status = 0; + + while (nleft > 0 && status == 0) + { + nxfer = min (nleft, VX_MEMXFER_MAX); + + ptrace_in.addr = (int) memaddr; + ptrace_in.data = nxfer; + data.bytes = (caddr_t) myaddr; + data.len = nxfer; + + /* Request a block from the remote debug server; if RPC fails, + report an error and return to debugger command level. */ + + if (net_ptrace_clnt_call (request, &ptrace_in, &ptrace_out)) + error (rpcerr); + + status = ptrace_out.status; + if (status == 0) + { + memaddr += nxfer; + myaddr += nxfer; + nleft -= nxfer; + } + else + { + /* A target-side error has ocurred. Set errno to the error + code chosen by the target so that a later perror () will + say something meaningful. */ + + errno = ptrace_out.errno_num; + } + } + + /* Return the number of bytes transferred. */ + + return (len - nleft); +} + +static void +vx_files_info () +{ + printf_unfiltered ("\tAttached to host `%s'", vx_host); + printf_unfiltered (", which has %sfloating point", target_has_fp? "": "no "); + printf_unfiltered (".\n"); +} + +static void +vx_run_files_info () +{ + printf_unfiltered ("\tRunning %s VxWorks process %s", + vx_running ? "child" : "attached", + local_hex_string (inferior_pid)); + if (vx_running) + printf_unfiltered (", function `%s'", vx_running); + printf_unfiltered(".\n"); +} + +static void +vx_resume (pid, step, siggnal) + int pid; + int step; + enum target_signal siggnal; +{ + int status; + Rptrace ptrace_in; + Ptrace_return ptrace_out; + CORE_ADDR cont_addr; + + if (pid == -1) + pid = inferior_pid; + + if (siggnal != 0 && siggnal != stop_signal) + error ("Cannot send signals to VxWorks processes"); + + /* Set CONT_ADDR to the address at which we are continuing, + or to 1 if we are continuing from where the program stopped. + This conforms to traditional ptrace () usage, but at the same + time has special meaning for the VxWorks remote debug server. + If the address is not 1, the server knows that the target + program is jumping to a new address, which requires special + handling if there is a breakpoint at the new address. */ + + cont_addr = read_register (PC_REGNUM); + if (cont_addr == stop_pc) + cont_addr = 1; + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out)); + + ptrace_in.pid = pid; + ptrace_in.addr = cont_addr; /* Target side insists on this, or it panics. */ + + if (step) + status = net_step(); + else + status = net_ptrace_clnt_call (PTRACE_CONT, &ptrace_in, &ptrace_out); + + if (status) + error (rpcerr); + if (ptrace_out.status == -1) + { + errno = ptrace_out.errno_num; + perror_with_name ("Resuming remote process"); + } +} + +static void +vx_mourn_inferior () +{ + pop_target (); /* Pop back to no-child state */ + generic_mourn_inferior (); +} + + +static void vx_add_symbols PARAMS ((char *, int, CORE_ADDR, CORE_ADDR, + CORE_ADDR)); + +struct find_sect_args { + CORE_ADDR text_start; + CORE_ADDR data_start; + CORE_ADDR bss_start; +}; + +static void find_sect PARAMS ((bfd *, asection *, void *)); + +static void +find_sect (abfd, sect, obj) + bfd *abfd; + asection *sect; + PTR obj; +{ + struct find_sect_args *args = (struct find_sect_args *)obj; + + if (bfd_get_section_flags (abfd, sect) & (SEC_CODE & SEC_READONLY)) + args->text_start = bfd_get_section_vma (abfd, sect); + else if (bfd_get_section_flags (abfd, sect) & SEC_ALLOC) + { + if (bfd_get_section_flags (abfd, sect) & SEC_LOAD) + { + /* Exclude .ctor and .dtor sections which have SEC_CODE set but not + SEC_DATA. */ + if (bfd_get_section_flags (abfd, sect) & SEC_DATA) + args->data_start = bfd_get_section_vma (abfd, sect); + } + else + args->bss_start = bfd_get_section_vma (abfd, sect); + } +} + +static void +vx_add_symbols (name, from_tty, text_addr, data_addr, bss_addr) + char *name; + int from_tty; + CORE_ADDR text_addr; + CORE_ADDR data_addr; + CORE_ADDR bss_addr; +{ + struct section_offsets *offs; + struct objfile *objfile; + struct find_sect_args ss; + + /* It might be nice to suppress the breakpoint_re_set which happens here + because we are going to do one again after the objfile_relocate. */ + objfile = symbol_file_add (name, from_tty, 0, 0, 0, 0, 0, 0); + + /* This is a (slightly cheesy) way of superceding the old symbols. A less + cheesy way would be to find the objfile with the same name and + free_objfile it. */ + objfile_to_front (objfile); + + offs = (struct section_offsets *) + alloca (sizeof (struct section_offsets) + + objfile->num_sections * sizeof (offs->offsets)); + memcpy (offs, objfile->section_offsets, + sizeof (struct section_offsets) + + objfile->num_sections * sizeof (offs->offsets)); + + ss.text_start = 0; + ss.data_start = 0; + ss.bss_start = 0; + bfd_map_over_sections (objfile->obfd, find_sect, &ss); + + /* Both COFF and b.out frontends use these SECT_OFF_* values. */ + ANOFFSET (offs, SECT_OFF_TEXT) = text_addr - ss.text_start; + ANOFFSET (offs, SECT_OFF_DATA) = data_addr - ss.data_start; + ANOFFSET (offs, SECT_OFF_BSS) = bss_addr - ss.bss_start; + objfile_relocate (objfile, offs); +} + +/* This function allows the addition of incrementally linked object files. */ + +static void +vx_load_command (arg_string, from_tty) + char *arg_string; + int from_tty; +{ + CORE_ADDR text_addr; + CORE_ADDR data_addr; + CORE_ADDR bss_addr; + + if (arg_string == 0) + error ("The load command takes a file name"); + + arg_string = tilde_expand (arg_string); + make_cleanup (free, arg_string); + + dont_repeat (); + + /* Refuse to load the module if a debugged task is running. Doing so + can have a number of unpleasant consequences to the running task. */ + + if (inferior_pid != 0 && target_has_execution) + { + if (query ("You may not load a module while the target task is running.\n\ +Kill the target task? ")) + target_kill (); + else + error ("Load cancelled."); + } + + QUIT; + immediate_quit++; + if (net_load (arg_string, &text_addr, &data_addr, &bss_addr) == -1) + error ("Load failed on target machine"); + immediate_quit--; + + vx_add_symbols (arg_string, from_tty, text_addr, data_addr, bss_addr); + + /* Getting new symbols may change our opinion about what is + frameless. */ + reinit_frame_cache (); +} + +/* Single step the target program at the source or machine level. + Takes an error exit if rpc fails. + Returns -1 if remote single-step operation fails, else 0. */ + +static int +net_step () +{ + enum clnt_stat status; + int step_status; + SOURCE_STEP source_step; + + source_step.taskId = inferior_pid; + + if (step_range_end) + { + source_step.startAddr = step_range_start; + source_step.endAddr = step_range_end; + } + else + { + source_step.startAddr = 0; + source_step.endAddr = 0; + } + + status = net_clnt_call (VX_SOURCE_STEP, xdr_SOURCE_STEP, &source_step, + xdr_int, &step_status); + + if (status == RPC_SUCCESS) + return step_status; + else + error (rpcerr); +} + +/* Emulate ptrace using RPC calls to the VxWorks target system. + Returns nonzero (-1) if RPC status to VxWorks is bad, 0 otherwise. */ + +static int +net_ptrace_clnt_call (request, pPtraceIn, pPtraceOut) + enum ptracereq request; + Rptrace *pPtraceIn; + Ptrace_return *pPtraceOut; +{ + enum clnt_stat status; + + status = net_clnt_call (request, xdr_rptrace, pPtraceIn, xdr_ptrace_return, + pPtraceOut); + + if (status != RPC_SUCCESS) + return -1; + + return 0; +} + +/* Query the target for the name of the file from which VxWorks was + booted. pBootFile is the address of a pointer to the buffer to + receive the file name; if the pointer pointed to by pBootFile is + NULL, memory for the buffer will be allocated by XDR. + Returns -1 if rpc failed, 0 otherwise. */ + +static int +net_get_boot_file (pBootFile) + char **pBootFile; +{ + enum clnt_stat status; + + status = net_clnt_call (VX_BOOT_FILE_INQ, xdr_void, (char *) 0, + xdr_wrapstring, pBootFile); + return (status == RPC_SUCCESS) ? 0 : -1; +} + +/* Fetch a list of loaded object modules from the VxWorks target. + Returns -1 if rpc failed, 0 otherwise + There's no way to check if the returned loadTable is correct. + VxWorks doesn't check it. */ + +static int +net_get_symbols (pLoadTable) + ldtabl *pLoadTable; /* return pointer to ldtabl here */ +{ + enum clnt_stat status; + + memset ((char *) pLoadTable, '\0', sizeof (struct ldtabl)); + + status = net_clnt_call (VX_STATE_INQ, xdr_void, 0, xdr_ldtabl, pLoadTable); + return (status == RPC_SUCCESS) ? 0 : -1; +} + +/* Look up a symbol in the VxWorks target's symbol table. + Returns status of symbol read on target side (0=success, -1=fail) + Returns -1 and complain()s if rpc fails. */ + +struct complaint cant_contact_target = + {"Lost contact with VxWorks target", 0, 0}; + +static int +vx_lookup_symbol (name, pAddr) + char *name; /* symbol name */ + CORE_ADDR *pAddr; +{ + enum clnt_stat status; + SYMBOL_ADDR symbolAddr; + + *pAddr = 0; + memset ((char *) &symbolAddr, '\0', sizeof (symbolAddr)); + + status = net_clnt_call (VX_SYMBOL_INQ, xdr_wrapstring, &name, + xdr_SYMBOL_ADDR, &symbolAddr); + if (status != RPC_SUCCESS) + { + complain (&cant_contact_target); + return -1; + } + + *pAddr = symbolAddr.addr; + return symbolAddr.status; +} + +/* Check to see if the VxWorks target has a floating point coprocessor. + Returns 1 if target has floating point processor, 0 otherwise. + Calls error() if rpc fails. */ + +static int +net_check_for_fp () +{ + enum clnt_stat status; + bool_t fp = 0; /* true if fp processor is present on target board */ + + status = net_clnt_call (VX_FP_INQUIRE, xdr_void, 0, xdr_bool, &fp); + if (status != RPC_SUCCESS) + error (rpcerr); + + return (int) fp; +} + +/* Establish an RPC connection with the VxWorks target system. + Calls error () if unable to establish connection. */ + +static void +net_connect (host) + char *host; +{ + struct sockaddr_in destAddr; + struct hostent *destHost; + unsigned long addr; + + /* Get the internet address for the given host. Allow a numeric + IP address or a hostname. */ + + addr = inet_addr (host); + if (addr == -1) + { + destHost = (struct hostent *) gethostbyname (host); + if (destHost == NULL) + /* FIXME: Probably should include hostname here in quotes. + For example if the user types "target vxworks vx960 " it should + say "Invalid host `vx960 '." not just "Invalid hostname". */ + error ("Invalid hostname. Couldn't find remote host address."); + addr = * (unsigned long *) destHost->h_addr; + } + + memset (&destAddr, '\0', sizeof (destAddr)); + + destAddr.sin_addr.s_addr = addr; + destAddr.sin_family = AF_INET; + destAddr.sin_port = 0; /* set to actual port that remote + ptrace is listening on. */ + + /* Create a tcp client transport on which to issue + calls to the remote ptrace server. */ + + ptraceSock = RPC_ANYSOCK; + pClient = clnttcp_create (&destAddr, RDBPROG, RDBVERS, &ptraceSock, 0, 0); + /* FIXME, here is where we deal with different version numbers of the + proto */ + + if (pClient == NULL) + { + clnt_pcreateerror ("\tnet_connect"); + error ("Couldn't connect to remote target."); + } +} + +/* Sleep for the specified number of milliseconds + * (assumed to be less than 1000). + * If select () is interrupted, returns immediately; + * takes an error exit if select () fails for some other reason. + */ + +static void +sleep_ms (ms) + long ms; +{ + struct timeval select_timeout; + int status; + + select_timeout.tv_sec = 0; + select_timeout.tv_usec = ms * 1000; + + status = select (0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, + &select_timeout); + + if (status < 0 && errno != EINTR) + perror_with_name ("select"); +} + +static int +vx_wait (pid_to_wait_for, status) + int pid_to_wait_for; + struct target_waitstatus *status; +{ + register int pid; + RDB_EVENT rdbEvent; + int quit_failed; + + do + { + /* If CTRL-C is hit during this loop, + suspend the inferior process. */ + + quit_failed = 0; + if (quit_flag) + { + quit_failed = (net_quit () == -1); + quit_flag = 0; + } + + /* If a net_quit () or net_wait () call has failed, + allow the user to break the connection with the target. + We can't simply error () out of this loop, since the + data structures representing the state of the inferior + are in an inconsistent state. */ + + if (quit_failed || net_wait (&rdbEvent) == -1) + { + terminal_ours (); + if (query ("Can't %s. Disconnect from target system? ", + (quit_failed) ? "suspend remote task" + : "get status of remote task")) + { + target_mourn_inferior(); + error ("Use the \"target\" command to reconnect."); + } + else + { + terminal_inferior (); + continue; + } + } + + pid = rdbEvent.taskId; + if (pid == 0) + { + sleep_ms (200); /* FIXME Don't kill the network too badly */ + } + else if (pid != inferior_pid) + fatal ("Bad pid for debugged task: %s\n", + local_hex_string((unsigned long) pid)); + } while (pid == 0); + + /* The mostly likely kind. */ + status->kind = TARGET_WAITKIND_STOPPED; + + switch (rdbEvent.eventType) + { + case EVENT_EXIT: + status->kind = TARGET_WAITKIND_EXITED; + /* FIXME is it possible to distinguish between a + normal vs abnormal exit in VxWorks? */ + status->value.integer = 0; + break; + + case EVENT_START: + /* Task was just started. */ + status->value.sig = TARGET_SIGNAL_TRAP; + break; + + case EVENT_STOP: + status->value.sig = TARGET_SIGNAL_TRAP; + /* XXX was it stopped by a signal? act accordingly */ + break; + + case EVENT_BREAK: /* Breakpoint was hit. */ + status->value.sig = TARGET_SIGNAL_TRAP; + break; + + case EVENT_SUSPEND: /* Task was suspended, probably by ^C. */ + status->value.sig = TARGET_SIGNAL_INT; + break; + + case EVENT_BUS_ERR: /* Task made evil nasty reference. */ + status->value.sig = TARGET_SIGNAL_BUS; + break; + + case EVENT_ZERO_DIV: /* Division by zero */ + status->value.sig = TARGET_SIGNAL_FPE; + break; + + case EVENT_SIGNAL: +#ifdef I80960 + status->value.sig = i960_fault_to_signal (rdbEvent.sigType); +#else + /* Back in the old days, before enum target_signal, this code used + to add NSIG to the signal number and claim that PRINT_RANDOM_SIGNAL + would take care of it. But PRINT_RANDOM_SIGNAL has never been + defined except on the i960, so I don't really know what we are + supposed to do on other architectures. */ + status->value.sig = TARGET_SIGNAL_UNKNOWN; +#endif + break; + } /* switch */ + return pid; +} + +static int +symbol_stub (arg) + char *arg; +{ + symbol_file_command (arg, 0); + return 1; +} + +static int +add_symbol_stub (arg) + char *arg; +{ + struct ldfile *pLoadFile = (struct ldfile *)arg; + + printf_unfiltered("\t%s: ", pLoadFile->name); + vx_add_symbols (pLoadFile->name, 0, pLoadFile->txt_addr, + pLoadFile->data_addr, pLoadFile->bss_addr); + printf_unfiltered ("ok\n"); + return 1; +} +/* Target command for VxWorks target systems. + + Used in vxgdb. Takes the name of a remote target machine + running vxWorks and connects to it to initialize remote network + debugging. */ + +static void +vx_open (args, from_tty) + char *args; + int from_tty; +{ + extern int close (); + char *bootFile; + extern char *source_path; + struct ldtabl loadTable; + struct ldfile *pLoadFile; + int i; + extern CLIENT *pClient; + int symbols_added = 0; + + if (!args) + error_no_arg ("target machine name"); + + target_preopen (from_tty); + + unpush_target (&vx_ops); + printf_unfiltered ("Attaching remote machine across net...\n"); + gdb_flush (gdb_stdout); + + /* Allow the user to kill the connect attempt by typing ^C. + Wait until the call to target_has_fp () completes before + disallowing an immediate quit, since even if net_connect () + is successful, the remote debug server might be hung. */ + + immediate_quit++; + + net_connect (args); + target_has_fp = net_check_for_fp (); + printf_filtered ("Connected to %s.\n", args); + + immediate_quit--; + + push_target (&vx_ops); + + /* Save a copy of the target host's name. */ + vx_host = savestring (args, strlen (args)); + + /* Find out the name of the file from which the target was booted + and load its symbol table. */ + + printf_filtered ("Looking in Unix path for all loaded modules:\n"); + bootFile = NULL; + if (!net_get_boot_file (&bootFile)) + { + if (*bootFile) + { + printf_filtered ("\t%s: ", bootFile); + /* This assumes that the kernel is never relocated. Hope that is an + accurate assumption. */ + if (catch_errors + (symbol_stub, + bootFile, + "Error while reading symbols from boot file:\n", + RETURN_MASK_ALL)) + puts_filtered ("ok\n"); + } + else if (from_tty) + printf_unfiltered ("VxWorks kernel symbols not loaded.\n"); + } + else + error ("Can't retrieve boot file name from target machine."); + + clnt_freeres (pClient, xdr_wrapstring, &bootFile); + + if (net_get_symbols (&loadTable) != 0) + error ("Can't read loaded modules from target machine"); + + i = 0-1; + while (++i < loadTable.tbl_size) + { + QUIT; /* FIXME, avoids clnt_freeres below: mem leak */ + pLoadFile = &loadTable.tbl_ent [i]; +#ifdef WRS_ORIG + { + register int desc; + struct cleanup *old_chain; + char *fullname = NULL; + + desc = openp (source_path, 0, pLoadFile->name, O_RDONLY, 0, &fullname); + if (desc < 0) + perror_with_name (pLoadFile->name); + old_chain = make_cleanup (close, desc); + add_file_at_addr (fullname, desc, pLoadFile->txt_addr, pLoadFile->data_addr, + pLoadFile->bss_addr); + do_cleanups (old_chain); + } +#else + /* FIXME: Is there something better to search than the PATH? (probably + not the source path, since source might be in different directories + than objects. */ + + if (catch_errors (add_symbol_stub, (char *)pLoadFile, (char *)0, + RETURN_MASK_ALL)) + symbols_added = 1; +#endif + } + printf_filtered ("Done.\n"); + + clnt_freeres (pClient, xdr_ldtabl, &loadTable); + + /* Getting new symbols may change our opinion about what is + frameless. */ + if (symbols_added) + reinit_frame_cache (); +} + +/* Takes a task started up outside of gdb and ``attaches'' to it. + This stops it cold in its tracks and allows us to start tracing it. */ + +static void +vx_attach (args, from_tty) + char *args; + int from_tty; +{ + unsigned long pid; + char *cptr = 0; + Rptrace ptrace_in; + Ptrace_return ptrace_out; + int status; + + if (!args) + error_no_arg ("process-id to attach"); + + pid = strtoul (args, &cptr, 0); + if ((cptr == args) || (*cptr != '\0')) + error ("Invalid process-id -- give a single number in decimal or 0xhex"); + + if (from_tty) + printf_unfiltered ("Attaching pid %s.\n", + local_hex_string((unsigned long) pid)); + + memset ((char *)&ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *)&ptrace_out, '\0', sizeof (ptrace_out)); + ptrace_in.pid = pid; + + status = net_ptrace_clnt_call (PTRACE_ATTACH, &ptrace_in, &ptrace_out); + if (status == -1) + error (rpcerr); + if (ptrace_out.status == -1) + { + errno = ptrace_out.errno_num; + perror_with_name ("Attaching remote process"); + } + + /* It worked... */ + + inferior_pid = pid; + push_target (&vx_run_ops); + + if (vx_running) + free (vx_running); + vx_running = 0; +} + +/* detach_command -- + takes a program previously attached to and detaches it. + The program resumes execution and will no longer stop + on signals, etc. We better not have left any breakpoints + in the program or it'll die when it hits one. For this + to work, it may be necessary for the process to have been + previously attached. It *might* work if the program was + started via the normal ptrace (PTRACE_TRACEME). */ + +static void +vx_detach (args, from_tty) + char *args; + int from_tty; +{ + Rptrace ptrace_in; + Ptrace_return ptrace_out; + int signal = 0; + int status; + + if (args) + error ("Argument given to VxWorks \"detach\"."); + + if (from_tty) + printf_unfiltered ("Detaching pid %s.\n", + local_hex_string((unsigned long) inferior_pid)); + + if (args) /* FIXME, should be possible to leave suspended */ + signal = atoi (args); + + memset ((char *)&ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *)&ptrace_out, '\0', sizeof (ptrace_out)); + ptrace_in.pid = inferior_pid; + + status = net_ptrace_clnt_call (PTRACE_DETACH, &ptrace_in, &ptrace_out); + if (status == -1) + error (rpcerr); + if (ptrace_out.status == -1) + { + errno = ptrace_out.errno_num; + perror_with_name ("Detaching VxWorks process"); + } + + inferior_pid = 0; + pop_target (); /* go back to non-executing VxWorks connection */ +} + +/* vx_kill -- takes a running task and wipes it out. */ + +static void +vx_kill () +{ + Rptrace ptrace_in; + Ptrace_return ptrace_out; + int status; + + printf_unfiltered ("Killing pid %s.\n", local_hex_string((unsigned long) inferior_pid)); + + memset ((char *)&ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *)&ptrace_out, '\0', sizeof (ptrace_out)); + ptrace_in.pid = inferior_pid; + + status = net_ptrace_clnt_call (PTRACE_KILL, &ptrace_in, &ptrace_out); + if (status == -1) + warning (rpcerr); + else if (ptrace_out.status == -1) + { + errno = ptrace_out.errno_num; + perror_with_name ("Killing VxWorks process"); + } + + /* If it gives good status, the process is *gone*, no events remain. + If the kill failed, assume the process is gone anyhow. */ + inferior_pid = 0; + pop_target (); /* go back to non-executing VxWorks connection */ +} + +/* Clean up from the VxWorks process target as it goes away. */ + +static void +vx_proc_close (quitting) + int quitting; +{ + inferior_pid = 0; /* No longer have a process. */ + if (vx_running) + free (vx_running); + vx_running = 0; +} + +/* Make an RPC call to the VxWorks target. + Returns RPC status. */ + +static enum clnt_stat +net_clnt_call (procNum, inProc, in, outProc, out) + enum ptracereq procNum; + xdrproc_t inProc; + char *in; + xdrproc_t outProc; + char *out; +{ + enum clnt_stat status; + + status = clnt_call (pClient, procNum, inProc, in, outProc, out, rpcTimeout); + + if (status != RPC_SUCCESS) + clnt_perrno (status); + + return status; +} + +/* Clean up before losing control. */ + +static void +vx_close (quitting) + int quitting; +{ + if (pClient) + clnt_destroy (pClient); /* The net connection */ + pClient = 0; + + if (vx_host) + free (vx_host); /* The hostname */ + vx_host = 0; +} + +/* A vxprocess target should be started via "run" not "target". */ +/*ARGSUSED*/ +static void +vx_proc_open (name, from_tty) + char *name; + int from_tty; +{ + error ("Use the \"run\" command to start a VxWorks process."); +} + +static void +init_vx_ops () +{ + vx_ops.to_shortname = "vxworks"; + vx_ops.to_longname = "VxWorks target memory via RPC over TCP/IP"; + vx_ops.to_doc = "Use VxWorks target memory. \n\ +Specify the name of the machine to connect to."; + vx_ops.to_open = vx_open; + vx_ops.to_close = vx_close; + vx_ops.to_attach = vx_attach; + vx_ops.to_xfer_memory = vx_xfer_memory; + vx_ops.to_files_info = vx_files_info; + vx_ops.to_load = vx_load_command; + vx_ops.to_lookup_symbol = vx_lookup_symbol; + vx_ops.to_create_inferior = vx_create_inferior; + vx_ops.to_stratum = core_stratum; + vx_ops.to_has_all_memory = 1; + vx_ops.to_has_memory = 1; + vx_ops.to_magic = OPS_MAGIC; /* Always the last thing */ +}; + +static void +init_vx_run_ops () +{ + vx_run_ops.to_shortname = "vxprocess"; + vx_run_ops.to_longname = "VxWorks process"; + vx_run_ops.to_doc = "VxWorks process; started by the \"run\" command."; + vx_run_ops.to_open = vx_proc_open; + vx_run_ops.to_close = vx_proc_close; + vx_run_ops.to_detach = vx_detach; + vx_run_ops.to_resume = vx_resume; + vx_run_ops.to_wait = vx_wait; + vx_run_ops.to_fetch_registers = vx_read_register; + vx_run_ops.to_store_registers = vx_write_register; + vx_run_ops.to_prepare_to_store = vx_prepare_to_store; + vx_run_ops.to_xfer_memory = vx_xfer_memory; + vx_run_ops.to_files_info = vx_run_files_info; + vx_run_ops.to_insert_breakpoint = vx_insert_breakpoint; + vx_run_ops.to_remove_breakpoint = vx_remove_breakpoint; + vx_run_ops.to_kill = vx_kill; + vx_run_ops.to_load = vx_load_command; + vx_run_ops.to_lookup_symbol = vx_lookup_symbol; + vx_run_ops.to_mourn_inferior = vx_mourn_inferior ; + vx_run_ops.to_stratum = process_stratum; + vx_run_ops.to_has_memory = 1; + vx_run_ops.to_has_stack = 1; + vx_run_ops.to_has_registers = 1; + vx_run_ops.to_has_execution = 1; + vx_run_ops.to_magic = OPS_MAGIC; +} + +void +_initialize_vx () +{ + init_vx_ops (); + add_target (&vx_ops); + init_vx_run_ops (); + add_target (&vx_run_ops); + + add_show_from_set + (add_set_cmd ("vxworks-timeout", class_support, var_uinteger, + (char *) &rpcTimeout.tv_sec, + "Set seconds to wait for rpc calls to return.\n\ +Set the number of seconds to wait for rpc calls to return.", &setlist), + &showlist); +} |