/* Low level interface to ptrace, for the remote server for GDB.
- Copyright 1995, 1996, 1998, 1999, 2000, 2001, 2002
- Free Software Foundation, Inc.
+ Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ 2006, 2007 Free Software Foundation, Inc.
This file is part of GDB.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
#include "server.h"
#include "linux-low.h"
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
+#include <errno.h>
+#include <sys/syscall.h>
+
+#ifndef PTRACE_GETSIGINFO
+# define PTRACE_GETSIGINFO 0x4202
+# define PTRACE_SETSIGINFO 0x4203
+#endif
+
+#ifdef __UCLIBC__
+#if !(defined(__UCLIBC_HAS_MMU__) || defined(__ARCH_HAS_MMU__))
+#define HAS_NOMMU
+#endif
+#endif
/* ``all_threads'' is keyed by the LWP ID - it should be the thread ID instead,
however. This requires changing the ID in place when we go from !using_threads
int using_threads;
static void linux_resume_one_process (struct inferior_list_entry *entry,
- int step, int signal);
-static void linux_resume (int step, int signal);
+ int step, int signal, siginfo_t *info);
+static void linux_resume (struct thread_resume *resume_info);
static void stop_all_processes (void);
static int linux_wait_for_event (struct thread_info *child);
struct pending_signals
{
int signal;
+ siginfo_t info;
struct pending_signals *prev;
};
static int use_regsets_p = 1;
#endif
-extern int errno;
-
-int debug_threads = 0;
-
#define pid_of(proc) ((proc)->head.id)
/* FIXME: Delete eventually. */
}
static void *
-add_process (int pid)
+add_process (unsigned long pid)
{
struct process_info *process;
void *new_process;
int pid;
+#if defined(__UCLIBC__) && defined(HAS_NOMMU)
+ pid = vfork ();
+#else
pid = fork ();
+#endif
if (pid < 0)
perror_with_name ("fork");
setpgid (0, 0);
- execv (program, allargs);
+ execvp (program, allargs);
fprintf (stderr, "Cannot exec %s: %s.\n", program,
strerror (errno));
}
new_process = add_process (pid);
- add_thread (pid, new_process);
+ add_thread (pid, new_process, pid);
return pid;
}
/* Attach to an inferior process. */
void
-linux_attach_lwp (int pid, int tid)
+linux_attach_lwp (unsigned long pid, unsigned long tid)
{
struct process_info *new_process;
if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0)
{
- fprintf (stderr, "Cannot attach to process %d: %s (%d)\n", pid,
- errno < sys_nerr ? sys_errlist[errno] : "unknown error",
- errno);
+ fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid,
+ strerror (errno), errno);
fflush (stderr);
/* If we fail to attach to an LWP, just return. */
}
new_process = (struct process_info *) add_process (pid);
- add_thread (tid, new_process);
+ add_thread (tid, new_process, pid);
/* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH
brings it to a halt. We should ignore that SIGSTOP and resume the process
}
int
-linux_attach (int pid)
+linux_attach (unsigned long pid)
{
struct process_info *process;
struct process_info *process = get_thread_process (thread);
int wstat;
+ /* We avoid killing the first thread here, because of a Linux kernel (at
+ least 2.6.0-test7 through 2.6.8-rc4) bug; if we kill the parent before
+ the children get a chance to be reaped, it will remain a zombie
+ forever. */
+ if (entry == all_threads.head)
+ return;
+
do
{
ptrace (PTRACE_KILL, pid_of (process), 0, 0);
} while (WIFSTOPPED (wstat));
}
-/* Return nonzero if the given thread is still alive. */
static void
linux_kill (void)
{
+ struct thread_info *thread = (struct thread_info *) all_threads.head;
+ struct process_info *process;
+ int wstat;
+
+ if (thread == NULL)
+ return;
+
for_each_inferior (&all_threads, linux_kill_one_process);
+
+ /* See the comment in linux_kill_one_process. We did not kill the first
+ thread in the list, so do so now. */
+ process = get_thread_process (thread);
+ do
+ {
+ ptrace (PTRACE_KILL, pid_of (process), 0, 0);
+
+ /* Make sure it died. The loop is most likely unnecessary. */
+ wstat = linux_wait_for_event (thread);
+ } while (WIFSTOPPED (wstat));
+}
+
+static void
+linux_detach_one_process (struct inferior_list_entry *entry)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+ struct process_info *process = get_thread_process (thread);
+
+ ptrace (PTRACE_DETACH, pid_of (process), 0, 0);
}
static int
-linux_thread_alive (int tid)
+linux_detach (void)
+{
+ for_each_inferior (&all_threads, linux_detach_one_process);
+ return 0;
+}
+
+static void
+linux_join (void)
+{
+ extern unsigned long signal_pid;
+ int status, ret;
+
+ do {
+ ret = waitpid (signal_pid, &status, 0);
+ if (WIFEXITED (status) || WIFSIGNALED (status))
+ break;
+ } while (ret != -1 || errno != ECHILD);
+}
+
+/* Return nonzero if the given thread is still alive. */
+static int
+linux_thread_alive (unsigned long tid)
{
if (find_inferior_id (&all_threads, tid) != NULL)
return 1;
(*the_low_target.set_pc) (stop_pc);
/* We consumed the pending SIGTRAP. */
+ event_child->pending_is_breakpoint = 0;
event_child->status_pending_p = 0;
event_child->status_pending = 0;
So instead of reporting the old SIGTRAP, pretend we got to
the breakpoint just after it was removed instead of just
before; resume the process. */
- linux_resume_one_process (&process->head, 0, 0);
+ linux_resume_one_process (&process->head, 0, 0, NULL);
return 0;
}
(*childp)->stopped = 1;
(*childp)->pending_is_breakpoint = 0;
+ (*childp)->last_status = *wstatp;
+
if (debug_threads
&& WIFSTOPPED (*wstatp))
{
/* Check for a process with a pending status. */
/* It is possible that the user changed the pending task's registers since
it stopped. We correctly handle the change of PC if we hit a breakpoint
- (in check_removed_breakpoints); signals should be reported anyway. */
+ (in check_removed_breakpoint); signals should be reported anyway. */
if (child == NULL)
{
event_child = (struct process_info *)
find_inferior (&all_processes, status_pending_p, NULL);
if (debug_threads && event_child)
- fprintf (stderr, "Got a pending child %d\n", event_child->lwpid);
+ fprintf (stderr, "Got a pending child %ld\n", event_child->lwpid);
}
else
{
if (event_child->status_pending_p)
{
if (debug_threads)
- fprintf (stderr, "Got an event from pending child %d (%04x)\n",
+ fprintf (stderr, "Got an event from pending child %ld (%04x)\n",
event_child->lwpid, event_child->status_pending);
wstat = event_child->status_pending;
event_child->status_pending_p = 0;
current_inferior = (struct thread_info *)
find_inferior_id (&all_threads, event_child->tid);
- if (using_threads)
+ /* Check for thread exit. */
+ if (using_threads && ! WIFSTOPPED (wstat))
{
- /* Check for thread exit. */
- if (! WIFSTOPPED (wstat))
- {
- if (debug_threads)
- fprintf (stderr, "Thread %d (LWP %d) exiting\n",
- event_child->tid, event_child->head.id);
+ if (debug_threads)
+ fprintf (stderr, "Thread %ld (LWP %ld) exiting\n",
+ event_child->tid, event_child->head.id);
- /* If the last thread is exiting, just return. */
- if (all_threads.head == all_threads.tail)
- return wstat;
+ /* If the last thread is exiting, just return. */
+ if (all_threads.head == all_threads.tail)
+ return wstat;
- dead_thread_notify (event_child->tid);
+ dead_thread_notify (event_child->tid);
- remove_inferior (&all_processes, &event_child->head);
- free (event_child);
- remove_thread (current_inferior);
- current_inferior = (struct thread_info *) all_threads.head;
+ remove_inferior (&all_processes, &event_child->head);
+ free (event_child);
+ remove_thread (current_inferior);
+ current_inferior = (struct thread_info *) all_threads.head;
- /* If we were waiting for this particular child to do something...
- well, it did something. */
- if (child != NULL)
- return wstat;
+ /* If we were waiting for this particular child to do something...
+ well, it did something. */
+ if (child != NULL)
+ return wstat;
- /* Wait for a more interesting event. */
- continue;
- }
+ /* Wait for a more interesting event. */
+ continue;
+ }
- if (WIFSTOPPED (wstat)
- && WSTOPSIG (wstat) == SIGSTOP
- && event_child->stop_expected)
- {
- if (debug_threads)
- fprintf (stderr, "Expected stop.\n");
- event_child->stop_expected = 0;
- linux_resume_one_process (&event_child->head,
- event_child->stepping, 0);
- continue;
- }
+ if (using_threads
+ && WIFSTOPPED (wstat)
+ && WSTOPSIG (wstat) == SIGSTOP
+ && event_child->stop_expected)
+ {
+ if (debug_threads)
+ fprintf (stderr, "Expected stop.\n");
+ event_child->stop_expected = 0;
+ linux_resume_one_process (&event_child->head,
+ event_child->stepping, 0, NULL);
+ continue;
+ }
- /* FIXME drow/2002-06-09: Get signal numbers from the inferior's
- thread library? */
- if (WIFSTOPPED (wstat)
- && (WSTOPSIG (wstat) == __SIGRTMIN
- || WSTOPSIG (wstat) == __SIGRTMIN + 1))
- {
- if (debug_threads)
- fprintf (stderr, "Ignored signal %d for %d (LWP %d).\n",
- WSTOPSIG (wstat), event_child->tid,
- event_child->head.id);
- linux_resume_one_process (&event_child->head,
- event_child->stepping,
- WSTOPSIG (wstat));
- continue;
- }
+ /* If GDB is not interested in this signal, don't stop other
+ threads, and don't report it to GDB. Just resume the
+ inferior right away. We do this for threading-related
+ signals as well as any that GDB specifically requested
+ we ignore. But never ignore SIGSTOP if we sent it
+ ourselves. */
+ /* FIXME drow/2002-06-09: Get signal numbers from the inferior's
+ thread library? */
+ if (WIFSTOPPED (wstat)
+ && ((using_threads && (WSTOPSIG (wstat) == __SIGRTMIN
+ || WSTOPSIG (wstat) == __SIGRTMIN + 1))
+ || (pass_signals[target_signal_from_host (WSTOPSIG (wstat))]
+ && (WSTOPSIG (wstat) != SIGSTOP
+ || !event_child->sigstop_sent))))
+ {
+ siginfo_t info, *info_p;
+
+ if (debug_threads)
+ fprintf (stderr, "Ignored signal %d for %ld (LWP %ld).\n",
+ WSTOPSIG (wstat), event_child->tid,
+ event_child->head.id);
+
+ if (ptrace (PTRACE_GETSIGINFO, event_child->lwpid, 0, &info) == 0)
+ info_p = &info;
+ else
+ info_p = NULL;
+ linux_resume_one_process (&event_child->head,
+ event_child->stepping,
+ WSTOPSIG (wstat), info_p);
+ continue;
}
/* If this event was not handled above, and is not a SIGTRAP, report
event_child->bp_reinsert = 0;
/* Clear the single-stepping flag and SIGTRAP as we resume. */
- linux_resume_one_process (&event_child->head, 0, 0);
+ linux_resume_one_process (&event_child->head, 0, 0, NULL);
continue;
}
if (check_breakpoints (stop_pc) != 0)
{
/* We hit one of our own breakpoints. We mark it as a pending
- breakpoint, so that check_removed_breakpoints () will do the PC
+ breakpoint, so that check_removed_breakpoint () will do the PC
adjustment for us at the appropriate time. */
event_child->pending_is_breakpoint = 1;
event_child->pending_stop_pc = stop_pc;
{
event_child->bp_reinsert = stop_pc;
uninsert_breakpoint (stop_pc);
- linux_resume_one_process (&event_child->head, 1, 0);
+ linux_resume_one_process (&event_child->head, 1, 0, NULL);
}
else
{
reinsert_breakpoint_by_bp
(stop_pc, (*the_low_target.breakpoint_reinsert_addr) ());
- linux_resume_one_process (&event_child->head, 0, 0);
+ linux_resume_one_process (&event_child->head, 0, 0, NULL);
}
continue;
/* If we were single-stepping, we definitely want to report the
SIGTRAP. The single-step operation has completed, so also
- clear the stepping flag; in general this does not matter,
+ clear the stepping flag; in general this does not matter,
because the SIGTRAP will be reported to the client, which
will give us a new action for this thread, but clear it for
consistency anyway. It's safe to clear the stepping flag
because the only consumer of get_stop_pc () after this point
- is check_removed_breakpoints, and pending_is_breakpoint is not
+ is check_removed_breakpoint, and pending_is_breakpoint is not
set. It might be wiser to use a step_completed flag instead. */
if (event_child->stepping)
{
then we need to make sure we restart the other threads. We could
pick a thread at random or restart all; restarting all is less
arbitrary. */
- if (cont_thread > 0)
+ if (cont_thread != 0 && cont_thread != -1)
{
child = (struct thread_info *) find_inferior_id (&all_threads,
cont_thread);
/* No stepping, no signal - unless one is pending already, of course. */
if (child == NULL)
- linux_resume (0, 0);
+ {
+ struct thread_resume resume_info;
+ resume_info.thread = -1;
+ resume_info.step = resume_info.sig = resume_info.leave_stopped = 0;
+ linux_resume (&resume_info);
+ }
}
enable_async_io ();
+ unblock_async_io ();
w = linux_wait_for_event (child);
stop_all_processes ();
disable_async_io ();
fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w));
*status = 'W';
clear_inferiors ();
- return ((unsigned char) WEXITSTATUS (w));
+ free (all_processes.head);
+ all_processes.head = all_processes.tail = NULL;
+ return WEXITSTATUS (w);
}
else if (!WIFSTOPPED (w))
{
fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w));
- clear_inferiors ();
*status = 'X';
- return ((unsigned char) WTERMSIG (w));
+ clear_inferiors ();
+ free (all_processes.head);
+ all_processes.head = all_processes.tail = NULL;
+ return target_signal_from_host (WTERMSIG (w));
}
}
else
}
*status = 'T';
- return ((unsigned char) WSTOPSIG (w));
+ return target_signal_from_host (WSTOPSIG (w));
+}
+
+/* Send a signal to an LWP. For LinuxThreads, kill is enough; however, if
+ thread groups are in use, we need to use tkill. */
+
+static int
+kill_lwp (unsigned long lwpid, int signo)
+{
+ static int tkill_failed;
+
+ errno = 0;
+
+#ifdef SYS_tkill
+ if (!tkill_failed)
+ {
+ int ret = syscall (SYS_tkill, lwpid, signo);
+ if (errno != ENOSYS)
+ return ret;
+ errno = 0;
+ tkill_failed = 1;
+ }
+#endif
+
+ return kill (lwpid, signo);
}
static void
}
if (debug_threads)
- fprintf (stderr, "Sending sigstop to process %d\n", process->head.id);
+ fprintf (stderr, "Sending sigstop to process %ld\n", process->head.id);
- kill (process->head.id, SIGSTOP);
+ kill_lwp (process->head.id, SIGSTOP);
process->sigstop_sent = 1;
}
{
struct process_info *process = (struct process_info *) entry;
struct thread_info *saved_inferior, *thread;
- int wstat, saved_tid;
+ int wstat;
+ unsigned long saved_tid;
if (process->stopped)
return;
static void
linux_resume_one_process (struct inferior_list_entry *entry,
- int step, int signal)
+ int step, int signal, siginfo_t *info)
{
struct process_info *process = (struct process_info *) entry;
struct thread_info *saved_inferior;
p_sig = malloc (sizeof (*p_sig));
p_sig->prev = process->pending_signals;
p_sig->signal = signal;
+ if (info == NULL)
+ memset (&p_sig->info, 0, sizeof (siginfo_t));
+ else
+ memcpy (&p_sig->info, info, sizeof (siginfo_t));
process->pending_signals = p_sig;
}
- if (process->status_pending_p)
+ if (process->status_pending_p && !check_removed_breakpoint (process))
return;
saved_inferior = current_inferior;
current_inferior = get_process_thread (process);
if (debug_threads)
- fprintf (stderr, "Resuming process %d (%s, signal %d, stop %s)\n", inferior_pid,
+ fprintf (stderr, "Resuming process %ld (%s, signal %d, stop %s)\n", inferior_pid,
step ? "step" : "continue", signal,
process->stop_expected ? "expected" : "not expected");
check_removed_breakpoint (process);
- if (debug_threads && the_low_target.get_pc != NULL)
+ if (debug_threads && the_low_target.get_pc != NULL)
{
fprintf (stderr, " ");
- (long) (*the_low_target.get_pc) ();
+ (*the_low_target.get_pc) ();
}
/* If we have pending signals, consume one unless we are trying to reinsert
p_sig = &(*p_sig)->prev;
signal = (*p_sig)->signal;
+ if ((*p_sig)->info.si_signo != 0)
+ ptrace (PTRACE_SETSIGINFO, process->lwpid, 0, &(*p_sig)->info);
+
free (*p_sig);
*p_sig = NULL;
}
perror_with_name ("ptrace");
}
-/* This function is called once per process other than the first
- one. The first process we are told the signal to continue
- with, and whether to step or continue; for all others, any
- existing signals will be marked in status_pending_p to be
- reported momentarily, and we preserve the stepping flag. */
+static struct thread_resume *resume_ptr;
+
+/* This function is called once per thread. We look up the thread
+ in RESUME_PTR, and mark the thread with a pointer to the appropriate
+ resume request.
+
+ This algorithm is O(threads * resume elements), but resume elements
+ is small (and will remain small at least until GDB supports thread
+ suspension). */
+static void
+linux_set_resume_request (struct inferior_list_entry *entry)
+{
+ struct process_info *process;
+ struct thread_info *thread;
+ int ndx;
+
+ thread = (struct thread_info *) entry;
+ process = get_thread_process (thread);
+
+ ndx = 0;
+ while (resume_ptr[ndx].thread != -1 && resume_ptr[ndx].thread != entry->id)
+ ndx++;
+
+ process->resume = &resume_ptr[ndx];
+}
+
+/* This function is called once per thread. We check the thread's resume
+ request, which will tell us whether to resume, step, or leave the thread
+ stopped; and what signal, if any, it should be sent. For threads which
+ we aren't explicitly told otherwise, we preserve the stepping flag; this
+ is used for stepping over gdbserver-placed breakpoints. */
+
static void
-linux_continue_one_process (struct inferior_list_entry *entry)
+linux_continue_one_thread (struct inferior_list_entry *entry)
{
struct process_info *process;
+ struct thread_info *thread;
+ int step;
- process = (struct process_info *) entry;
- linux_resume_one_process (entry, process->stepping, 0);
+ thread = (struct thread_info *) entry;
+ process = get_thread_process (thread);
+
+ if (process->resume->leave_stopped)
+ return;
+
+ if (process->resume->thread == -1)
+ step = process->stepping || process->resume->step;
+ else
+ step = process->resume->step;
+
+ linux_resume_one_process (&process->head, step, process->resume->sig, NULL);
+
+ process->resume = NULL;
}
+/* This function is called once per thread. We check the thread's resume
+ request, which will tell us whether to resume, step, or leave the thread
+ stopped; and what signal, if any, it should be sent. We queue any needed
+ signals, since we won't actually resume. We already have a pending event
+ to report, so we don't need to preserve any step requests; they should
+ be re-issued if necessary. */
+
static void
-linux_resume (int step, int signal)
+linux_queue_one_thread (struct inferior_list_entry *entry)
{
struct process_info *process;
+ struct thread_info *thread;
- process = get_thread_process (current_inferior);
+ thread = (struct thread_info *) entry;
+ process = get_thread_process (thread);
- /* If the current process has a status pending, this signal will
- be enqueued and sent later. */
- linux_resume_one_process (&process->head, step, signal);
+ if (process->resume->leave_stopped)
+ return;
- if (cont_thread == 0 || cont_thread == -1)
- for_each_inferior (&all_processes, linux_continue_one_process);
+ /* If we have a new signal, enqueue the signal. */
+ if (process->resume->sig != 0)
+ {
+ struct pending_signals *p_sig;
+ p_sig = malloc (sizeof (*p_sig));
+ p_sig->prev = process->pending_signals;
+ p_sig->signal = process->resume->sig;
+ memset (&p_sig->info, 0, sizeof (siginfo_t));
+
+ /* If this is the same signal we were previously stopped by,
+ make sure to queue its siginfo. We can ignore the return
+ value of ptrace; if it fails, we'll skip
+ PTRACE_SETSIGINFO. */
+ if (WIFSTOPPED (process->last_status)
+ && WSTOPSIG (process->last_status) == process->resume->sig)
+ ptrace (PTRACE_GETSIGINFO, process->lwpid, 0, &p_sig->info);
+
+ process->pending_signals = p_sig;
+ }
+
+ process->resume = NULL;
+}
+
+/* Set DUMMY if this process has an interesting status pending. */
+static int
+resume_status_pending_p (struct inferior_list_entry *entry, void *flag_p)
+{
+ struct process_info *process = (struct process_info *) entry;
+
+ /* Processes which will not be resumed are not interesting, because
+ we might not wait for them next time through linux_wait. */
+ if (process->resume->leave_stopped)
+ return 0;
+
+ /* If this thread has a removed breakpoint, we won't have any
+ events to report later, so check now. check_removed_breakpoint
+ may clear status_pending_p. We avoid calling check_removed_breakpoint
+ for any thread that we are not otherwise going to resume - this
+ lets us preserve stopped status when two threads hit a breakpoint.
+ GDB removes the breakpoint to single-step a particular thread
+ past it, then re-inserts it and resumes all threads. We want
+ to report the second thread without resuming it in the interim. */
+ if (process->status_pending_p)
+ check_removed_breakpoint (process);
+
+ if (process->status_pending_p)
+ * (int *) flag_p = 1;
+
+ return 0;
+}
+
+static void
+linux_resume (struct thread_resume *resume_info)
+{
+ int pending_flag;
+
+ /* Yes, the use of a global here is rather ugly. */
+ resume_ptr = resume_info;
+
+ for_each_inferior (&all_threads, linux_set_resume_request);
+
+ /* If there is a thread which would otherwise be resumed, which
+ has a pending status, then don't resume any threads - we can just
+ report the pending status. Make sure to queue any signals
+ that would otherwise be sent. */
+ pending_flag = 0;
+ find_inferior (&all_processes, resume_status_pending_p, &pending_flag);
+
+ if (debug_threads)
+ {
+ if (pending_flag)
+ fprintf (stderr, "Not resuming, pending status\n");
+ else
+ fprintf (stderr, "Resuming, no pending status\n");
+ }
+
+ if (pending_flag)
+ for_each_inferior (&all_threads, linux_queue_one_thread);
+ else
+ {
+ block_async_io ();
+ enable_async_io ();
+ for_each_inferior (&all_threads, linux_continue_one_thread);
+ }
}
#ifdef HAVE_LINUX_USRREGS
fetch_register (int regno)
{
CORE_ADDR regaddr;
- register int i;
+ int i, size;
char *buf;
if (regno >= the_low_target.num_regs)
regaddr = register_addr (regno);
if (regaddr == -1)
return;
- buf = alloca (register_size (regno));
- for (i = 0; i < register_size (regno); i += sizeof (PTRACE_XFER_TYPE))
+ size = (register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
+ & - sizeof (PTRACE_XFER_TYPE);
+ buf = alloca (size);
+ for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
{
errno = 0;
*(PTRACE_XFER_TYPE *) (buf + i) =
goto error_exit;
}
}
- supply_register (regno, buf);
+ if (the_low_target.left_pad_xfer
+ && register_size (regno) < sizeof (PTRACE_XFER_TYPE))
+ supply_register (regno, (buf + sizeof (PTRACE_XFER_TYPE)
+ - register_size (regno)));
+ else
+ supply_register (regno, buf);
error_exit:;
}
usr_store_inferior_registers (int regno)
{
CORE_ADDR regaddr;
- int i;
+ int i, size;
char *buf;
if (regno >= 0)
if (regaddr == -1)
return;
errno = 0;
- buf = alloca (register_size (regno));
- collect_register (regno, buf);
- for (i = 0; i < register_size (regno); i += sizeof (PTRACE_XFER_TYPE))
+ size = (register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
+ & - sizeof (PTRACE_XFER_TYPE);
+ buf = alloca (size);
+ memset (buf, 0, size);
+ if (the_low_target.left_pad_xfer
+ && register_size (regno) < sizeof (PTRACE_XFER_TYPE))
+ collect_register (regno, (buf + sizeof (PTRACE_XFER_TYPE)
+ - register_size (regno)));
+ else
+ collect_register (regno, buf);
+ for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
{
errno = 0;
ptrace (PTRACE_POKEUSER, inferior_pid, (PTRACE_ARG3_TYPE) regaddr,
- *(int *) (buf + i));
+ *(PTRACE_XFER_TYPE *) (buf + i));
if (errno != 0)
{
if ((*the_low_target.cannot_store_register) (regno) == 0)
return;
}
}
- regaddr += sizeof (int);
+ regaddr += sizeof (PTRACE_XFER_TYPE);
}
}
else
regsets_fetch_inferior_registers ()
{
struct regset_info *regset;
+ int saw_general_regs = 0;
regset = target_regsets;
else
{
char s[256];
- sprintf (s, "ptrace(regsets_fetch_inferior_registers) PID=%d",
+ sprintf (s, "ptrace(regsets_fetch_inferior_registers) PID=%ld",
inferior_pid);
perror (s);
}
}
+ else if (regset->type == GENERAL_REGS)
+ saw_general_regs = 1;
regset->store_function (buf);
regset ++;
}
- return 0;
+ if (saw_general_regs)
+ return 0;
+ else
+ return 1;
}
static int
regsets_store_inferior_registers ()
{
struct regset_info *regset;
+ int saw_general_regs = 0;
regset = target_regsets;
}
buf = malloc (regset->size);
- regset->fill_function (buf);
- res = ptrace (regset->set_request, inferior_pid, 0, buf);
+
+ /* First fill the buffer with the current register set contents,
+ in case there are any items in the kernel's regset that are
+ not in gdbserver's regcache. */
+ res = ptrace (regset->get_request, inferior_pid, 0, buf);
+
+ if (res == 0)
+ {
+ /* Then overlay our cached registers on that. */
+ regset->fill_function (buf);
+
+ /* Only now do we write the register set. */
+ res = ptrace (regset->set_request, inferior_pid, 0, buf);
+ }
+
if (res < 0)
{
if (errno == EIO)
perror ("Warning: ptrace(regsets_store_inferior_registers)");
}
}
+ else if (regset->type == GENERAL_REGS)
+ saw_general_regs = 1;
regset ++;
free (buf);
}
+ if (saw_general_regs)
+ return 0;
+ else
+ return 1;
return 0;
}
/* Copy LEN bytes from inferior's memory starting at MEMADDR
to debugger memory starting at MYADDR. */
-static void
-linux_read_memory (CORE_ADDR memaddr, char *myaddr, int len)
+static int
+linux_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
{
register int i;
/* Round starting address down to longword boundary. */
register CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
/* Round ending address up; get number of longwords that makes. */
- register int count
- = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
+ register int count
+ = (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
/ sizeof (PTRACE_XFER_TYPE);
/* Allocate buffer of that many longwords. */
- register PTRACE_XFER_TYPE *buffer
+ register PTRACE_XFER_TYPE *buffer
= (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE));
/* Read all the longwords */
for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
{
+ errno = 0;
buffer[i] = ptrace (PTRACE_PEEKTEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, 0);
+ if (errno)
+ return errno;
}
/* Copy appropriate bytes out of the buffer. */
memcpy (myaddr, (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), len);
+
+ return 0;
}
/* Copy LEN bytes of data from debugger memory at MYADDR
returns the value of errno. */
static int
-linux_write_memory (CORE_ADDR memaddr, const char *myaddr, int len)
+linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
{
register int i;
/* Round starting address down to longword boundary. */
#endif
}
-\f
+static void
+linux_request_interrupt (void)
+{
+ extern unsigned long signal_pid;
+
+ if (cont_thread != 0 && cont_thread != -1)
+ {
+ struct process_info *process;
+
+ process = get_thread_process (current_inferior);
+ kill_lwp (process->lwpid, SIGINT);
+ }
+ else
+ kill_lwp (signal_pid, SIGINT);
+}
+
+/* Copy LEN bytes from inferior's auxiliary vector starting at OFFSET
+ to debugger memory starting at MYADDR. */
+
+static int
+linux_read_auxv (CORE_ADDR offset, unsigned char *myaddr, unsigned int len)
+{
+ char filename[PATH_MAX];
+ int fd, n;
+
+ snprintf (filename, sizeof filename, "/proc/%ld/auxv", inferior_pid);
+
+ fd = open (filename, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ if (offset != (CORE_ADDR) 0
+ && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
+ n = -1;
+ else
+ n = read (fd, myaddr, len);
+
+ close (fd);
+
+ return n;
+}
+
+/* These watchpoint related wrapper functions simply pass on the function call
+ if the target has registered a corresponding function. */
+
+static int
+linux_insert_watchpoint (char type, CORE_ADDR addr, int len)
+{
+ if (the_low_target.insert_watchpoint != NULL)
+ return the_low_target.insert_watchpoint (type, addr, len);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+linux_remove_watchpoint (char type, CORE_ADDR addr, int len)
+{
+ if (the_low_target.remove_watchpoint != NULL)
+ return the_low_target.remove_watchpoint (type, addr, len);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+linux_stopped_by_watchpoint (void)
+{
+ if (the_low_target.stopped_by_watchpoint != NULL)
+ return the_low_target.stopped_by_watchpoint ();
+ else
+ return 0;
+}
+
+static CORE_ADDR
+linux_stopped_data_address (void)
+{
+ if (the_low_target.stopped_data_address != NULL)
+ return the_low_target.stopped_data_address ();
+ else
+ return 0;
+}
+
+#if defined(__UCLIBC__) && defined(HAS_NOMMU)
+#if defined(__mcoldfire__)
+/* These should really be defined in the kernel's ptrace.h header. */
+#define PT_TEXT_ADDR 49*4
+#define PT_DATA_ADDR 50*4
+#define PT_TEXT_END_ADDR 51*4
+#endif
+
+/* Under uClinux, programs are loaded at non-zero offsets, which we need
+ to tell gdb about. */
+
+static int
+linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
+{
+#if defined(PT_TEXT_ADDR) && defined(PT_DATA_ADDR) && defined(PT_TEXT_END_ADDR)
+ unsigned long text, text_end, data;
+ int pid = get_thread_process (current_inferior)->head.id;
+
+ errno = 0;
+
+ text = ptrace (PTRACE_PEEKUSER, pid, (long)PT_TEXT_ADDR, 0);
+ text_end = ptrace (PTRACE_PEEKUSER, pid, (long)PT_TEXT_END_ADDR, 0);
+ data = ptrace (PTRACE_PEEKUSER, pid, (long)PT_DATA_ADDR, 0);
+
+ if (errno == 0)
+ {
+ /* Both text and data offsets produced at compile-time (and so
+ used by gdb) are relative to the beginning of the program,
+ with the data segment immediately following the text segment.
+ However, the actual runtime layout in memory may put the data
+ somewhere else, so when we send gdb a data base-address, we
+ use the real data base address and subtract the compile-time
+ data base-address from it (which is just the length of the
+ text segment). BSS immediately follows data in both
+ cases. */
+ *text_p = text;
+ *data_p = data - (text_end - text);
+
+ return 1;
+ }
+#endif
+ return 0;
+}
+#endif
+
+static const char *
+linux_arch_string (void)
+{
+ return the_low_target.arch_string;
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
linux_kill,
+ linux_detach,
+ linux_join,
linux_thread_alive,
linux_resume,
linux_wait,
linux_read_memory,
linux_write_memory,
linux_look_up_symbols,
+ linux_request_interrupt,
+ linux_read_auxv,
+ linux_insert_watchpoint,
+ linux_remove_watchpoint,
+ linux_stopped_by_watchpoint,
+ linux_stopped_data_address,
+#if defined(__UCLIBC__) && defined(HAS_NOMMU)
+ linux_read_offsets,
+#else
+ NULL,
+#endif
+#ifdef USE_THREAD_DB
+ thread_db_get_tls_address,
+#else
+ NULL,
+#endif
+ linux_arch_string,
};
static void