aboutsummaryrefslogtreecommitdiff
path: root/gdb/gdbserver
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/gdbserver')
-rw-r--r--gdb/gdbserver/ChangeLog10
-rw-r--r--gdb/gdbserver/Makefile.in4
-rwxr-xr-xgdb/gdbserver/configure2
-rw-r--r--gdb/gdbserver/configure.ac2
-rw-r--r--gdb/gdbserver/configure.srv4
-rw-r--r--gdb/gdbserver/lynx-low.c810
-rw-r--r--gdb/gdbserver/lynx-low.h52
-rw-r--r--gdb/gdbserver/lynx-ppc-low.c186
8 files changed, 1070 insertions, 0 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index e6ab46f..37f0686 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,5 +1,15 @@
2010-09-01 Joel Brobecker <brobecker@adacore.com>
+ * gdbserver/lynx-low.c, gdbserver/lynx-low.h,
+ gdbserver/lynx-ppc-low.c: New files.
+ * Makefile.in (lynx_low_h): New variable.
+ (lynx-low.o, lynx-ppc-low.o): New rules.
+ * configure.ac: On LynxOS, link with -lnetinet.
+ * configure.srv: Add handling of powerpc-*-lynxos* targets.
+ * configure: regenerate.
+
+2010-09-01 Joel Brobecker <brobecker@adacore.com>
+
* Makefile.in (vasprintf.o, vsnprintf.o): New rules.
* configure.ac: Add check for vasprintf and vsnprintf.
* configure, config.in: Regenerate.
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index a12d895..fa6c102 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -303,6 +303,8 @@ server_h = $(srcdir)/server.h $(regcache_h) config.h $(srcdir)/target.h \
linux_low_h = $(srcdir)/linux-low.h
+lynx_low_h = $(srcdir)/lynx-low.h $(srcdir)/server.h
+
nto_low_h = $(srcdir)/nto-low.h
UST_CFLAGS = $(ustinc) -DCONFIG_UST_GDB_INTEGRATION
@@ -390,6 +392,8 @@ linux-x86-low.o: linux-x86-low.c $(linux_low_h) $(server_h) \
$(gdb_proc_service_h) $(i386_low_h)
linux-xtensa-low.o: linux-xtensa-low.c xtensa-xtregs.c $(linux_low_h) $(server_h)
+lynx-low.o: lynx-low.c $(server_h) $(target_h) $(lynx_low_h)
+lynx-ppc-low.o: lynx-ppc-low.c $(server_h) $(lynx_low_h)
nto-low.o: nto-low.c $(server_h) $(nto_low_h)
nto-x86-low.o: nto-x86-low.c $(server_h) $(nto_low_h) $(regdef_h) $(regcache_h)
diff --git a/gdb/gdbserver/configure b/gdb/gdbserver/configure
index 957cbc8..556cbb3 100755
--- a/gdb/gdbserver/configure
+++ b/gdb/gdbserver/configure
@@ -4439,6 +4439,8 @@ elif test "${srv_mingw}" = "yes"; then
LIBS="$LIBS -lws2_32"
elif test "${srv_qnx}" = "yes"; then
LIBS="$LIBS -lsocket"
+elif test "${srv_lynxos}" = "yes"; then
+ LIBS="$LIBS -lnetinet"
fi
if test "${srv_mingw}" = "yes"; then
diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac
index c61ab54..85b1a7b 100644
--- a/gdb/gdbserver/configure.ac
+++ b/gdb/gdbserver/configure.ac
@@ -212,6 +212,8 @@ elif test "${srv_mingw}" = "yes"; then
LIBS="$LIBS -lws2_32"
elif test "${srv_qnx}" = "yes"; then
LIBS="$LIBS -lsocket"
+elif test "${srv_lynxos}" = "yes"; then
+ LIBS="$LIBS -lnetinet"
fi
if test "${srv_mingw}" = "yes"; then
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index 99187a5..8a14759 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -200,6 +200,10 @@ case "${target}" in
srv_linux_regsets=yes
srv_linux_thread_db=yes
;;
+ powerpc-*-lynxos*) srv_regobj="powerpc-32.o"
+ srv_tgtobj="lynx-low.o lynx-ppc-low.o"
+ srv_lynxos=yes
+ ;;
s390*-*-linux*) srv_regobj="s390-linux32.o"
srv_regobj="${srv_regobj} s390-linux64.o"
srv_regobj="${srv_regobj} s390x-linux64.o"
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
new file mode 100644
index 0000000..7a949c0
--- /dev/null
+++ b/gdb/gdbserver/lynx-low.c
@@ -0,0 +1,810 @@
+/* Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "target.h"
+#include "lynx-low.h"
+
+#include <limits.h>
+#include <ptrace.h>
+#include <sys/piddef.h> /* Provides PIDGET, TIDGET, BUILDPID, etc. */
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+int using_threads = 1;
+
+/* Print a debug trace on standard output if debug_threads is set. */
+
+static void
+lynx_debug (char *string, ...)
+{
+ va_list args;
+
+ if (!debug_threads)
+ return;
+
+ va_start (args, string);
+ fprintf (stderr, "DEBUG(lynx): ");
+ vfprintf (stderr, string, args);
+ fprintf (stderr, "\n");
+ va_end (args);
+}
+
+/* Build a ptid_t given a PID and a LynxOS TID. */
+
+static ptid_t
+lynx_ptid_build (int pid, long tid)
+{
+ /* brobecker/2010-06-21: It looks like the LWP field in ptids
+ should be distinct for each thread (see write_ptid where it
+ writes the thread ID from the LWP). So instead of storing
+ the LynxOS tid in the tid field of the ptid, we store it in
+ the lwp field. */
+ return ptid_build (pid, tid, 0);
+}
+
+/* Return the process ID of the given PTID.
+
+ This function has little reason to exist, it's just a wrapper around
+ ptid_get_pid. But since we have a getter function for the lynxos
+ ptid, it feels cleaner to have a getter for the pid as well. */
+
+static int
+lynx_ptid_get_pid (ptid_t ptid)
+{
+ return ptid_get_pid (ptid);
+}
+
+/* Return the LynxOS tid of the given PTID. */
+
+static long
+lynx_ptid_get_tid (ptid_t ptid)
+{
+ /* See lynx_ptid_build: The LynxOS tid is stored inside the lwp field
+ of the ptid. */
+ return ptid_get_lwp (ptid);
+}
+
+/* For a given PTID, return the associated PID as known by the LynxOS
+ ptrace layer. */
+
+static int
+lynx_ptrace_pid_from_ptid (ptid_t ptid)
+{
+ return BUILDPID (lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
+}
+
+/* Return a string image of the ptrace REQUEST number. */
+
+static char *
+ptrace_request_to_str (int request)
+{
+ switch (request)
+ {
+ case PTRACE_TRACEME:
+ return "PTRACE_TRACEME";
+ break;
+ case PTRACE_PEEKTEXT:
+ return "PTRACE_PEEKTEXT";
+ break;
+ case PTRACE_PEEKDATA:
+ return "PTRACE_PEEKDATA";
+ break;
+ case PTRACE_PEEKUSER:
+ return "PTRACE_PEEKUSER";
+ break;
+ case PTRACE_POKETEXT:
+ return "PTRACE_POKETEXT";
+ break;
+ case PTRACE_POKEDATA:
+ return "PTRACE_POKEDATA";
+ break;
+ case PTRACE_POKEUSER:
+ return "PTRACE_POKEUSER";
+ break;
+ case PTRACE_CONT:
+ return "PTRACE_CONT";
+ break;
+ case PTRACE_KILL:
+ return "PTRACE_KILL";
+ break;
+ case PTRACE_SINGLESTEP:
+ return "PTRACE_SINGLESTEP";
+ break;
+ case PTRACE_ATTACH:
+ return "PTRACE_ATTACH";
+ break;
+ case PTRACE_DETACH:
+ return "PTRACE_DETACH";
+ break;
+ case PTRACE_GETREGS:
+ return "PTRACE_GETREGS";
+ break;
+ case PTRACE_SETREGS:
+ return "PTRACE_SETREGS";
+ break;
+ case PTRACE_GETFPREGS:
+ return "PTRACE_GETFPREGS";
+ break;
+ case PTRACE_SETFPREGS:
+ return "PTRACE_SETFPREGS";
+ break;
+ case PTRACE_READDATA:
+ return "PTRACE_READDATA";
+ break;
+ case PTRACE_WRITEDATA:
+ return "PTRACE_WRITEDATA";
+ break;
+ case PTRACE_READTEXT:
+ return "PTRACE_READTEXT";
+ break;
+ case PTRACE_WRITETEXT:
+ return "PTRACE_WRITETEXT";
+ break;
+ case PTRACE_GETFPAREGS:
+ return "PTRACE_GETFPAREGS";
+ break;
+ case PTRACE_SETFPAREGS:
+ return "PTRACE_SETFPAREGS";
+ break;
+ case PTRACE_GETWINDOW:
+ return "PTRACE_GETWINDOW";
+ break;
+ case PTRACE_SETWINDOW:
+ return "PTRACE_SETWINDOW";
+ break;
+ case PTRACE_SYSCALL:
+ return "PTRACE_SYSCALL";
+ break;
+ case PTRACE_DUMPCORE:
+ return "PTRACE_DUMPCORE";
+ break;
+ case PTRACE_SETWRBKPT:
+ return "PTRACE_SETWRBKPT";
+ break;
+ case PTRACE_SETACBKPT:
+ return "PTRACE_SETACBKPT";
+ break;
+ case PTRACE_CLRBKPT:
+ return "PTRACE_CLRBKPT";
+ break;
+ case PTRACE_GET_UCODE:
+ return "PTRACE_GET_UCODE";
+ break;
+#ifdef PT_READ_GPR
+ case PT_READ_GPR:
+ return "PT_READ_GPR";
+ break;
+#endif
+#ifdef PT_WRITE_GPR
+ case PT_WRITE_GPR:
+ return "PT_WRITE_GPR";
+ break;
+#endif
+#ifdef PT_READ_FPR
+ case PT_READ_FPR:
+ return "PT_READ_FPR";
+ break;
+#endif
+#ifdef PT_WRITE_FPR
+ case PT_WRITE_FPR:
+ return "PT_WRITE_FPR";
+ break;
+#endif
+#ifdef PTRACE_GETVECREGS
+ case PTRACE_GETVECREGS:
+ return "PTRACE_GETVECREGS";
+ break;
+#endif
+#ifdef PTRACE_SETVECREGS
+ case PTRACE_SETVECREGS:
+ return "PTRACE_SETVECREGS";
+ break;
+#endif
+#ifdef PT_READ_VPR
+ case PT_READ_VPR:
+ return "PT_READ_VPR";
+ break;
+#endif
+#ifdef PT_WRITE_VPR
+ case PT_WRITE_VPR:
+ return "PT_WRITE_VPR";
+ break;
+#endif
+#ifdef PTRACE_PEEKUSP
+ case PTRACE_PEEKUSP:
+ return "PTRACE_PEEKUSP";
+ break;
+#endif
+#ifdef PTRACE_POKEUSP
+ case PTRACE_POKEUSP:
+ return "PTRACE_POKEUSP";
+ break;
+#endif
+ case PTRACE_PEEKTHREAD:
+ return "PTRACE_PEEKTHREAD";
+ break;
+ case PTRACE_THREADUSER:
+ return "PTRACE_THREADUSER";
+ break;
+ case PTRACE_FPREAD:
+ return "PTRACE_FPREAD";
+ break;
+ case PTRACE_FPWRITE:
+ return "PTRACE_FPWRITE";
+ break;
+ case PTRACE_SETSIG:
+ return "PTRACE_SETSIG";
+ break;
+ case PTRACE_CONT_ONE:
+ return "PTRACE_CONT_ONE";
+ break;
+ case PTRACE_KILL_ONE:
+ return "PTRACE_KILL_ONE";
+ break;
+ case PTRACE_SINGLESTEP_ONE:
+ return "PTRACE_SINGLESTEP_ONE";
+ break;
+ case PTRACE_GETLOADINFO:
+ return "PTRACE_GETLOADINFO";
+ break;
+ case PTRACE_GETTHREADLIST:
+ return "PTRACE_GETTHREADLIST";
+ break;
+ case PTRACE_POSTSYSCALL:
+ return "PTRACE_POSTSYSCALL";
+ break;
+ case PTRACE_USE_SIGEXECED:
+ return "PTRACE_USE_SIGEXECED";
+ break;
+ case PTRACE_GETTRACESIG:
+ return "PTRACE_GETTRACESIG";
+ break;
+ case PTRACE_GETCWD:
+ return "PTRACE_GETCWD";
+ break;
+ case PTRACE_TRAPFORK:
+ return "PTRACE_TRAPFORK";
+ break;
+ case PTRACE_GETCHILDPID:
+ return "PTRACE_GETCHILDPID";
+ break;
+ case PTRACE_SYSCALL_ONE:
+ return "PTRACE_SYSCALL_ONE";
+ break;
+ case PTRACE_SIGMASK:
+ return "PTRACE_SIGMASK";
+ break;
+ case PTRACE_GETIWD:
+ return "PTRACE_GETIWD";
+ break;
+ case PTRACE_GETEXECFILE:
+ return "PTRACE_GETEXECFILE";
+ break;
+ }
+ return "<unknown-request>";
+}
+
+/* A wrapper around ptrace that allows us to print debug traces of
+ ptrace calls if debug traces are activated. */
+
+static int
+lynx_ptrace (int request, ptid_t ptid, int addr, int data, int addr2)
+{
+ int result;
+ const int pid = lynx_ptrace_pid_from_ptid (ptid);
+ int saved_errno;
+
+ if (debug_threads)
+ fprintf (stderr, "PTRACE (%s, pid=%d(pid=%d, tid=%d), addr=0x%x, "
+ "data=0x%x, addr2=0x%x)",
+ ptrace_request_to_str (request), pid, PIDGET (pid), TIDGET (pid),
+ addr, data, addr2);
+ result = ptrace (request, pid, addr, data, addr2);
+ saved_errno = errno;
+ if (debug_threads)
+ fprintf (stderr, " -> %d (=0x%x)\n", result, result);
+
+ errno = saved_errno;
+ return result;
+}
+
+/* Implement the create_inferior method of the target_ops vector. */
+
+static int
+lynx_create_inferior (char *program, char **allargs)
+{
+ struct process_info *new_process;
+ int pid;
+
+ lynx_debug ("lynx_create_inferior ()");
+
+ pid = fork ();
+ if (pid < 0)
+ perror_with_name ("fork");
+
+ if (pid == 0)
+ {
+ int pgrp;
+
+ /* Switch child to its own process group so that signals won't
+ directly affect gdbserver. */
+ pgrp = getpid();
+ setpgid (0, pgrp);
+ ioctl (0, TIOCSPGRP, &pgrp);
+ lynx_ptrace (PTRACE_TRACEME, null_ptid, 0, 0, 0);
+ execv (program, allargs);
+ fprintf (stderr, "Cannot exec %s: %s.\n", program, strerror (errno));
+ fflush (stderr);
+ _exit (0177);
+ }
+
+ new_process = add_process (pid, 0);
+ /* Do not add the process thread just yet, as we do not know its tid.
+ We will add it later, during the wait for the STOP event corresponding
+ to the lynx_ptrace (PTRACE_TRACEME) call above. */
+ return pid;
+}
+
+/* Implement the attach target_ops method. */
+
+static int
+lynx_attach (unsigned long pid)
+{
+ struct process_info *new_process;
+ ptid_t ptid = lynx_ptid_build (pid, 0);
+
+ if (lynx_ptrace (PTRACE_ATTACH, ptid, 0, 0, 0) != 0)
+ error ("Cannot attach to process %lu: %s (%d)\n", pid,
+ strerror (errno), errno);
+
+ new_process = add_process (pid, 1);
+ add_thread (ptid, NULL);
+
+ return 0;
+}
+
+/* Implement the resume target_ops method. */
+
+static void
+lynx_resume (struct thread_resume *resume_info, size_t n)
+{
+ ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
+ /* FIXME: Assume for now that n == 1. */
+ const int request = (resume_info[0].kind == resume_step
+ ? PTRACE_SINGLESTEP : PTRACE_CONT);
+ const int signal = resume_info[0].sig;
+ int ret;
+
+ regcache_invalidate ();
+ ret = lynx_ptrace (request, inferior_ptid, 1, signal, 0);
+}
+
+/* Resume the execution of the given PTID. */
+
+static void
+lynx_continue (ptid_t ptid)
+{
+ struct thread_resume resume_info;
+
+ resume_info.thread = ptid;
+ resume_info.kind = resume_continue;
+ resume_info.sig = 0;
+
+ lynx_resume (&resume_info, 1);
+}
+
+/* Remove all inferiors and associated threads. */
+
+static void
+lynx_clear_inferiors (void)
+{
+ /* We do not use private data, so nothing much to do except calling
+ clear_inferiors. */
+ clear_inferiors ();
+}
+
+/* A wrapper around waitpid that handles the various idiosyncrasies
+ of LynxOS' waitpid. */
+
+static int
+lynx_waitpid (int pid, int *stat_loc)
+{
+ int ret = 0;
+
+ while (1)
+ {
+ ret = waitpid (pid, stat_loc, WNOHANG);
+ if (ret < 0)
+ {
+ /* An ECHILD error is not indicative of a real problem.
+ It happens for instance while waiting for the inferior
+ to stop after attaching to it. */
+ if (errno != ECHILD)
+ perror_with_name ("waitpid (WNOHANG)");
+ }
+ if (ret > 0)
+ break;
+ /* No event with WNOHANG. See if there is one with WUNTRACED. */
+ ret = waitpid (pid, stat_loc, WNOHANG | WUNTRACED);
+ if (ret < 0)
+ {
+ /* An ECHILD error is not indicative of a real problem.
+ It happens for instance while waiting for the inferior
+ to stop after attaching to it. */
+ if (errno != ECHILD)
+ perror_with_name ("waitpid (WNOHANG|WUNTRACED)");
+ }
+ if (ret > 0)
+ break;
+ usleep (1000);
+ }
+ return ret;
+}
+
+/* Implement the wait target_ops method. */
+
+static ptid_t
+lynx_wait_1 (ptid_t ptid, struct target_waitstatus *status, int options)
+{
+ int pid;
+ int ret;
+ int wstat;
+ ptid_t new_ptid;
+
+ if (ptid_equal (ptid, minus_one_ptid))
+ pid = lynx_ptid_get_pid (thread_to_gdb_id (current_inferior));
+ else
+ pid = BUILDPID (lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
+
+retry:
+
+ ret = lynx_waitpid (pid, &wstat);
+ new_ptid = lynx_ptid_build (ret, ((union wait *) &wstat)->w_tid);
+
+ /* If this is a new thread, then add it now. The reason why we do
+ this here instead of when handling new-thread events is because
+ we need to add the thread associated to the "main" thread - even
+ for non-threaded applications where the new-thread events are not
+ generated. */
+ if (!find_thread_ptid (new_ptid))
+ add_thread (new_ptid, NULL);
+
+ if (WIFSTOPPED (wstat))
+ {
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.integer = target_signal_from_host (WSTOPSIG (wstat));
+ lynx_debug ("process stopped with signal: %d",
+ status->value.integer);
+ }
+ else if (WIFEXITED (wstat))
+ {
+ status->kind = TARGET_WAITKIND_EXITED;
+ status->value.integer = WEXITSTATUS (wstat);
+ lynx_debug ("process exited with code: %d", status->value.integer);
+ }
+ else if (WIFSIGNALED (wstat))
+ {
+ status->kind = TARGET_WAITKIND_SIGNALLED;
+ status->value.integer = target_signal_from_host (WTERMSIG (wstat));
+ lynx_debug ("process terminated with code: %d",
+ status->value.integer);
+ }
+ else
+ {
+ /* Not sure what happened if we get here, or whether we can
+ in fact get here. But if we do, handle the event the best
+ we can. */
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.integer = target_signal_from_host (0);
+ lynx_debug ("unknown event ????");
+ }
+
+ /* SIGTRAP events are generated for situations other than single-step/
+ breakpoint events (Eg. new-thread events). Handle those other types
+ of events, and resume the execution if necessary. */
+ if (status->kind == TARGET_WAITKIND_STOPPED
+ && status->value.integer == TARGET_SIGNAL_TRAP)
+ {
+ const int realsig = lynx_ptrace (PTRACE_GETTRACESIG, new_ptid, 0, 0, 0);
+
+ lynx_debug ("(realsig = %d)", realsig);
+ switch (realsig)
+ {
+ case SIGNEWTHREAD:
+ /* We just added the new thread above. No need to do anything
+ further. Just resume the execution again. */
+ lynx_continue (ptid);
+ goto retry;
+
+ case SIGTHREADEXIT:
+ remove_thread (find_thread_ptid (new_ptid));
+ lynx_continue (ptid);
+ goto retry;
+ }
+ }
+
+ return new_ptid;
+}
+
+/* A wrapper around lynx_wait_1 that also prints debug traces when
+ such debug traces have been activated. */
+
+static ptid_t
+lynx_wait (ptid_t ptid, struct target_waitstatus *status, int options)
+{
+ ptid_t new_ptid;
+
+ lynx_debug ("lynx_wait (pid = %d, tid = %ld)",
+ lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
+ new_ptid = lynx_wait_1 (ptid, status, options);
+ lynx_debug (" -> (pid=%d, tid=%ld, status->kind = %d)",
+ lynx_ptid_get_pid (new_ptid), lynx_ptid_get_tid (new_ptid),
+ status->kind);
+ return new_ptid;
+}
+
+/* Implement the kill target_ops method. */
+
+static int
+lynx_kill (int pid)
+{
+ ptid_t ptid = lynx_ptid_build (pid, 0);
+ struct target_waitstatus status;
+ struct process_info *process;
+
+ process = find_process_pid (pid);
+ if (process == NULL)
+ return -1;
+
+ lynx_ptrace (PTRACE_KILL, ptid, 0, 0, 0);
+ lynx_wait (ptid, &status, 0);
+ the_target->mourn (process);
+ return 0;
+}
+
+/* Implement the detach target_ops method. */
+
+static int
+lynx_detach (int pid)
+{
+ ptid_t ptid = lynx_ptid_build (pid, 0);
+ struct process_info *process;
+
+ process = find_process_pid (pid);
+ if (process == NULL)
+ return -1;
+
+ lynx_ptrace (PTRACE_DETACH, ptid, 0, 0, 0);
+ the_target->mourn (process);
+ return 0;
+}
+
+/* Implement the mourn target_ops method. */
+
+static void
+lynx_mourn (struct process_info *proc)
+{
+ lynx_clear_inferiors ();
+}
+
+/* Implement the join target_ops method. */
+
+static void
+lynx_join (int pid)
+{
+ /* The PTRACE_DETACH is sufficient to detach from the process.
+ So no need to do anything extra. */
+}
+
+/* Implement the thread_alive target_ops method. */
+
+static int
+lynx_thread_alive (ptid_t ptid)
+{
+ /* The list of threads is updated at the end of each wait, so it
+ should be up to date. No need to re-fetch it. */
+ return (find_thread_ptid (ptid) != NULL);
+}
+
+/* Implement the fetch_registers target_ops method. */
+
+static void
+lynx_fetch_registers (struct regcache *regcache, int regno)
+{
+ struct lynx_regset_info *regset = lynx_target_regsets;
+ ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
+
+ lynx_debug ("lynx_fetch_registers (regno = %d)", regno);
+
+ while (regset->size >= 0)
+ {
+ char *buf;
+ int res;
+
+ buf = xmalloc (regset->size);
+ res = lynx_ptrace (regset->get_request, inferior_ptid, (int) buf, 0, 0);
+ if (res < 0)
+ perror ("ptrace");
+ regset->store_function (regcache, buf);
+ free (buf);
+ regset++;
+ }
+}
+
+/* Implement the store_registers target_ops method. */
+
+static void
+lynx_store_registers (struct regcache *regcache, int regno)
+{
+ struct lynx_regset_info *regset = lynx_target_regsets;
+ ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
+
+ lynx_debug ("lynx_store_registers (regno = %d)", regno);
+
+ while (regset->size >= 0)
+ {
+ char *buf;
+ int res;
+
+ buf = xmalloc (regset->size);
+ res = lynx_ptrace (regset->get_request, inferior_ptid, (int) buf, 0, 0);
+ if (res == 0)
+ {
+ /* Then overlay our cached registers on that. */
+ regset->fill_function (regcache, buf);
+ /* Only now do we write the register set. */
+ res = lynx_ptrace (regset->set_request, inferior_ptid, (int) buf,
+ 0, 0);
+ }
+ if (res < 0)
+ perror ("ptrace");
+ free (buf);
+ regset++;
+ }
+}
+
+/* Implement the read_memory target_ops method. */
+
+static int
+lynx_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ /* On LynxOS, memory reads needs to be performed in chunks the size
+ of int types, and they should also be aligned accordingly. */
+ int buf;
+ const int xfer_size = sizeof (buf);
+ CORE_ADDR addr = memaddr & -(CORE_ADDR) xfer_size;
+ ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
+
+ while (addr < memaddr + len)
+ {
+ int skip = 0;
+ int truncate = 0;
+
+ errno = 0;
+ if (addr < memaddr)
+ skip = memaddr - addr;
+ if (addr + xfer_size > memaddr + len)
+ truncate = addr + xfer_size - memaddr - len;
+ buf = lynx_ptrace (PTRACE_PEEKTEXT, inferior_ptid, addr, 0, 0);
+ if (errno)
+ return errno;
+ memcpy (myaddr + (addr - memaddr) + skip, (gdb_byte *) &buf + skip,
+ xfer_size - skip - truncate);
+ addr += xfer_size;
+ }
+
+ return 0;
+}
+
+/* Implement the write_memory target_ops method. */
+
+static int
+lynx_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
+{
+ /* On LynxOS, memory writes needs to be performed in chunks the size
+ of int types, and they should also be aligned accordingly. */
+ int buf;
+ const int xfer_size = sizeof (buf);
+ CORE_ADDR addr = memaddr & -(CORE_ADDR) xfer_size;
+ ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
+
+ while (addr < memaddr + len)
+ {
+ int skip = 0;
+ int truncate = 0;
+
+ if (addr < memaddr)
+ skip = memaddr - addr;
+ if (addr + xfer_size > memaddr + len)
+ truncate = addr + xfer_size - memaddr - len;
+ if (skip > 0 || truncate > 0)
+ /* We need to read the memory at this address in order to preserve
+ the data that we are not overwriting. */
+ lynx_read_memory (addr, (unsigned char *) &buf, xfer_size);
+ if (errno)
+ return errno;
+ memcpy ((gdb_byte *) &buf + skip, myaddr + (addr - memaddr) + skip,
+ xfer_size - skip - truncate);
+ errno = 0;
+ lynx_ptrace (PTRACE_POKETEXT, inferior_ptid, addr, buf, 0);
+ if (errno)
+ return errno;
+ addr += xfer_size;
+ }
+
+ return 0;
+}
+
+/* Implement the kill_request target_ops method. */
+
+static void
+lynx_request_interrupt (void)
+{
+ ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
+
+ kill (lynx_ptid_get_pid (inferior_ptid), SIGINT);
+}
+
+/* The LynxOS target_ops vector. */
+
+static struct target_ops lynx_target_ops = {
+ lynx_create_inferior,
+ lynx_attach,
+ lynx_kill,
+ lynx_detach,
+ lynx_mourn,
+ lynx_join,
+ lynx_thread_alive,
+ lynx_resume,
+ lynx_wait,
+ lynx_fetch_registers,
+ lynx_store_registers,
+ NULL, /* prepare_to_access_memory */
+ NULL, /* done_accessing_memory */
+ lynx_read_memory,
+ lynx_write_memory,
+ NULL, /* look_up_symbols */
+ lynx_request_interrupt,
+ NULL, /* read_auxv */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* read_offsets */
+ NULL, /* get_tls_address */
+ NULL, /* qxfer_spu */
+ NULL, /* hostio_last_error */
+ NULL, /* qxfer_osdata */
+ NULL, /* qxfer_siginfo */
+ NULL, /* supports_non_stop */
+ NULL, /* async */
+ NULL, /* start_non_stop */
+ NULL, /* supports_multi_process */
+ NULL, /* handle_monitor_command */
+};
+
+void
+initialize_low (void)
+{
+ set_target_ops (&lynx_target_ops);
+ the_low_target.arch_setup ();
+}
+
diff --git a/gdb/gdbserver/lynx-low.h b/gdb/gdbserver/lynx-low.h
new file mode 100644
index 0000000..1e004bf
--- /dev/null
+++ b/gdb/gdbserver/lynx-low.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+
+struct regcache;
+
+/* Some information relative to a given register set. */
+
+struct lynx_regset_info
+{
+ /* The ptrace request needed to get/set registers of this set. */
+ int get_request, set_request;
+ /* The size of the register set. */
+ int size;
+ /* Fill the buffer BUF from the contents of the given REGCACHE. */
+ void (*fill_function) (struct regcache *regcache, char *buf);
+ /* Store the register value in BUF in the given REGCACHE. */
+ void (*store_function) (struct regcache *regcache, const char *buf);
+};
+
+/* A list of regsets for the target being debugged, terminated by an entry
+ where the size is negative.
+
+ This list should be created by the target-specific code. */
+
+extern struct lynx_regset_info lynx_target_regsets[];
+
+/* The target-specific operations for LynxOS support. */
+
+struct lynx_target_ops
+{
+ /* Architecture-specific setup. */
+ void (*arch_setup) (void);
+};
+
+extern struct lynx_target_ops the_low_target;
+
diff --git a/gdb/gdbserver/lynx-ppc-low.c b/gdb/gdbserver/lynx-ppc-low.c
new file mode 100644
index 0000000..9caa8ac
--- /dev/null
+++ b/gdb/gdbserver/lynx-ppc-low.c
@@ -0,0 +1,186 @@
+/* Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "lynx-low.h"
+
+#include <stdint.h>
+#include <stddef.h>
+#include <limits.h>
+#include <ptrace.h>
+
+/* The following two typedefs are defined in a .h file which is not
+ in the standard include path (/sys/include/family/ppc/ucontext.h),
+ so we just duplicate them here. */
+
+/* General register context */
+typedef struct usr_econtext_s
+{
+ uint32_t uec_iregs[32];
+ uint32_t uec_inum;
+ uint32_t uec_srr0;
+ uint32_t uec_srr1;
+ uint32_t uec_lr;
+ uint32_t uec_ctr;
+ uint32_t uec_cr;
+ uint32_t uec_xer;
+ uint32_t uec_dar;
+ uint32_t uec_mq;
+ uint32_t uec_msr;
+ uint32_t uec_sregs[16];
+ uint32_t uec_ss_count;
+ uint32_t uec_ss_addr1;
+ uint32_t uec_ss_addr2;
+ uint32_t uec_ss_code1;
+ uint32_t uec_ss_code2;
+} usr_econtext_t;
+
+/* Floating point register context */
+typedef struct usr_fcontext_s
+{
+ uint64_t ufc_freg[32];
+ uint32_t ufc_fpscr[2];
+} usr_fcontext_t;
+
+/* Index of for various registers inside the regcache. */
+#define R0_REGNUM 0
+#define F0_REGNUM 32
+#define PC_REGNUM 64
+#define MSR_REGNUM 65
+#define CR_REGNUM 66
+#define LR_REGNUM 67
+#define CTR_REGNUM 68
+#define XER_REGNUM 69
+#define FPSCR_REGNUM 70
+
+/* Defined in auto-generated file powerpc-32.c. */
+extern void init_registers_powerpc_32 (void);
+
+/* The fill_function for the general-purpose register set. */
+
+static void
+lynx_ppc_fill_gregset (struct regcache *regcache, char *buf)
+{
+ int i;
+
+ /* r0 - r31 */
+ for (i = 0; i < 32; i++)
+ collect_register (regcache, R0_REGNUM + i,
+ buf + offsetof (usr_econtext_t, uec_iregs[i]));
+
+ /* The other registers provided in the GP register context. */
+ collect_register (regcache, PC_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_srr0));
+ collect_register (regcache, MSR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_srr1));
+ collect_register (regcache, CR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_cr));
+ collect_register (regcache, LR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_lr));
+ collect_register (regcache, CTR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_ctr));
+ collect_register (regcache, XER_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_xer));
+}
+
+/* The store_function for the general-purpose register set. */
+
+static void
+lynx_ppc_store_gregset (struct regcache *regcache, const char *buf)
+{
+ int i;
+
+ /* r0 - r31 */
+ for (i = 0; i < 32; i++)
+ supply_register (regcache, R0_REGNUM + i,
+ buf + offsetof (usr_econtext_t, uec_iregs[i]));
+
+ /* The other registers provided in the GP register context. */
+ supply_register (regcache, PC_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_srr0));
+ supply_register (regcache, MSR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_srr1));
+ supply_register (regcache, CR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_cr));
+ supply_register (regcache, LR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_lr));
+ supply_register (regcache, CTR_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_ctr));
+ supply_register (regcache, XER_REGNUM,
+ buf + offsetof (usr_econtext_t, uec_xer));
+}
+
+/* The fill_function for the floating-point register set. */
+
+static void
+lynx_ppc_fill_fpregset (struct regcache *regcache, char *buf)
+{
+ int i;
+
+ /* f0 - f31 */
+ for (i = 0; i < 32; i++)
+ collect_register (regcache, F0_REGNUM + i,
+ buf + offsetof (usr_fcontext_t, ufc_freg[i]));
+
+ /* fpscr */
+ collect_register (regcache, FPSCR_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_fpscr));
+}
+
+/* The store_function for the floating-point register set. */
+
+static void
+lynx_ppc_store_fpregset (struct regcache *regcache, const char *buf)
+{
+ int i;
+
+ /* f0 - f31 */
+ for (i = 0; i < 32; i++)
+ supply_register (regcache, F0_REGNUM + i,
+ buf + offsetof (usr_fcontext_t, ufc_freg[i]));
+
+ /* fpscr */
+ supply_register (regcache, FPSCR_REGNUM,
+ buf + offsetof (usr_fcontext_t, ufc_fpscr));
+}
+
+/* Implements the lynx_target_ops.arch_setup routine. */
+
+static void
+lynx_ppc_arch_setup (void)
+{
+ init_registers_powerpc_32 ();
+}
+
+/* Description of all the powerpc-lynx register sets. */
+
+struct lynx_regset_info lynx_target_regsets[] = {
+ /* General Purpose Registers. */
+ {PTRACE_GETREGS, PTRACE_SETREGS, sizeof(usr_econtext_t),
+ lynx_ppc_fill_gregset, lynx_ppc_store_gregset},
+ /* Floating Point Registers. */
+ { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof(usr_fcontext_t),
+ lynx_ppc_fill_fpregset, lynx_ppc_store_fpregset },
+ /* End of list marker. */
+ {0, 0, -1, NULL, NULL }
+};
+
+/* The lynx_target_ops vector for powerpc-lynxos. */
+
+struct lynx_target_ops the_low_target = {
+ lynx_ppc_arch_setup,
+};