From: Aleksandar Ristovski <aristovski@qnx.com>
To: gdb-patches@sourceware.org
Cc: Pedro Alves <pedro@codesourcery.com>
Subject: Re: [patch] gdbserver: Add qnx target
Date: Wed, 24 Jun 2009 18:51:00 -0000 [thread overview]
Message-ID: <4A42757F.4020804@qnx.com> (raw)
In-Reply-To: <200906200102.25411.pedro@codesourcery.com>
[-- Attachment #1: Type: text/plain, Size: 6542 bytes --]
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.
[-- Attachment #2: gdbserver-ntotarget-20090624.diff --]
[-- Type: text/x-patch, Size: 31273 bytes --]
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),
+ ®size) == 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
next prev parent reply other threads:[~2009-06-24 18:51 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-06-17 15:05 Aleksandar Ristovski
2009-06-17 18:50 ` Aleksandar Ristovski
2009-06-20 16:01 ` Pedro Alves
2009-06-19 16:37 ` Pedro Alves
2009-06-19 16:42 ` Pedro Alves
2009-06-19 19:58 ` Aleksandar Ristovski
2009-06-20 0:01 ` Pedro Alves
2009-06-24 18:51 ` Aleksandar Ristovski [this message]
2009-06-30 12:11 ` Pedro Alves
2009-07-06 16:02 ` Aleksandar Ristovski
2009-07-06 16:49 ` Pedro Alves
2009-07-06 18:35 ` Aleksandar Ristovski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4A42757F.4020804@qnx.com \
--to=aristovski@qnx.com \
--cc=gdb-patches@sourceware.org \
--cc=pedro@codesourcery.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox