1 /* Copyright (C) 2009-2013 Free Software Foundation, Inc.
2 
3    This file is part of GDB.
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17 
18 #include "server.h"
19 #include "target.h"
20 #include "lynx-low.h"
21 
22 #include <limits.h>
23 #include <sys/ptrace.h>
24 #include <sys/piddef.h> /* Provides PIDGET, TIDGET, BUILDPID, etc.  */
25 #include <unistd.h>
26 #include <sys/ioctl.h>
27 #include <sys/types.h>
28 #include "gdb_wait.h"
29 #include <signal.h>
30 
31 int using_threads = 1;
32 
33 /* Print a debug trace on standard output if debug_threads is set.  */
34 
35 static void
lynx_debug(char * string,...)36 lynx_debug (char *string, ...)
37 {
38   va_list args;
39 
40   if (!debug_threads)
41     return;
42 
43   va_start (args, string);
44   fprintf (stderr, "DEBUG(lynx): ");
45   vfprintf (stderr, string, args);
46   fprintf (stderr, "\n");
47   va_end (args);
48 }
49 
50 /* Build a ptid_t given a PID and a LynxOS TID.  */
51 
52 static ptid_t
lynx_ptid_build(int pid,long tid)53 lynx_ptid_build (int pid, long tid)
54 {
55   /* brobecker/2010-06-21: It looks like the LWP field in ptids
56      should be distinct for each thread (see write_ptid where it
57      writes the thread ID from the LWP).  So instead of storing
58      the LynxOS tid in the tid field of the ptid, we store it in
59      the lwp field.  */
60   return ptid_build (pid, tid, 0);
61 }
62 
63 /* Return the process ID of the given PTID.
64 
65    This function has little reason to exist, it's just a wrapper around
66    ptid_get_pid.  But since we have a getter function for the lynxos
67    ptid, it feels cleaner to have a getter for the pid as well.  */
68 
69 static int
lynx_ptid_get_pid(ptid_t ptid)70 lynx_ptid_get_pid (ptid_t ptid)
71 {
72   return ptid_get_pid (ptid);
73 }
74 
75 /* Return the LynxOS tid of the given PTID.  */
76 
77 static long
lynx_ptid_get_tid(ptid_t ptid)78 lynx_ptid_get_tid (ptid_t ptid)
79 {
80   /* See lynx_ptid_build: The LynxOS tid is stored inside the lwp field
81      of the ptid.  */
82   return ptid_get_lwp (ptid);
83 }
84 
85 /* For a given PTID, return the associated PID as known by the LynxOS
86    ptrace layer.  */
87 
88 static int
lynx_ptrace_pid_from_ptid(ptid_t ptid)89 lynx_ptrace_pid_from_ptid (ptid_t ptid)
90 {
91   return BUILDPID (lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
92 }
93 
94 /* Return a string image of the ptrace REQUEST number.  */
95 
96 static char *
ptrace_request_to_str(int request)97 ptrace_request_to_str (int request)
98 {
99 #define CASE(X) case X: return #X
100   switch (request)
101     {
102       CASE(PTRACE_TRACEME);
103       CASE(PTRACE_PEEKTEXT);
104       CASE(PTRACE_PEEKDATA);
105       CASE(PTRACE_PEEKUSER);
106       CASE(PTRACE_POKETEXT);
107       CASE(PTRACE_POKEDATA);
108       CASE(PTRACE_POKEUSER);
109       CASE(PTRACE_CONT);
110       CASE(PTRACE_KILL);
111       CASE(PTRACE_SINGLESTEP);
112       CASE(PTRACE_ATTACH);
113       CASE(PTRACE_DETACH);
114       CASE(PTRACE_GETREGS);
115       CASE(PTRACE_SETREGS);
116       CASE(PTRACE_GETFPREGS);
117       CASE(PTRACE_SETFPREGS);
118       CASE(PTRACE_READDATA);
119       CASE(PTRACE_WRITEDATA);
120       CASE(PTRACE_READTEXT);
121       CASE(PTRACE_WRITETEXT);
122       CASE(PTRACE_GETFPAREGS);
123       CASE(PTRACE_SETFPAREGS);
124       CASE(PTRACE_GETWINDOW);
125       CASE(PTRACE_SETWINDOW);
126       CASE(PTRACE_SYSCALL);
127       CASE(PTRACE_DUMPCORE);
128       CASE(PTRACE_SETWRBKPT);
129       CASE(PTRACE_SETACBKPT);
130       CASE(PTRACE_CLRBKPT);
131       CASE(PTRACE_GET_UCODE);
132 #ifdef PT_READ_GPR
133       CASE(PT_READ_GPR);
134 #endif
135 #ifdef PT_WRITE_GPR
136       CASE(PT_WRITE_GPR);
137 #endif
138 #ifdef PT_READ_FPR
139       CASE(PT_READ_FPR);
140 #endif
141 #ifdef PT_WRITE_FPR
142       CASE(PT_WRITE_FPR);
143 #endif
144 #ifdef PT_READ_VPR
145       CASE(PT_READ_VPR);
146 #endif
147 #ifdef PT_WRITE_VPR
148       CASE(PT_WRITE_VPR);
149 #endif
150 #ifdef PTRACE_PEEKUSP
151       CASE(PTRACE_PEEKUSP);
152 #endif
153 #ifdef PTRACE_POKEUSP
154       CASE(PTRACE_POKEUSP);
155 #endif
156       CASE(PTRACE_PEEKTHREAD);
157       CASE(PTRACE_THREADUSER);
158       CASE(PTRACE_FPREAD);
159       CASE(PTRACE_FPWRITE);
160       CASE(PTRACE_SETSIG);
161       CASE(PTRACE_CONT_ONE);
162       CASE(PTRACE_KILL_ONE);
163       CASE(PTRACE_SINGLESTEP_ONE);
164       CASE(PTRACE_GETLOADINFO);
165       CASE(PTRACE_GETTRACESIG);
166 #ifdef PTRACE_GETTHREADLIST
167       CASE(PTRACE_GETTHREADLIST);
168 #endif
169     }
170 #undef CASE
171 
172   return "<unknown-request>";
173 }
174 
175 /* A wrapper around ptrace that allows us to print debug traces of
176    ptrace calls if debug traces are activated.  */
177 
178 static int
lynx_ptrace(int request,ptid_t ptid,int addr,int data,int addr2)179 lynx_ptrace (int request, ptid_t ptid, int addr, int data, int addr2)
180 {
181   int result;
182   const int pid = lynx_ptrace_pid_from_ptid (ptid);
183   int saved_errno;
184 
185   if (debug_threads)
186     fprintf (stderr, "PTRACE (%s, pid=%d(pid=%d, tid=%d), addr=0x%x, "
187              "data=0x%x, addr2=0x%x)",
188              ptrace_request_to_str (request), pid, PIDGET (pid), TIDGET (pid),
189              addr, data, addr2);
190   result = ptrace (request, pid, addr, data, addr2);
191   saved_errno = errno;
192   if (debug_threads)
193     fprintf (stderr, " -> %d (=0x%x)\n", result, result);
194 
195   errno = saved_errno;
196   return result;
197 }
198 
199 /* Implement the create_inferior method of the target_ops vector.  */
200 
201 static int
lynx_create_inferior(char * program,char ** allargs)202 lynx_create_inferior (char *program, char **allargs)
203 {
204   int pid;
205 
206   lynx_debug ("lynx_create_inferior ()");
207 
208   pid = fork ();
209   if (pid < 0)
210     perror_with_name ("fork");
211 
212   if (pid == 0)
213     {
214       int pgrp;
215 
216       /* Switch child to its own process group so that signals won't
217          directly affect gdbserver. */
218       pgrp = getpid();
219       setpgid (0, pgrp);
220       ioctl (0, TIOCSPGRP, &pgrp);
221       if (!remote_connection_is_stdio ())
222 	{
223 	  close (listen_desc);
224 	  if (gdb_connected ())
225 	    close (remote_desc);
226 	}
227       lynx_ptrace (PTRACE_TRACEME, null_ptid, 0, 0, 0);
228       execv (program, allargs);
229       fprintf (stderr, "Cannot exec %s: %s.\n", program, strerror (errno));
230       fflush (stderr);
231       _exit (0177);
232     }
233 
234   add_process (pid, 0);
235   /* Do not add the process thread just yet, as we do not know its tid.
236      We will add it later, during the wait for the STOP event corresponding
237      to the lynx_ptrace (PTRACE_TRACEME) call above.  */
238   return pid;
239 }
240 
241 /* Implement the attach target_ops method.  */
242 
243 static int
lynx_attach(unsigned long pid)244 lynx_attach (unsigned long pid)
245 {
246   ptid_t ptid = lynx_ptid_build (pid, 0);
247 
248   if (lynx_ptrace (PTRACE_ATTACH, ptid, 0, 0, 0) != 0)
249     error ("Cannot attach to process %lu: %s (%d)\n", pid,
250 	   strerror (errno), errno);
251 
252   add_process (pid, 1);
253   add_thread (ptid, NULL);
254 
255   return 0;
256 }
257 
258 /* Implement the resume target_ops method.  */
259 
260 static void
lynx_resume(struct thread_resume * resume_info,size_t n)261 lynx_resume (struct thread_resume *resume_info, size_t n)
262 {
263   /* FIXME: Assume for now that n == 1.  */
264   ptid_t ptid = resume_info[0].thread;
265   const int request = (resume_info[0].kind == resume_step
266                        ? PTRACE_SINGLESTEP : PTRACE_CONT);
267   const int signal = resume_info[0].sig;
268 
269   if (ptid_equal (ptid, minus_one_ptid))
270     ptid = thread_to_gdb_id (current_inferior);
271 
272   regcache_invalidate ();
273 
274   errno = 0;
275   lynx_ptrace (request, ptid, 1, signal, 0);
276   if (errno)
277     perror_with_name ("ptrace");
278 }
279 
280 /* Resume the execution of the given PTID.  */
281 
282 static void
lynx_continue(ptid_t ptid)283 lynx_continue (ptid_t ptid)
284 {
285   struct thread_resume resume_info;
286 
287   resume_info.thread = ptid;
288   resume_info.kind = resume_continue;
289   resume_info.sig = 0;
290 
291   lynx_resume (&resume_info, 1);
292 }
293 
294 /* Remove all inferiors and associated threads.  */
295 
296 static void
lynx_clear_inferiors(void)297 lynx_clear_inferiors (void)
298 {
299   /* We do not use private data, so nothing much to do except calling
300      clear_inferiors.  */
301   clear_inferiors ();
302 }
303 
304 /* A wrapper around waitpid that handles the various idiosyncrasies
305    of LynxOS' waitpid.  */
306 
307 static int
lynx_waitpid(int pid,int * stat_loc)308 lynx_waitpid (int pid, int *stat_loc)
309 {
310   int ret = 0;
311 
312   while (1)
313     {
314       ret = waitpid (pid, stat_loc, WNOHANG);
315       if (ret < 0)
316         {
317 	  /* An ECHILD error is not indicative of a real problem.
318 	     It happens for instance while waiting for the inferior
319 	     to stop after attaching to it.  */
320 	  if (errno != ECHILD)
321 	    perror_with_name ("waitpid (WNOHANG)");
322 	}
323       if (ret > 0)
324         break;
325       /* No event with WNOHANG.  See if there is one with WUNTRACED.  */
326       ret = waitpid (pid, stat_loc, WNOHANG | WUNTRACED);
327       if (ret < 0)
328         {
329 	  /* An ECHILD error is not indicative of a real problem.
330 	     It happens for instance while waiting for the inferior
331 	     to stop after attaching to it.  */
332 	  if (errno != ECHILD)
333 	    perror_with_name ("waitpid (WNOHANG|WUNTRACED)");
334 	}
335       if (ret > 0)
336         break;
337       usleep (1000);
338     }
339   return ret;
340 }
341 
342 /* Implement the wait target_ops method.  */
343 
344 static ptid_t
lynx_wait_1(ptid_t ptid,struct target_waitstatus * status,int options)345 lynx_wait_1 (ptid_t ptid, struct target_waitstatus *status, int options)
346 {
347   int pid;
348   int ret;
349   int wstat;
350   ptid_t new_ptid;
351 
352   if (ptid_equal (ptid, minus_one_ptid))
353     pid = lynx_ptid_get_pid (thread_to_gdb_id (current_inferior));
354   else
355     pid = BUILDPID (lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
356 
357 retry:
358 
359   ret = lynx_waitpid (pid, &wstat);
360   new_ptid = lynx_ptid_build (ret, ((union wait *) &wstat)->w_tid);
361 
362   /* If this is a new thread, then add it now.  The reason why we do
363      this here instead of when handling new-thread events is because
364      we need to add the thread associated to the "main" thread - even
365      for non-threaded applications where the new-thread events are not
366      generated.  */
367   if (!find_thread_ptid (new_ptid))
368     {
369       lynx_debug ("New thread: (pid = %d, tid = %d)",
370 		  lynx_ptid_get_pid (new_ptid), lynx_ptid_get_tid (new_ptid));
371       add_thread (new_ptid, NULL);
372     }
373 
374   if (WIFSTOPPED (wstat))
375     {
376       status->kind = TARGET_WAITKIND_STOPPED;
377       status->value.integer = gdb_signal_from_host (WSTOPSIG (wstat));
378       lynx_debug ("process stopped with signal: %d",
379                   status->value.integer);
380     }
381   else if (WIFEXITED (wstat))
382     {
383       status->kind = TARGET_WAITKIND_EXITED;
384       status->value.integer = WEXITSTATUS (wstat);
385       lynx_debug ("process exited with code: %d", status->value.integer);
386     }
387   else if (WIFSIGNALED (wstat))
388     {
389       status->kind = TARGET_WAITKIND_SIGNALLED;
390       status->value.integer = gdb_signal_from_host (WTERMSIG (wstat));
391       lynx_debug ("process terminated with code: %d",
392                   status->value.integer);
393     }
394   else
395     {
396       /* Not sure what happened if we get here, or whether we can
397 	 in fact get here.  But if we do, handle the event the best
398 	 we can.  */
399       status->kind = TARGET_WAITKIND_STOPPED;
400       status->value.integer = gdb_signal_from_host (0);
401       lynx_debug ("unknown event ????");
402     }
403 
404   /* SIGTRAP events are generated for situations other than single-step/
405      breakpoint events (Eg. new-thread events).  Handle those other types
406      of events, and resume the execution if necessary.  */
407   if (status->kind == TARGET_WAITKIND_STOPPED
408       && status->value.integer == GDB_SIGNAL_TRAP)
409     {
410       const int realsig = lynx_ptrace (PTRACE_GETTRACESIG, new_ptid, 0, 0, 0);
411 
412       lynx_debug ("(realsig = %d)", realsig);
413       switch (realsig)
414 	{
415 	  case SIGNEWTHREAD:
416 	    /* We just added the new thread above.  No need to do anything
417 	       further.  Just resume the execution again.  */
418 	    lynx_continue (new_ptid);
419 	    goto retry;
420 
421 	  case SIGTHREADEXIT:
422 	    remove_thread (find_thread_ptid (new_ptid));
423 	    lynx_continue (new_ptid);
424 	    goto retry;
425 	}
426     }
427 
428   return new_ptid;
429 }
430 
431 /* A wrapper around lynx_wait_1 that also prints debug traces when
432    such debug traces have been activated.  */
433 
434 static ptid_t
lynx_wait(ptid_t ptid,struct target_waitstatus * status,int options)435 lynx_wait (ptid_t ptid, struct target_waitstatus *status, int options)
436 {
437   ptid_t new_ptid;
438 
439   lynx_debug ("lynx_wait (pid = %d, tid = %ld)",
440               lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
441   new_ptid = lynx_wait_1 (ptid, status, options);
442   lynx_debug ("          -> (pid=%d, tid=%ld, status->kind = %d)",
443 	      lynx_ptid_get_pid (new_ptid), lynx_ptid_get_tid (new_ptid),
444 	      status->kind);
445   return new_ptid;
446 }
447 
448 /* Implement the kill target_ops method.  */
449 
450 static int
lynx_kill(int pid)451 lynx_kill (int pid)
452 {
453   ptid_t ptid = lynx_ptid_build (pid, 0);
454   struct target_waitstatus status;
455   struct process_info *process;
456 
457   process = find_process_pid (pid);
458   if (process == NULL)
459     return -1;
460 
461   lynx_ptrace (PTRACE_KILL, ptid, 0, 0, 0);
462   lynx_wait (ptid, &status, 0);
463   the_target->mourn (process);
464   return 0;
465 }
466 
467 /* Implement the detach target_ops method.  */
468 
469 static int
lynx_detach(int pid)470 lynx_detach (int pid)
471 {
472   ptid_t ptid = lynx_ptid_build (pid, 0);
473   struct process_info *process;
474 
475   process = find_process_pid (pid);
476   if (process == NULL)
477     return -1;
478 
479   lynx_ptrace (PTRACE_DETACH, ptid, 0, 0, 0);
480   the_target->mourn (process);
481   return 0;
482 }
483 
484 /* Implement the mourn target_ops method.  */
485 
486 static void
lynx_mourn(struct process_info * proc)487 lynx_mourn (struct process_info *proc)
488 {
489   lynx_clear_inferiors ();
490 }
491 
492 /* Implement the join target_ops method.  */
493 
494 static void
lynx_join(int pid)495 lynx_join (int pid)
496 {
497   /* The PTRACE_DETACH is sufficient to detach from the process.
498      So no need to do anything extra.  */
499 }
500 
501 /* Implement the thread_alive target_ops method.  */
502 
503 static int
lynx_thread_alive(ptid_t ptid)504 lynx_thread_alive (ptid_t ptid)
505 {
506   /* The list of threads is updated at the end of each wait, so it
507      should be up to date.  No need to re-fetch it.  */
508   return (find_thread_ptid (ptid) != NULL);
509 }
510 
511 /* Implement the fetch_registers target_ops method.  */
512 
513 static void
lynx_fetch_registers(struct regcache * regcache,int regno)514 lynx_fetch_registers (struct regcache *regcache, int regno)
515 {
516   struct lynx_regset_info *regset = lynx_target_regsets;
517   ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
518 
519   lynx_debug ("lynx_fetch_registers (regno = %d)", regno);
520 
521   while (regset->size >= 0)
522     {
523       char *buf;
524       int res;
525 
526       buf = xmalloc (regset->size);
527       res = lynx_ptrace (regset->get_request, inferior_ptid, (int) buf, 0, 0);
528       if (res < 0)
529         perror ("ptrace");
530       regset->store_function (regcache, buf);
531       free (buf);
532       regset++;
533     }
534 }
535 
536 /* Implement the store_registers target_ops method.  */
537 
538 static void
lynx_store_registers(struct regcache * regcache,int regno)539 lynx_store_registers (struct regcache *regcache, int regno)
540 {
541   struct lynx_regset_info *regset = lynx_target_regsets;
542   ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
543 
544   lynx_debug ("lynx_store_registers (regno = %d)", regno);
545 
546   while (regset->size >= 0)
547     {
548       char *buf;
549       int res;
550 
551       buf = xmalloc (regset->size);
552       res = lynx_ptrace (regset->get_request, inferior_ptid, (int) buf, 0, 0);
553       if (res == 0)
554         {
555 	  /* Then overlay our cached registers on that.  */
556 	  regset->fill_function (regcache, buf);
557 	  /* Only now do we write the register set.  */
558 	  res = lynx_ptrace (regset->set_request, inferior_ptid, (int) buf,
559 			     0, 0);
560         }
561       if (res < 0)
562         perror ("ptrace");
563       free (buf);
564       regset++;
565     }
566 }
567 
568 /* Implement the read_memory target_ops method.  */
569 
570 static int
lynx_read_memory(CORE_ADDR memaddr,unsigned char * myaddr,int len)571 lynx_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
572 {
573   /* On LynxOS, memory reads needs to be performed in chunks the size
574      of int types, and they should also be aligned accordingly.  */
575   int buf;
576   const int xfer_size = sizeof (buf);
577   CORE_ADDR addr = memaddr & -(CORE_ADDR) xfer_size;
578   ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
579 
580   while (addr < memaddr + len)
581     {
582       int skip = 0;
583       int truncate = 0;
584 
585       errno = 0;
586       if (addr < memaddr)
587         skip = memaddr - addr;
588       if (addr + xfer_size > memaddr + len)
589         truncate = addr + xfer_size - memaddr - len;
590       buf = lynx_ptrace (PTRACE_PEEKTEXT, inferior_ptid, addr, 0, 0);
591       if (errno)
592         return errno;
593       memcpy (myaddr + (addr - memaddr) + skip, (gdb_byte *) &buf + skip,
594               xfer_size - skip - truncate);
595       addr += xfer_size;
596     }
597 
598   return 0;
599 }
600 
601 /* Implement the write_memory target_ops method.  */
602 
603 static int
lynx_write_memory(CORE_ADDR memaddr,const unsigned char * myaddr,int len)604 lynx_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
605 {
606   /* On LynxOS, memory writes needs to be performed in chunks the size
607      of int types, and they should also be aligned accordingly.  */
608   int buf;
609   const int xfer_size = sizeof (buf);
610   CORE_ADDR addr = memaddr & -(CORE_ADDR) xfer_size;
611   ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
612 
613   while (addr < memaddr + len)
614     {
615       int skip = 0;
616       int truncate = 0;
617 
618       if (addr < memaddr)
619         skip = memaddr - addr;
620       if (addr + xfer_size > memaddr + len)
621         truncate = addr + xfer_size - memaddr - len;
622       if (skip > 0 || truncate > 0)
623         /* We need to read the memory at this address in order to preserve
624            the data that we are not overwriting.  */
625         lynx_read_memory (addr, (unsigned char *) &buf, xfer_size);
626         if (errno)
627           return errno;
628       memcpy ((gdb_byte *) &buf + skip, myaddr + (addr - memaddr) + skip,
629               xfer_size - skip - truncate);
630       errno = 0;
631       lynx_ptrace (PTRACE_POKETEXT, inferior_ptid, addr, buf, 0);
632       if (errno)
633         return errno;
634       addr += xfer_size;
635     }
636 
637   return 0;
638 }
639 
640 /* Implement the kill_request target_ops method.  */
641 
642 static void
lynx_request_interrupt(void)643 lynx_request_interrupt (void)
644 {
645   ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
646 
647   kill (lynx_ptid_get_pid (inferior_ptid), SIGINT);
648 }
649 
650 /* The LynxOS target_ops vector.  */
651 
652 static struct target_ops lynx_target_ops = {
653   lynx_create_inferior,
654   lynx_attach,
655   lynx_kill,
656   lynx_detach,
657   lynx_mourn,
658   lynx_join,
659   lynx_thread_alive,
660   lynx_resume,
661   lynx_wait,
662   lynx_fetch_registers,
663   lynx_store_registers,
664   NULL,  /* prepare_to_access_memory */
665   NULL,  /* done_accessing_memory */
666   lynx_read_memory,
667   lynx_write_memory,
668   NULL,  /* look_up_symbols */
669   lynx_request_interrupt,
670   NULL,  /* read_auxv */
671   NULL,  /* insert_point */
672   NULL,  /* remove_point */
673   NULL,  /* stopped_by_watchpoint */
674   NULL,  /* stopped_data_address */
675   NULL,  /* read_offsets */
676   NULL,  /* get_tls_address */
677   NULL,  /* qxfer_spu */
678   NULL,  /* hostio_last_error */
679   NULL,  /* qxfer_osdata */
680   NULL,  /* qxfer_siginfo */
681   NULL,  /* supports_non_stop */
682   NULL,  /* async */
683   NULL,  /* start_non_stop */
684   NULL,  /* supports_multi_process */
685   NULL,  /* handle_monitor_command */
686 };
687 
688 void
initialize_low(void)689 initialize_low (void)
690 {
691   set_target_ops (&lynx_target_ops);
692   the_low_target.arch_setup ();
693 }
694 
695