Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(106)

Unified Diff: gdb/gdbserver/linux-low.c

Issue 11969036: Merge GDB 7.5.1 (Closed) Base URL: http://git.chromium.org/native_client/nacl-gdb.git@master
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « gdb/gdbserver/linux-low.h ('k') | gdb/gdbserver/linux-m32r-low.c » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: gdb/gdbserver/linux-low.c
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 1a1b39a2111237f0ec15361ad5d5e25e6535b23b..a476031faf48be31b9dc5911771caf525d23cbbc 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -19,6 +19,7 @@
#include "server.h"
#include "linux-low.h"
#include "linux-osdata.h"
+#include "agent.h"
#include <sys/wait.h>
#include <stdio.h>
@@ -77,10 +78,42 @@
#ifdef __UCLIBC__
#if !(defined(__UCLIBC_HAS_MMU__) || defined(__ARCH_HAS_MMU__))
+/* PTRACE_TEXT_ADDR and friends. */
+#include <asm/ptrace.h>
#define HAS_NOMMU
#endif
#endif
+#ifndef HAVE_ELF32_AUXV_T
+/* Copied from glibc's elf.h. */
+typedef struct
+{
+ uint32_t a_type; /* Entry type */
+ union
+ {
+ uint32_t a_val; /* Integer value */
+ /* We use to have pointer elements added here. We cannot do that,
+ though, since it does not work when using 32-bit definitions
+ on 64-bit platforms and vice versa. */
+ } a_un;
+} Elf32_auxv_t;
+#endif
+
+#ifndef HAVE_ELF64_AUXV_T
+/* Copied from glibc's elf.h. */
+typedef struct
+{
+ uint64_t a_type; /* Entry type */
+ union
+ {
+ uint64_t a_val; /* Integer value */
+ /* We use to have pointer elements added here. We cannot do that,
+ though, since it does not work when using 32-bit definitions
+ on 64-bit platforms and vice versa. */
+ } a_un;
+} Elf64_auxv_t;
+#endif
+
/* ``all_threads'' is keyed by the LWP ID, which we use as the GDB protocol
representation of the thread ID.
@@ -92,14 +125,69 @@
struct inferior_list all_lwps;
-/* A list of all unknown processes which receive stop signals. Some other
- process will presumably claim each of these as forked children
- momentarily. */
+/* A list of all unknown processes which receive stop signals. Some
+ other process will presumably claim each of these as forked
+ children momentarily. */
+
+struct simple_pid_list
+{
+ /* The process ID. */
+ int pid;
+
+ /* The status as reported by waitpid. */
+ int status;
+
+ /* Next in chain. */
+ struct simple_pid_list *next;
+};
+struct simple_pid_list *stopped_pids;
+
+/* Trivial list manipulation functions to keep track of a list of new
+ stopped processes. */
+
+static void
+add_to_pid_list (struct simple_pid_list **listp, int pid, int status)
+{
+ struct simple_pid_list *new_pid = xmalloc (sizeof (struct simple_pid_list));
+
+ new_pid->pid = pid;
+ new_pid->status = status;
+ new_pid->next = *listp;
+ *listp = new_pid;
+}
+
+static int
+pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
+{
+ struct simple_pid_list **p;
+
+ for (p = listp; *p != NULL; p = &(*p)->next)
+ if ((*p)->pid == pid)
+ {
+ struct simple_pid_list *next = (*p)->next;
+
+ *statusp = (*p)->status;
+ xfree (*p);
+ *p = next;
+ return 1;
+ }
+ return 0;
+}
+
+enum stopping_threads_kind
+ {
+ /* Not stopping threads presently. */
+ NOT_STOPPING_THREADS,
+
+ /* Stopping threads. */
+ STOPPING_THREADS,
-struct inferior_list stopped_pids;
+ /* Stopping and suspending threads. */
+ STOPPING_AND_SUSPENDING_THREADS
+ };
-/* FIXME this is a bit of a hack, and could be removed. */
-int stopping_threads;
+/* This is set while stop_all_lwps is in effect. */
+enum stopping_threads_kind stopping_threads = NOT_STOPPING_THREADS;
/* FIXME make into a target method? */
int using_threads = 1;
@@ -167,10 +255,6 @@ struct pending_signals
struct pending_signals *prev;
};
-#define PTRACE_ARG3_TYPE void *
-#define PTRACE_ARG4_TYPE void *
-#define PTRACE_XFER_TYPE long
-
#ifdef HAVE_LINUX_REGSETS
static char *disabled_regsets;
static int num_regsets;
@@ -186,50 +270,30 @@ static int linux_event_pipe[2] = { -1, -1 };
static void send_sigstop (struct lwp_info *lwp);
static void wait_for_sigstop (struct inferior_list_entry *entry);
-/* Accepts an integer PID; Returns a string representing a file that
- can be opened to get info for the child process.
- Space for the result is malloc'd, caller must free. */
-
-char *
-linux_child_pid_to_exec_file (int pid)
-{
- char *name1, *name2;
-
- name1 = xmalloc (MAXPATHLEN);
- name2 = xmalloc (MAXPATHLEN);
- memset (name2, 0, MAXPATHLEN);
-
- sprintf (name1, "/proc/%d/exe", pid);
- if (readlink (name1, name2, MAXPATHLEN) > 0)
- {
- free (name1);
- return name2;
- }
- else
- {
- free (name2);
- return name1;
- }
-}
-
/* Return non-zero if HEADER is a 64-bit ELF file. */
static int
-elf_64_header_p (const Elf64_Ehdr *header)
+elf_64_header_p (const Elf64_Ehdr *header, unsigned int *machine)
{
- return (header->e_ident[EI_MAG0] == ELFMAG0
- && header->e_ident[EI_MAG1] == ELFMAG1
- && header->e_ident[EI_MAG2] == ELFMAG2
- && header->e_ident[EI_MAG3] == ELFMAG3
- && header->e_ident[EI_CLASS] == ELFCLASS64);
+ if (header->e_ident[EI_MAG0] == ELFMAG0
+ && header->e_ident[EI_MAG1] == ELFMAG1
+ && header->e_ident[EI_MAG2] == ELFMAG2
+ && header->e_ident[EI_MAG3] == ELFMAG3)
+ {
+ *machine = header->e_machine;
+ return header->e_ident[EI_CLASS] == ELFCLASS64;
+
+ }
+ *machine = EM_NONE;
+ return -1;
}
/* Return non-zero if FILE is a 64-bit ELF file,
zero if the file is not a 64-bit ELF file,
and -1 if the file is not accessible or doesn't exist. */
-int
-elf_64_file_p (const char *file)
+static int
+elf_64_file_p (const char *file, unsigned int *machine)
{
Elf64_Ehdr header;
int fd;
@@ -245,7 +309,19 @@ elf_64_file_p (const char *file)
}
close (fd);
- return elf_64_header_p (&header);
+ return elf_64_header_p (&header, machine);
+}
+
+/* Accepts an integer PID; Returns true if the executable PID is
+ running is a 64-bit ELF file.. */
+
+int
+linux_pid_exe_is_elf_64_file (int pid, unsigned int *machine)
+{
+ char file[MAXPATHLEN];
+
+ sprintf (file, "/proc/%d/exe", pid);
+ return elf_64_file_p (file, machine);
}
static void
@@ -367,12 +443,12 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
{
ptid_t ptid;
unsigned long new_pid;
- int ret, status = W_STOPCODE (SIGSTOP);
+ int ret, status;
ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_child), 0, &new_pid);
/* If we haven't already seen the new PID stop, wait for it now. */
- if (! pull_pid_from_list (&stopped_pids, new_pid))
+ if (!pull_pid_from_list (&stopped_pids, new_pid, &status))
{
/* The new child has a pending SIGSTOP. We can't affect it until it
hits the SIGSTOP, but we're already attached. */
@@ -399,12 +475,17 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
before calling linux_resume_one_lwp. */
new_lwp->stopped = 1;
+ /* If we're suspending all threads, leave this one suspended
+ too. */
+ if (stopping_threads == STOPPING_AND_SUSPENDING_THREADS)
+ new_lwp->suspended = 1;
+
/* Normally we will get the pending SIGSTOP. But in some cases
we might get another signal delivered to the group first.
If we do get another signal, be sure not to lose it. */
if (WSTOPSIG (status) == SIGSTOP)
{
- if (stopping_threads)
+ if (stopping_threads != NOT_STOPPING_THREADS)
new_lwp->stop_pc = get_stop_pc (new_lwp);
else
linux_resume_one_lwp (new_lwp, 0, 0, NULL);
@@ -413,7 +494,7 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
{
new_lwp->stop_expected = 1;
- if (stopping_threads)
+ if (stopping_threads != NOT_STOPPING_THREADS)
{
new_lwp->stop_pc = get_stop_pc (new_lwp);
new_lwp->status_pending_p = 1;
@@ -568,6 +649,19 @@ linux_create_inferior (char *program, char **allargs)
setpgid (0, 0);
+ /* If gdbserver is connected to gdb via stdio, redirect the inferior's
+ stdout to stderr so that inferior i/o doesn't corrupt the connection.
+ Also, redirect stdin to /dev/null. */
+ if (remote_connection_is_stdio ())
+ {
+ close (0);
+ open ("/dev/null", O_RDONLY);
+ dup2 (2, 1);
+ if (write (2, "stdin/stdout redirected\n",
+ sizeof ("stdin/stdout redirected\n") - 1) < 0)
+ /* Errors ignored. */;
+ }
+
execv (program, allargs);
if (errno == ENOENT)
execvp (program, allargs);
@@ -609,6 +703,8 @@ linux_attach_lwp_1 (unsigned long lwpid, int initial)
if (ptrace (PTRACE_ATTACH, lwpid, 0, 0) != 0)
{
+ struct buffer buffer;
+
if (!initial)
{
/* If we fail to attach to an LWP, just warn. */
@@ -617,10 +713,13 @@ linux_attach_lwp_1 (unsigned long lwpid, int initial)
fflush (stderr);
return;
}
- else
- /* If we fail to attach to a process, report an error. */
- error ("Cannot attach to lwp %ld: %s (%d)\n", lwpid,
- strerror (errno), errno);
+
+ /* If we fail to attach to a process, report an error. */
+ buffer_init (&buffer);
+ linux_ptrace_attach_warnings (lwpid, &buffer);
+ buffer_grow_str0 (&buffer, "");
+ error ("%sCannot attach to lwp %ld: %s (%d)", buffer_finish (&buffer),
+ lwpid, strerror (errno), errno);
}
if (initial)
@@ -644,6 +743,33 @@ linux_attach_lwp_1 (unsigned long lwpid, int initial)
ptrace call on this LWP. */
new_lwp->must_set_ptrace_flags = 1;
+ if (linux_proc_pid_is_stopped (lwpid))
+ {
+ if (debug_threads)
+ fprintf (stderr,
+ "Attached to a stopped process\n");
+
+ /* The process is definitely stopped. It is in a job control
+ stop, unless the kernel predates the TASK_STOPPED /
+ TASK_TRACED distinction, in which case it might be in a
+ ptrace stop. Make sure it is in a ptrace stop; from there we
+ can kill it, signal it, et cetera.
+
+ First make sure there is a pending SIGSTOP. Since we are
+ already attached, the process can not transition from stopped
+ to running without a PTRACE_CONT; so we know this signal will
+ go into the queue. The SIGSTOP generated by PTRACE_ATTACH is
+ probably already in the queue (unless this kernel is old
+ enough to use TASK_STOPPED for ptrace stops); but since
+ SIGSTOP is not an RT signal, it can only be queued once. */
+ kill_lwp (lwpid, SIGSTOP);
+
+ /* Finally, resume the stopped process. This will deliver the
+ SIGSTOP (or a higher priority signal, just like normal
+ PTRACE_ATTACH), which we'll catch later on. */
+ ptrace (PTRACE_CONT, lwpid, 0, 0);
+ }
+
/* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH
brings it to a halt.
@@ -691,7 +817,7 @@ linux_attach_lwp (unsigned long lwpid)
/* Attach to PID. If PID is the tgid, attach to it and all
of its threads. */
-int
+static int
linux_attach (unsigned long pid)
{
/* Attach to PID. We will check for other threads
@@ -802,10 +928,49 @@ last_thread_of_process_p (struct thread_info *thread)
second_thread_of_pid_p, &counter) == NULL);
}
-/* Kill the inferior lwp. */
+/* Kill LWP. */
+
+static void
+linux_kill_one_lwp (struct lwp_info *lwp)
+{
+ int pid = lwpid_of (lwp);
+
+ /* PTRACE_KILL is unreliable. After stepping into a signal handler,
+ there is no signal context, and ptrace(PTRACE_KILL) (or
+ ptrace(PTRACE_CONT, SIGKILL), pretty much the same) acts like
+ ptrace(CONT, pid, 0,0) and just resumes the tracee. A better
+ alternative is to kill with SIGKILL. We only need one SIGKILL
+ per process, not one for each thread. But since we still support
+ linuxthreads, and we also support debugging programs using raw
+ clone without CLONE_THREAD, we send one for each thread. For
+ years, we used PTRACE_KILL only, so we're being a bit paranoid
+ about some old kernels where PTRACE_KILL might work better
+ (dubious if there are any such, but that's why it's paranoia), so
+ we try SIGKILL first, PTRACE_KILL second, and so we're fine
+ everywhere. */
+
+ errno = 0;
+ kill (pid, SIGKILL);
+ if (debug_threads)
+ fprintf (stderr,
+ "LKL: kill (SIGKILL) %s, 0, 0 (%s)\n",
+ target_pid_to_str (ptid_of (lwp)),
+ errno ? strerror (errno) : "OK");
+
+ errno = 0;
+ ptrace (PTRACE_KILL, pid, 0, 0);
+ if (debug_threads)
+ fprintf (stderr,
+ "LKL: PTRACE_KILL %s, 0, 0 (%s)\n",
+ target_pid_to_str (ptid_of (lwp)),
+ errno ? strerror (errno) : "OK");
+}
+
+/* Callback for `find_inferior'. Kills an lwp of a given process,
+ except the leader. */
static int
-linux_kill_one_lwp (struct inferior_list_entry *entry, void *args)
+kill_one_lwp_callback (struct inferior_list_entry *entry, void *args)
{
struct thread_info *thread = (struct thread_info *) entry;
struct lwp_info *lwp = get_thread_lwp (thread);
@@ -830,7 +995,7 @@ linux_kill_one_lwp (struct inferior_list_entry *entry, void *args)
do
{
- ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0);
+ linux_kill_one_lwp (lwp);
/* Make sure it died. The loop is most likely unnecessary. */
pid = linux_wait_for_event (lwp->head.id, &wstat, __WALL);
@@ -855,7 +1020,7 @@ linux_kill (int pid)
first, as PTRACE_KILL will not work otherwise. */
stop_all_lwps (0, NULL);
- find_inferior (&all_threads, linux_kill_one_lwp, &pid);
+ find_inferior (&all_threads, kill_one_lwp_callback , &pid);
/* See the comment in linux_kill_one_lwp. We did not kill the first
thread in the list, so do so now. */
@@ -875,7 +1040,7 @@ linux_kill (int pid)
do
{
- ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0);
+ linux_kill_one_lwp (lwp);
/* Make sure it died. The loop is most likely unnecessary. */
lwpid = linux_wait_for_event (lwp->head.id, &wstat, __WALL);
@@ -890,34 +1055,128 @@ linux_kill (int pid)
return 0;
}
+/* Get pending signal of THREAD, for detaching purposes. This is the
+ signal the thread last stopped for, which we need to deliver to the
+ thread when detaching, otherwise, it'd be suppressed/lost. */
+
+static int
+get_detach_signal (struct thread_info *thread)
+{
+ enum gdb_signal signo = GDB_SIGNAL_0;
+ int status;
+ struct lwp_info *lp = get_thread_lwp (thread);
+
+ if (lp->status_pending_p)
+ status = lp->status_pending;
+ else
+ {
+ /* If the thread had been suspended by gdbserver, and it stopped
+ cleanly, then it'll have stopped with SIGSTOP. But we don't
+ want to deliver that SIGSTOP. */
+ if (thread->last_status.kind != TARGET_WAITKIND_STOPPED
+ || thread->last_status.value.sig == GDB_SIGNAL_0)
+ return 0;
+
+ /* Otherwise, we may need to deliver the signal we
+ intercepted. */
+ status = lp->last_status;
+ }
+
+ if (!WIFSTOPPED (status))
+ {
+ if (debug_threads)
+ fprintf (stderr,
+ "GPS: lwp %s hasn't stopped: no pending signal\n",
+ target_pid_to_str (ptid_of (lp)));
+ return 0;
+ }
+
+ /* Extended wait statuses aren't real SIGTRAPs. */
+ if (WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ {
+ if (debug_threads)
+ fprintf (stderr,
+ "GPS: lwp %s had stopped with extended "
+ "status: no pending signal\n",
+ target_pid_to_str (ptid_of (lp)));
+ return 0;
+ }
+
+ signo = gdb_signal_from_host (WSTOPSIG (status));
+
+ if (program_signals_p && !program_signals[signo])
+ {
+ if (debug_threads)
+ fprintf (stderr,
+ "GPS: lwp %s had signal %s, but it is in nopass state\n",
+ target_pid_to_str (ptid_of (lp)),
+ gdb_signal_to_string (signo));
+ return 0;
+ }
+ else if (!program_signals_p
+ /* If we have no way to know which signals GDB does not
+ want to have passed to the program, assume
+ SIGTRAP/SIGINT, which is GDB's default. */
+ && (signo == GDB_SIGNAL_TRAP || signo == GDB_SIGNAL_INT))
+ {
+ if (debug_threads)
+ fprintf (stderr,
+ "GPS: lwp %s had signal %s, "
+ "but we don't know if we should pass it. Default to not.\n",
+ target_pid_to_str (ptid_of (lp)),
+ gdb_signal_to_string (signo));
+ return 0;
+ }
+ else
+ {
+ if (debug_threads)
+ fprintf (stderr,
+ "GPS: lwp %s has pending signal %s: delivering it.\n",
+ target_pid_to_str (ptid_of (lp)),
+ gdb_signal_to_string (signo));
+
+ return WSTOPSIG (status);
+ }
+}
+
static int
linux_detach_one_lwp (struct inferior_list_entry *entry, void *args)
{
struct thread_info *thread = (struct thread_info *) entry;
struct lwp_info *lwp = get_thread_lwp (thread);
int pid = * (int *) args;
+ int sig;
if (ptid_get_pid (entry->id) != pid)
return 0;
- /* If this process is stopped but is expecting a SIGSTOP, then make
- sure we take care of that now. This isn't absolutely guaranteed
- to collect the SIGSTOP, but is fairly likely to. */
+ /* If there is a pending SIGSTOP, get rid of it. */
if (lwp->stop_expected)
{
- int wstat;
- /* Clear stop_expected, so that the SIGSTOP will be reported. */
+ if (debug_threads)
+ fprintf (stderr,
+ "Sending SIGCONT to %s\n",
+ target_pid_to_str (ptid_of (lwp)));
+
+ kill_lwp (lwpid_of (lwp), SIGCONT);
lwp->stop_expected = 0;
- linux_resume_one_lwp (lwp, 0, 0, NULL);
- linux_wait_for_event (lwp->head.id, &wstat, __WALL);
}
/* Flush any pending changes to the process's registers. */
regcache_invalidate_one ((struct inferior_list_entry *)
get_lwp_thread (lwp));
+ /* Pass on any pending signal for this thread. */
+ sig = get_detach_signal (thread);
+
/* Finally, let it resume. */
- ptrace (PTRACE_DETACH, lwpid_of (lwp), 0, 0);
+ if (the_low_target.prepare_to_resume != NULL)
+ the_low_target.prepare_to_resume (lwp);
+ if (ptrace (PTRACE_DETACH, lwpid_of (lwp), 0,
+ (PTRACE_ARG4_TYPE) (long) sig) < 0)
+ error (_("Can't detach %s: %s"),
+ target_pid_to_str (ptid_of (lwp)),
+ strerror (errno));
delete_lwp (lwp);
return 0;
@@ -1103,7 +1362,7 @@ retry:
was reported to us by the kernel. Save its PID. */
if (child == NULL && WIFSTOPPED (*wstatp))
{
- add_pid_to_list (&stopped_pids, ret);
+ add_to_pid_list (&stopped_pids, ret, *wstatp);
goto retry;
}
else if (child == NULL)
@@ -1280,7 +1539,7 @@ maybe_move_out_of_jump_pad (struct lwp_info *lwp, int *wstat)
if ((wstat == NULL
|| (WIFSTOPPED (*wstat) && WSTOPSIG (*wstat) != SIGTRAP))
&& supports_fast_tracepoints ()
- && in_process_agent_loaded ())
+ && agent_loaded_p ())
{
struct fast_tpoint_collect_status status;
int r;
@@ -1554,17 +1813,17 @@ ptid_t step_over_bkpt;
the stopped child otherwise. */
static int
-linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options)
+linux_wait_for_event (ptid_t ptid, int *wstat, int options)
{
struct lwp_info *event_child, *requested_child;
+ ptid_t wait_ptid;
event_child = NULL;
requested_child = NULL;
/* Check for a lwp with a pending status. */
- if (ptid_equal (ptid, minus_one_ptid)
- || ptid_equal (pid_to_ptid (ptid_get_pid (ptid)), ptid))
+ if (ptid_equal (ptid, minus_one_ptid) || ptid_is_pid (ptid))
{
event_child = (struct lwp_info *)
find_inferior (&all_lwps, status_pending_p_callback, &ptid);
@@ -1575,7 +1834,7 @@ linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options)
{
requested_child = find_lwp_pid (ptid);
- if (!stopping_threads
+ if (stopping_threads == NOT_STOPPING_THREADS
&& requested_child->status_pending_p
&& requested_child->collecting_fast_tracepoint)
{
@@ -1606,13 +1865,24 @@ linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options)
return lwpid_of (event_child);
}
+ if (ptid_is_pid (ptid))
+ {
+ /* A request to wait for a specific tgid. This is not possible
+ with waitpid, so instead, we wait for any child, and leave
+ children we're not interested in right now with a pending
+ status to report later. */
+ wait_ptid = minus_one_ptid;
+ }
+ else
+ wait_ptid = ptid;
+
/* We only enter this loop if no process has a pending wait status. Thus
any action taken in response to a wait status inside this loop is
responding as soon as we detect the status, not after any pending
events. */
while (1)
{
- event_child = linux_wait_for_lwp (ptid, wstat, options);
+ event_child = linux_wait_for_lwp (wait_ptid, wstat, options);
if ((options & WNOHANG) && event_child == NULL)
{
@@ -1624,6 +1894,19 @@ linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options)
if (event_child == NULL)
error ("event from unknown child");
+ if (ptid_is_pid (ptid)
+ && ptid_get_pid (ptid) != ptid_get_pid (ptid_of (event_child)))
+ {
+ if (! WIFSTOPPED (*wstat))
+ mark_lwp_dead (event_child, *wstat);
+ else
+ {
+ event_child->status_pending_p = 1;
+ event_child->status_pending = *wstat;
+ }
+ continue;
+ }
+
current_inferior = get_lwp_thread (event_child);
/* Check for thread exit. */
@@ -1699,7 +1982,7 @@ linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options)
event_child->stop_expected = 0;
should_stop = (current_inferior->last_resume_kind == resume_stop
- || stopping_threads);
+ || stopping_threads != NOT_STOPPING_THREADS);
if (!should_stop)
{
@@ -1716,48 +1999,6 @@ linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options)
return 0;
}
-static int
-linux_wait_for_event (ptid_t ptid, int *wstat, int options)
-{
- ptid_t wait_ptid;
-
- if (ptid_is_pid (ptid))
- {
- /* A request to wait for a specific tgid. This is not possible
- with waitpid, so instead, we wait for any child, and leave
- children we're not interested in right now with a pending
- status to report later. */
- wait_ptid = minus_one_ptid;
- }
- else
- wait_ptid = ptid;
-
- while (1)
- {
- int event_pid;
-
- event_pid = linux_wait_for_event_1 (wait_ptid, wstat, options);
-
- if (event_pid > 0
- && ptid_is_pid (ptid) && ptid_get_pid (ptid) != event_pid)
- {
- struct lwp_info *event_child
- = find_lwp_pid (pid_to_ptid (event_pid));
-
- if (! WIFSTOPPED (*wstat))
- mark_lwp_dead (event_child, *wstat);
- else
- {
- event_child->status_pending_p = 1;
- event_child->status_pending = *wstat;
- }
- }
- else
- return event_pid;
- }
-}
-
-
/* Count the LWP's that have had events. */
static int
@@ -2021,10 +2262,10 @@ linux_stabilize_threads (void)
/* Lock it. */
lwp->suspended++;
- if (ourstatus.value.sig != TARGET_SIGNAL_0
+ if (ourstatus.value.sig != GDB_SIGNAL_0
|| current_inferior->last_resume_kind == resume_stop)
{
- wstat = W_STOPCODE (target_signal_to_host (ourstatus.value.sig));
+ wstat = W_STOPCODE (gdb_signal_to_host (ourstatus.value.sig));
enqueue_one_deferred_signal (lwp, &wstat);
}
}
@@ -2145,7 +2386,7 @@ retry:
else
{
ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
- ourstatus->value.sig = target_signal_from_host (WTERMSIG (w));
+ ourstatus->value.sig = gdb_signal_from_host (WTERMSIG (w));
if (debug_threads)
fprintf (stderr,
@@ -2229,7 +2470,7 @@ retry:
if (WIFSTOPPED (w)
&& WSTOPSIG (w) != SIGTRAP
&& supports_fast_tracepoints ()
- && in_process_agent_loaded ())
+ && agent_loaded_p ())
{
if (debug_threads)
fprintf (stderr,
@@ -2317,7 +2558,7 @@ Check if we're already there.\n",
if (stabilizing_threads)
{
ourstatus->kind = TARGET_WAITKIND_STOPPED;
- ourstatus->value.sig = TARGET_SIGNAL_0;
+ ourstatus->value.sig = GDB_SIGNAL_0;
return ptid_of (event_child);
}
}
@@ -2344,7 +2585,7 @@ Check if we're already there.\n",
|| WSTOPSIG (w) == __SIGRTMIN + 1))
||
#endif
- (pass_signals[target_signal_from_host (WSTOPSIG (w))]
+ (pass_signals[gdb_signal_from_host (WSTOPSIG (w))]
&& !(WSTOPSIG (w) == SIGSTOP
&& current_inferior->last_resume_kind == resume_stop))))
{
@@ -2376,7 +2617,11 @@ Check if we're already there.\n",
|| event_child->stopped_by_watchpoint
|| (!step_over_finished
&& !bp_explains_trap && !trace_event)
- || gdb_breakpoint_here (event_child->stop_pc));
+ || (gdb_breakpoint_here (event_child->stop_pc)
+ && gdb_condition_true_at_breakpoint (event_child->stop_pc)
+ && gdb_no_commands_at_breakpoint (event_child->stop_pc)));
+
+ run_breakpoint_commands (event_child->stop_pc);
/* We found no reason GDB would want us to stop. We either hit one
of our own breakpoints, or finished an internal step GDB
@@ -2459,6 +2704,15 @@ Check if we're already there.\n",
why. */
find_inferior (&all_lwps, cancel_breakpoints_callback, event_child);
+ /* If we were going a step-over, all other threads but the stepping one
+ had been paused in start_step_over, with their suspend counts
+ incremented. We don't want to do a full unstop/unpause, because we're
+ in all-stop mode (so we want threads stopped), but we still need to
+ unsuspend the other threads, to decrement their `suspended' count
+ back. */
+ if (step_over_finished)
+ unsuspend_all_lwps (event_child);
+
/* Stabilize threads (move out of jump pads). */
stabilize_threads ();
}
@@ -2480,18 +2734,18 @@ Check if we're already there.\n",
/* A thread that has been requested to stop by GDB with vCont;t,
and it stopped cleanly, so report as SIG0. The use of
SIGSTOP is an implementation detail. */
- ourstatus->value.sig = TARGET_SIGNAL_0;
+ ourstatus->value.sig = GDB_SIGNAL_0;
}
else if (current_inferior->last_resume_kind == resume_stop
&& WSTOPSIG (w) != SIGSTOP)
{
/* A thread that has been requested to stop by GDB with vCont;t,
but, it stopped for other reasons. */
- ourstatus->value.sig = target_signal_from_host (WSTOPSIG (w));
+ ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
}
else
{
- ourstatus->value.sig = target_signal_from_host (WSTOPSIG (w));
+ ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
}
gdb_assert (ptid_equal (step_over_bkpt, null_ptid));
@@ -2773,7 +3027,7 @@ stuck_in_jump_pad_callback (struct inferior_list_entry *entry, void *data)
/* Allow debugging the jump pad, gdb_collect, etc.. */
return (supports_fast_tracepoints ()
- && in_process_agent_loaded ()
+ && agent_loaded_p ()
&& (gdb_breakpoint_here (lwp->stop_pc)
|| lwp->stopped_by_watchpoint
|| thread->last_resume_kind == resume_step)
@@ -2840,14 +3094,19 @@ lwp_running (struct inferior_list_entry *entry, void *data)
static void
stop_all_lwps (int suspend, struct lwp_info *except)
{
- stopping_threads = 1;
+ /* Should not be called recursively. */
+ gdb_assert (stopping_threads == NOT_STOPPING_THREADS);
+
+ stopping_threads = (suspend
+ ? STOPPING_AND_SUSPENDING_THREADS
+ : STOPPING_THREADS);
if (suspend)
find_inferior (&all_lwps, suspend_and_send_sigstop_callback, except);
else
find_inferior (&all_lwps, send_sigstop_callback, except);
for_each_inferior (&all_lwps, wait_for_sigstop);
- stopping_threads = 0;
+ stopping_threads = NOT_STOPPING_THREADS;
}
/* Resume execution of the inferior process.
@@ -3083,10 +3342,11 @@ linux_set_resume_request (struct inferior_list_entry *entry, void *arg)
ptid_t ptid = r->resume[ndx].thread;
if (ptid_equal (ptid, minus_one_ptid)
|| ptid_equal (ptid, entry->id)
- || (ptid_is_pid (ptid)
- && (ptid_get_pid (ptid) == pid_of (lwp)))
- || (ptid_get_lwp (ptid) == -1
- && (ptid_get_pid (ptid) == pid_of (lwp))))
+ /* Handle both 'pPID' and 'pPID.-1' as meaning 'all threads
+ of PID'. */
+ || (ptid_get_pid (ptid) == pid_of (lwp)
+ && (ptid_is_pid (ptid)
+ || ptid_get_lwp (ptid) == -1)))
{
if (r->resume[ndx].kind == resume_stop
&& thread->last_resume_kind == resume_stop)
@@ -3239,8 +3499,11 @@ need_step_over_p (struct inferior_list_entry *entry, void *dummy)
if (breakpoint_here (pc) || fast_tracepoint_jump_here (pc))
{
/* Don't step over a breakpoint that GDB expects to hit
- though. */
- if (gdb_breakpoint_here (pc))
+ though. If the condition is being evaluated on the target's side
+ and it evaluate to false, step over this breakpoint as well. */
+ if (gdb_breakpoint_here (pc)
+ && gdb_condition_true_at_breakpoint (pc)
+ && gdb_no_commands_at_breakpoint (pc))
{
if (debug_threads)
fprintf (stderr,
@@ -3720,166 +3983,32 @@ unstop_all_lwps (int unsuspend, struct lwp_info *except)
find_inferior (&all_lwps, proceed_one_lwp, except);
}
-#ifdef HAVE_LINUX_USRREGS
-
-int
-register_addr (int regnum)
-{
- int addr;
-
- if (regnum < 0 || regnum >= the_low_target.num_regs)
- error ("Invalid register number %d.", regnum);
- addr = the_low_target.regmap[regnum];
+#ifdef HAVE_LINUX_REGSETS
- return addr;
-}
+#define use_linux_regsets 1
-/* Fetch one register. */
-static void
-fetch_register (struct regcache *regcache, int regno)
+static int
+regsets_fetch_inferior_registers (struct regcache *regcache)
{
- CORE_ADDR regaddr;
- int i, size;
- char *buf;
+ struct regset_info *regset;
+ int saw_general_regs = 0;
int pid;
+ struct iovec iov;
- if (regno >= the_low_target.num_regs)
- return;
- if ((*the_low_target.cannot_fetch_register) (regno))
- return;
-
- regaddr = register_addr (regno);
- if (regaddr == -1)
- return;
-
- size = ((register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
- & -sizeof (PTRACE_XFER_TYPE));
- buf = alloca (size);
+ regset = target_regsets;
pid = lwpid_of (get_thread_lwp (current_inferior));
- for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
+ while (regset->size >= 0)
{
- errno = 0;
- *(PTRACE_XFER_TYPE *) (buf + i) =
- ptrace (PTRACE_PEEKUSER, pid,
- /* Coerce to a uintptr_t first to avoid potential gcc warning
- of coercing an 8 byte integer to a 4 byte pointer. */
- (PTRACE_ARG3_TYPE) (uintptr_t) regaddr, 0);
- regaddr += sizeof (PTRACE_XFER_TYPE);
- if (errno != 0)
- error ("reading register %d: %s", regno, strerror (errno));
- }
+ void *buf, *data;
+ int nt_type, res;
- if (the_low_target.supply_ptrace_register)
- the_low_target.supply_ptrace_register (regcache, regno, buf);
- else
- supply_register (regcache, regno, buf);
-}
-
-/* Store one register. */
-static void
-store_register (struct regcache *regcache, int regno)
-{
- CORE_ADDR regaddr;
- int i, size;
- char *buf;
- int pid;
-
- if (regno >= the_low_target.num_regs)
- return;
- if ((*the_low_target.cannot_store_register) (regno))
- return;
-
- regaddr = register_addr (regno);
- if (regaddr == -1)
- return;
-
- size = ((register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
- & -sizeof (PTRACE_XFER_TYPE));
- buf = alloca (size);
- memset (buf, 0, size);
-
- if (the_low_target.collect_ptrace_register)
- the_low_target.collect_ptrace_register (regcache, regno, buf);
- else
- collect_register (regcache, regno, buf);
-
- pid = lwpid_of (get_thread_lwp (current_inferior));
- for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
- {
- errno = 0;
- ptrace (PTRACE_POKEUSER, pid,
- /* Coerce to a uintptr_t first to avoid potential gcc warning
- about coercing an 8 byte integer to a 4 byte pointer. */
- (PTRACE_ARG3_TYPE) (uintptr_t) regaddr,
- (PTRACE_ARG4_TYPE) *(PTRACE_XFER_TYPE *) (buf + i));
- if (errno != 0)
- {
- /* At this point, ESRCH should mean the process is
- already gone, in which case we simply ignore attempts
- to change its registers. See also the related
- comment in linux_resume_one_lwp. */
- if (errno == ESRCH)
- return;
-
- if ((*the_low_target.cannot_store_register) (regno) == 0)
- error ("writing register %d: %s", regno, strerror (errno));
- }
- regaddr += sizeof (PTRACE_XFER_TYPE);
- }
-}
-
-/* Fetch all registers, or just one, from the child process. */
-static void
-usr_fetch_inferior_registers (struct regcache *regcache, int regno)
-{
- if (regno == -1)
- for (regno = 0; regno < the_low_target.num_regs; regno++)
- fetch_register (regcache, regno);
- else
- fetch_register (regcache, regno);
-}
-
-/* Store our register values back into the inferior.
- If REGNO is -1, do this for all registers.
- Otherwise, REGNO specifies which register (so we can save time). */
-static void
-usr_store_inferior_registers (struct regcache *regcache, int regno)
-{
- if (regno == -1)
- for (regno = 0; regno < the_low_target.num_regs; regno++)
- store_register (regcache, regno);
- else
- store_register (regcache, regno);
-}
-#endif /* HAVE_LINUX_USRREGS */
-
-
-
-#ifdef HAVE_LINUX_REGSETS
-
-static int
-regsets_fetch_inferior_registers (struct regcache *regcache)
-{
- struct regset_info *regset;
- int saw_general_regs = 0;
- int pid;
- struct iovec iov;
-
- regset = target_regsets;
-
- pid = lwpid_of (get_thread_lwp (current_inferior));
- while (regset->size >= 0)
- {
- void *buf, *data;
- int nt_type, res;
-
- if (regset->size == 0 || disabled_regsets[regset - target_regsets])
- {
- regset ++;
- continue;
- }
+ if (regset->size == 0 || disabled_regsets[regset - target_regsets])
+ {
+ regset ++;
+ continue;
+ }
buf = xmalloc (regset->size);
@@ -3894,7 +4023,8 @@ regsets_fetch_inferior_registers (struct regcache *regcache)
data = buf;
#ifndef __sparc__
- res = ptrace (regset->get_request, pid, nt_type, data);
+ res = ptrace (regset->get_request, pid,
+ (PTRACE_ARG3_TYPE) (long) nt_type, data);
#else
res = ptrace (regset->get_request, pid, data, nt_type);
#endif
@@ -3967,9 +4097,10 @@ regsets_store_inferior_registers (struct regcache *regcache)
data = buf;
#ifndef __sparc__
- res = ptrace (regset->get_request, pid, nt_type, data);
+ res = ptrace (regset->get_request, pid,
+ (PTRACE_ARG3_TYPE) (long) nt_type, data);
#else
- res = ptrace (regset->get_request, pid, &iov, data);
+ res = ptrace (regset->get_request, pid, data, nt_type);
#endif
if (res == 0)
@@ -3979,7 +4110,8 @@ regsets_store_inferior_registers (struct regcache *regcache)
/* Only now do we write the register set. */
#ifndef __sparc__
- res = ptrace (regset->set_request, pid, nt_type, data);
+ res = ptrace (regset->set_request, pid,
+ (PTRACE_ARG3_TYPE) (long) nt_type, data);
#else
res = ptrace (regset->set_request, pid, data, nt_type);
#endif
@@ -4018,34 +4150,232 @@ regsets_store_inferior_registers (struct regcache *regcache)
return 0;
else
return 1;
- return 0;
}
-#endif /* HAVE_LINUX_REGSETS */
+#else /* !HAVE_LINUX_REGSETS */
+#define use_linux_regsets 0
+#define regsets_fetch_inferior_registers(regcache) 1
+#define regsets_store_inferior_registers(regcache) 1
-void
-linux_fetch_registers (struct regcache *regcache, int regno)
-{
-#ifdef HAVE_LINUX_REGSETS
- if (regsets_fetch_inferior_registers (regcache) == 0)
- return;
#endif
+
+/* Return 1 if register REGNO is supported by one of the regset ptrace
+ calls or 0 if it has to be transferred individually. */
+
+static int
+linux_register_in_regsets (int regno)
+{
+ unsigned char mask = 1 << (regno % 8);
+ size_t index = regno / 8;
+
+ return (use_linux_regsets
+ && (the_low_target.regset_bitmap == NULL
+ || (the_low_target.regset_bitmap[index] & mask) != 0));
+}
+
#ifdef HAVE_LINUX_USRREGS
- usr_fetch_inferior_registers (regcache, regno);
+
+int
+register_addr (int regnum)
+{
+ int addr;
+
+ if (regnum < 0 || regnum >= the_low_target.num_regs)
+ error ("Invalid register number %d.", regnum);
+
+ addr = the_low_target.regmap[regnum];
+
+ return addr;
+}
+
+/* Fetch one register. */
+static void
+fetch_register (struct regcache *regcache, int regno)
+{
+ CORE_ADDR regaddr;
+ int i, size;
+ char *buf;
+ int pid;
+
+ if (regno >= the_low_target.num_regs)
+ return;
+ if ((*the_low_target.cannot_fetch_register) (regno))
+ return;
+
+ regaddr = register_addr (regno);
+ if (regaddr == -1)
+ return;
+
+ size = ((register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
+ & -sizeof (PTRACE_XFER_TYPE));
+ buf = alloca (size);
+
+ pid = lwpid_of (get_thread_lwp (current_inferior));
+ for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
+ {
+ errno = 0;
+ *(PTRACE_XFER_TYPE *) (buf + i) =
+ ptrace (PTRACE_PEEKUSER, pid,
+ /* Coerce to a uintptr_t first to avoid potential gcc warning
+ of coercing an 8 byte integer to a 4 byte pointer. */
+ (PTRACE_ARG3_TYPE) (uintptr_t) regaddr, 0);
+ regaddr += sizeof (PTRACE_XFER_TYPE);
+ if (errno != 0)
+ error ("reading register %d: %s", regno, strerror (errno));
+ }
+
+ if (the_low_target.supply_ptrace_register)
+ the_low_target.supply_ptrace_register (regcache, regno, buf);
+ else
+ supply_register (regcache, regno, buf);
+}
+
+/* Store one register. */
+static void
+store_register (struct regcache *regcache, int regno)
+{
+ CORE_ADDR regaddr;
+ int i, size;
+ char *buf;
+ int pid;
+
+ if (regno >= the_low_target.num_regs)
+ return;
+ if ((*the_low_target.cannot_store_register) (regno))
+ return;
+
+ regaddr = register_addr (regno);
+ if (regaddr == -1)
+ return;
+
+ size = ((register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
+ & -sizeof (PTRACE_XFER_TYPE));
+ buf = alloca (size);
+ memset (buf, 0, size);
+
+ if (the_low_target.collect_ptrace_register)
+ the_low_target.collect_ptrace_register (regcache, regno, buf);
+ else
+ collect_register (regcache, regno, buf);
+
+ pid = lwpid_of (get_thread_lwp (current_inferior));
+ for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
+ {
+ errno = 0;
+ ptrace (PTRACE_POKEUSER, pid,
+ /* Coerce to a uintptr_t first to avoid potential gcc warning
+ about coercing an 8 byte integer to a 4 byte pointer. */
+ (PTRACE_ARG3_TYPE) (uintptr_t) regaddr,
+ (PTRACE_ARG4_TYPE) *(PTRACE_XFER_TYPE *) (buf + i));
+ if (errno != 0)
+ {
+ /* At this point, ESRCH should mean the process is
+ already gone, in which case we simply ignore attempts
+ to change its registers. See also the related
+ comment in linux_resume_one_lwp. */
+ if (errno == ESRCH)
+ return;
+
+ if ((*the_low_target.cannot_store_register) (regno) == 0)
+ error ("writing register %d: %s", regno, strerror (errno));
+ }
+ regaddr += sizeof (PTRACE_XFER_TYPE);
+ }
+}
+
+/* Fetch all registers, or just one, from the child process.
+ If REGNO is -1, do this for all registers, skipping any that are
+ assumed to have been retrieved by regsets_fetch_inferior_registers,
+ unless ALL is non-zero.
+ Otherwise, REGNO specifies which register (so we can save time). */
+static void
+usr_fetch_inferior_registers (struct regcache *regcache, int regno, int all)
+{
+ if (regno == -1)
+ {
+ for (regno = 0; regno < the_low_target.num_regs; regno++)
+ if (all || !linux_register_in_regsets (regno))
+ fetch_register (regcache, regno);
+ }
+ else
+ fetch_register (regcache, regno);
+}
+
+/* Store our register values back into the inferior.
+ If REGNO is -1, do this for all registers, skipping any that are
+ assumed to have been saved by regsets_store_inferior_registers,
+ unless ALL is non-zero.
+ Otherwise, REGNO specifies which register (so we can save time). */
+static void
+usr_store_inferior_registers (struct regcache *regcache, int regno, int all)
+{
+ if (regno == -1)
+ {
+ for (regno = 0; regno < the_low_target.num_regs; regno++)
+ if (all || !linux_register_in_regsets (regno))
+ store_register (regcache, regno);
+ }
+ else
+ store_register (regcache, regno);
+}
+
+#else /* !HAVE_LINUX_USRREGS */
+
+#define usr_fetch_inferior_registers(regcache, regno, all) do {} while (0)
+#define usr_store_inferior_registers(regcache, regno, all) do {} while (0)
+
#endif
+
+
+void
+linux_fetch_registers (struct regcache *regcache, int regno)
+{
+ int use_regsets;
+ int all = 0;
+
+ if (regno == -1)
+ {
+ if (the_low_target.fetch_register != NULL)
+ for (regno = 0; regno < the_low_target.num_regs; regno++)
+ (*the_low_target.fetch_register) (regcache, regno);
+
+ all = regsets_fetch_inferior_registers (regcache);
+ usr_fetch_inferior_registers (regcache, -1, all);
+ }
+ else
+ {
+ if (the_low_target.fetch_register != NULL
+ && (*the_low_target.fetch_register) (regcache, regno))
+ return;
+
+ use_regsets = linux_register_in_regsets (regno);
+ if (use_regsets)
+ all = regsets_fetch_inferior_registers (regcache);
+ if (!use_regsets || all)
+ usr_fetch_inferior_registers (regcache, regno, 1);
+ }
}
void
linux_store_registers (struct regcache *regcache, int regno)
{
-#ifdef HAVE_LINUX_REGSETS
- if (regsets_store_inferior_registers (regcache) == 0)
- return;
-#endif
-#ifdef HAVE_LINUX_USRREGS
- usr_store_inferior_registers (regcache, regno);
-#endif
+ int use_regsets;
+ int all = 0;
+
+ if (regno == -1)
+ {
+ all = regsets_store_inferior_registers (regcache);
+ usr_store_inferior_registers (regcache, regno, all);
+ }
+ else
+ {
+ use_regsets = linux_register_in_regsets (regno);
+ if (use_regsets)
+ all = regsets_store_inferior_registers (regcache);
+ if (!use_regsets || all)
+ usr_store_inferior_registers (regcache, regno, 1);
+ }
}
@@ -4055,23 +4385,20 @@ linux_store_registers (struct regcache *regcache, int regno)
static int
linux_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
{
+ int pid = lwpid_of (get_thread_lwp (current_inferior));
+ register PTRACE_XFER_TYPE *buffer;
+ register CORE_ADDR addr;
+ register int count;
+ char filename[64];
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)
- / sizeof (PTRACE_XFER_TYPE);
- /* Allocate buffer of that many longwords. */
- register PTRACE_XFER_TYPE *buffer
- = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE));
+ int ret;
int fd;
- char filename[64];
- int pid = lwpid_of (get_thread_lwp (current_inferior));
/* Try using /proc. Don't bother for one word. */
if (len >= 3 * sizeof (long))
{
+ int bytes;
+
/* We could keep this file open and cache it - possibly one per
thread. That requires some juggling, but is even faster. */
sprintf (filename, "/proc/%d/mem", pid);
@@ -4084,38 +4411,59 @@ linux_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
32-bit platforms (for instance, SPARC debugging a SPARC64
application). */
#ifdef HAVE_PREAD64
- if (pread64 (fd, myaddr, len, memaddr) != len)
+ bytes = pread64 (fd, myaddr, len, memaddr);
#else
- if (lseek (fd, memaddr, SEEK_SET) == -1 || read (fd, myaddr, len) != len)
+ bytes = -1;
+ if (lseek (fd, memaddr, SEEK_SET) != -1)
+ bytes = read (fd, myaddr, len);
#endif
- {
- close (fd);
- goto no_proc;
- }
close (fd);
- return 0;
+ if (bytes == len)
+ return 0;
+
+ /* Some data was read, we'll try to get the rest with ptrace. */
+ if (bytes > 0)
+ {
+ memaddr += bytes;
+ myaddr += bytes;
+ len -= bytes;
+ }
}
no_proc:
+ /* Round starting address down to longword boundary. */
+ addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_XFER_TYPE);
+ /* Round ending address up; get number of longwords that makes. */
+ count = ((((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1)
+ / sizeof (PTRACE_XFER_TYPE));
+ /* Allocate buffer of that many longwords. */
+ buffer = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE));
+
/* Read all the longwords */
+ errno = 0;
for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
{
- errno = 0;
/* Coerce the 3rd arg to a uintptr_t first to avoid potential gcc warning
about coercing an 8 byte integer to a 4 byte pointer. */
buffer[i] = ptrace (PTRACE_PEEKTEXT, pid,
(PTRACE_ARG3_TYPE) (uintptr_t) addr, 0);
if (errno)
- return errno;
+ break;
}
+ ret = errno;
/* Copy appropriate bytes out of the buffer. */
- memcpy (myaddr,
- (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)),
- len);
+ if (i > 0)
+ {
+ i *= sizeof (PTRACE_XFER_TYPE);
+ i -= memaddr & (sizeof (PTRACE_XFER_TYPE) - 1);
+ memcpy (myaddr,
+ (char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)),
+ i < len ? i : len);
+ }
- return 0;
+ return ret;
}
/* Copy LEN bytes of data from debugger memory at MYADDR to inferior's
@@ -4458,6 +4806,9 @@ linux_stopped_data_address (void)
}
#if defined(__UCLIBC__) && defined(HAS_NOMMU)
+#if ! (defined(PT_TEXT_ADDR) \
+ || defined(PT_DATA_ADDR) \
+ || defined(PT_TEXT_END_ADDR))
#if defined(__mcoldfire__)
/* These should really be defined in the kernel's ptrace.h header. */
#define PT_TEXT_ADDR 49*4
@@ -4472,6 +4823,7 @@ linux_stopped_data_address (void)
#define PT_DATA_ADDR (0x10004*4)
#define PT_TEXT_END_ADDR (0x10008*4)
#endif
+#endif
/* Under uClinux, programs are loaded at non-zero offsets, which we need
to tell gdb about. */
@@ -4522,7 +4874,7 @@ linux_qxfer_osdata (const char *annex,
layout of the inferiors' architecture. */
static void
-siginfo_fixup (struct siginfo *siginfo, void *inf_siginfo, int direction)
+siginfo_fixup (siginfo_t *siginfo, void *inf_siginfo, int direction)
{
int done = 0;
@@ -4534,9 +4886,9 @@ siginfo_fixup (struct siginfo *siginfo, void *inf_siginfo, int direction)
if (!done)
{
if (direction == 1)
- memcpy (siginfo, inf_siginfo, sizeof (struct siginfo));
+ memcpy (siginfo, inf_siginfo, sizeof (siginfo_t));
else
- memcpy (inf_siginfo, siginfo, sizeof (struct siginfo));
+ memcpy (inf_siginfo, siginfo, sizeof (siginfo_t));
}
}
@@ -4545,8 +4897,8 @@ linux_xfer_siginfo (const char *annex, unsigned char *readbuf,
unsigned const char *writebuf, CORE_ADDR offset, int len)
{
int pid;
- struct siginfo siginfo;
- char inf_siginfo[sizeof (struct siginfo)];
+ siginfo_t siginfo;
+ char inf_siginfo[sizeof (siginfo_t)];
if (current_inferior == NULL)
return -1;
@@ -4694,6 +5046,12 @@ linux_supports_disable_randomization (void)
#endif
}
+static int
+linux_supports_agent (void)
+{
+ return 1;
+}
+
/* Enumerate spufs IDs for process PID. */
static int
spu_enumerate_spu_ids (long pid, unsigned char *buf, CORE_ADDR offset, int len)
@@ -5083,7 +5441,16 @@ get_dynamic (const int pid, const int is_elf64)
if (relocation == -1)
{
- warning ("Unexpected missing PT_PHDR");
+ /* PT_PHDR is optional, but necessary for PIE in general. Fortunately
+ any real world executables, including PIE executables, have always
+ PT_PHDR present. PT_PHDR is not present in some shared libraries or
+ in fpc (Free Pascal 2.4) binaries but neither of those have a need for
+ or present DT_DEBUG anyway (fpc binaries are statically linked).
+
+ Therefore if there exists DT_DEBUG there is always also PT_PHDR.
+
+ GDB could find RELOCATION also from AT_ENTRY - e_entry. */
+
return 0;
}
@@ -5109,7 +5476,9 @@ get_dynamic (const int pid, const int is_elf64)
}
/* Return &_r_debug in the inferior, or -1 if not present. Return value
- can be 0 if the inferior does not yet have the library list initialized. */
+ can be 0 if the inferior does not yet have the library list initialized.
+ We look for DT_MIPS_RLD_MAP first. MIPS executables use this instead of
+ DT_DEBUG, although they sometimes contain an unused DT_DEBUG entry too. */
static CORE_ADDR
get_r_debug (const int pid, const int is_elf64)
@@ -5117,19 +5486,37 @@ get_r_debug (const int pid, const int is_elf64)
CORE_ADDR dynamic_memaddr;
const int dyn_size = is_elf64 ? sizeof (Elf64_Dyn) : sizeof (Elf32_Dyn);
unsigned char buf[sizeof (Elf64_Dyn)]; /* The larger of the two. */
+ CORE_ADDR map = -1;
dynamic_memaddr = get_dynamic (pid, is_elf64);
if (dynamic_memaddr == 0)
- return (CORE_ADDR) -1;
+ return map;
while (linux_read_memory (dynamic_memaddr, buf, dyn_size) == 0)
{
if (is_elf64)
{
Elf64_Dyn *const dyn = (Elf64_Dyn *) buf;
+#ifdef DT_MIPS_RLD_MAP
+ union
+ {
+ Elf64_Xword map;
+ unsigned char buf[sizeof (Elf64_Xword)];
+ }
+ rld_map;
+
+ if (dyn->d_tag == DT_MIPS_RLD_MAP)
+ {
+ if (linux_read_memory (dyn->d_un.d_val,
+ rld_map.buf, sizeof (rld_map.buf)) == 0)
+ return rld_map.map;
+ else
+ break;
+ }
+#endif /* DT_MIPS_RLD_MAP */
- if (dyn->d_tag == DT_DEBUG)
- return dyn->d_un.d_val;
+ if (dyn->d_tag == DT_DEBUG && map == -1)
+ map = dyn->d_un.d_val;
if (dyn->d_tag == DT_NULL)
break;
@@ -5137,9 +5524,26 @@ get_r_debug (const int pid, const int is_elf64)
else
{
Elf32_Dyn *const dyn = (Elf32_Dyn *) buf;
+#ifdef DT_MIPS_RLD_MAP
+ union
+ {
+ Elf32_Word map;
+ unsigned char buf[sizeof (Elf32_Word)];
+ }
+ rld_map;
+
+ if (dyn->d_tag == DT_MIPS_RLD_MAP)
+ {
+ if (linux_read_memory (dyn->d_un.d_val,
+ rld_map.buf, sizeof (rld_map.buf)) == 0)
+ return rld_map.map;
+ else
+ break;
+ }
+#endif /* DT_MIPS_RLD_MAP */
- if (dyn->d_tag == DT_DEBUG)
- return dyn->d_un.d_val;
+ if (dyn->d_tag == DT_DEBUG && map == -1)
+ map = dyn->d_un.d_val;
if (dyn->d_tag == DT_NULL)
break;
@@ -5148,7 +5552,7 @@ get_r_debug (const int pid, const int is_elf64)
dynamic_memaddr += dyn_size;
}
- return (CORE_ADDR) -1;
+ return map;
}
/* Read one pointer from MEMADDR in the inferior. */
@@ -5156,8 +5560,30 @@ get_r_debug (const int pid, const int is_elf64)
static int
read_one_ptr (CORE_ADDR memaddr, CORE_ADDR *ptr, int ptr_size)
{
- *ptr = 0;
- return linux_read_memory (memaddr, (unsigned char *) ptr, ptr_size);
+ int ret;
+
+ /* Go through a union so this works on either big or little endian
+ hosts, when the inferior's pointer size is smaller than the size
+ of CORE_ADDR. It is assumed the inferior's endianness is the
+ same of the superior's. */
+ union
+ {
+ CORE_ADDR core_addr;
+ unsigned int ui;
+ unsigned char uc;
+ } addr;
+
+ ret = linux_read_memory (memaddr, &addr.uc, ptr_size);
+ if (ret == 0)
+ {
+ if (ptr_size == sizeof (CORE_ADDR))
+ *ptr = addr.core_addr;
+ else if (ptr_size == sizeof (unsigned int))
+ *ptr = addr.ui;
+ else
+ gdb_assert_not_reached ("unhandled pointer size");
+ }
+ return ret;
}
struct link_map_offsets
@@ -5184,7 +5610,7 @@ struct link_map_offsets
int l_prev_offset;
};
-/* Construct qXfer:libraries:read reply. */
+/* Construct qXfer:libraries-svr4:read reply. */
static int
linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
@@ -5219,6 +5645,7 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
32 /* l_prev offset in link_map. */
};
const struct link_map_offsets *lmo;
+ unsigned int machine;
if (writebuf != NULL)
return -2;
@@ -5227,13 +5654,19 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
pid = lwpid_of (get_thread_lwp (current_inferior));
xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid);
- is_elf64 = elf_64_file_p (filename);
+ is_elf64 = elf_64_file_p (filename, &machine);
lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets;
if (priv->r_debug == 0)
priv->r_debug = get_r_debug (pid, is_elf64);
- if (priv->r_debug == (CORE_ADDR) -1 || priv->r_debug == 0)
+ /* We failed to find DT_DEBUG. Such situation will not change for this
+ inferior - do not retry it. Report it to GDB as E01, see for the reasons
+ at the GDB solib-svr4.c side. */
+ if (priv->r_debug == (CORE_ADDR) -1)
+ return -1;
+
+ if (priv->r_debug == 0)
{
document = xstrdup ("<library-list-svr4 version=\"1.0\"/>\n");
}
@@ -5336,7 +5769,13 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
lm_addr = l_next;
}
done:
- strcpy (p, "</library-list-svr4>");
+ if (!header_done)
+ {
+ /* Empty list; terminate `<library-list-svr4'. */
+ strcpy (p, "/>");
+ }
+ else
+ strcpy (p, "</library-list-svr4>");
}
document_len = strlen (document);
@@ -5416,6 +5855,7 @@ static struct target_ops linux_target_ops = {
linux_supports_disable_randomization,
linux_get_min_fast_tracepoint_insn_len,
linux_qxfer_libraries_svr4,
+ linux_supports_agent,
};
static void
@@ -5438,6 +5878,7 @@ initialize_low (void)
the_low_target.breakpoint_len);
linux_init_signals ();
linux_test_for_tracefork ();
+ linux_ptrace_init_warnings ();
#ifdef HAVE_LINUX_REGSETS
for (num_regsets = 0; target_regsets[num_regsets].size >= 0; num_regsets++)
;
« no previous file with comments | « gdb/gdbserver/linux-low.h ('k') | gdb/gdbserver/linux-m32r-low.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698