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