Share fork_inferior et al with gdbserver
[external/binutils.git] / gdb / gdbserver / lynx-low.c
1 /* Copyright (C) 2009-2017 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 #include "filestuff.h"
31 #include "common-inferior.h"
32 #include "nat/fork-inferior.h"
33
34 int using_threads = 1;
35
36 const struct target_desc *lynx_tdesc;
37
38 /* Per-process private data.  */
39
40 struct process_info_private
41 {
42   /* The PTID obtained from the last wait performed on this process.
43      Initialized to null_ptid until the first wait is performed.  */
44   ptid_t last_wait_event_ptid;
45 };
46
47 /* Print a debug trace on standard output if debug_threads is set.  */
48
49 static void
50 lynx_debug (char *string, ...)
51 {
52   va_list args;
53
54   if (!debug_threads)
55     return;
56
57   va_start (args, string);
58   fprintf (stderr, "DEBUG(lynx): ");
59   vfprintf (stderr, string, args);
60   fprintf (stderr, "\n");
61   va_end (args);
62 }
63
64 /* Build a ptid_t given a PID and a LynxOS TID.  */
65
66 static ptid_t
67 lynx_ptid_build (int pid, long tid)
68 {
69   /* brobecker/2010-06-21: It looks like the LWP field in ptids
70      should be distinct for each thread (see write_ptid where it
71      writes the thread ID from the LWP).  So instead of storing
72      the LynxOS tid in the tid field of the ptid, we store it in
73      the lwp field.  */
74   return ptid_build (pid, tid, 0);
75 }
76
77 /* Return the process ID of the given PTID.
78
79    This function has little reason to exist, it's just a wrapper around
80    ptid_get_pid.  But since we have a getter function for the lynxos
81    ptid, it feels cleaner to have a getter for the pid as well.  */
82
83 static int
84 lynx_ptid_get_pid (ptid_t ptid)
85 {
86   return ptid_get_pid (ptid);
87 }
88
89 /* Return the LynxOS tid of the given PTID.  */
90
91 static long
92 lynx_ptid_get_tid (ptid_t ptid)
93 {
94   /* See lynx_ptid_build: The LynxOS tid is stored inside the lwp field
95      of the ptid.  */
96   return ptid_get_lwp (ptid);
97 }
98
99 /* For a given PTID, return the associated PID as known by the LynxOS
100    ptrace layer.  */
101
102 static int
103 lynx_ptrace_pid_from_ptid (ptid_t ptid)
104 {
105   return BUILDPID (lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
106 }
107
108 /* Return a string image of the ptrace REQUEST number.  */
109
110 static char *
111 ptrace_request_to_str (int request)
112 {
113 #define CASE(X) case X: return #X
114   switch (request)
115     {
116       CASE(PTRACE_TRACEME);
117       CASE(PTRACE_PEEKTEXT);
118       CASE(PTRACE_PEEKDATA);
119       CASE(PTRACE_PEEKUSER);
120       CASE(PTRACE_POKETEXT);
121       CASE(PTRACE_POKEDATA);
122       CASE(PTRACE_POKEUSER);
123       CASE(PTRACE_CONT);
124       CASE(PTRACE_KILL);
125       CASE(PTRACE_SINGLESTEP);
126       CASE(PTRACE_ATTACH);
127       CASE(PTRACE_DETACH);
128       CASE(PTRACE_GETREGS);
129       CASE(PTRACE_SETREGS);
130       CASE(PTRACE_GETFPREGS);
131       CASE(PTRACE_SETFPREGS);
132       CASE(PTRACE_READDATA);
133       CASE(PTRACE_WRITEDATA);
134       CASE(PTRACE_READTEXT);
135       CASE(PTRACE_WRITETEXT);
136       CASE(PTRACE_GETFPAREGS);
137       CASE(PTRACE_SETFPAREGS);
138       CASE(PTRACE_GETWINDOW);
139       CASE(PTRACE_SETWINDOW);
140       CASE(PTRACE_SYSCALL);
141       CASE(PTRACE_DUMPCORE);
142       CASE(PTRACE_SETWRBKPT);
143       CASE(PTRACE_SETACBKPT);
144       CASE(PTRACE_CLRBKPT);
145       CASE(PTRACE_GET_UCODE);
146 #ifdef PT_READ_GPR
147       CASE(PT_READ_GPR);
148 #endif
149 #ifdef PT_WRITE_GPR
150       CASE(PT_WRITE_GPR);
151 #endif
152 #ifdef PT_READ_FPR
153       CASE(PT_READ_FPR);
154 #endif
155 #ifdef PT_WRITE_FPR
156       CASE(PT_WRITE_FPR);
157 #endif
158 #ifdef PT_READ_VPR
159       CASE(PT_READ_VPR);
160 #endif
161 #ifdef PT_WRITE_VPR
162       CASE(PT_WRITE_VPR);
163 #endif
164 #ifdef PTRACE_PEEKUSP
165       CASE(PTRACE_PEEKUSP);
166 #endif
167 #ifdef PTRACE_POKEUSP
168       CASE(PTRACE_POKEUSP);
169 #endif
170       CASE(PTRACE_PEEKTHREAD);
171       CASE(PTRACE_THREADUSER);
172       CASE(PTRACE_FPREAD);
173       CASE(PTRACE_FPWRITE);
174       CASE(PTRACE_SETSIG);
175       CASE(PTRACE_CONT_ONE);
176       CASE(PTRACE_KILL_ONE);
177       CASE(PTRACE_SINGLESTEP_ONE);
178       CASE(PTRACE_GETLOADINFO);
179       CASE(PTRACE_GETTRACESIG);
180 #ifdef PTRACE_GETTHREADLIST
181       CASE(PTRACE_GETTHREADLIST);
182 #endif
183     }
184 #undef CASE
185
186   return "<unknown-request>";
187 }
188
189 /* A wrapper around ptrace that allows us to print debug traces of
190    ptrace calls if debug traces are activated.  */
191
192 static int
193 lynx_ptrace (int request, ptid_t ptid, int addr, int data, int addr2)
194 {
195   int result;
196   const int pid = lynx_ptrace_pid_from_ptid (ptid);
197   int saved_errno;
198
199   if (debug_threads)
200     fprintf (stderr, "PTRACE (%s, pid=%d(pid=%d, tid=%d), addr=0x%x, "
201              "data=0x%x, addr2=0x%x)",
202              ptrace_request_to_str (request), pid, PIDGET (pid), TIDGET (pid),
203              addr, data, addr2);
204   result = ptrace (request, pid, addr, data, addr2);
205   saved_errno = errno;
206   if (debug_threads)
207     fprintf (stderr, " -> %d (=0x%x)\n", result, result);
208
209   errno = saved_errno;
210   return result;
211 }
212
213 /* Call add_process with the given parameters, and initializes
214    the process' private data.  */
215
216 static struct process_info *
217 lynx_add_process (int pid, int attached)
218 {
219   struct process_info *proc;
220
221   proc = add_process (pid, attached);
222   proc->tdesc = lynx_tdesc;
223   proc->priv = XCNEW (struct process_info_private);
224   proc->priv->last_wait_event_ptid = null_ptid;
225
226   return proc;
227 }
228
229 /* Callback used by fork_inferior to start tracing the inferior.  */
230
231 static void
232 lynx_ptrace_fun ()
233 {
234   int pgrp;
235
236   /* Switch child to its own process group so that signals won't
237      directly affect GDBserver. */
238   pgrp = getpid();
239   if (pgrp < 0)
240     trace_start_error_with_name ("pgrp");
241   if (setpgid (0, pgrp) < 0)
242     trace_start_error_with_name ("setpgid");
243   if (ioctl (0, TIOCSPGRP, &pgrp) < 0)
244     trace_start_error_with_name ("ioctl");
245   if (lynx_ptrace (PTRACE_TRACEME, null_ptid, 0, 0, 0) < 0)
246     trace_start_error_with_name ("lynx_ptrace");
247 }
248
249 /* Implement the create_inferior method of the target_ops vector.  */
250
251 static int
252 lynx_create_inferior (const char *program,
253                       const std::vector<char *> &program_args)
254 {
255   int pid;
256   std::string str_program_args = stringify_argv (program_args);
257
258   lynx_debug ("lynx_create_inferior ()");
259
260   pid = fork_inferior (program,
261                        str_program_args.c_str (),
262                        environ_vector (get_environ ()), lynx_ptrace_fun,
263                        NULL, NULL, NULL, NULL);
264
265   post_fork_inferior (pid, program);
266
267   lynx_add_process (pid, 0);
268   /* Do not add the process thread just yet, as we do not know its tid.
269      We will add it later, during the wait for the STOP event corresponding
270      to the lynx_ptrace (PTRACE_TRACEME) call above.  */
271   return pid;
272 }
273
274 /* Assuming we've just attached to a running inferior whose pid is PID,
275    add all threads running in that process.  */
276
277 static void
278 lynx_add_threads_after_attach (int pid)
279 {
280   /* Ugh!  There appears to be no way to get the list of threads
281      in the program we just attached to.  So get the list by calling
282      the "ps" command.  This is only needed now, as we will then
283      keep the thread list up to date thanks to thread creation and
284      exit notifications.  */
285   FILE *f;
286   char buf[256];
287   int thread_pid, thread_tid;
288
289   f = popen ("ps atx", "r");
290   if (f == NULL)
291     perror_with_name ("Cannot get thread list");
292
293   while (fgets (buf, sizeof (buf), f) != NULL)
294     if ((sscanf (buf, "%d %d", &thread_pid, &thread_tid) == 2
295          && thread_pid == pid))
296     {
297       ptid_t thread_ptid = lynx_ptid_build (pid, thread_tid);
298
299       if (!find_thread_ptid (thread_ptid))
300         {
301           lynx_debug ("New thread: (pid = %d, tid = %d)",
302                       pid, thread_tid);
303           add_thread (thread_ptid, NULL);
304         }
305     }
306
307   pclose (f);
308 }
309
310 /* Implement the attach target_ops method.  */
311
312 static int
313 lynx_attach (unsigned long pid)
314 {
315   ptid_t ptid = lynx_ptid_build (pid, 0);
316
317   if (lynx_ptrace (PTRACE_ATTACH, ptid, 0, 0, 0) != 0)
318     error ("Cannot attach to process %lu: %s (%d)\n", pid,
319            strerror (errno), errno);
320
321   lynx_add_process (pid, 1);
322   lynx_add_threads_after_attach (pid);
323
324   return 0;
325 }
326
327 /* Implement the resume target_ops method.  */
328
329 static void
330 lynx_resume (struct thread_resume *resume_info, size_t n)
331 {
332   ptid_t ptid = resume_info[0].thread;
333   const int request
334     = (resume_info[0].kind == resume_step
335        ? (n == 1 ? PTRACE_SINGLESTEP_ONE : PTRACE_SINGLESTEP)
336        : PTRACE_CONT);
337   const int signal = resume_info[0].sig;
338
339   /* If given a minus_one_ptid, then try using the current_process'
340      private->last_wait_event_ptid.  On most LynxOS versions,
341      using any of the process' thread works well enough, but
342      LynxOS 178 is a little more sensitive, and triggers some
343      unexpected signals (Eg SIG61) when we resume the inferior
344      using a different thread.  */
345   if (ptid_equal (ptid, minus_one_ptid))
346     ptid = current_process()->priv->last_wait_event_ptid;
347
348   /* The ptid might still be minus_one_ptid; this can happen between
349      the moment we create the inferior or attach to a process, and
350      the moment we resume its execution for the first time.  It is
351      fine to use the current_thread's ptid in those cases.  */
352   if (ptid_equal (ptid, minus_one_ptid))
353     ptid = thread_to_gdb_id (current_thread);
354
355   regcache_invalidate_pid (ptid_get_pid (ptid));
356
357   errno = 0;
358   lynx_ptrace (request, ptid, 1, signal, 0);
359   if (errno)
360     perror_with_name ("ptrace");
361 }
362
363 /* Resume the execution of the given PTID.  */
364
365 static void
366 lynx_continue (ptid_t ptid)
367 {
368   struct thread_resume resume_info;
369
370   resume_info.thread = ptid;
371   resume_info.kind = resume_continue;
372   resume_info.sig = 0;
373
374   lynx_resume (&resume_info, 1);
375 }
376
377 /* A wrapper around waitpid that handles the various idiosyncrasies
378    of LynxOS' waitpid.  */
379
380 static int
381 lynx_waitpid (int pid, int *stat_loc)
382 {
383   int ret = 0;
384
385   while (1)
386     {
387       ret = waitpid (pid, stat_loc, WNOHANG);
388       if (ret < 0)
389         {
390           /* An ECHILD error is not indicative of a real problem.
391              It happens for instance while waiting for the inferior
392              to stop after attaching to it.  */
393           if (errno != ECHILD)
394             perror_with_name ("waitpid (WNOHANG)");
395         }
396       if (ret > 0)
397         break;
398       /* No event with WNOHANG.  See if there is one with WUNTRACED.  */
399       ret = waitpid (pid, stat_loc, WNOHANG | WUNTRACED);
400       if (ret < 0)
401         {
402           /* An ECHILD error is not indicative of a real problem.
403              It happens for instance while waiting for the inferior
404              to stop after attaching to it.  */
405           if (errno != ECHILD)
406             perror_with_name ("waitpid (WNOHANG|WUNTRACED)");
407         }
408       if (ret > 0)
409         break;
410       usleep (1000);
411     }
412   return ret;
413 }
414
415 /* Implement the wait target_ops method.  */
416
417 static ptid_t
418 lynx_wait_1 (ptid_t ptid, struct target_waitstatus *status, int options)
419 {
420   int pid;
421   int ret;
422   int wstat;
423   ptid_t new_ptid;
424
425   if (ptid_equal (ptid, minus_one_ptid))
426     pid = lynx_ptid_get_pid (thread_to_gdb_id (current_thread));
427   else
428     pid = BUILDPID (lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
429
430 retry:
431
432   ret = lynx_waitpid (pid, &wstat);
433   new_ptid = lynx_ptid_build (ret, ((union wait *) &wstat)->w_tid);
434   find_process_pid (ret)->priv->last_wait_event_ptid = new_ptid;
435
436   /* If this is a new thread, then add it now.  The reason why we do
437      this here instead of when handling new-thread events is because
438      we need to add the thread associated to the "main" thread - even
439      for non-threaded applications where the new-thread events are not
440      generated.  */
441   if (!find_thread_ptid (new_ptid))
442     {
443       lynx_debug ("New thread: (pid = %d, tid = %d)",
444                   lynx_ptid_get_pid (new_ptid), lynx_ptid_get_tid (new_ptid));
445       add_thread (new_ptid, NULL);
446     }
447
448   if (WIFSTOPPED (wstat))
449     {
450       status->kind = TARGET_WAITKIND_STOPPED;
451       status->value.integer = gdb_signal_from_host (WSTOPSIG (wstat));
452       lynx_debug ("process stopped with signal: %d",
453                   status->value.integer);
454     }
455   else if (WIFEXITED (wstat))
456     {
457       status->kind = TARGET_WAITKIND_EXITED;
458       status->value.integer = WEXITSTATUS (wstat);
459       lynx_debug ("process exited with code: %d", status->value.integer);
460     }
461   else if (WIFSIGNALED (wstat))
462     {
463       status->kind = TARGET_WAITKIND_SIGNALLED;
464       status->value.integer = gdb_signal_from_host (WTERMSIG (wstat));
465       lynx_debug ("process terminated with code: %d",
466                   status->value.integer);
467     }
468   else
469     {
470       /* Not sure what happened if we get here, or whether we can
471          in fact get here.  But if we do, handle the event the best
472          we can.  */
473       status->kind = TARGET_WAITKIND_STOPPED;
474       status->value.integer = gdb_signal_from_host (0);
475       lynx_debug ("unknown event ????");
476     }
477
478   /* SIGTRAP events are generated for situations other than single-step/
479      breakpoint events (Eg. new-thread events).  Handle those other types
480      of events, and resume the execution if necessary.  */
481   if (status->kind == TARGET_WAITKIND_STOPPED
482       && status->value.integer == GDB_SIGNAL_TRAP)
483     {
484       const int realsig = lynx_ptrace (PTRACE_GETTRACESIG, new_ptid, 0, 0, 0);
485
486       lynx_debug ("(realsig = %d)", realsig);
487       switch (realsig)
488         {
489           case SIGNEWTHREAD:
490             /* We just added the new thread above.  No need to do anything
491                further.  Just resume the execution again.  */
492             lynx_continue (new_ptid);
493             goto retry;
494
495           case SIGTHREADEXIT:
496             remove_thread (find_thread_ptid (new_ptid));
497             lynx_continue (new_ptid);
498             goto retry;
499         }
500     }
501
502   return new_ptid;
503 }
504
505 /* A wrapper around lynx_wait_1 that also prints debug traces when
506    such debug traces have been activated.  */
507
508 static ptid_t
509 lynx_wait (ptid_t ptid, struct target_waitstatus *status, int options)
510 {
511   ptid_t new_ptid;
512
513   lynx_debug ("lynx_wait (pid = %d, tid = %ld)",
514               lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
515   new_ptid = lynx_wait_1 (ptid, status, options);
516   lynx_debug ("          -> (pid=%d, tid=%ld, status->kind = %d)",
517               lynx_ptid_get_pid (new_ptid), lynx_ptid_get_tid (new_ptid),
518               status->kind);
519   return new_ptid;
520 }
521
522 /* Implement the kill target_ops method.  */
523
524 static int
525 lynx_kill (int pid)
526 {
527   ptid_t ptid = lynx_ptid_build (pid, 0);
528   struct target_waitstatus status;
529   struct process_info *process;
530
531   process = find_process_pid (pid);
532   if (process == NULL)
533     return -1;
534
535   lynx_ptrace (PTRACE_KILL, ptid, 0, 0, 0);
536   lynx_wait (ptid, &status, 0);
537   the_target->mourn (process);
538   return 0;
539 }
540
541 /* Implement the detach target_ops method.  */
542
543 static int
544 lynx_detach (int pid)
545 {
546   ptid_t ptid = lynx_ptid_build (pid, 0);
547   struct process_info *process;
548
549   process = find_process_pid (pid);
550   if (process == NULL)
551     return -1;
552
553   lynx_ptrace (PTRACE_DETACH, ptid, 0, 0, 0);
554   the_target->mourn (process);
555   return 0;
556 }
557
558 /* A callback for find_inferior which removes from the thread list
559    all threads belonging to process PROC.  */
560
561 static int
562 lynx_delete_thread_callback (struct inferior_list_entry *entry, void *proc)
563 {
564   struct process_info *process = (struct process_info *) proc;
565
566   if (ptid_get_pid (entry->id) == pid_of (process))
567     {
568       struct thread_info *thr = find_thread_ptid (entry->id);
569
570       remove_thread (thr);
571     }
572
573   return 0;
574 }
575
576 /* Implement the mourn target_ops method.  */
577
578 static void
579 lynx_mourn (struct process_info *proc)
580 {
581   find_inferior (&all_threads, lynx_delete_thread_callback, proc);
582
583   /* Free our private data.  */
584   free (proc->priv);
585   proc->priv = NULL;
586
587   remove_process (proc);
588 }
589
590 /* Implement the join target_ops method.  */
591
592 static void
593 lynx_join (int pid)
594 {
595   /* The PTRACE_DETACH is sufficient to detach from the process.
596      So no need to do anything extra.  */
597 }
598
599 /* Implement the thread_alive target_ops method.  */
600
601 static int
602 lynx_thread_alive (ptid_t ptid)
603 {
604   /* The list of threads is updated at the end of each wait, so it
605      should be up to date.  No need to re-fetch it.  */
606   return (find_thread_ptid (ptid) != NULL);
607 }
608
609 /* Implement the fetch_registers target_ops method.  */
610
611 static void
612 lynx_fetch_registers (struct regcache *regcache, int regno)
613 {
614   struct lynx_regset_info *regset = lynx_target_regsets;
615   ptid_t inferior_ptid = thread_to_gdb_id (current_thread);
616
617   lynx_debug ("lynx_fetch_registers (regno = %d)", regno);
618
619   while (regset->size >= 0)
620     {
621       char *buf;
622       int res;
623
624       buf = xmalloc (regset->size);
625       res = lynx_ptrace (regset->get_request, inferior_ptid, (int) buf, 0, 0);
626       if (res < 0)
627         perror ("ptrace");
628       regset->store_function (regcache, buf);
629       free (buf);
630       regset++;
631     }
632 }
633
634 /* Implement the store_registers target_ops method.  */
635
636 static void
637 lynx_store_registers (struct regcache *regcache, int regno)
638 {
639   struct lynx_regset_info *regset = lynx_target_regsets;
640   ptid_t inferior_ptid = thread_to_gdb_id (current_thread);
641
642   lynx_debug ("lynx_store_registers (regno = %d)", regno);
643
644   while (regset->size >= 0)
645     {
646       char *buf;
647       int res;
648
649       buf = xmalloc (regset->size);
650       res = lynx_ptrace (regset->get_request, inferior_ptid, (int) buf, 0, 0);
651       if (res == 0)
652         {
653           /* Then overlay our cached registers on that.  */
654           regset->fill_function (regcache, buf);
655           /* Only now do we write the register set.  */
656           res = lynx_ptrace (regset->set_request, inferior_ptid, (int) buf,
657                              0, 0);
658         }
659       if (res < 0)
660         perror ("ptrace");
661       free (buf);
662       regset++;
663     }
664 }
665
666 /* Implement the read_memory target_ops method.  */
667
668 static int
669 lynx_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
670 {
671   /* On LynxOS, memory reads needs to be performed in chunks the size
672      of int types, and they should also be aligned accordingly.  */
673   int buf;
674   const int xfer_size = sizeof (buf);
675   CORE_ADDR addr = memaddr & -(CORE_ADDR) xfer_size;
676   ptid_t inferior_ptid = thread_to_gdb_id (current_thread);
677
678   while (addr < memaddr + len)
679     {
680       int skip = 0;
681       int truncate = 0;
682
683       errno = 0;
684       if (addr < memaddr)
685         skip = memaddr - addr;
686       if (addr + xfer_size > memaddr + len)
687         truncate = addr + xfer_size - memaddr - len;
688       buf = lynx_ptrace (PTRACE_PEEKTEXT, inferior_ptid, addr, 0, 0);
689       if (errno)
690         return errno;
691       memcpy (myaddr + (addr - memaddr) + skip, (gdb_byte *) &buf + skip,
692               xfer_size - skip - truncate);
693       addr += xfer_size;
694     }
695
696   return 0;
697 }
698
699 /* Implement the write_memory target_ops method.  */
700
701 static int
702 lynx_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
703 {
704   /* On LynxOS, memory writes needs to be performed in chunks the size
705      of int types, and they should also be aligned accordingly.  */
706   int buf;
707   const int xfer_size = sizeof (buf);
708   CORE_ADDR addr = memaddr & -(CORE_ADDR) xfer_size;
709   ptid_t inferior_ptid = thread_to_gdb_id (current_thread);
710
711   while (addr < memaddr + len)
712     {
713       int skip = 0;
714       int truncate = 0;
715
716       if (addr < memaddr)
717         skip = memaddr - addr;
718       if (addr + xfer_size > memaddr + len)
719         truncate = addr + xfer_size - memaddr - len;
720       if (skip > 0 || truncate > 0)
721         {
722           /* We need to read the memory at this address in order to preserve
723              the data that we are not overwriting.  */
724           lynx_read_memory (addr, (unsigned char *) &buf, xfer_size);
725           if (errno)
726             return errno;
727         }
728       memcpy ((gdb_byte *) &buf + skip, myaddr + (addr - memaddr) + skip,
729               xfer_size - skip - truncate);
730       errno = 0;
731       lynx_ptrace (PTRACE_POKETEXT, inferior_ptid, addr, buf, 0);
732       if (errno)
733         return errno;
734       addr += xfer_size;
735     }
736
737   return 0;
738 }
739
740 /* Implement the kill_request target_ops method.  */
741
742 static void
743 lynx_request_interrupt (void)
744 {
745   ptid_t inferior_ptid = thread_to_gdb_id (get_first_thread ());
746
747   kill (lynx_ptid_get_pid (inferior_ptid), SIGINT);
748 }
749
750 /* The LynxOS target_ops vector.  */
751
752 static struct target_ops lynx_target_ops = {
753   lynx_create_inferior,
754   NULL,  /* post_create_inferior */
755   lynx_attach,
756   lynx_kill,
757   lynx_detach,
758   lynx_mourn,
759   lynx_join,
760   lynx_thread_alive,
761   lynx_resume,
762   lynx_wait,
763   lynx_fetch_registers,
764   lynx_store_registers,
765   NULL,  /* prepare_to_access_memory */
766   NULL,  /* done_accessing_memory */
767   lynx_read_memory,
768   lynx_write_memory,
769   NULL,  /* look_up_symbols */
770   lynx_request_interrupt,
771   NULL,  /* read_auxv */
772   NULL,  /* supports_z_point_type */
773   NULL,  /* insert_point */
774   NULL,  /* remove_point */
775   NULL,  /* stopped_by_sw_breakpoint */
776   NULL,  /* supports_stopped_by_sw_breakpoint */
777   NULL,  /* stopped_by_hw_breakpoint */
778   NULL,  /* supports_stopped_by_hw_breakpoint */
779   target_can_do_hardware_single_step,
780   NULL,  /* stopped_by_watchpoint */
781   NULL,  /* stopped_data_address */
782   NULL,  /* read_offsets */
783   NULL,  /* get_tls_address */
784   NULL,  /* qxfer_spu */
785   NULL,  /* hostio_last_error */
786   NULL,  /* qxfer_osdata */
787   NULL,  /* qxfer_siginfo */
788   NULL,  /* supports_non_stop */
789   NULL,  /* async */
790   NULL,  /* start_non_stop */
791   NULL,  /* supports_multi_process */
792   NULL,  /* supports_fork_events */
793   NULL,  /* supports_vfork_events */
794   NULL,  /* supports_exec_events */
795   NULL,  /* handle_new_gdb_connection */
796   NULL,  /* handle_monitor_command */
797 };
798
799 void
800 initialize_low (void)
801 {
802   set_target_ops (&lynx_target_ops);
803   the_low_target.arch_setup ();
804 }
805