771 lines
20 KiB
C
771 lines
20 KiB
C
/* Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "server.h"
|
|
#include "target.h"
|
|
#include "lynx-low.h"
|
|
|
|
#include <limits.h>
|
|
#include <sys/ptrace.h>
|
|
#include <sys/piddef.h> /* Provides PIDGET, TIDGET, BUILDPID, etc. */
|
|
#include <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <signal.h>
|
|
|
|
int using_threads = 1;
|
|
|
|
/* Print a debug trace on standard output if debug_threads is set. */
|
|
|
|
static void
|
|
lynx_debug (char *string, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (!debug_threads)
|
|
return;
|
|
|
|
va_start (args, string);
|
|
fprintf (stderr, "DEBUG(lynx): ");
|
|
vfprintf (stderr, string, args);
|
|
fprintf (stderr, "\n");
|
|
va_end (args);
|
|
}
|
|
|
|
/* Build a ptid_t given a PID and a LynxOS TID. */
|
|
|
|
static ptid_t
|
|
lynx_ptid_build (int pid, long tid)
|
|
{
|
|
/* brobecker/2010-06-21: It looks like the LWP field in ptids
|
|
should be distinct for each thread (see write_ptid where it
|
|
writes the thread ID from the LWP). So instead of storing
|
|
the LynxOS tid in the tid field of the ptid, we store it in
|
|
the lwp field. */
|
|
return ptid_build (pid, tid, 0);
|
|
}
|
|
|
|
/* Return the process ID of the given PTID.
|
|
|
|
This function has little reason to exist, it's just a wrapper around
|
|
ptid_get_pid. But since we have a getter function for the lynxos
|
|
ptid, it feels cleaner to have a getter for the pid as well. */
|
|
|
|
static int
|
|
lynx_ptid_get_pid (ptid_t ptid)
|
|
{
|
|
return ptid_get_pid (ptid);
|
|
}
|
|
|
|
/* Return the LynxOS tid of the given PTID. */
|
|
|
|
static long
|
|
lynx_ptid_get_tid (ptid_t ptid)
|
|
{
|
|
/* See lynx_ptid_build: The LynxOS tid is stored inside the lwp field
|
|
of the ptid. */
|
|
return ptid_get_lwp (ptid);
|
|
}
|
|
|
|
/* For a given PTID, return the associated PID as known by the LynxOS
|
|
ptrace layer. */
|
|
|
|
static int
|
|
lynx_ptrace_pid_from_ptid (ptid_t ptid)
|
|
{
|
|
return BUILDPID (lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
|
|
}
|
|
|
|
/* Return a string image of the ptrace REQUEST number. */
|
|
|
|
static char *
|
|
ptrace_request_to_str (int request)
|
|
{
|
|
switch (request)
|
|
{
|
|
case PTRACE_TRACEME:
|
|
return "PTRACE_TRACEME";
|
|
break;
|
|
case PTRACE_PEEKTEXT:
|
|
return "PTRACE_PEEKTEXT";
|
|
break;
|
|
case PTRACE_PEEKDATA:
|
|
return "PTRACE_PEEKDATA";
|
|
break;
|
|
case PTRACE_PEEKUSER:
|
|
return "PTRACE_PEEKUSER";
|
|
break;
|
|
case PTRACE_POKETEXT:
|
|
return "PTRACE_POKETEXT";
|
|
break;
|
|
case PTRACE_POKEDATA:
|
|
return "PTRACE_POKEDATA";
|
|
break;
|
|
case PTRACE_POKEUSER:
|
|
return "PTRACE_POKEUSER";
|
|
break;
|
|
case PTRACE_CONT:
|
|
return "PTRACE_CONT";
|
|
break;
|
|
case PTRACE_KILL:
|
|
return "PTRACE_KILL";
|
|
break;
|
|
case PTRACE_SINGLESTEP:
|
|
return "PTRACE_SINGLESTEP";
|
|
break;
|
|
case PTRACE_ATTACH:
|
|
return "PTRACE_ATTACH";
|
|
break;
|
|
case PTRACE_DETACH:
|
|
return "PTRACE_DETACH";
|
|
break;
|
|
case PTRACE_GETREGS:
|
|
return "PTRACE_GETREGS";
|
|
break;
|
|
case PTRACE_SETREGS:
|
|
return "PTRACE_SETREGS";
|
|
break;
|
|
case PTRACE_GETFPREGS:
|
|
return "PTRACE_GETFPREGS";
|
|
break;
|
|
case PTRACE_SETFPREGS:
|
|
return "PTRACE_SETFPREGS";
|
|
break;
|
|
case PTRACE_READDATA:
|
|
return "PTRACE_READDATA";
|
|
break;
|
|
case PTRACE_WRITEDATA:
|
|
return "PTRACE_WRITEDATA";
|
|
break;
|
|
case PTRACE_READTEXT:
|
|
return "PTRACE_READTEXT";
|
|
break;
|
|
case PTRACE_WRITETEXT:
|
|
return "PTRACE_WRITETEXT";
|
|
break;
|
|
case PTRACE_GETFPAREGS:
|
|
return "PTRACE_GETFPAREGS";
|
|
break;
|
|
case PTRACE_SETFPAREGS:
|
|
return "PTRACE_SETFPAREGS";
|
|
break;
|
|
case PTRACE_GETWINDOW:
|
|
return "PTRACE_GETWINDOW";
|
|
break;
|
|
case PTRACE_SETWINDOW:
|
|
return "PTRACE_SETWINDOW";
|
|
break;
|
|
case PTRACE_SYSCALL:
|
|
return "PTRACE_SYSCALL";
|
|
break;
|
|
case PTRACE_DUMPCORE:
|
|
return "PTRACE_DUMPCORE";
|
|
break;
|
|
case PTRACE_SETWRBKPT:
|
|
return "PTRACE_SETWRBKPT";
|
|
break;
|
|
case PTRACE_SETACBKPT:
|
|
return "PTRACE_SETACBKPT";
|
|
break;
|
|
case PTRACE_CLRBKPT:
|
|
return "PTRACE_CLRBKPT";
|
|
break;
|
|
case PTRACE_GET_UCODE:
|
|
return "PTRACE_GET_UCODE";
|
|
break;
|
|
#ifdef PT_READ_GPR
|
|
case PT_READ_GPR:
|
|
return "PT_READ_GPR";
|
|
break;
|
|
#endif
|
|
#ifdef PT_WRITE_GPR
|
|
case PT_WRITE_GPR:
|
|
return "PT_WRITE_GPR";
|
|
break;
|
|
#endif
|
|
#ifdef PT_READ_FPR
|
|
case PT_READ_FPR:
|
|
return "PT_READ_FPR";
|
|
break;
|
|
#endif
|
|
#ifdef PT_WRITE_FPR
|
|
case PT_WRITE_FPR:
|
|
return "PT_WRITE_FPR";
|
|
break;
|
|
#endif
|
|
#ifdef PT_READ_VPR
|
|
case PT_READ_VPR:
|
|
return "PT_READ_VPR";
|
|
break;
|
|
#endif
|
|
#ifdef PT_WRITE_VPR
|
|
case PT_WRITE_VPR:
|
|
return "PT_WRITE_VPR";
|
|
break;
|
|
#endif
|
|
#ifdef PTRACE_PEEKUSP
|
|
case PTRACE_PEEKUSP:
|
|
return "PTRACE_PEEKUSP";
|
|
break;
|
|
#endif
|
|
#ifdef PTRACE_POKEUSP
|
|
case PTRACE_POKEUSP:
|
|
return "PTRACE_POKEUSP";
|
|
break;
|
|
#endif
|
|
case PTRACE_PEEKTHREAD:
|
|
return "PTRACE_PEEKTHREAD";
|
|
break;
|
|
case PTRACE_THREADUSER:
|
|
return "PTRACE_THREADUSER";
|
|
break;
|
|
case PTRACE_FPREAD:
|
|
return "PTRACE_FPREAD";
|
|
break;
|
|
case PTRACE_FPWRITE:
|
|
return "PTRACE_FPWRITE";
|
|
break;
|
|
case PTRACE_SETSIG:
|
|
return "PTRACE_SETSIG";
|
|
break;
|
|
case PTRACE_CONT_ONE:
|
|
return "PTRACE_CONT_ONE";
|
|
break;
|
|
case PTRACE_KILL_ONE:
|
|
return "PTRACE_KILL_ONE";
|
|
break;
|
|
case PTRACE_SINGLESTEP_ONE:
|
|
return "PTRACE_SINGLESTEP_ONE";
|
|
break;
|
|
case PTRACE_GETLOADINFO:
|
|
return "PTRACE_GETLOADINFO";
|
|
break;
|
|
case PTRACE_GETTHREADLIST:
|
|
return "PTRACE_GETTHREADLIST";
|
|
break;
|
|
}
|
|
return "<unknown-request>";
|
|
}
|
|
|
|
/* A wrapper around ptrace that allows us to print debug traces of
|
|
ptrace calls if debug traces are activated. */
|
|
|
|
static int
|
|
lynx_ptrace (int request, ptid_t ptid, int addr, int data, int addr2)
|
|
{
|
|
int result;
|
|
const int pid = lynx_ptrace_pid_from_ptid (ptid);
|
|
int saved_errno;
|
|
|
|
if (debug_threads)
|
|
fprintf (stderr, "PTRACE (%s, pid=%d(pid=%d, tid=%d), addr=0x%x, "
|
|
"data=0x%x, addr2=0x%x)",
|
|
ptrace_request_to_str (request), pid, PIDGET (pid), TIDGET (pid),
|
|
addr, data, addr2);
|
|
result = ptrace (request, pid, addr, data, addr2);
|
|
saved_errno = errno;
|
|
if (debug_threads)
|
|
fprintf (stderr, " -> %d (=0x%x)\n", result, result);
|
|
|
|
errno = saved_errno;
|
|
return result;
|
|
}
|
|
|
|
/* Implement the create_inferior method of the target_ops vector. */
|
|
|
|
static int
|
|
lynx_create_inferior (char *program, char **allargs)
|
|
{
|
|
struct process_info *new_process;
|
|
int pid;
|
|
|
|
lynx_debug ("lynx_create_inferior ()");
|
|
|
|
pid = fork ();
|
|
if (pid < 0)
|
|
perror_with_name ("fork");
|
|
|
|
if (pid == 0)
|
|
{
|
|
int pgrp;
|
|
|
|
/* Switch child to its own process group so that signals won't
|
|
directly affect gdbserver. */
|
|
pgrp = getpid();
|
|
setpgid (0, pgrp);
|
|
ioctl (0, TIOCSPGRP, &pgrp);
|
|
lynx_ptrace (PTRACE_TRACEME, null_ptid, 0, 0, 0);
|
|
execv (program, allargs);
|
|
fprintf (stderr, "Cannot exec %s: %s.\n", program, strerror (errno));
|
|
fflush (stderr);
|
|
_exit (0177);
|
|
}
|
|
|
|
new_process = add_process (pid, 0);
|
|
/* Do not add the process thread just yet, as we do not know its tid.
|
|
We will add it later, during the wait for the STOP event corresponding
|
|
to the lynx_ptrace (PTRACE_TRACEME) call above. */
|
|
return pid;
|
|
}
|
|
|
|
/* Implement the attach target_ops method. */
|
|
|
|
static int
|
|
lynx_attach (unsigned long pid)
|
|
{
|
|
struct process_info *new_process;
|
|
ptid_t ptid = lynx_ptid_build (pid, 0);
|
|
|
|
if (lynx_ptrace (PTRACE_ATTACH, ptid, 0, 0, 0) != 0)
|
|
error ("Cannot attach to process %lu: %s (%d)\n", pid,
|
|
strerror (errno), errno);
|
|
|
|
new_process = add_process (pid, 1);
|
|
add_thread (ptid, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Implement the resume target_ops method. */
|
|
|
|
static void
|
|
lynx_resume (struct thread_resume *resume_info, size_t n)
|
|
{
|
|
ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
|
|
/* FIXME: Assume for now that n == 1. */
|
|
const int request = (resume_info[0].kind == resume_step
|
|
? PTRACE_SINGLESTEP : PTRACE_CONT);
|
|
const int signal = resume_info[0].sig;
|
|
int ret;
|
|
|
|
regcache_invalidate ();
|
|
ret = lynx_ptrace (request, inferior_ptid, 1, signal, 0);
|
|
}
|
|
|
|
/* Resume the execution of the given PTID. */
|
|
|
|
static void
|
|
lynx_continue (ptid_t ptid)
|
|
{
|
|
struct thread_resume resume_info;
|
|
|
|
resume_info.thread = ptid;
|
|
resume_info.kind = resume_continue;
|
|
resume_info.sig = 0;
|
|
|
|
lynx_resume (&resume_info, 1);
|
|
}
|
|
|
|
/* Remove all inferiors and associated threads. */
|
|
|
|
static void
|
|
lynx_clear_inferiors (void)
|
|
{
|
|
/* We do not use private data, so nothing much to do except calling
|
|
clear_inferiors. */
|
|
clear_inferiors ();
|
|
}
|
|
|
|
/* A wrapper around waitpid that handles the various idiosyncrasies
|
|
of LynxOS' waitpid. */
|
|
|
|
static int
|
|
lynx_waitpid (int pid, int *stat_loc)
|
|
{
|
|
int ret = 0;
|
|
|
|
while (1)
|
|
{
|
|
ret = waitpid (pid, stat_loc, WNOHANG);
|
|
if (ret < 0)
|
|
{
|
|
/* An ECHILD error is not indicative of a real problem.
|
|
It happens for instance while waiting for the inferior
|
|
to stop after attaching to it. */
|
|
if (errno != ECHILD)
|
|
perror_with_name ("waitpid (WNOHANG)");
|
|
}
|
|
if (ret > 0)
|
|
break;
|
|
/* No event with WNOHANG. See if there is one with WUNTRACED. */
|
|
ret = waitpid (pid, stat_loc, WNOHANG | WUNTRACED);
|
|
if (ret < 0)
|
|
{
|
|
/* An ECHILD error is not indicative of a real problem.
|
|
It happens for instance while waiting for the inferior
|
|
to stop after attaching to it. */
|
|
if (errno != ECHILD)
|
|
perror_with_name ("waitpid (WNOHANG|WUNTRACED)");
|
|
}
|
|
if (ret > 0)
|
|
break;
|
|
usleep (1000);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* Implement the wait target_ops method. */
|
|
|
|
static ptid_t
|
|
lynx_wait_1 (ptid_t ptid, struct target_waitstatus *status, int options)
|
|
{
|
|
int pid;
|
|
int ret;
|
|
int wstat;
|
|
ptid_t new_ptid;
|
|
|
|
if (ptid_equal (ptid, minus_one_ptid))
|
|
pid = lynx_ptid_get_pid (thread_to_gdb_id (current_inferior));
|
|
else
|
|
pid = BUILDPID (lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
|
|
|
|
retry:
|
|
|
|
ret = lynx_waitpid (pid, &wstat);
|
|
new_ptid = lynx_ptid_build (ret, ((union wait *) &wstat)->w_tid);
|
|
|
|
/* If this is a new thread, then add it now. The reason why we do
|
|
this here instead of when handling new-thread events is because
|
|
we need to add the thread associated to the "main" thread - even
|
|
for non-threaded applications where the new-thread events are not
|
|
generated. */
|
|
if (!find_thread_ptid (new_ptid))
|
|
add_thread (new_ptid, NULL);
|
|
|
|
if (WIFSTOPPED (wstat))
|
|
{
|
|
status->kind = TARGET_WAITKIND_STOPPED;
|
|
status->value.integer = target_signal_from_host (WSTOPSIG (wstat));
|
|
lynx_debug ("process stopped with signal: %d",
|
|
status->value.integer);
|
|
}
|
|
else if (WIFEXITED (wstat))
|
|
{
|
|
status->kind = TARGET_WAITKIND_EXITED;
|
|
status->value.integer = WEXITSTATUS (wstat);
|
|
lynx_debug ("process exited with code: %d", status->value.integer);
|
|
}
|
|
else if (WIFSIGNALED (wstat))
|
|
{
|
|
status->kind = TARGET_WAITKIND_SIGNALLED;
|
|
status->value.integer = target_signal_from_host (WTERMSIG (wstat));
|
|
lynx_debug ("process terminated with code: %d",
|
|
status->value.integer);
|
|
}
|
|
else
|
|
{
|
|
/* Not sure what happened if we get here, or whether we can
|
|
in fact get here. But if we do, handle the event the best
|
|
we can. */
|
|
status->kind = TARGET_WAITKIND_STOPPED;
|
|
status->value.integer = target_signal_from_host (0);
|
|
lynx_debug ("unknown event ????");
|
|
}
|
|
|
|
/* SIGTRAP events are generated for situations other than single-step/
|
|
breakpoint events (Eg. new-thread events). Handle those other types
|
|
of events, and resume the execution if necessary. */
|
|
if (status->kind == TARGET_WAITKIND_STOPPED
|
|
&& status->value.integer == TARGET_SIGNAL_TRAP)
|
|
{
|
|
const int realsig = lynx_ptrace (PTRACE_GETTRACESIG, new_ptid, 0, 0, 0);
|
|
|
|
lynx_debug ("(realsig = %d)", realsig);
|
|
switch (realsig)
|
|
{
|
|
case SIGNEWTHREAD:
|
|
/* We just added the new thread above. No need to do anything
|
|
further. Just resume the execution again. */
|
|
lynx_continue (ptid);
|
|
goto retry;
|
|
|
|
case SIGTHREADEXIT:
|
|
remove_thread (find_thread_ptid (new_ptid));
|
|
lynx_continue (ptid);
|
|
goto retry;
|
|
}
|
|
}
|
|
|
|
return new_ptid;
|
|
}
|
|
|
|
/* A wrapper around lynx_wait_1 that also prints debug traces when
|
|
such debug traces have been activated. */
|
|
|
|
static ptid_t
|
|
lynx_wait (ptid_t ptid, struct target_waitstatus *status, int options)
|
|
{
|
|
ptid_t new_ptid;
|
|
|
|
lynx_debug ("lynx_wait (pid = %d, tid = %ld)",
|
|
lynx_ptid_get_pid (ptid), lynx_ptid_get_tid (ptid));
|
|
new_ptid = lynx_wait_1 (ptid, status, options);
|
|
lynx_debug (" -> (pid=%d, tid=%ld, status->kind = %d)",
|
|
lynx_ptid_get_pid (new_ptid), lynx_ptid_get_tid (new_ptid),
|
|
status->kind);
|
|
return new_ptid;
|
|
}
|
|
|
|
/* Implement the kill target_ops method. */
|
|
|
|
static int
|
|
lynx_kill (int pid)
|
|
{
|
|
ptid_t ptid = lynx_ptid_build (pid, 0);
|
|
struct target_waitstatus status;
|
|
struct process_info *process;
|
|
|
|
process = find_process_pid (pid);
|
|
if (process == NULL)
|
|
return -1;
|
|
|
|
lynx_ptrace (PTRACE_KILL, ptid, 0, 0, 0);
|
|
lynx_wait (ptid, &status, 0);
|
|
the_target->mourn (process);
|
|
return 0;
|
|
}
|
|
|
|
/* Implement the detach target_ops method. */
|
|
|
|
static int
|
|
lynx_detach (int pid)
|
|
{
|
|
ptid_t ptid = lynx_ptid_build (pid, 0);
|
|
struct process_info *process;
|
|
|
|
process = find_process_pid (pid);
|
|
if (process == NULL)
|
|
return -1;
|
|
|
|
lynx_ptrace (PTRACE_DETACH, ptid, 0, 0, 0);
|
|
the_target->mourn (process);
|
|
return 0;
|
|
}
|
|
|
|
/* Implement the mourn target_ops method. */
|
|
|
|
static void
|
|
lynx_mourn (struct process_info *proc)
|
|
{
|
|
lynx_clear_inferiors ();
|
|
}
|
|
|
|
/* Implement the join target_ops method. */
|
|
|
|
static void
|
|
lynx_join (int pid)
|
|
{
|
|
/* The PTRACE_DETACH is sufficient to detach from the process.
|
|
So no need to do anything extra. */
|
|
}
|
|
|
|
/* Implement the thread_alive target_ops method. */
|
|
|
|
static int
|
|
lynx_thread_alive (ptid_t ptid)
|
|
{
|
|
/* The list of threads is updated at the end of each wait, so it
|
|
should be up to date. No need to re-fetch it. */
|
|
return (find_thread_ptid (ptid) != NULL);
|
|
}
|
|
|
|
/* Implement the fetch_registers target_ops method. */
|
|
|
|
static void
|
|
lynx_fetch_registers (struct regcache *regcache, int regno)
|
|
{
|
|
struct lynx_regset_info *regset = lynx_target_regsets;
|
|
ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
|
|
|
|
lynx_debug ("lynx_fetch_registers (regno = %d)", regno);
|
|
|
|
while (regset->size >= 0)
|
|
{
|
|
char *buf;
|
|
int res;
|
|
|
|
buf = xmalloc (regset->size);
|
|
res = lynx_ptrace (regset->get_request, inferior_ptid, (int) buf, 0, 0);
|
|
if (res < 0)
|
|
perror ("ptrace");
|
|
regset->store_function (regcache, buf);
|
|
free (buf);
|
|
regset++;
|
|
}
|
|
}
|
|
|
|
/* Implement the store_registers target_ops method. */
|
|
|
|
static void
|
|
lynx_store_registers (struct regcache *regcache, int regno)
|
|
{
|
|
struct lynx_regset_info *regset = lynx_target_regsets;
|
|
ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
|
|
|
|
lynx_debug ("lynx_store_registers (regno = %d)", regno);
|
|
|
|
while (regset->size >= 0)
|
|
{
|
|
char *buf;
|
|
int res;
|
|
|
|
buf = xmalloc (regset->size);
|
|
res = lynx_ptrace (regset->get_request, inferior_ptid, (int) buf, 0, 0);
|
|
if (res == 0)
|
|
{
|
|
/* Then overlay our cached registers on that. */
|
|
regset->fill_function (regcache, buf);
|
|
/* Only now do we write the register set. */
|
|
res = lynx_ptrace (regset->set_request, inferior_ptid, (int) buf,
|
|
0, 0);
|
|
}
|
|
if (res < 0)
|
|
perror ("ptrace");
|
|
free (buf);
|
|
regset++;
|
|
}
|
|
}
|
|
|
|
/* Implement the read_memory target_ops method. */
|
|
|
|
static int
|
|
lynx_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
|
|
{
|
|
/* On LynxOS, memory reads needs to be performed in chunks the size
|
|
of int types, and they should also be aligned accordingly. */
|
|
int buf;
|
|
const int xfer_size = sizeof (buf);
|
|
CORE_ADDR addr = memaddr & -(CORE_ADDR) xfer_size;
|
|
ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
|
|
|
|
while (addr < memaddr + len)
|
|
{
|
|
int skip = 0;
|
|
int truncate = 0;
|
|
|
|
errno = 0;
|
|
if (addr < memaddr)
|
|
skip = memaddr - addr;
|
|
if (addr + xfer_size > memaddr + len)
|
|
truncate = addr + xfer_size - memaddr - len;
|
|
buf = lynx_ptrace (PTRACE_PEEKTEXT, inferior_ptid, addr, 0, 0);
|
|
if (errno)
|
|
return errno;
|
|
memcpy (myaddr + (addr - memaddr) + skip, (gdb_byte *) &buf + skip,
|
|
xfer_size - skip - truncate);
|
|
addr += xfer_size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Implement the write_memory target_ops method. */
|
|
|
|
static int
|
|
lynx_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
|
|
{
|
|
/* On LynxOS, memory writes needs to be performed in chunks the size
|
|
of int types, and they should also be aligned accordingly. */
|
|
int buf;
|
|
const int xfer_size = sizeof (buf);
|
|
CORE_ADDR addr = memaddr & -(CORE_ADDR) xfer_size;
|
|
ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
|
|
|
|
while (addr < memaddr + len)
|
|
{
|
|
int skip = 0;
|
|
int truncate = 0;
|
|
|
|
if (addr < memaddr)
|
|
skip = memaddr - addr;
|
|
if (addr + xfer_size > memaddr + len)
|
|
truncate = addr + xfer_size - memaddr - len;
|
|
if (skip > 0 || truncate > 0)
|
|
/* We need to read the memory at this address in order to preserve
|
|
the data that we are not overwriting. */
|
|
lynx_read_memory (addr, (unsigned char *) &buf, xfer_size);
|
|
if (errno)
|
|
return errno;
|
|
memcpy ((gdb_byte *) &buf + skip, myaddr + (addr - memaddr) + skip,
|
|
xfer_size - skip - truncate);
|
|
errno = 0;
|
|
lynx_ptrace (PTRACE_POKETEXT, inferior_ptid, addr, buf, 0);
|
|
if (errno)
|
|
return errno;
|
|
addr += xfer_size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Implement the kill_request target_ops method. */
|
|
|
|
static void
|
|
lynx_request_interrupt (void)
|
|
{
|
|
ptid_t inferior_ptid = thread_to_gdb_id (current_inferior);
|
|
|
|
kill (lynx_ptid_get_pid (inferior_ptid), SIGINT);
|
|
}
|
|
|
|
/* The LynxOS target_ops vector. */
|
|
|
|
static struct target_ops lynx_target_ops = {
|
|
lynx_create_inferior,
|
|
lynx_attach,
|
|
lynx_kill,
|
|
lynx_detach,
|
|
lynx_mourn,
|
|
lynx_join,
|
|
lynx_thread_alive,
|
|
lynx_resume,
|
|
lynx_wait,
|
|
lynx_fetch_registers,
|
|
lynx_store_registers,
|
|
NULL, /* prepare_to_access_memory */
|
|
NULL, /* done_accessing_memory */
|
|
lynx_read_memory,
|
|
lynx_write_memory,
|
|
NULL, /* look_up_symbols */
|
|
lynx_request_interrupt,
|
|
NULL, /* read_auxv */
|
|
NULL, /* insert_point */
|
|
NULL, /* remove_point */
|
|
NULL, /* stopped_by_watchpoint */
|
|
NULL, /* stopped_data_address */
|
|
NULL, /* read_offsets */
|
|
NULL, /* get_tls_address */
|
|
NULL, /* qxfer_spu */
|
|
NULL, /* hostio_last_error */
|
|
NULL, /* qxfer_osdata */
|
|
NULL, /* qxfer_siginfo */
|
|
NULL, /* supports_non_stop */
|
|
NULL, /* async */
|
|
NULL, /* start_non_stop */
|
|
NULL, /* supports_multi_process */
|
|
NULL, /* handle_monitor_command */
|
|
};
|
|
|
|
void
|
|
initialize_low (void)
|
|
{
|
|
set_target_ops (&lynx_target_ops);
|
|
the_low_target.arch_setup ();
|
|
}
|
|
|