diff options
author | Aleksandar Ristovski <aristovski@qnx.com> | 2009-07-06 18:31:20 +0000 |
---|---|---|
committer | Aleksandar Ristovski <aristovski@qnx.com> | 2009-07-06 18:31:20 +0000 |
commit | ac8c974e4fc01aa939e29bf9e20dc4e074eefbc8 (patch) | |
tree | 0c674c14944154c6dac7d14faae191c24be2effb /gdb/gdbserver/nto-low.c | |
parent | e23578923827cd7506e82b255fb0ed3839b880f2 (diff) | |
download | gdb-ac8c974e4fc01aa939e29bf9e20dc4e074eefbc8.zip gdb-ac8c974e4fc01aa939e29bf9e20dc4e074eefbc8.tar.gz gdb-ac8c974e4fc01aa939e29bf9e20dc4e074eefbc8.tar.bz2 |
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.
Diffstat (limited to 'gdb/gdbserver/nto-low.c')
-rw-r--r-- | gdb/gdbserver/nto-low.c | 950 |
1 files changed, 950 insertions, 0 deletions
diff --git a/gdb/gdbserver/nto-low.c b/gdb/gdbserver/nto-low.c new file mode 100644 index 0000000..a2ddd44 --- /dev/null +++ b/gdb/gdbserver/nto-low.c @@ -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), + ®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_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); +} + |