This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [patch] gdbserver: Add qnx target


Thanks for reviewing, Pedro.


Pedro Alves wrote:
On Wednesday 24 June 2009 19:50:39, Aleksandar Ristovski wrote:

Ok, here is my new patch. I addressed all of the above, and probably introduced some new issues :-). For my bonus points, I added comments for each function definition in nto-low.c

:-) Thanks for that.


On Wednesday 24 June 2009 19:50:39, Aleksandar Ristovski wrote:
+ /* Skip over argc, argv and envp... (see comment in ldd.c) */

Are ldd.c's sources available? :-) If not, could you paste that comment there too?

Comment from ldd.c pasted.



On Wednesday 24 June 2009 19:50:39, Aleksandar Ristovski wrote
+  i[34567]86-*-nto*)   srv_regobj=reg-i386.o
+                       srv_tgtobj="nto-low.o nto-x86-low.o"
+                       srv_qnx_LIBS=-lsocket
+                       srv_qnx="yes"

Do you think the set of libs will change depending on the qnx arch? In other words, if you're adding a $srv_qnx, do you really need a new srv_qnx_LIBS variable?

Unlikely. I just thought it was nicer. But in this patch I removed it and -lsocket is now "hard-coded" in configure.ac/configure.



+  //TODO: FPU, XMM registers
+  return -1;

Is this a planned feature? An nto limitation? Anyway, the reason this caught my eye was due to use of C++ style comment. For consistency, please always use C-style comments (yes, even though gcc supports those as C extension for ages).

I will be adding this; for now, just removed the comment.




Let me know what you think (once this goes in, I will change gdb's configure.tgt to say "yes" to generating gdbserver for Neutrino - in a separate patch submission).


Other than the obvious watchpoint/point interface changes that happened meanwhile, and the nits above, it looks OK to me.



Renamed.



Additionally, there are a few small fixes for problems I discovered while testing it:
- in find_new_threads, I needed to check if returned status.tid is zero (which may happen when program dies).
- in nto_resume added FLTSTACK and FLTACCESS.
- in nto_wait, assign reported signal to nto_inferior.exit_signo. Current code in nto-procfs.c doesn't do that but it's a bug that results gdb reporting "program exited normally" after program dies due to unhandled signal.



ChangeLog:
Adding Neutrino gdbserver.
* configure: Regenerated.
* configure.ac: Add case for srv_qnx and set LIBS accordingly..
* configure.srv (i[34567]86-*-nto*): New target.
* nto-low.c, nto-low.h, nto-x86-low.c: New files.
* remote-utils.c [__QNX__]: Include sys/iomgr.h
(nto_comctrl) [__QNX__]: New function.
(enable_async_io, disable_async_io) [__QNX__]: Call nto_comctrl.



-- Aleksandar Ristovski QNX Software Systems

Index: configure
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure,v
retrieving revision 1.38
diff -u -p -r1.38 configure
--- configure	22 Mar 2009 23:57:10 -0000	1.38
+++ configure	6 Jul 2009 15:46:50 -0000
@@ -3816,6 +3816,8 @@ if test "${srv_mingwce}" = "yes"; then
   LIBS="$LIBS -lws2"
 elif test "${srv_mingw}" = "yes"; then
   LIBS="$LIBS -lwsock32"
+elif test "${srv_qnx}" = "yes"; then
+  LIBS="$LIBS -lsocket"
 fi
 
 if test "${srv_mingw}" = "yes"; then
Index: configure.ac
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure.ac,v
retrieving revision 1.25
diff -u -p -r1.25 configure.ac
--- configure.ac	22 Mar 2009 23:57:10 -0000	1.25
+++ configure.ac	6 Jul 2009 15:46:51 -0000
@@ -79,6 +79,8 @@ if test "${srv_mingwce}" = "yes"; then
   LIBS="$LIBS -lws2"
 elif test "${srv_mingw}" = "yes"; then
   LIBS="$LIBS -lwsock32"
+elif test "${srv_qnx}" = "yes"; then
+  LIBS="$LIBS -lsocket"
 fi
 
 if test "${srv_mingw}" = "yes"; then
Index: configure.srv
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure.srv,v
retrieving revision 1.42
diff -u -p -r1.42 configure.srv
--- configure.srv	30 Jun 2009 16:35:25 -0000	1.42
+++ configure.srv	6 Jul 2009 15:46:51 -0000
@@ -74,6 +74,10 @@ case "${target}" in
 			srv_tgtobj="i386-low.o win32-low.o win32-i386-low.o"
 			srv_mingw=yes
 			;;
+  i[34567]86-*-nto*)	srv_regobj=reg-i386.o
+			srv_tgtobj="nto-low.o nto-x86-low.o"
+			srv_qnx="yes"
+			;;
   ia64-*-linux*)	srv_regobj=reg-ia64.o
 			srv_tgtobj="linux-low.o linux-ia64-low.o"
 			srv_linux_usrregs=yes
