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