* a29k-tdep.c (get_longjmp_target): add this function, from WRS.
[platform/upstream/binutils.git] / gdb / remote-vx.c
1 /* Memory-access and commands for remote VxWorks processes, for GDB.
2    Copyright 1990, 1991, 1992 Free Software Foundation, Inc.
3    Contributed by Wind River Systems and Cygnus Support.
4
5 This file is part of GDB.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
20
21 #include "defs.h"
22 #include "frame.h"
23 #include "inferior.h"
24 #include "wait.h"
25 #include "target.h"
26 #include "gdbcore.h"
27 #include "command.h"
28 #include "symtab.h"
29 #include "complaints.h"
30 #include "gdbcmd.h"
31 #include "bfd.h" /* Required by objfiles.h.  */
32 #include "symfile.h" /* Required by objfiles.h.  */
33 #include "objfiles.h"
34 #include "gdb-stabs.h"
35
36 #include <string.h>
37 #include <errno.h>
38 #include <signal.h>
39 #include <fcntl.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #define malloc bogon_malloc     /* Sun claims "char *malloc()" not void * */
43 #define free bogon_free         /* Sun claims "int free()" not void */
44 #define realloc bogon_realloc   /* Sun claims "char *realloc()", not void * */
45 #include <rpc/rpc.h>
46 #undef malloc
47 #undef free
48 #undef realloc
49 #include <sys/time.h>           /* UTek's <rpc/rpc.h> doesn't #incl this */
50 #include <netdb.h>
51 #include "vx-share/ptrace.h"
52 #include "vx-share/xdr_ptrace.h"
53 #include "vx-share/xdr_ld.h"
54 #include "vx-share/xdr_rdb.h"
55 #include "vx-share/dbgRpcLib.h"
56
57 #include <symtab.h>
58
59 /* Maximum number of bytes to transfer in a single
60    PTRACE_{READ,WRITE}DATA request.  */
61 #define VX_MEMXFER_MAX 4096
62
63 extern void vx_read_register ();
64 extern void vx_write_register ();
65 extern void symbol_file_command ();
66 extern int stop_soon_quietly;           /* for wait_for_inferior */
67
68 static int net_step ();
69 static int net_ptrace_clnt_call ();     /* Forward decl */
70 static enum clnt_stat net_clnt_call (); /* Forward decl */
71 extern struct target_ops vx_ops, vx_run_ops;    /* Forward declaration */
72
73 /* Saved name of target host and called function for "info files".
74    Both malloc'd.  */
75
76 static char *vx_host;
77 static char *vx_running;                /* Called function */
78
79 /* Nonzero means target that is being debugged remotely has a floating
80    point processor.  */
81
82 int target_has_fp;
83
84 /* Default error message when the network is forking up.  */
85
86 static const char rpcerr[] = "network target debugging:  rpc error";
87
88 CLIENT *pClient;         /* client used in net debugging */
89 static int ptraceSock = RPC_ANYSOCK;
90
91 enum clnt_stat net_clnt_call();
92 static void parse_args ();
93
94 static struct timeval rpcTimeout = { 10, 0 };
95
96 static char *skip_white_space ();
97 static char *find_white_space ();
98  
99 /* Tell the VxWorks target system to download a file.
100    The load addresses of the text, data, and bss segments are
101    stored in *pTextAddr, *pDataAddr, and *pBssAddr (respectively).
102    Returns 0 for success, -1 for failure.  */
103
104 static int
105 net_load (filename, pTextAddr, pDataAddr, pBssAddr)
106      char *filename;
107      CORE_ADDR *pTextAddr;
108      CORE_ADDR *pDataAddr;
109      CORE_ADDR *pBssAddr;
110 {
111   enum clnt_stat status;
112   struct ldfile ldstruct;
113   struct timeval load_timeout;
114
115   memset ((char *) &ldstruct, '\0', sizeof (ldstruct));
116
117   /* We invoke clnt_call () here directly, instead of through
118      net_clnt_call (), because we need to set a large timeout value.
119      The load on the target side can take quite a while, easily
120      more than 10 seconds.  The user can kill this call by typing
121      CTRL-C if there really is a problem with the load.  
122
123      Do not change the tv_sec value without checking -- select() imposes
124      a limit of 10**8 on it for no good reason that I can see...  */
125
126   load_timeout.tv_sec = 99999999;   /* A large number, effectively inf. */
127   load_timeout.tv_usec = 0;
128  
129   status = clnt_call (pClient, VX_LOAD, xdr_wrapstring, &filename, xdr_ldfile,
130                       &ldstruct, load_timeout);
131
132   if (status == RPC_SUCCESS)
133     {
134       if (*ldstruct.name == 0)  /* load failed on VxWorks side */
135         return -1;
136       *pTextAddr = ldstruct.txt_addr;
137       *pDataAddr = ldstruct.data_addr;
138       *pBssAddr = ldstruct.bss_addr;
139       return 0;
140     }
141   else
142     return -1;
143 }
144
145 /* returns 0 if successful, errno if RPC failed or VxWorks complains. */
146
147 static int
148 net_break (addr, procnum)
149      int addr;
150      u_long procnum;
151 {
152   enum clnt_stat status;
153   int break_status;
154   Rptrace ptrace_in;  /* XXX This is stupid.  It doesn't need to be a ptrace
155                          structure.  How about something smaller? */
156
157   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
158   break_status = 0;
159
160   ptrace_in.addr = addr;
161   ptrace_in.pid = inferior_pid;
162
163   status = net_clnt_call (procnum, xdr_rptrace, &ptrace_in, xdr_int,
164                           &break_status);
165
166   if (status != RPC_SUCCESS)
167       return errno;
168
169   if (break_status == -1)
170     return ENOMEM;
171   return break_status;  /* probably (FIXME) zero */
172 }
173
174 /* returns 0 if successful, errno otherwise */
175
176 static int
177 vx_insert_breakpoint (addr)
178      int addr;
179 {
180   return net_break (addr, VX_BREAK_ADD);
181 }
182
183 /* returns 0 if successful, errno otherwise */
184
185 static int
186 vx_remove_breakpoint (addr)
187      int addr;
188 {
189   return net_break (addr, VX_BREAK_DELETE);
190 }
191
192 /* Start an inferior process and sets inferior_pid to its pid.
193    EXEC_FILE is the file to run.
194    ALLARGS is a string containing the arguments to the program.
195    ENV is the environment vector to pass.
196    Returns process id.  Errors reported with error().
197    On VxWorks, we ignore exec_file.  */
198  
199 static void
200 vx_create_inferior (exec_file, args, env)
201      char *exec_file;
202      char *args;
203      char **env;
204 {
205   enum clnt_stat status;
206   arg_array passArgs;
207   TASK_START taskStart;
208
209   memset ((char *) &passArgs, '\0', sizeof (passArgs));
210   memset ((char *) &taskStart, '\0', sizeof (taskStart));
211
212   /* parse arguments, put them in passArgs */
213
214   parse_args (args, &passArgs);
215
216   if (passArgs.arg_array_len == 0)
217     error ("You must specify a function name to run, and arguments if any");
218
219   status = net_clnt_call (PROCESS_START, xdr_arg_array, &passArgs,
220                           xdr_TASK_START, &taskStart);
221
222   if ((status != RPC_SUCCESS) || (taskStart.status == -1))
223     error ("Can't create process on remote target machine");
224
225   /* Save the name of the running function */
226   vx_running = savestring (passArgs.arg_array_val[0],
227                            strlen (passArgs.arg_array_val[0]));
228
229   push_target (&vx_run_ops);
230   inferior_pid = taskStart.pid;
231
232   /* We will get a trace trap after one instruction.
233      Insert breakpoints and continue.  */
234
235   init_wait_for_inferior ();
236
237   /* Set up the "saved terminal modes" of the inferior
238      based on what modes we are starting it with.  */
239   target_terminal_init ();
240
241   /* Install inferior's terminal modes.  */
242   target_terminal_inferior ();
243
244   stop_soon_quietly = 1;
245   wait_for_inferior ();         /* Get the task spawn event */
246   stop_soon_quietly = 0;
247
248   /* insert_step_breakpoint ();  FIXME, do we need this?  */
249   proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
250 }
251
252 /* Fill ARGSTRUCT in argc/argv form with the arguments from the
253    argument string ARGSTRING.  */
254
255 static void
256 parse_args (arg_string, arg_struct)
257      register char *arg_string;
258      arg_array *arg_struct;
259 {
260   register int arg_count = 0;   /* number of arguments */
261   register int arg_index = 0;
262   register char *p0;
263  
264   memset ((char *) arg_struct, '\0', sizeof (arg_array));
265  
266   /* first count how many arguments there are */
267
268   p0 = arg_string;
269   while (*p0 != '\0')
270     {
271       if (*(p0 = skip_white_space (p0)) == '\0')
272         break;
273       p0 = find_white_space (p0);
274       arg_count++;
275     }
276
277   arg_struct->arg_array_len = arg_count;
278   arg_struct->arg_array_val = (char **) xmalloc ((arg_count + 1)
279                                                  * sizeof (char *));
280
281   /* now copy argument strings into arg_struct.  */
282
283   while (*(arg_string = skip_white_space (arg_string)))
284     {
285       p0 = find_white_space (arg_string);
286       arg_struct->arg_array_val[arg_index++] = savestring (arg_string,
287                                                            p0 - arg_string);
288       arg_string = p0;
289     }
290
291   arg_struct->arg_array_val[arg_count] = NULL;
292 }
293
294 /* Advance a string pointer across whitespace and return a pointer
295    to the first non-white character.  */
296
297 static char *
298 skip_white_space (p)
299      register char *p;
300 {
301   while (*p == ' ' || *p == '\t')
302     p++;
303   return p;
304 }
305     
306 /* Search for the first unquoted whitespace character in a string.
307    Returns a pointer to the character, or to the null terminator
308    if no whitespace is found.  */
309
310 static char *
311 find_white_space (p)
312      register char *p;
313 {
314   register int c;
315
316   while ((c = *p) != ' ' && c != '\t' && c)
317     {
318       if (c == '\'' || c == '"')
319         {
320           while (*++p != c && *p)
321             {
322               if (*p == '\\')
323                 p++;
324             }
325           if (!*p)
326             break;
327         }
328       p++;
329     }
330   return p;
331 }
332     
333 /* Poll the VxWorks target system for an event related
334    to the debugged task.
335    Returns -1 if remote wait failed, task status otherwise.  */
336
337 static int
338 net_wait (pEvent)
339     RDB_EVENT *pEvent;
340 {
341     int pid;
342     enum clnt_stat status;
343
344     memset ((char *) pEvent, '\0', sizeof (RDB_EVENT));
345
346     pid = inferior_pid;
347     status = net_clnt_call (PROCESS_WAIT, xdr_int, &pid, xdr_RDB_EVENT,
348                             pEvent);
349
350     /* return (status == RPC_SUCCESS)? pEvent->status: -1; */
351     if (status == RPC_SUCCESS)
352       return ((pEvent->status) ? 1 : 0);
353     else if (status == RPC_TIMEDOUT)
354       return (1);
355     else
356       return (-1);
357 }
358     
359 /* Suspend the remote task.
360    Returns -1 if suspend fails on target system, 0 otherwise.  */
361
362 static int
363 net_quit ()
364 {
365   int pid;
366   int quit_status;
367   enum clnt_stat status;
368
369   quit_status = 0;
370
371   /* don't let rdbTask suspend itself by passing a pid of 0 */
372
373   if ((pid = inferior_pid) == 0)
374     return -1;
375
376   status = net_clnt_call (VX_TASK_SUSPEND, xdr_int, &pid, xdr_int,
377                           &quit_status);
378
379   return (status == RPC_SUCCESS)? quit_status: -1;
380 }
381
382 /* Read a register or registers from the remote system.  */
383
384 void
385 net_read_registers (reg_buf, len, procnum)
386      char *reg_buf;
387      int len;
388      u_long procnum;
389 {
390   int status;
391   Rptrace ptrace_in;
392   Ptrace_return ptrace_out;
393   C_bytes out_data;
394   char message[100];
395
396   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
397   memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out));
398
399   /* Initialize RPC input argument structure.  */
400
401   ptrace_in.pid = inferior_pid;
402   ptrace_in.info.ttype = NOINFO;
403
404   /* Initialize RPC return value structure.  */
405
406   out_data.bytes = reg_buf;
407   out_data.len = len;
408   ptrace_out.info.more_data = (caddr_t) &out_data;
409
410   /* Call RPC; take an error exit if appropriate.  */
411
412   status = net_ptrace_clnt_call (procnum, &ptrace_in, &ptrace_out);
413   if (status)
414     error (rpcerr);
415   if (ptrace_out.status == -1)
416     {
417       errno = ptrace_out.errno;
418       sprintf (message, "reading %s registers", (procnum == PTRACE_GETREGS)
419                                                  ? "general-purpose"
420                                                  : "floating-point");
421       perror_with_name (message);
422     }
423 }
424
425 /* Write register values to a VxWorks target.  REG_BUF points to a buffer
426    containing the raw register values, LEN is the length of REG_BUF in
427    bytes, and PROCNUM is the RPC procedure number (PTRACE_SETREGS or
428    PTRACE_SETFPREGS).  An error exit is taken if the RPC call fails or
429    if an error status is returned by the remote debug server.  This is
430    a utility routine used by vx_write_register ().  */
431
432 void
433 net_write_registers (reg_buf, len, procnum)
434      char *reg_buf;
435      int len;
436      u_long procnum;
437 {
438   int status;
439   Rptrace ptrace_in;
440   Ptrace_return ptrace_out;
441   C_bytes in_data;
442   char message[100];
443
444   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
445   memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out));
446
447   /* Initialize RPC input argument structure.  */
448
449   in_data.bytes = reg_buf;
450   in_data.len = len;
451
452   ptrace_in.pid = inferior_pid;
453   ptrace_in.info.ttype = DATA;
454   ptrace_in.info.more_data = (caddr_t) &in_data;
455
456   /* Call RPC; take an error exit if appropriate.  */
457
458   status = net_ptrace_clnt_call (procnum, &ptrace_in, &ptrace_out);
459   if (status)
460     error (rpcerr);
461   if (ptrace_out.status == -1)
462     {
463       errno = ptrace_out.errno;
464       sprintf (message, "writing %s registers", (procnum == PTRACE_SETREGS)
465                                                  ? "general-purpose"
466                                                  : "floating-point");
467       perror_with_name (message);
468     }
469 }
470
471 /* Prepare to store registers.  Since we will store all of them,
472    read out their current values now.  */
473
474 static void
475 vx_prepare_to_store ()
476 {
477   /* Fetch all registers, if any of them are not yet fetched.  */
478   read_register_bytes (0, NULL, REGISTER_BYTES);
479 }
480
481 /* Copy LEN bytes to or from remote inferior's memory starting at MEMADDR
482    to debugger memory starting at MYADDR.  WRITE is true if writing to the
483    inferior.
484    Result is the number of bytes written or read (zero if error).  The
485    protocol allows us to return a negative count, indicating that we can't
486    handle the current address but can handle one N bytes further, but
487    vxworks doesn't give us that information.  */
488
489 static int
490 vx_xfer_memory (memaddr, myaddr, len, write, target)
491      CORE_ADDR memaddr;
492      char *myaddr;
493      int len;
494      int write;
495      struct target_ops *target;                 /* ignored */
496 {
497   int status;
498   Rptrace ptrace_in;
499   Ptrace_return ptrace_out;
500   C_bytes data;
501   enum ptracereq request;
502   int nleft, nxfer;
503
504   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
505   memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out));
506
507   ptrace_in.pid = inferior_pid;         /* XXX pid unnecessary for READDATA */
508   ptrace_in.addr = (int) memaddr;       /* Where from */
509   ptrace_in.data = len;                 /* How many bytes */
510
511   if (write)
512     {
513       ptrace_in.info.ttype     = DATA;
514       ptrace_in.info.more_data = (caddr_t) &data;
515
516       data.bytes = (caddr_t) myaddr;    /* Where from */
517       data.len   = len;                 /* How many bytes (again, for XDR) */
518       request = PTRACE_WRITEDATA;
519     }
520   else
521     {
522       ptrace_out.info.more_data = (caddr_t) &data;
523       request = PTRACE_READDATA;
524     }
525   /* Loop until the entire request has been satisfied, transferring
526      at most VX_MEMXFER_MAX bytes per iteration.  Break from the loop
527      if an error status is returned by the remote debug server.  */
528
529   nleft = len;
530   status = 0;
531
532   while (nleft > 0 && status == 0)
533     {
534       nxfer = min (nleft, VX_MEMXFER_MAX);
535
536       ptrace_in.addr = (int) memaddr;
537       ptrace_in.data = nxfer;
538       data.bytes = (caddr_t) myaddr;
539       data.len = nxfer;
540
541       /* Request a block from the remote debug server; if RPC fails,
542          report an error and return to debugger command level.  */
543
544       if (net_ptrace_clnt_call (request, &ptrace_in, &ptrace_out))
545         error (rpcerr);
546
547       status = ptrace_out.status;
548       if (status == 0)
549         {
550           memaddr += nxfer;
551           myaddr += nxfer;
552           nleft -= nxfer;
553         }
554       else
555         {
556           /* A target-side error has ocurred.  Set errno to the error
557              code chosen by the target so that a later perror () will
558              say something meaningful.  */
559
560           errno = ptrace_out.errno;
561         }
562     }
563
564   /* Return the number of bytes transferred.  */
565
566   return (len - nleft);
567 }
568
569 static void
570 vx_files_info ()
571 {
572   printf_unfiltered ("\tAttached to host `%s'", vx_host);
573   printf_unfiltered (", which has %sfloating point", target_has_fp? "": "no ");
574   printf_unfiltered (".\n");
575 }
576
577 static void
578 vx_run_files_info ()
579 {
580   printf_unfiltered ("\tRunning %s VxWorks process %s", 
581                      vx_running ? "child" : "attached",
582                      local_hex_string (inferior_pid));
583   if (vx_running)
584     printf_unfiltered (", function `%s'", vx_running);
585   printf_unfiltered(".\n");
586 }
587
588 static void
589 vx_resume (pid, step, siggnal)
590      int pid;
591      int step;
592      enum target_signal siggnal;
593 {
594   int status;
595   Rptrace ptrace_in;
596   Ptrace_return ptrace_out;
597   CORE_ADDR cont_addr;
598
599   if (pid == -1)
600     pid = inferior_pid;
601
602   if (siggnal != 0 && siggnal != stop_signal)
603     error ("Cannot send signals to VxWorks processes");
604
605   /* Set CONT_ADDR to the address at which we are continuing,
606      or to 1 if we are continuing from where the program stopped.
607      This conforms to traditional ptrace () usage, but at the same
608      time has special meaning for the VxWorks remote debug server.
609      If the address is not 1, the server knows that the target
610      program is jumping to a new address, which requires special
611      handling if there is a breakpoint at the new address.  */
612
613   cont_addr = read_register (PC_REGNUM);
614   if (cont_addr == stop_pc)
615     cont_addr = 1;
616
617   memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in));
618   memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out));
619
620   ptrace_in.pid = pid;
621   ptrace_in.addr = cont_addr; /* Target side insists on this, or it panics.  */
622
623   if (step)
624     status = net_step();
625   else
626     status = net_ptrace_clnt_call (PTRACE_CONT, &ptrace_in, &ptrace_out);
627
628   if (status)
629     error (rpcerr);
630   if (ptrace_out.status == -1)
631     {
632       errno = ptrace_out.errno;
633       perror_with_name ("Resuming remote process");
634     }
635 }
636
637 static void
638 vx_mourn_inferior ()
639 {
640   pop_target ();                /* Pop back to no-child state */
641   generic_mourn_inferior ();
642 }
643
644 \f
645 static void vx_add_symbols PARAMS ((char *, int, CORE_ADDR, CORE_ADDR,
646                                     CORE_ADDR));
647
648 struct find_sect_args {
649   CORE_ADDR text_start;
650   CORE_ADDR data_start;
651   CORE_ADDR bss_start;
652 };
653
654 static void find_sect PARAMS ((bfd *, asection *, void *));
655
656 static void
657 find_sect (abfd, sect, obj)
658      bfd *abfd;
659      asection *sect;
660      PTR obj;
661 {
662   struct find_sect_args *args = (struct find_sect_args *)obj;
663
664   if (bfd_get_section_flags (abfd, sect) & (SEC_CODE & SEC_READONLY))
665     args->text_start = bfd_get_section_vma (abfd, sect);
666   else if (bfd_get_section_flags (abfd, sect) & SEC_ALLOC)
667     {
668       if (bfd_get_section_flags (abfd, sect) & SEC_LOAD)
669         {
670           /* Exclude .ctor and .dtor sections which have SEC_CODE set but not
671              SEC_DATA.  */
672           if (bfd_get_section_flags (abfd, sect) & SEC_DATA)
673             args->data_start = bfd_get_section_vma (abfd, sect);
674         }
675       else
676         args->bss_start = bfd_get_section_vma (abfd, sect);
677     }
678 }
679
680 static void
681 vx_add_symbols (name, from_tty, text_addr, data_addr, bss_addr)
682      char *name;
683      int from_tty;
684      CORE_ADDR text_addr;
685      CORE_ADDR data_addr;
686      CORE_ADDR bss_addr;
687 {
688   struct section_offsets *offs;
689   struct objfile *objfile;
690   struct find_sect_args ss;
691
692   /* It might be nice to suppress the breakpoint_re_set which happens here
693      because we are going to do one again after the objfile_relocate.  */
694   objfile = symbol_file_add (name, from_tty, 0, 0, 0, 0);
695
696   /* This is a (slightly cheesy) way of superceding the old symbols.  A less
697      cheesy way would be to find the objfile with the same name and
698      free_objfile it.  */
699   objfile_to_front (objfile);
700
701   offs = (struct section_offsets *)
702     alloca (sizeof (struct section_offsets)
703             + objfile->num_sections * sizeof (offs->offsets));
704   memcpy (offs, objfile->section_offsets,
705           sizeof (struct section_offsets)
706           + objfile->num_sections * sizeof (offs->offsets));
707
708   ss.text_start = 0;
709   ss.data_start = 0;
710   ss.bss_start = 0;
711   bfd_map_over_sections (objfile->obfd, find_sect, &ss);
712
713   /* Both COFF and b.out frontends use these SECT_OFF_* values.  */
714   ANOFFSET (offs, SECT_OFF_TEXT) = text_addr - ss.text_start;
715   ANOFFSET (offs, SECT_OFF_DATA) = data_addr - ss.data_start;
716   ANOFFSET (offs, SECT_OFF_BSS) = bss_addr - ss.bss_start;
717   objfile_relocate (objfile, offs);
718
719   /* Need to do this *after* things are relocated.  */
720   breakpoint_re_set ();
721 }
722
723 /* This function allows the addition of incrementally linked object files.  */
724
725 static void
726 vx_load_command (arg_string, from_tty)
727      char *arg_string;
728      int from_tty;
729 {
730   CORE_ADDR text_addr;
731   CORE_ADDR data_addr;
732   CORE_ADDR bss_addr;
733
734   if (arg_string == 0)
735     error ("The load command takes a file name");
736
737   arg_string = tilde_expand (arg_string);
738   make_cleanup (free, arg_string);
739
740   dont_repeat ();
741
742   /* Refuse to load the module if a debugged task is running.  Doing so
743      can have a number of unpleasant consequences to the running task.  */
744
745   if (inferior_pid != 0 && target_has_execution)
746     {
747       if (query ("You may not load a module while the target task is running.\n\
748 Kill the target task? "))
749         target_kill ();
750       else
751         error ("Load cancelled.");
752     }
753
754   QUIT;
755   immediate_quit++;
756   if (net_load (arg_string, &text_addr, &data_addr, &bss_addr) == -1)
757     error ("Load failed on target machine");
758   immediate_quit--;
759
760   vx_add_symbols (arg_string, from_tty, text_addr, data_addr, bss_addr);
761
762   /* Getting new symbols may change our opinion about what is
763      frameless.  */
764   reinit_frame_cache ();
765 }
766
767 /* Single step the target program at the source or machine level.
768    Takes an error exit if rpc fails.
769    Returns -1 if remote single-step operation fails, else 0.  */
770
771 static int
772 net_step ()
773 {
774   enum clnt_stat status;
775   int step_status;
776   SOURCE_STEP source_step;
777
778   source_step.taskId = inferior_pid;
779
780   if (step_range_end)
781     {
782       source_step.startAddr = step_range_start;
783       source_step.endAddr = step_range_end;
784     }
785   else
786     {
787       source_step.startAddr = 0;
788       source_step.endAddr = 0;
789     }
790
791   status = net_clnt_call (VX_SOURCE_STEP, xdr_SOURCE_STEP, &source_step,
792                           xdr_int, &step_status);
793
794   if (status == RPC_SUCCESS)
795     return step_status;
796   else 
797     error (rpcerr);
798 }
799
800 /* Emulate ptrace using RPC calls to the VxWorks target system.
801    Returns nonzero (-1) if RPC status to VxWorks is bad, 0 otherwise.  */
802
803 static int
804 net_ptrace_clnt_call (request, pPtraceIn, pPtraceOut)
805     enum ptracereq request;
806     Rptrace *pPtraceIn;
807     Ptrace_return *pPtraceOut;
808 {
809   enum clnt_stat status;
810
811   status = net_clnt_call (request, xdr_rptrace, pPtraceIn, xdr_ptrace_return,
812                           pPtraceOut);
813
814   if (status != RPC_SUCCESS)
815       return -1;
816
817   return 0;
818 }
819
820 /* Query the target for the name of the file from which VxWorks was
821    booted.  pBootFile is the address of a pointer to the buffer to
822    receive the file name; if the pointer pointed to by pBootFile is 
823    NULL, memory for the buffer will be allocated by XDR.
824    Returns -1 if rpc failed, 0 otherwise.  */
825
826 static int
827 net_get_boot_file (pBootFile)
828      char **pBootFile;
829 {
830   enum clnt_stat status;
831
832   status = net_clnt_call (VX_BOOT_FILE_INQ, xdr_void, (char *) 0,
833                           xdr_wrapstring, pBootFile);
834   return (status == RPC_SUCCESS) ? 0 : -1;
835 }
836
837 /* Fetch a list of loaded object modules from the VxWorks target.
838    Returns -1 if rpc failed, 0 otherwise
839    There's no way to check if the returned loadTable is correct.
840    VxWorks doesn't check it.  */
841
842 static int
843 net_get_symbols (pLoadTable)
844      ldtabl *pLoadTable;                /* return pointer to ldtabl here */
845 {
846   enum clnt_stat status;
847
848   memset ((char *) pLoadTable, '\0', sizeof (struct ldtabl));
849
850   status = net_clnt_call (VX_STATE_INQ, xdr_void, 0, xdr_ldtabl, pLoadTable);
851   return (status == RPC_SUCCESS) ? 0 : -1;
852 }
853
854 /* Look up a symbol in the VxWorks target's symbol table.
855    Returns status of symbol read on target side (0=success, -1=fail)
856    Returns -1 and complain()s if rpc fails.  */
857
858 struct complaint cant_contact_target =
859   {"Lost contact with VxWorks target", 0, 0};
860
861 static int
862 vx_lookup_symbol (name, pAddr)
863      char *name;                /* symbol name */
864      CORE_ADDR *pAddr;
865 {
866   enum clnt_stat status;
867   SYMBOL_ADDR symbolAddr;
868
869   *pAddr = 0;
870   memset ((char *) &symbolAddr, '\0', sizeof (symbolAddr));
871
872   status = net_clnt_call (VX_SYMBOL_INQ, xdr_wrapstring, &name,
873                           xdr_SYMBOL_ADDR, &symbolAddr);
874   if (status != RPC_SUCCESS)
875     {
876       complain (&cant_contact_target);
877       return -1;
878     }
879
880   *pAddr = symbolAddr.addr;
881   return symbolAddr.status;
882 }
883
884 /* Check to see if the VxWorks target has a floating point coprocessor.
885    Returns 1 if target has floating point processor, 0 otherwise.
886    Calls error() if rpc fails.  */
887
888 static int
889 net_check_for_fp ()
890 {
891   enum clnt_stat status;
892   bool_t fp = 0;        /* true if fp processor is present on target board */
893
894   status = net_clnt_call (VX_FP_INQUIRE, xdr_void, 0, xdr_bool, &fp);
895   if (status != RPC_SUCCESS)
896     error (rpcerr);
897
898    return (int) fp;
899 }
900
901 /* Establish an RPC connection with the VxWorks target system.
902    Calls error () if unable to establish connection.  */
903
904 static void
905 net_connect (host)
906      char *host;
907 {
908   struct sockaddr_in destAddr;
909   struct hostent *destHost;
910   unsigned long addr;
911
912   /* Get the internet address for the given host.  Allow a numeric
913      IP address or a hostname.  */
914
915   addr = inet_addr (host);
916   if (addr == -1)
917     {
918       destHost = (struct hostent *) gethostbyname (host);
919       if (destHost == NULL)
920         /* FIXME: Probably should include hostname here in quotes.
921            For example if the user types "target vxworks vx960 " it should
922            say "Invalid host `vx960 '." not just "Invalid hostname".  */
923         error ("Invalid hostname.  Couldn't find remote host address.");
924       addr = * (unsigned long *) destHost->h_addr;
925     }
926
927   memset (&destAddr, '\0', sizeof (destAddr));
928
929   destAddr.sin_addr.s_addr = addr;
930   destAddr.sin_family      = AF_INET;
931   destAddr.sin_port        = 0; /* set to actual port that remote
932                                    ptrace is listening on.  */
933
934   /* Create a tcp client transport on which to issue
935      calls to the remote ptrace server.  */
936
937   ptraceSock = RPC_ANYSOCK;
938   pClient = clnttcp_create (&destAddr, RDBPROG, RDBVERS, &ptraceSock, 0, 0);
939   /* FIXME, here is where we deal with different version numbers of the
940      proto */
941
942   if (pClient == NULL)
943     {
944       clnt_pcreateerror ("\tnet_connect");
945       error ("Couldn't connect to remote target.");
946     }
947 }
948 \f
949 /* Sleep for the specified number of milliseconds 
950  * (assumed to be less than 1000).
951  * If select () is interrupted, returns immediately;
952  * takes an error exit if select () fails for some other reason.
953  */
954
955 static void
956 sleep_ms (ms)
957      long ms;
958 {
959   struct timeval select_timeout;
960   int status;
961
962   select_timeout.tv_sec = 0;
963   select_timeout.tv_usec = ms * 1000;
964
965   status = select (0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0,
966                    &select_timeout);
967
968   if (status < 0 && errno != EINTR)
969     perror_with_name ("select");
970 }
971
972 static int
973 vx_wait (pid_to_wait_for, status)
974      int pid_to_wait_for;
975      struct target_waitstatus *status;
976 {
977   register int pid;
978   RDB_EVENT rdbEvent;
979   int quit_failed;
980
981   do
982     {
983       /* If CTRL-C is hit during this loop,
984          suspend the inferior process.  */
985
986       quit_failed = 0;
987       if (quit_flag)
988         {
989           quit_failed = (net_quit () == -1);
990           quit_flag = 0;
991         }
992
993       /* If a net_quit () or net_wait () call has failed,
994          allow the user to break the connection with the target.
995          We can't simply error () out of this loop, since the 
996          data structures representing the state of the inferior
997          are in an inconsistent state.  */
998
999       if (quit_failed || net_wait (&rdbEvent) == -1)
1000         {
1001           terminal_ours ();
1002           if (query ("Can't %s.  Disconnect from target system? ",
1003                      (quit_failed) ? "suspend remote task"
1004                                    : "get status of remote task"))
1005             {
1006               target_mourn_inferior();
1007               error ("Use the \"target\" command to reconnect.");
1008             }
1009           else
1010             {
1011               terminal_inferior ();
1012               continue;
1013             }
1014         }
1015       
1016       pid = rdbEvent.taskId;
1017       if (pid == 0)
1018         {
1019           sleep_ms (200);       /* FIXME Don't kill the network too badly */
1020         }
1021       else if (pid != inferior_pid)
1022         fatal ("Bad pid for debugged task: %s\n",
1023                local_hex_string((unsigned long) pid));
1024     } while (pid == 0);
1025
1026   /* The mostly likely kind.  */
1027   status->kind = TARGET_WAITKIND_STOPPED;
1028
1029   switch (rdbEvent.eventType)
1030     {
1031     case EVENT_EXIT:
1032       status->kind = TARGET_WAITKIND_EXITED;
1033       /* FIXME is it possible to distinguish between a
1034          normal vs abnormal exit in VxWorks? */
1035       status->value.integer = 0;
1036       break;
1037
1038     case EVENT_START:
1039       /* Task was just started. */
1040       status->value.sig = TARGET_SIGNAL_TRAP;
1041       break;
1042
1043     case EVENT_STOP:
1044       status->value.sig = TARGET_SIGNAL_TRAP;
1045       /* XXX was it stopped by a signal?  act accordingly */
1046       break;
1047
1048     case EVENT_BREAK:           /* Breakpoint was hit. */
1049       status->value.sig = TARGET_SIGNAL_TRAP;
1050       break;
1051
1052     case EVENT_SUSPEND:         /* Task was suspended, probably by ^C. */
1053       status->value.sig = TARGET_SIGNAL_INT;
1054       break;
1055
1056     case EVENT_BUS_ERR:         /* Task made evil nasty reference. */
1057       status->value.sig = TARGET_SIGNAL_BUS;
1058       break;
1059
1060     case EVENT_ZERO_DIV:        /* Division by zero */
1061       status->value.sig = TARGET_SIGNAL_FPE;
1062       break;
1063
1064     case EVENT_SIGNAL:
1065 #ifdef I80960
1066       status->value.sig = i960_fault_to_signal (rdbEvent.sigType);
1067 #else
1068       /* Back in the old days, before enum target_signal, this code used
1069          to add NSIG to the signal number and claim that PRINT_RANDOM_SIGNAL
1070          would take care of it.  But PRINT_RANDOM_SIGNAL has never been
1071          defined except on the i960, so I don't really know what we are
1072          supposed to do on other architectures.  */
1073       status->value.sig = TARGET_SIGNAL_UNKNOWN;
1074 #endif
1075       break;
1076     } /* switch */
1077   return pid;
1078 }
1079 \f
1080 static int
1081 symbol_stub (arg)
1082      char *arg;
1083 {
1084   symbol_file_command (arg, 0);
1085   return 1;
1086 }
1087
1088 static int
1089 add_symbol_stub (arg)
1090      char *arg;
1091 {
1092   struct ldfile *pLoadFile = (struct ldfile *)arg;
1093
1094   printf_unfiltered("\t%s: ", pLoadFile->name);
1095   vx_add_symbols (pLoadFile->name, 0, pLoadFile->txt_addr,
1096                   pLoadFile->data_addr, pLoadFile->bss_addr);
1097   printf_unfiltered ("ok\n");
1098   return 1;
1099 }
1100 /* Target command for VxWorks target systems.
1101
1102    Used in vxgdb.  Takes the name of a remote target machine
1103    running vxWorks and connects to it to initialize remote network
1104    debugging.  */
1105
1106 static void
1107 vx_open (args, from_tty)
1108      char *args;
1109      int from_tty;
1110 {
1111   extern int close ();
1112   char *bootFile;
1113   extern char *source_path;
1114   struct ldtabl loadTable;
1115   struct ldfile *pLoadFile;
1116   int i;
1117   extern CLIENT *pClient;
1118   int symbols_added = 0;
1119
1120   if (!args)
1121     error_no_arg ("target machine name");
1122
1123   target_preopen (from_tty);
1124   
1125   unpush_target (&vx_ops);
1126   printf_unfiltered ("Attaching remote machine across net...\n");
1127   gdb_flush (gdb_stdout);
1128
1129   /* Allow the user to kill the connect attempt by typing ^C.
1130      Wait until the call to target_has_fp () completes before
1131      disallowing an immediate quit, since even if net_connect ()
1132      is successful, the remote debug server might be hung.  */
1133
1134   immediate_quit++;
1135
1136   net_connect (args);
1137   target_has_fp = net_check_for_fp ();
1138   printf_filtered ("Connected to %s.\n", args);
1139
1140   immediate_quit--;
1141
1142   push_target (&vx_ops);
1143
1144   /* Save a copy of the target host's name.  */
1145   vx_host = savestring (args, strlen (args));
1146
1147   /* Find out the name of the file from which the target was booted
1148      and load its symbol table.  */
1149
1150   printf_filtered ("Looking in Unix path for all loaded modules:\n");
1151   bootFile = NULL;
1152   if (!net_get_boot_file (&bootFile))
1153     {
1154       if (*bootFile)
1155         {
1156           printf_filtered ("\t%s: ", bootFile);
1157           /* This assumes that the kernel is never relocated.  Hope that is an
1158              accurate assumption.  */
1159           if (catch_errors
1160               (symbol_stub,
1161                bootFile,
1162                "Error while reading symbols from boot file:\n",
1163                RETURN_MASK_ALL))
1164             puts_filtered ("ok\n");
1165         }
1166       else if (from_tty)
1167         printf_unfiltered ("VxWorks kernel symbols not loaded.\n");
1168     }
1169   else
1170     error ("Can't retrieve boot file name from target machine.");
1171
1172   clnt_freeres (pClient, xdr_wrapstring, &bootFile);
1173
1174   if (net_get_symbols (&loadTable) != 0)
1175     error ("Can't read loaded modules from target machine");
1176
1177   i = 0-1;
1178   while (++i < loadTable.tbl_size)
1179     {
1180       QUIT;     /* FIXME, avoids clnt_freeres below:  mem leak */
1181       pLoadFile = &loadTable.tbl_ent [i];
1182 #ifdef WRS_ORIG
1183   {
1184     register int desc;
1185     struct cleanup *old_chain;
1186     char *fullname = NULL;
1187
1188     desc = openp (source_path, 0, pLoadFile->name, O_RDONLY, 0, &fullname);
1189     if (desc < 0)
1190         perror_with_name (pLoadFile->name);
1191     old_chain = make_cleanup (close, desc);
1192     add_file_at_addr (fullname, desc, pLoadFile->txt_addr, pLoadFile->data_addr,
1193                       pLoadFile->bss_addr);
1194     do_cleanups (old_chain);
1195   }
1196 #else
1197       /* FIXME: Is there something better to search than the PATH? (probably
1198          not the source path, since source might be in different directories
1199          than objects.  */
1200
1201       if (catch_errors (add_symbol_stub, (char *)pLoadFile, (char *)0,
1202                         RETURN_MASK_ALL))
1203         symbols_added = 1;
1204 #endif
1205     }
1206   printf_filtered ("Done.\n");
1207
1208   clnt_freeres (pClient, xdr_ldtabl, &loadTable);
1209
1210   /* Getting new symbols may change our opinion about what is
1211      frameless.  */
1212   if (symbols_added)
1213     reinit_frame_cache ();
1214 }
1215 \f
1216 /* Takes a task started up outside of gdb and ``attaches'' to it.
1217    This stops it cold in its tracks and allows us to start tracing it.  */
1218
1219 static void
1220 vx_attach (args, from_tty)
1221      char *args;
1222      int from_tty;
1223 {
1224   unsigned long pid;
1225   char *cptr = 0;
1226   Rptrace ptrace_in;
1227   Ptrace_return ptrace_out;
1228   int status;
1229
1230   if (!args)
1231     error_no_arg ("process-id to attach");
1232
1233   pid = strtoul (args, &cptr, 0);
1234   if ((cptr == args) || (*cptr != '\0'))
1235     error ("Invalid process-id -- give a single number in decimal or 0xhex");
1236
1237   if (from_tty)
1238     printf_unfiltered ("Attaching pid %s.\n",
1239                        local_hex_string((unsigned long) pid));
1240
1241   memset ((char *)&ptrace_in,  '\0', sizeof (ptrace_in));
1242   memset ((char *)&ptrace_out, '\0', sizeof (ptrace_out));
1243   ptrace_in.pid = pid;
1244
1245   status = net_ptrace_clnt_call (PTRACE_ATTACH, &ptrace_in, &ptrace_out);
1246   if (status == -1)
1247     error (rpcerr);
1248   if (ptrace_out.status == -1)
1249     {
1250       errno = ptrace_out.errno;
1251       perror_with_name ("Attaching remote process");
1252     }
1253
1254   /* It worked... */
1255   push_target (&vx_run_ops);
1256   /* The unsigned long pid will get turned into a signed int here,
1257      but it doesn't seem to matter.  inferior_pid must be signed
1258      in order for other parts of GDB to work correctly.  */
1259   inferior_pid = pid;
1260   vx_running = 0;
1261 #if defined (START_INFERIOR_HOOK)
1262   START_INFERIOR_HOOK ();
1263 #endif
1264
1265   mark_breakpoints_out ();
1266
1267   /* Set up the "saved terminal modes" of the inferior
1268      based on what modes we are starting it with.  */
1269
1270   target_terminal_init ();
1271
1272   /* Install inferior's terminal modes.  */
1273
1274   target_terminal_inferior ();
1275
1276   /* We will get a task spawn event immediately.  */
1277
1278   init_wait_for_inferior ();
1279   clear_proceed_status ();
1280   stop_soon_quietly = 1;
1281   wait_for_inferior ();
1282   stop_soon_quietly = 0;
1283   normal_stop ();
1284 }
1285
1286
1287 /* detach_command --
1288    takes a program previously attached to and detaches it.
1289    The program resumes execution and will no longer stop
1290    on signals, etc.  We better not have left any breakpoints
1291    in the program or it'll die when it hits one.  For this
1292    to work, it may be necessary for the process to have been
1293    previously attached.  It *might* work if the program was
1294    started via the normal ptrace (PTRACE_TRACEME).  */
1295
1296 static void
1297 vx_detach (args, from_tty)
1298      char *args;
1299      int from_tty;
1300 {
1301   Rptrace ptrace_in;
1302   Ptrace_return ptrace_out;
1303   int signal = 0;
1304   int status;
1305
1306   if (args)
1307     error ("Argument given to VxWorks \"detach\".");
1308
1309   if (from_tty)
1310       printf_unfiltered ("Detaching pid %s.\n",
1311               local_hex_string((unsigned long) inferior_pid));
1312
1313   if (args)             /* FIXME, should be possible to leave suspended */
1314     signal = atoi (args);
1315   
1316   memset ((char *)&ptrace_in,  '\0', sizeof (ptrace_in));
1317   memset ((char *)&ptrace_out, '\0', sizeof (ptrace_out));
1318   ptrace_in.pid = inferior_pid;
1319
1320   status = net_ptrace_clnt_call (PTRACE_DETACH, &ptrace_in, &ptrace_out);
1321   if (status == -1)
1322     error (rpcerr);
1323   if (ptrace_out.status == -1)
1324     {
1325       errno = ptrace_out.errno;
1326       perror_with_name ("Detaching VxWorks process");
1327     }
1328
1329   inferior_pid = 0;
1330   pop_target ();        /* go back to non-executing VxWorks connection */
1331 }
1332
1333 /* vx_kill -- takes a running task and wipes it out.  */
1334
1335 static void
1336 vx_kill ()
1337 {
1338   Rptrace ptrace_in;
1339   Ptrace_return ptrace_out;
1340   int status;
1341
1342   printf_unfiltered ("Killing pid %s.\n", local_hex_string((unsigned long) inferior_pid));
1343
1344   memset ((char *)&ptrace_in,  '\0', sizeof (ptrace_in));
1345   memset ((char *)&ptrace_out, '\0', sizeof (ptrace_out));
1346   ptrace_in.pid = inferior_pid;
1347
1348   status = net_ptrace_clnt_call (PTRACE_KILL, &ptrace_in, &ptrace_out);
1349   if (status == -1)
1350     warning (rpcerr);
1351   else if (ptrace_out.status == -1)
1352     {
1353       errno = ptrace_out.errno;
1354       perror_with_name ("Killing VxWorks process");
1355     }
1356
1357   /* If it gives good status, the process is *gone*, no events remain.
1358      If the kill failed, assume the process is gone anyhow.  */
1359   inferior_pid = 0;
1360   pop_target ();        /* go back to non-executing VxWorks connection */
1361 }
1362
1363 /* Clean up from the VxWorks process target as it goes away.  */
1364
1365 static void
1366 vx_proc_close (quitting)
1367      int quitting;
1368 {
1369   inferior_pid = 0;             /* No longer have a process.  */
1370   if (vx_running)
1371     free (vx_running);
1372   vx_running = 0;
1373 }
1374 \f
1375 /* Make an RPC call to the VxWorks target.
1376    Returns RPC status.  */
1377
1378 static enum clnt_stat
1379 net_clnt_call (procNum, inProc, in, outProc, out)
1380     enum ptracereq procNum;
1381     xdrproc_t inProc;
1382     char *in;
1383     xdrproc_t outProc;
1384     char *out;
1385 {
1386   enum clnt_stat status;
1387   
1388   status = clnt_call (pClient, procNum, inProc, in, outProc, out, rpcTimeout);
1389
1390   if (status != RPC_SUCCESS)
1391       clnt_perrno (status);
1392
1393   return status;
1394 }
1395
1396 /* Clean up before losing control.  */
1397
1398 static void
1399 vx_close (quitting)
1400      int quitting;
1401 {
1402   if (pClient)
1403     clnt_destroy (pClient);     /* The net connection */
1404   pClient = 0;
1405
1406   if (vx_host)
1407     free (vx_host);             /* The hostname */
1408   vx_host = 0;
1409 }
1410
1411 /* A vxprocess target should be started via "run" not "target".  */
1412 /*ARGSUSED*/
1413 static void
1414 vx_proc_open (name, from_tty)
1415      char *name;
1416      int from_tty;
1417 {
1418   error ("Use the \"run\" command to start a VxWorks process.");
1419 }
1420
1421 /* Target ops structure for accessing memory and such over the net */
1422
1423 struct target_ops vx_ops = {
1424   "vxworks", "VxWorks target memory via RPC over TCP/IP",
1425   "Use VxWorks target memory.  \n\
1426 Specify the name of the machine to connect to.",
1427   vx_open, vx_close, vx_attach, 0, /* vx_detach, */
1428   0, 0, /* resume, wait */
1429   0, 0, /* read_reg, write_reg */
1430   0, /* prep_to_store, */
1431   vx_xfer_memory, vx_files_info,
1432   0, 0, /* insert_breakpoint, remove_breakpoint */
1433   0, 0, 0, 0, 0,        /* terminal stuff */
1434   0, /* vx_kill, */
1435   vx_load_command,
1436   vx_lookup_symbol,
1437   vx_create_inferior, 0,  /* mourn_inferior */
1438   0, /* can_run */
1439   0, /* notice_signals */
1440   0,                            /* to_stop */
1441   core_stratum, 0, /* next */
1442   1, 1, 0, 0, 0,        /* all mem, mem, stack, regs, exec */
1443   0, 0,                 /* Section pointers */
1444   OPS_MAGIC,            /* Always the last thing */
1445 };
1446
1447 /* Target ops structure for accessing VxWorks child processes over the net */
1448
1449 struct target_ops vx_run_ops = {
1450   "vxprocess", "VxWorks process",
1451   "VxWorks process, started by the \"run\" command.",
1452   vx_proc_open, vx_proc_close, 0, vx_detach, /* vx_attach */
1453   vx_resume, vx_wait,
1454   vx_read_register, vx_write_register,
1455   vx_prepare_to_store,
1456   vx_xfer_memory, vx_run_files_info,
1457   vx_insert_breakpoint, vx_remove_breakpoint,
1458   0, 0, 0, 0, 0,        /* terminal stuff */
1459   vx_kill,
1460   vx_load_command,
1461   vx_lookup_symbol,
1462   0, vx_mourn_inferior,
1463   0, /* can_run */
1464   0, /* notice_signals */
1465   0,                            /* to_stop */
1466   process_stratum, 0, /* next */
1467   0, /* all_mem--off to avoid spurious msg in "i files" */
1468   1, 1, 1, 1,   /* mem, stack, regs, exec */
1469   0, 0,                 /* Section pointers */
1470   OPS_MAGIC,            /* Always the last thing */
1471 };
1472 /* ==> Remember when reading at end of file, there are two "ops" structs here. */
1473 \f
1474 void
1475 _initialize_vx ()
1476 {
1477   add_show_from_set
1478     (add_set_cmd ("vxworks-timeout", class_support, var_uinteger,
1479                   (char *) &rpcTimeout.tv_sec,
1480                   "Set seconds to wait for rpc calls to return.\n\
1481 Set the number of seconds to wait for rpc calls to return.", &setlist),
1482      &showlist);
1483
1484   add_target (&vx_ops);
1485   add_target (&vx_run_ops);
1486 }