Index: nto-low.c
===================================================================
RCS file: nto-low.c
diff -N nto-low.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ nto-low.c	6 Jul 2009 15:46:51 -0000
@@ -0,0 +1,950 @@
+/* QNX Neutrino specific low level interface, for the remote server
+   for GDB.
+   Copyright (C) 2009
+   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 "nto-low.h"
+
+#include <limits.h>
+#include <fcntl.h>
+#include <spawn.h>
+#include <sys/procfs.h>
+#include <sys/auxv.h>
+#include <stdarg.h>
+#include <sys/iomgr.h>
+#include <sys/neutrino.h>
+
+
+extern int using_threads;
+int using_threads = 1;
+
+static void
+nto_trace (const char *fmt, ...)
+{
+  va_list arg_list;
+
+  if (debug_threads == 0)
+    return;
+  fprintf (stderr, "nto:");
+  va_start (arg_list, fmt);
+  vfprintf (stderr, fmt, arg_list);
+  va_end (arg_list);
+}
+
+#define TRACE nto_trace
+
+/* Structure holding neutrino specific information about
+   inferior.  */
+
+struct nto_inferior
+{
+  char nto_procfs_path[PATH_MAX];
+  int ctl_fd;
+  pid_t pid;
+  int exit_signo; /* For tracking exit status.  */
+};
+
+static struct nto_inferior nto_inferior;
+
+static void
+init_nto_inferior (struct nto_inferior *nto_inferior)
+{
+  memset (nto_inferior, 0, sizeof (struct nto_inferior));
+  nto_inferior->ctl_fd = -1;
+  nto_inferior->pid = -1;
+}
+
+static void
+do_detach (void)
+{
+  if (nto_inferior.ctl_fd != -1)
+    {
+      nto_trace ("Closing fd\n");
+      close (nto_inferior.ctl_fd);
+      init_nto_inferior (&nto_inferior);
+    }
+}
+
+/* Set current thread. Return 1 on success, 0 otherwise.  */
+
+static int
+nto_set_thread (ptid_t ptid)
+{
+  int res = 0;
+
+  TRACE ("%s pid: %d tid: %ld\n", __func__, ptid_get_pid (ptid),
+	 ptid_get_lwp (ptid));
+  if (nto_inferior.ctl_fd != -1
+      && !ptid_equal (ptid, null_ptid)
+      && !ptid_equal (ptid, minus_one_ptid))
+    {
+      pthread_t tid = ptid_get_lwp (ptid);
+
+      if (EOK == devctl (nto_inferior.ctl_fd, DCMD_PROC_CURTHREAD, &tid,
+	  sizeof (tid), 0))
+	res = 1;
+      else
+	TRACE ("%s: Error: failed to set current thread\n", __func__);
+    }
+  return res;
+}
+
+/* This function will determine all alive threads.  Note that we do not list
+   dead but unjoined threads even though they are still in the process' thread
+   list.  
+
+   NTO_INFERIOR must not be NULL.  */
+
+static void
+nto_find_new_threads (struct nto_inferior *nto_inferior)
+{
+  pthread_t tid;
+
+  TRACE ("%s pid:%d\n", __func__, nto_inferior->pid);
+
+  if (nto_inferior->ctl_fd == -1)
+    return;
+
+  for (tid = 1;; ++tid)
+    {
+      procfs_status status;
+      ptid_t ptid;
+      int err;
+
+      status.tid = tid;
+      err = devctl (nto_inferior->ctl_fd, DCMD_PROC_TIDSTATUS, &status,
+		    sizeof (status), 0);
+
+      if (err != EOK || status.tid == 0)
+	break;
+
+      /* All threads in between are gone.  */
+      while (tid != status.tid || status.state == STATE_DEAD)
+	{
+	  struct thread_info *ti;
+
+	  ptid = ptid_build (nto_inferior->pid, tid, 0);
+	  ti = find_thread_ptid (ptid);
+	  if (ti != NULL)
+	    {
+	      TRACE ("Removing thread %d\n", tid);
+	      remove_thread (ti);
+	    }
+	  if (tid == status.tid)
+	    break;
+	  ++tid;
+	}
+
+      if (status.state != STATE_DEAD)
+	{
+	  TRACE ("Adding thread %d\n", tid);
+	  ptid = ptid_build (nto_inferior->pid, tid, 0);
+	  if (!find_thread_ptid (ptid))
+	    add_thread (ptid, NULL);
+	}
+    }
+}
+
+/* Given pid, open procfs path.  */
+
+static pid_t
+do_attach (pid_t pid)
+{
+  procfs_status status;
+  struct sigevent event;
+
+  if (nto_inferior.ctl_fd != -1)
+    {
+      close (nto_inferior.ctl_fd);
+      init_nto_inferior (&nto_inferior);
+    }
+  snprintf (nto_inferior.nto_procfs_path, PATH_MAX - 1, "/proc/%d/as", pid);
+  nto_inferior.ctl_fd = open (nto_inferior.nto_procfs_path, O_RDWR);
+  if (nto_inferior.ctl_fd == -1)
+    {
+      TRACE ("Failed to open %s\n", nto_inferior.nto_procfs_path);
+      init_nto_inferior (&nto_inferior);
+      return -1;
+    }
+  if (devctl (nto_inferior.ctl_fd, DCMD_PROC_STOP, &status, sizeof (status), 0)
+      != EOK)
+    {
+      do_detach ();
+      return -1;
+    }
+  nto_inferior.pid = pid;
+  /* Define a sigevent for process stopped notification.  */
+  event.sigev_notify = SIGEV_SIGNAL_THREAD;
+  event.sigev_signo = SIGUSR1;
+  event.sigev_code = 0;
+  event.sigev_value.sival_ptr = NULL;
+  event.sigev_priority = -1;
+  devctl (nto_inferior.ctl_fd, DCMD_PROC_EVENT, &event, sizeof (event), 0);
+
+  if (devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status),
+	      0) == EOK
+      && (status.flags & _DEBUG_FLAG_STOPPED))
+    {
+      ptid_t ptid;
+
+      kill (pid, SIGCONT);
+      ptid = ptid_build (status.pid, status.tid, 0);
+      the_low_target.arch_setup ();
+      add_process (status.pid, 1);
+      TRACE ("Adding thread: pid=%d tid=%ld\n", status.pid,
+	     ptid_get_lwp (ptid));
+      nto_find_new_threads (&nto_inferior);
+    }
+  else
+    {
+      do_detach ();
+      return -1;
+    }
+
+  return pid;
+}
+
+/* Read or write LEN bytes from/to inferior's MEMADDR memory address
+   into gdbservers's MYADDR buffer.  Return number of bytes actually
+   transfered.  */
+
+static int
+nto_xfer_memory (off_t memaddr, unsigned char *myaddr, int len,
+		 int dowrite)
+{
+  int nbytes = 0;
+
+  if (lseek (nto_inferior.ctl_fd, memaddr, SEEK_SET) == memaddr)
+    {
+      if (dowrite)
+	nbytes = write (nto_inferior.ctl_fd, myaddr, len);
+      else
+	nbytes = read (nto_inferior.ctl_fd, myaddr, len);
+      if (nbytes < 0)
+	nbytes = 0;
+    }
+  if (nbytes == 0)
+    {
+      int e = errno;
+      TRACE ("Error in %s : errno=%d (%s)\n", __func__, e, strerror (e));
+    }
+  return nbytes;
+}
+
+/* Insert or remove breakpoint or watchpoint at address ADDR.
+   TYPE can be one of Neutrino breakpoint types.  SIZE must be 0 for
+   inserting the point, -1 for removing it.  
+
+   Return 0 on success, 1 otherwise.  */
+
+static int
+nto_breakpoint (CORE_ADDR addr, int type, int size)
+{
+  procfs_break brk;
+
+  brk.type = type;
+  brk.addr = addr;
+  brk.size = size;
+  if (devctl (nto_inferior.ctl_fd, DCMD_PROC_BREAK, &brk, sizeof (brk), 0)
+      != EOK)
+    return 1;
+  return 0;
+}
+
+/* Read auxiliary vector from inferior's initial stack into gdbserver's
+   MYADDR buffer, up to LEN bytes.  
+
+   Return number of bytes read.  */
+
+static int
+nto_read_auxv_from_initial_stack (CORE_ADDR initial_stack,
+				  unsigned char *myaddr,
+				  unsigned int len)
+{
+  int data_ofs = 0;
+  int anint;
+  unsigned int len_read = 0;
+
+  /* Skip over argc, argv and envp... Comment from ldd.c:
+
+     The startup frame is set-up so that we have:
+     auxv
+     NULL
+     ...
+     envp2
+     envp1 <----- void *frame + (argc + 2) * sizeof(char *)
+     NULL
+     ...
+     argv2
+     argv1
+     argc  <------ void * frame
+
+     On entry to ldd, frame gives the address of argc on the stack.  */
+  if (nto_xfer_memory (initial_stack, (unsigned char *)&anint,
+		       sizeof (anint), 0) != sizeof (anint))
+    return 0;
+
+  /* Size of pointer is assumed to be 4 bytes (32 bit arch. ) */
+  data_ofs += (anint + 2) * sizeof (void *); /* + 2 comes from argc itself and
+						NULL terminating pointer in
+						argv.  */
+
+  /* Now loop over env table:  */
+  while (nto_xfer_memory (initial_stack + data_ofs,
+			  (unsigned char *)&anint, sizeof (anint), 0)
+	 == sizeof (anint))
+    {
+      data_ofs += sizeof (anint);
+      if (anint == 0)
+	break;
+    }
+  initial_stack += data_ofs;
+
+  memset (myaddr, 0, len);
+  while (len_read <= len - sizeof (auxv_t))
+    {
+      auxv_t *auxv = (auxv_t *)myaddr;
+
+      /* Search backwards until we have read AT_PHDR (num. 3),
+	 AT_PHENT (num 4), AT_PHNUM (num 5)  */
+      if (nto_xfer_memory (initial_stack, (unsigned char *)auxv,
+			   sizeof (auxv_t), 0) == sizeof (auxv_t))
+	{
+	  if (auxv->a_type != AT_NULL)
+	    {
+	      auxv++;
+	      len_read += sizeof (auxv_t);
+	    }
+	  if (auxv->a_type == AT_PHNUM) /* That's all we need.  */
+	    break;
+	  initial_stack += sizeof (auxv_t);
+	}
+      else
+	break;
+    }
+  TRACE ("auxv: len_read: %d\n", len_read);
+  return len_read;
+}
+
+/* Start inferior specified by PROGRAM passing arguments ALLARGS.  */
+
+static int
+nto_create_inferior (char *program, char **allargs)
+{
+  struct inheritance inherit;
+  pid_t pid;
+  sigset_t set;
+
+  TRACE ("%s %s\n", __func__, program);
+  /* Clear any pending SIGUSR1's but keep the behavior the same.  */
+  signal (SIGUSR1, signal (SIGUSR1, SIG_IGN));
+
+  sigemptyset (&set);
+  sigaddset (&set, SIGUSR1);
+  sigprocmask (SIG_UNBLOCK, &set, NULL);
+
+  memset (&inherit, 0, sizeof (inherit));
+  inherit.flags |= SPAWN_SETGROUP | SPAWN_HOLD;
+  inherit.pgroup = SPAWN_NEWPGROUP;
+  pid = spawnp (program, 0, NULL, &inherit, allargs, 0);
+  sigprocmask (SIG_BLOCK, &set, NULL);
+
+  if (pid == -1)
+    return -1;
+
+  if (do_attach (pid) != pid)
+    return -1;
+
+  return pid;
+}
+
+/* Attach to process PID.  */
+
+static int
+nto_attach (unsigned long pid)
+{
+  TRACE ("%s %ld\n", __func__, pid);
+  if (do_attach (pid) != pid)
+    error ("Unable to attach to %ld\n", pid);
+  return 0;
+}
+
+/* Send signal to process PID.  */
+
+static int
+nto_kill (int pid)
+{
+  TRACE ("%s %d\n", __func__, pid);
+  kill (pid, SIGKILL);
+  do_detach ();
+  return 0;
+}
+
+/* Detach from process PID.  */
+
+static int
+nto_detach (int pid)
+{
+  TRACE ("%s %d\n", __func__, pid);
+  do_detach ();
+  return 0;
+}
+
+/* Check if the given thread is alive.  
+
+   Return 1 if alive, 0 otherwise.  */
+
+static int
+nto_thread_alive (ptid_t ptid)
+{
+  int res;
+
+  TRACE ("%s pid:%d tid:%d\n", __func__, ptid_get_pid (ptid),
+	 ptid_get_lwp (ptid));
+  if (SignalKill (0, ptid_get_pid (ptid), ptid_get_lwp (ptid),
+		  0, 0, 0) == -1)
+    res = 0;
+  else
+    res = 1;
+  TRACE ("%s: %s\n", __func__, res ? "yes" : "no");
+  return res;
+}
+
+/* Resume inferior's execution.  */
+
+static void
+nto_resume (struct thread_resume *resume_info, size_t n)
+{
+  /* We can only work in all-stop mode.  */
+  procfs_status status;
+  procfs_run run;
+  int err;
+
+  TRACE ("%s\n", __func__);
+  /* Workaround for aliasing rules violation. */
+  sigset_t *run_fault = (sigset_t *) (void *) &run.fault;
+
+  nto_set_thread (resume_info->thread);
+
+  run.flags = _DEBUG_RUN_FAULT | _DEBUG_RUN_TRACE;
+  if (resume_info->kind == resume_step)
+    run.flags |= _DEBUG_RUN_STEP;
+  run.flags |= _DEBUG_RUN_ARM;
+
+  sigemptyset (run_fault);
+  sigaddset (run_fault, FLTBPT);
+  sigaddset (run_fault, FLTTRACE);
+  sigaddset (run_fault, FLTILL);
+  sigaddset (run_fault, FLTPRIV);
+  sigaddset (run_fault, FLTBOUNDS);
+  sigaddset (run_fault, FLTIOVF);
+  sigaddset (run_fault, FLTIZDIV);
+  sigaddset (run_fault, FLTFPE);
+  sigaddset (run_fault, FLTPAGE);
+  sigaddset (run_fault, FLTSTACK);
+  sigaddset (run_fault, FLTACCESS);
+
+  sigemptyset (&run.trace);
+  if (resume_info->sig)
+    {
+      int signal_to_pass;
+
+      devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status),
+	      0);
+      signal_to_pass = resume_info->sig;
+      if (status.why & (_DEBUG_WHY_SIGNALLED | _DEBUG_WHY_FAULTED))
+	{
+	  if (signal_to_pass != status.info.si_signo)
+	    {
+	      kill (status.pid, signal_to_pass);
+	      run.flags |= _DEBUG_RUN_CLRFLT | _DEBUG_RUN_CLRSIG;
+	    }
+	  else		/* Let it kill the program without telling us.  */
+	    sigdelset (&run.trace, signal_to_pass);
+	}
+    }
+  else
+    run.flags |= _DEBUG_RUN_CLRSIG | _DEBUG_RUN_CLRFLT;
+
+  sigfillset (&run.trace);
+
+  regcache_invalidate ();
+
+  err = devctl (nto_inferior.ctl_fd, DCMD_PROC_RUN, &run, sizeof (run), 0);
+  if (err != EOK)
+    TRACE ("Error: %d \"%s\"\n", err, strerror (err));
+}
+
+/* Wait for inferior's event.  
+
+   Return ptid of thread that caused the event.  */
+
+static ptid_t
+nto_wait (ptid_t ptid,
+	  struct target_waitstatus *ourstatus, int target_options)
+{
+  sigset_t set;
+  siginfo_t info;
+  procfs_status status;
+  const int trace_mask = (_DEBUG_FLAG_TRACE_EXEC | _DEBUG_FLAG_TRACE_RD
+			  | _DEBUG_FLAG_TRACE_WR | _DEBUG_FLAG_TRACE_MODIFY);
+
+  TRACE ("%s\n", __func__);
+
+  ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+
+  sigemptyset (&set);
+  sigaddset (&set, SIGUSR1);
+
+  devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status), 0);
+  while (!(status.flags & _DEBUG_FLAG_ISTOP))
+    {
+      sigwaitinfo (&set, &info);
+      devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status),
+	      0);
+    }
+  nto_find_new_threads (&nto_inferior);
+
+  if (status.flags & _DEBUG_FLAG_SSTEP)
+    {
+      TRACE ("SSTEP\n");
+      ourstatus->kind = TARGET_WAITKIND_STOPPED;
+      ourstatus->value.sig = TARGET_SIGNAL_TRAP;
+    }
+  /* Was it a breakpoint?  */
+  else if (status.flags & trace_mask)
+    {
+      TRACE ("STOPPED\n");
+      ourstatus->kind = TARGET_WAITKIND_STOPPED;
+      ourstatus->value.sig = TARGET_SIGNAL_TRAP;
+    }
+  else if (status.flags & _DEBUG_FLAG_ISTOP)
+    {
+      TRACE ("ISTOP\n");
+      switch (status.why)
+	{
+	case _DEBUG_WHY_SIGNALLED:
+	  TRACE ("  SIGNALLED\n");
+	  ourstatus->kind = TARGET_WAITKIND_STOPPED;
+	  ourstatus->value.sig =
+	    target_signal_from_host (status.info.si_signo);
+	  nto_inferior.exit_signo = ourstatus->value.sig;
+	  break;
+	case _DEBUG_WHY_FAULTED:
+	  TRACE ("  FAULTED\n");
+	  ourstatus->kind = TARGET_WAITKIND_STOPPED;
+	  if (status.info.si_signo == SIGTRAP)
+	    {
+	      ourstatus->value.sig = 0;
+	      nto_inferior.exit_signo = 0;
+	    }
+	  else
+	    {
+	      ourstatus->value.sig =
+		target_signal_from_host (status.info.si_signo);
+	      nto_inferior.exit_signo = ourstatus->value.sig;
+	    }
+	  break;
+
+	case _DEBUG_WHY_TERMINATED:
+	  {
+	    int waitval = 0;
+
+	    TRACE ("  TERMINATED\n");
+	    waitpid (ptid_get_pid (ptid), &waitval, WNOHANG);
+	    if (nto_inferior.exit_signo)
+	      {
+		/* Abnormal death.  */
+		ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+		ourstatus->value.sig = nto_inferior.exit_signo;
+	      }
+	    else
+	      {
+		/* Normal death.  */
+		ourstatus->kind = TARGET_WAITKIND_EXITED;
+		ourstatus->value.integer = WEXITSTATUS (waitval);
+	      }
+	    nto_inferior.exit_signo = 0;
+	    break;
+	  }
+
+	case _DEBUG_WHY_REQUESTED:
+	  TRACE ("REQUESTED\n");
+	  /* We are assuming a requested stop is due to a SIGINT.  */
+	  ourstatus->kind = TARGET_WAITKIND_STOPPED;
+	  ourstatus->value.sig = TARGET_SIGNAL_INT;
+	  nto_inferior.exit_signo = 0;
+	  break;
+	}
+    }
+
+  return ptid_build (status.pid, status.tid, 0);
+}
+
+/* Fetch inferior's registers for currently selected thread (CURRENT_INFERIOR).
+   If REGNO is -1, fetch all registers, or REGNO register only otherwise.  */
+
+static void
+nto_fetch_registers (int regno)
+{
+  int regsize;
+  procfs_greg greg;
+  ptid_t ptid;
+
+  TRACE ("%s (regno=%d)\n", __func__, regno);
+  if (regno >= the_low_target.num_regs)
+    return;
+
+  if (current_inferior == NULL)
+    {
+      TRACE ("current_inferior is NULL\n");
+      return;
+    }
+  ptid = thread_to_gdb_id (current_inferior);
+  if (!nto_set_thread (ptid))
+    return;
+
+  if (devctl (nto_inferior.ctl_fd, DCMD_PROC_GETGREG, &greg, sizeof (greg),
+	      &regsize) == EOK)
+    {
+      if (regno == -1) /* All registers. */
+	{
+	  for (regno = 0; regno != the_low_target.num_regs; ++regno)
+	    {
+	      const unsigned int registeroffset
+		= the_low_target.register_offset (regno);
+	      supply_register (regno, ((char *)&greg) + registeroffset);
+	    }
+	}
+      else
+	{
+	  const unsigned int registeroffset
+	    = the_low_target.register_offset (regno);
+	  if (registeroffset == -1)
+	    return;
+	  supply_register (regno, ((char *)&greg) + registeroffset);
+	}
+    }
+  else
+    TRACE ("ERROR reading registers from inferior.\n");
+}
+
+/* Store registers for currently selected thread (CURRENT_INFERIOR).  
+   We always store all registers, regardless of REGNO.  */
+
+static void
+nto_store_registers (int regno)
+{
+  procfs_greg greg;
+  int err;
+  ptid_t ptid;
+
+  TRACE ("%s (regno:%d)\n", __func__, regno);
+
+  if (current_inferior == NULL)
+    {
+      TRACE ("current_inferior is NULL\n");
+      return;
+    }
+  ptid = thread_to_gdb_id (current_inferior);
+  if (!nto_set_thread (ptid))
+    return;
+
+  memset (&greg, 0, sizeof (greg));
+  for  (regno = 0; regno != the_low_target.num_regs; ++regno)
+    {
+      const unsigned int regoffset
+	= the_low_target.register_offset (regno);
+      collect_register (regno, ((char *)&greg) + regoffset);
+    }
+  err = devctl (nto_inferior.ctl_fd, DCMD_PROC_SETGREG, &greg, sizeof (greg),
+		0);
+  if (err != EOK)
+    TRACE ("Error: setting registers.\n");
+}
+
+/* Read LEN bytes from inferior's memory address MEMADDR into
+   gdbserver's MYADDR buffer.  
+
+   Return 0 on success -1 otherwise.  */
+
+static int
+nto_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+  TRACE ("%s memaddr:0x%08lx, len:%d\n", __func__, memaddr, len);
+
+  if (nto_xfer_memory (memaddr, myaddr, len, 0) != len)
+    {
+      TRACE ("Failed to read memory\n");
+      return -1;
+    }
+
+  return 0;
+}
+
+/* Write LEN bytes from gdbserver's buffer MYADDR into inferior's
+   memory at address MEMADDR.  
+
+   Return 0 on success -1 otherwise.  */
+
+static int
+nto_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
+{
+  int len_written;
+
+  TRACE ("%s memaddr: 0x%08llx len: %d\n", __func__, memaddr, len);
+  if ((len_written = nto_xfer_memory (memaddr, (unsigned char *)myaddr, len,
+				      1))
+      != len)
+    {
+      TRACE ("Wanted to write: %d but written: %d\n", len, len_written);
+      return -1;
+    }
+
+  return 0;
+}
+
+/* Stop inferior.  We always stop all threads.  */
+
+static void
+nto_request_interrupt (void)
+{
+  TRACE ("%s\n", __func__);
+  nto_set_thread (ptid_build (nto_inferior.pid, 1, 0));
+  if (EOK != devctl (nto_inferior.ctl_fd, DCMD_PROC_STOP, NULL, 0, 0))
+    TRACE ("Error stopping inferior.\n");
+}
+
+/* Read auxiliary vector from inferior's memory into gdbserver's buffer
+   MYADDR.  We always read whole auxv.  
+   
+   Return number of bytes stored in MYADDR buffer, 0 if OFFSET > 0
+   or -1 on error.  */
+
+static int
+nto_read_auxv (CORE_ADDR offset, unsigned char *myaddr, unsigned int len)
+{
+  int err;
+  CORE_ADDR initial_stack;
+  procfs_info procinfo;
+
+  TRACE ("%s\n", __func__);
+  if (offset > 0)
+    return 0;
+
+  err = devctl (nto_inferior.ctl_fd, DCMD_PROC_INFO, &procinfo,
+		sizeof procinfo, 0);
+  if (err != EOK)
+    return -1;
+
+  initial_stack = procinfo.initial_stack;
+
+  return nto_read_auxv_from_initial_stack (initial_stack, myaddr, len);
+}
+
+/* Insert {break/watch}point at address ADDR.
+   TYPE must be in '0'..'4' range.  LEN is not used.  */
+
+static int
+nto_insert_point (char type, CORE_ADDR addr, int len)
+{
+  int wtype = _DEBUG_BREAK_HW; /* Always request HW.  */
+
+  TRACE ("%s type:%c addr: 0x%08lx len:%d\n", __func__, (int)type, addr, len);
+  switch (type)
+    {
+    case '0': /* software-breakpoint */
+      wtype = _DEBUG_BREAK_EXEC;
+      break;
+    case '1': /* hardware-breakpoint */
+      wtype |= _DEBUG_BREAK_EXEC;
+      break;
+    case '2':  /* write watchpoint */
+      wtype |= _DEBUG_BREAK_RW;
+      break;
+    case '3':  /* read watchpoint */
+      wtype |= _DEBUG_BREAK_RD;
+      break;
+    case '4':  /* access watchpoint */
+      wtype |= _DEBUG_BREAK_RW;
+      break;
+    default:
+      return 1; /* Not supported.  */
+    }
+  return nto_breakpoint (addr, wtype, 0);
+}
+
+/* Remove {break/watch}point at address ADDR.
+   TYPE must be in '0'..'4' range.  LEN is not used.  */
+
+static int
+nto_remove_point (char type, CORE_ADDR addr, int len)
+{
+  int wtype = _DEBUG_BREAK_HW; /* Always request HW.  */
+
+  TRACE ("%s type:%c addr: 0x%08lx len:%d\n", __func__, (int)type, addr, len);
+  switch (type)
+    {
+    case '0': /* software-breakpoint */
+      wtype = _DEBUG_BREAK_EXEC;
+      break;
+    case '1': /* hardware-breakpoint */
+      wtype |= _DEBUG_BREAK_EXEC;
+      break;
+    case '2':  /* write watchpoint */
+      wtype |= _DEBUG_BREAK_RW;
+      break;
+    case '3':  /* read watchpoint */
+      wtype |= _DEBUG_BREAK_RD;
+      break;
+    case '4':  /* access watchpoint */
+      wtype |= _DEBUG_BREAK_RW;
+      break;
+    default:
+      return 1; /* Not supported.  */
+    }
+  return nto_breakpoint (addr, wtype, -1);
+}
+
+/* Check if the reason of stop for current thread (CURRENT_INFERIOR) is
+   a watchpoint.
+
+   Return 1 if stopped by watchpoint, 0 otherwise.  */
+
+static int
+nto_stopped_by_watchpoint (void)
+{
+  int ret = 0;
+
+  TRACE ("%s\n", __func__);
+  if (nto_inferior.ctl_fd != -1 && current_inferior != NULL)
+    {
+      ptid_t ptid;
+
+      ptid = thread_to_gdb_id (current_inferior);
+      if (nto_set_thread (ptid))
+	{
+	  const int watchmask = _DEBUG_FLAG_TRACE_RD | _DEBUG_FLAG_TRACE_WR
+				| _DEBUG_FLAG_TRACE_MODIFY;
+	  procfs_status status;
+	  int err;
+
+	  err = devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status,
+			sizeof (status), 0);
+	  if (err == EOK && (status.flags & watchmask))
+	    ret = 1;
+	}
+    }
+  TRACE ("%s: %s\n", __func__, ret ? "yes" : "no");
+  return ret;
+}
+
+/* Get instruction pointer for CURRENT_INFERIOR thread.  
+
+   Return inferior's instruction pointer value, or 0 on error.  */ 
+
+static CORE_ADDR
+nto_stopped_data_address (void)
+{
+  CORE_ADDR ret = (CORE_ADDR)0;
+
+  TRACE ("%s\n", __func__);
+  if (nto_inferior.ctl_fd != -1 && current_inferior != NULL)
+    {
+      ptid_t ptid;
+
+      ptid = thread_to_gdb_id (current_inferior);
+
+      if (nto_set_thread (ptid))
+	{
+	  procfs_status status;
+
+	  if (devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status,
+		      sizeof (status), 0) == EOK)
+	    ret = status.ip;
+	}
+    }
+  TRACE ("%s: 0x%08lx\n", __func__, ret);
+  return ret;
+}
+
+/* We do not currently support non-stop.  */
+
+static int
+nto_supports_non_stop (void)
+{
+  TRACE ("%s\n", __func__);
+  return 0;
+}
+
+
+
+static struct target_ops nto_target_ops = {
+  nto_create_inferior,
+  nto_attach,
+  nto_kill,
+  nto_detach,
+  NULL, /* nto_join */
+  nto_thread_alive,
+  nto_resume,
+  nto_wait,
+  nto_fetch_registers,
+  nto_store_registers,
+  nto_read_memory,
+  nto_write_memory,
+  NULL, /* nto_look_up_symbols */
+  nto_request_interrupt,
+  nto_read_auxv,
+  nto_insert_point,
+  nto_remove_point,
+  nto_stopped_by_watchpoint,
+  nto_stopped_data_address,
+  NULL, /* nto_read_offsets */
+  NULL, /* thread_db_set_tls_address */
+  NULL,
+  hostio_last_error_from_errno,
+  NULL, /* nto_qxfer_osdata */
+  NULL, /* xfer_siginfo */
+  nto_supports_non_stop,
+  NULL, /* async */
+  NULL  /* start_non_stop */
+};
+
+
+/* Global function called by server.c.  Initializes QNX Neutrino
+   gdbserver.  */
+
+void
+initialize_low (void)
+{
+  sigset_t set;
+
+  TRACE ("%s\n", __func__);
+  set_target_ops (&nto_target_ops);
+  set_breakpoint_data (the_low_target.breakpoint,
+		       the_low_target.breakpoint_len);
+
+  /* We use SIGUSR1 to gain control after we block waiting for a process.
+     We use sigwaitevent to wait.  */
+  sigemptyset (&set);
+  sigaddset (&set, SIGUSR1);
+  sigprocmask (SIG_BLOCK, &set, NULL);
+}
+
Index: nto-low.h
===================================================================
RCS file: nto-low.h
diff -N nto-low.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ nto-low.h	6 Jul 2009 15:46:51 -0000
@@ -0,0 +1,45 @@
+/* Internal interfaces for the QNX Neutrino specific target code for gdbserver.
+   Copyright (C) 2009
+   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/>.  */
+
+#ifndef NTO_LOW_H
+#define NTO_LOW_H
+
+enum regset_type
+{
+  NTO_REG_GENERAL,
+  NTO_REG_FLOAT,
+  NTO_REG_SYSTEM,
+  NTO_REG_ALT,
+  NTO_REG_END
+};
+
+struct nto_target_ops
+{
+  /* Architecture specific setup.  */
+  void (*arch_setup) (void);
+  int num_regs;
+  int (*register_offset) (int gdbregno);
+  const unsigned char *breakpoint;
+  int breakpoint_len;
+};
+
+extern struct nto_target_ops the_low_target;
+
+#endif
+
Index: nto-x86-low.c
===================================================================
RCS file: nto-x86-low.c
diff -N nto-x86-low.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ nto-x86-low.c	6 Jul 2009 15:46:51 -0000
@@ -0,0 +1,107 @@
+/* QNX Neutrino specific low level interface, for the remote server
+   for GDB.
+   Copyright (C) 2009
+   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 "nto-low.h"
+#include "regdef.h"
+#include "regcache.h"
+
+#include <x86/context.h>
+
+
+/* Definition auto generated from reg-i386.dep.  */
+extern void init_registers_i386 ();
+extern struct reg *regs_i386;
+
+const unsigned char x86_breakpoint[] = { 0xCC };
+#define x86_breakpoint_len 1
+
+/* Returns offset in appropriate Neutrino's context structure.
+   Defined in x86/context.h.
+   GDBREGNO is index into regs_i386 array.  It is autogenerated and
+   hopefully doesn't change.  */
+static int
+nto_x86_register_offset (int gdbregno)
+{
+  if (gdbregno >= 0 && gdbregno < 16)
+    {
+      X86_CPU_REGISTERS *dummy = (void*)0;
+      /* GPRs  */
+      switch (gdbregno)
+	{
+	case 0: 
+	  return (int)&(dummy->eax);
+	case 1:
+	  return (int)&(dummy->ecx);
+	case 2:
+	  return (int)&(dummy->edx);
+	case 3:
+	  return (int)&(dummy->ebx);
+	case 4:
+	  return (int)&(dummy->esp);
+	case 5:
+	  return (int)&(dummy->ebp);
+	case 6:
+	  return (int)&(dummy->esi);
+	case 7:
+	  return (int)&(dummy->edi);
+	case 8:
+	  return (int)&(dummy->eip);
+	case 9:
+	  return (int)&(dummy->efl);
+	case 10:
+	  return (int)&(dummy->cs);
+	case 11:
+	  return (int)&(dummy->ss);
+#ifdef __SEGMENTS__
+	case 12:
+	  return (int)&(dummy->ds);
+	case 13:
+	  return (int)&(dummy->es);
+	case 14:
+	  return (int)&(dummy->fs);
+	case 15:
+	  return (int)&(dummy->gs);
+#endif
+	default:
+	  return -1;
+	}
+    }
+  return -1;
+}
+
+static void
+nto_x86_arch_setup (void)
+{
+  init_registers_i386 ();
+  the_low_target.num_regs = 16;
+}
+
+struct nto_target_ops the_low_target =
+{
+  nto_x86_arch_setup,
+  0, /* num_regs */
+  nto_x86_register_offset,
+  x86_breakpoint,
+  x86_breakpoint_len
+};
+
+
+
Index: remote-utils.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/remote-utils.c,v
retrieving revision 1.67
diff -u -p -r1.67 remote-utils.c
--- remote-utils.c	24 May 2009 21:06:53 -0000	1.67
+++ remote-utils.c	6 Jul 2009 15:46:51 -0000
@@ -66,6 +66,10 @@
 #include <winsock.h>
 #endif
 
