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


Pedro Alves wrote:
On Friday 19 June 2009 20:58:14, Aleksandar Ristovski wrote:
Pedro Alves wrote:
 > > +static void
 > > +do_detach ()
               ^ (void)


You missed this one.

ok.



> > +static ptid_t
> > +nto_wait (ptid_t ptid,
> > + struct target_waitstatus *ourstatus, int target_options)
> > +{
> > + sigset_t set;
> > + siginfo_t info;
> > + procfs_status status;
> > + static int exit_signo = 0; /* For tracking exit status. */


Pedro: Why is this static?

We first register that signal has hit the inferior, but it hasn't died just yet. We set exit_signo and return TARGET_WAITKIND_STOPPED. On next resume it really terminates, we set recorded signal from the static.

Ah, I see.


It should really go into "nto_inferior" structure. I plan to support multiple processes so there will be a chance for cleanups.

I don't see that it would take more than a few lines
of code to move this to struct nto_inferior.

Moved.



It looks to me that you could set the exit signo here instead:


nto_resume:
      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); <
}

(and always clear it on entry to nto_resume)

.... but I may be wrong.  It's OK to leave as is (I don't care *that*
much about this file ;-) ), but if you want to leave as is,
then please paste that explanation in the code.

Didn't change this.




Pedro: AFAICS, nowhere have you set the current_inferior.

add_thread will set it (see inferiors.c:184)

How does that help in the nto_wait case I pointed? You have to set current_inferior to the thread that is reporting the event, otherwise, the following register reads may read from the wrong thread (*). Question: did you run the testsuite against this? I'd be curious to know how does this compares against native gdb.

(*) - unless I'm missing/forgetting something.  Sounds
like something we can now assure / clean up in generic code,
since target_wait now returns a ptid_t...

I think your feeling was right: I did not have code for adding new threads. Rectified now - thanks for all your help!



Pedro: I don't really know a thing about nto/qnx apis, but,
do you really need this nto_comctrl enable/disable calls?
Can't you just do what nto_comctrl does once unconditionaly,
and then rely on signal (SIGIO, SIG_IGN|input_interrupt),
as you seem to already ?


Unfortunately, I couldn't find a way. Whatever I looked at requires that handle is present. The problem here is that while we are blocked, we will not receive SIGIO signal unless we explicitly setup an event. And we need to do it every time before we go into sigwaitinfo.

Ok, I peeked at ionotify's docs, and from what I've understood, this is fine. I was considering if this wasn't racy, but it seems like ionotify (_NOTIFY_ACTION_POLLARM) behaves a bit like select --- if there's data already there, it triggers an action immediately. I'm not all that happy with having __QNX__ wrapped code, but it's mostly contained, and not really worse than USE_WIN32API... If there's more of these to come, than let's think about abstracting out the posix|win32|qnx initialize|disable|enable_async_io functions to separate files somehow.


Comments on the new patch:


+static int
+nto_stopped_by_watchpoint (void)
+{
+  procfs_status status;
+  int ret = 0;
+  const int watchmask = _DEBUG_FLAG_TRACE_RD | _DEBUG_FLAG_TRACE_WR
+                       | _DEBUG_FLAG_TRACE_MODIFY;
+
+  TRACE ("%s...", __func__);
+  if (nto_inferior.ctl_fd != -1)
+    {
+      int err = devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status,
+                       sizeof (status), 0);

^^^^^^ tab vs space



+}
+
+

You used 1 line spacing everywhere else.

Ok, I *think* I addressed all spacing/tabbing issues.



+static int
+nto_supports_non_stop (void)
+{
+  TRACE ("%s\n", __func__);
+  return 0;
+}
+
+
+

This function isn't really needed though. The default is 0. (Not installing such callbacks may make life easier for someone else in the future, if e.g., the interface of such functions needs to change, but it's no biggie.)

I would leave it as a testimony to my commitment to enable non-stop on Neutrino. :-)



+ int (*register_offset)(int gdbregno);
^ missing space


+/* Activated by env. variable GDBSERVER_DEBUG.  */
+extern int nto_debug;

This isn't defined anywhere. Please remove.


* configure.srv (i[34567]86-*-nto*): New target.
* nto-low.c, nto-low.h, nto-x86-low.c: New files.

* remote-utils.c (include sys/iomgr.h): New include for __QNX__ only.
(nto_comctrl): New function for __QNX__ only.
(enable_async_io, disable_async_io): Call nto_comcntrl for __QNX__ only.

There's a standard way to mention these conditionalized changes. It goes something like this:

	* remote-utils.c [__QNX__]: Include sys/iomgr.h.
	(nto_comctrl) [__QNX__]: New function.
	(enable_async_io, disable_async_io) [__QNX__]: Call nto_comcntrl.


Other than the above remarks, this is good to go, once its dependencies are in.


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


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).

Thanks,

--
Aleksandar Ristovski
QNX Software Systems


ChangeLog (NOTE: I merged both configuration/Makefile changes and new files into one all-inclusive patch).


* 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.


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	24 Jun 2009 18:35:12 -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 ${srv_qnx_LIBS}"
 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	24 Jun 2009 18:35:12 -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 ${srv_qnx_LIBS}"
 fi
 
 if test "${srv_mingw}" = "yes"; then
Index: configure.srv
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure.srv,v
retrieving revision 1.41
diff -u -p -r1.41 configure.srv
--- configure.srv	12 May 2009 22:25:00 -0000	1.41
+++ configure.srv	24 Jun 2009 18:35:12 -0000
@@ -74,6 +74,11 @@ case "${target}" in
 			srv_tgtobj="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_LIBS=-lsocket
+			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	24 Jun 2009 18:35:13 -0000
@@ -0,0 +1,933 @@
+/* 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)
+	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... (see comment in ldd.c)  */
+  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);
+
+  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;
+
+  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 = 0;
+	  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_watchpoint (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_watchpoint (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_watchpoint,
+  nto_remove_watchpoint,
+  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	24 Jun 2009 18:35:13 -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	24 Jun 2009 18:35:13 -0000
@@ -0,0 +1,108 @@
+/* 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;
+	}
+    }
+  //TODO: FPU, XMM registers
+  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	24 Jun 2009 18:35:13 -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]