+#if __QNX__
+#include <sys/iomgr.h>
+#endif /* __QNX__ */
+
 #ifndef HAVE_SOCKLEN_T
 typedef int socklen_t;
 #endif
@@ -814,6 +818,28 @@ unblock_async_io (void)
 #endif
 }
 
+#ifdef __QNX__
+static void
+nto_comctrl (int enable)
+{
+  struct sigevent event;
+
+  if (enable)
+    {
+      event.sigev_notify = SIGEV_SIGNAL_THREAD;
+      event.sigev_signo = SIGIO;
+      event.sigev_code = 0;
+      event.sigev_value.sival_ptr = NULL;
+      event.sigev_priority = -1;
+      ionotify (remote_desc, _NOTIFY_ACTION_POLLARM, _NOTIFY_COND_INPUT,
+		&event);
+    }
+  else
+    ionotify (remote_desc, _NOTIFY_ACTION_POLL, _NOTIFY_COND_INPUT, NULL);
+}
+#endif /* __QNX__ */
+
+
 /* Current state of asynchronous I/O.  */
 static int async_io_enabled;
 
@@ -828,6 +854,9 @@ enable_async_io (void)
   signal (SIGIO, input_interrupt);
 #endif
   async_io_enabled = 1;
+#ifdef __QNX__
+  nto_comctrl (1);
+#endif /* __QNX__ */
 }
 
 /* Disable asynchronous I/O.  */
@@ -841,6 +870,10 @@ disable_async_io (void)
   signal (SIGIO, SIG_IGN);
 #endif
   async_io_enabled = 0;
+#ifdef __QNX__
+  nto_comctrl (0);
+#endif /* __QNX__ */
+
 }
 
 void

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]