/* Low level interface to ptrace, for the remote server for GDB.
- Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
- 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+ Copyright (C) 1995-1996, 1998-2012 Free Software Foundation, Inc.
This file is part of GDB.
#include "server.h"
#include "linux-low.h"
+#include "linux-osdata.h"
#include <sys/wait.h>
#include <stdio.h>
#include <sys/param.h>
#include <sys/ptrace.h>
+#include "linux-ptrace.h"
+#include "linux-procfs.h"
#include <signal.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#define SPUFS_MAGIC 0x23c9b64e
#endif
-#ifndef PTRACE_GETSIGINFO
-# define PTRACE_GETSIGINFO 0x4202
-# define PTRACE_SETSIGINFO 0x4203
+#ifdef HAVE_PERSONALITY
+# include <sys/personality.h>
+# if !HAVE_DECL_ADDR_NO_RANDOMIZE
+# define ADDR_NO_RANDOMIZE 0x0040000
+# endif
#endif
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif
-/* If the system headers did not provide the constants, hard-code the normal
- values. */
-#ifndef PTRACE_EVENT_FORK
-
-#define PTRACE_SETOPTIONS 0x4200
-#define PTRACE_GETEVENTMSG 0x4201
-
-/* options set using PTRACE_SETOPTIONS */
-#define PTRACE_O_TRACESYSGOOD 0x00000001
-#define PTRACE_O_TRACEFORK 0x00000002
-#define PTRACE_O_TRACEVFORK 0x00000004
-#define PTRACE_O_TRACECLONE 0x00000008
-#define PTRACE_O_TRACEEXEC 0x00000010
-#define PTRACE_O_TRACEVFORKDONE 0x00000020
-#define PTRACE_O_TRACEEXIT 0x00000040
-
-/* Wait extended result codes for the above trace options. */
-#define PTRACE_EVENT_FORK 1
-#define PTRACE_EVENT_VFORK 2
-#define PTRACE_EVENT_CLONE 3
-#define PTRACE_EVENT_EXEC 4
-#define PTRACE_EVENT_VFORK_DONE 5
-#define PTRACE_EVENT_EXIT 6
-
-#endif /* PTRACE_EVENT_FORK */
-
-/* We can't always assume that this flag is available, but all systems
- with the ptrace event handlers also have __WALL, so it's safe to use
- in some contexts. */
-#ifndef __WALL
-#define __WALL 0x40000000 /* Wait for any child. */
-#endif
-
#ifndef W_STOPCODE
#define W_STOPCODE(sig) ((sig) << 8 | 0x7f)
#endif
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;
-struct inferior_list stopped_pids;
+ *statusp = (*p)->status;
+ xfree (*p);
+ *p = next;
+ return 1;
+ }
+ return 0;
+}
/* FIXME this is a bit of a hack, and could be removed. */
int stopping_threads;
static void *add_lwp (ptid_t ptid);
static int linux_stopped_by_watchpoint (void);
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
-static int linux_core_of_thread (ptid_t ptid);
static void proceed_all_lwps (void);
static int finish_step_over (struct lwp_info *lwp);
static CORE_ADDR get_stop_pc (struct lwp_info *lwp);
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
zero if the file is not a 64-bit ELF file,
and -1 if the file is not accessible or doesn't exist. */
-int
+static int
elf_64_file_p (const char *file)
{
Elf64_Ehdr header;
return elf_64_header_p (&header);
}
+/* 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)
+{
+ char file[MAXPATHLEN];
+
+ sprintf (file, "/proc/%d/exe", pid);
+ return elf_64_file_p (file);
+}
+
static void
delete_lwp (struct lwp_info *lwp)
{
{
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. */
static int
linux_create_inferior (char *program, char **allargs)
{
+#ifdef HAVE_PERSONALITY
+ int personality_orig = 0, personality_set = 0;
+#endif
struct lwp_info *new_lwp;
int pid;
ptid_t ptid;
+#ifdef HAVE_PERSONALITY
+ if (disable_randomization)
+ {
+ errno = 0;
+ personality_orig = personality (0xffffffff);
+ if (errno == 0 && !(personality_orig & ADDR_NO_RANDOMIZE))
+ {
+ personality_set = 1;
+ personality (personality_orig | ADDR_NO_RANDOMIZE);
+ }
+ if (errno != 0 || (personality_set
+ && !(personality (0xffffffff) & ADDR_NO_RANDOMIZE)))
+ warning ("Error disabling address space randomization: %s",
+ strerror (errno));
+ }
+#endif
+
#if defined(__UCLIBC__) && defined(HAS_NOMMU)
pid = vfork ();
#else
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);
_exit (0177);
}
+#ifdef HAVE_PERSONALITY
+ if (personality_set)
+ {
+ errno = 0;
+ personality (personality_orig);
+ if (errno != 0)
+ warning ("Error restoring address space randomization: %s",
+ strerror (errno));
+ }
+#endif
+
linux_add_process (pid, 0);
ptid = ptid_build (pid, pid, 0);
}
if (initial)
- /* NOTE/FIXME: This lwp might have not been the tgid. */
+ /* If lwp is the tgid, we handle adding existing threads later.
+ Otherwise we just add lwp without bothering about any other
+ threads. */
ptid = ptid_build (lwpid, lwpid, 0);
else
{
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.
In this case we want the process thread to stop.
This is handled by having linux_attach set last_resume_kind ==
resume_stop after we return.
- ??? If the process already has several threads we leave the other
- threads running.
+
+ If the pid we are attaching to is also the tgid, we attach to and
+ stop all the existing threads. Otherwise, we attach to pid and
+ ignore any other threads in the same group as this pid.
3) GDB is connecting to gdbserver and is requesting an enumeration of all
existing threads.
linux_attach_lwp_1 (lwpid, 0);
}
+/* Attach to PID. If PID is the tgid, attach to it and all
+ of its threads. */
+
int
linux_attach (unsigned long pid)
{
+ /* Attach to PID. We will check for other threads
+ soon. */
linux_attach_lwp_1 (pid, 1);
linux_add_process (pid, 1);
thread->last_resume_kind = resume_stop;
}
+ if (linux_proc_get_tgid (pid) == pid)
+ {
+ DIR *dir;
+ char pathname[128];
+
+ sprintf (pathname, "/proc/%ld/task", pid);
+
+ dir = opendir (pathname);
+
+ if (!dir)
+ {
+ fprintf (stderr, "Could not open /proc/%ld/task.\n", pid);
+ fflush (stderr);
+ }
+ else
+ {
+ /* At this point we attached to the tgid. Scan the task for
+ existing threads. */
+ unsigned long lwp;
+ int new_threads_found;
+ int iterations = 0;
+ struct dirent *dp;
+
+ while (iterations < 2)
+ {
+ new_threads_found = 0;
+ /* Add all the other threads. While we go through the
+ threads, new threads may be spawned. Cycle through
+ the list of threads until we have done two iterations without
+ finding new threads. */
+ while ((dp = readdir (dir)) != NULL)
+ {
+ /* Fetch one lwp. */
+ lwp = strtoul (dp->d_name, NULL, 10);
+
+ /* Is this a new thread? */
+ if (lwp
+ && find_thread_ptid (ptid_build (pid, lwp, 0)) == NULL)
+ {
+ linux_attach_lwp_1 (lwp, 0);
+ new_threads_found++;
+
+ if (debug_threads)
+ fprintf (stderr, "\
+Found and attached to new lwp %ld\n", lwp);
+ }
+ }
+
+ if (!new_threads_found)
+ iterations++;
+ else
+ iterations = 0;
+
+ rewinddir (dir);
+ }
+ closedir (dir);
+ }
+ }
+
return 0;
}
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);
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);
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. */
lwp = find_lwp_pid (pid_to_ptid (pid));
- if (debug_threads)
- fprintf (stderr, "lk_1: killing lwp %ld, for pid: %d\n",
- lwpid_of (lwp), pid);
-
- do
+ if (lwp == NULL)
+ {
+ if (debug_threads)
+ fprintf (stderr, "lk_1: cannot find lwp %ld, for pid: %d\n",
+ lwpid_of (lwp), pid);
+ }
+ else
{
- ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0);
+ if (debug_threads)
+ fprintf (stderr, "lk_1: killing lwp %ld, for pid: %d\n",
+ lwpid_of (lwp), pid);
- /* Make sure it died. The loop is most likely unnecessary. */
- lwpid = linux_wait_for_event (lwp->head.id, &wstat, __WALL);
- } while (lwpid > 0 && WIFSTOPPED (wstat));
+ do
+ {
+ 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);
+ } while (lwpid > 0 && WIFSTOPPED (wstat));
+ }
the_target->mourn (process);
get_lwp_thread (lwp));
/* Finally, let it resume. */
+ if (the_low_target.prepare_to_resume != NULL)
+ the_low_target.prepare_to_resume (lwp);
ptrace (PTRACE_DETACH, lwpid_of (lwp), 0, 0);
delete_lwp (lwp);
linux_join (int pid)
{
int status, ret;
- struct process_info *process;
-
- process = find_process_pid (pid);
- if (process == NULL)
- return;
do {
ret = my_waitpid (pid, &status, 0);
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)
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);
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)
{
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. */
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
ourstatus->value.integer = WEXITSTATUS (w);
if (debug_threads)
- fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w));
+ fprintf (stderr,
+ "\nChild exited with retcode = %x \n",
+ WEXITSTATUS (w));
}
else
{
ourstatus->value.sig = target_signal_from_host (WTERMSIG (w));
if (debug_threads)
- fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w));
+ fprintf (stderr,
+ "\nChild terminated with signal = %x \n",
+ WTERMSIG (w));
}
report_to_gdb = (!maybe_internal_trap
|| current_inferior->last_resume_kind == resume_step
|| event_child->stopped_by_watchpoint
- || (!step_over_finished && !bp_explains_trap && !trace_event)
- || gdb_breakpoint_here (event_child->stop_pc));
+ || (!step_over_finished
+ && !bp_explains_trap && !trace_event)
+ || (gdb_breakpoint_here (event_child->stop_pc)
+ && gdb_condition_true_at_breakpoint (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
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 ();
}
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))
{
if (debug_threads)
fprintf (stderr,
{
if (debug_threads)
fprintf (stderr,
- "Need step over [LWP %ld]? yes, found breakpoint at 0x%s\n",
+ "Need step over [LWP %ld]? yes, "
+ "found breakpoint at 0x%s\n",
lwpid_of (lwp), paddress (pc));
/* We've found an lwp that needs stepping over --- return 1 so
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;
+ regset = target_regsets;
pid = lwpid_of (get_thread_lwp (current_inferior));
- 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))
+ 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);
-}
+ if (regset->size == 0 || disabled_regsets[regset - target_regsets])
+ {
+ regset ++;
+ continue;
+ }
-/* 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)
-{
- CORE_ADDR regaddr;
- int i, size;
- char *buf;
- int pid;
-
- if (regno >= 0)
- {
- if (regno >= the_low_target.num_regs)
- return;
-
- if ((*the_low_target.cannot_store_register) (regno) == 1)
- return;
-
- regaddr = register_addr (regno);
- if (regaddr == -1)
- return;
- errno = 0;
- 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);
- }
- }
- else
- for (regno = 0; regno < the_low_target.num_regs; regno++)
- usr_store_inferior_registers (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;
- }
-
- buf = xmalloc (regset->size);
+ buf = xmalloc (regset->size);
nt_type = regset->nt_type;
if (nt_type)
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)
+ {
+ all = regsets_fetch_inferior_registers (regcache);
+ usr_fetch_inferior_registers (regcache, regno, all);
+ }
+ else
+ {
+ 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);
+ }
}
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);
+ = (((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));
+ register PTRACE_XFER_TYPE *buffer = (PTRACE_XFER_TYPE *)
+ alloca (count * sizeof (PTRACE_XFER_TYPE));
+
int pid = lwpid_of (get_thread_lwp (current_inferior));
if (debug_threads)
/* Copy data to be written over corresponding part of buffer. */
- memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)), myaddr, len);
+ memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_XFER_TYPE) - 1)),
+ myaddr, len);
/* Write the entire buffer. */
__clone2 (linux_tracefork_grandchild, arg, STACK_SIZE,
CLONE_VM | SIGCHLD, NULL);
#else
- clone (linux_tracefork_grandchild, arg + STACK_SIZE,
+ clone (linux_tracefork_grandchild, (char *) arg + STACK_SIZE,
CLONE_VM | SIGCHLD, NULL);
#endif
#define PT_TEXT_ADDR 49*4
#define PT_DATA_ADDR 50*4
#define PT_TEXT_END_ADDR 51*4
+#elif defined(BFIN)
+#define PT_TEXT_ADDR 220
+#define PT_TEXT_END_ADDR 224
+#define PT_DATA_ADDR 228
+#elif defined(__TMS320C6X__)
+#define PT_TEXT_ADDR (0x10000*4)
+#define PT_DATA_ADDR (0x10004*4)
+#define PT_TEXT_END_ADDR (0x10008*4)
#endif
/* Under uClinux, programs are loaded at non-zero offsets, which we need
}
#endif
-static int
-compare_ints (const void *xa, const void *xb)
-{
- int a = *(const int *)xa;
- int b = *(const int *)xb;
-
- return a - b;
-}
-
-static int *
-unique (int *b, int *e)
-{
- int *d = b;
- while (++b != e)
- if (*d != *b)
- *++d = *b;
- return ++d;
-}
-
-/* Given PID, iterates over all threads in that process.
-
- Information about each thread, in a format suitable for qXfer:osdata:thread
- is printed to BUFFER, if it's not NULL. BUFFER is assumed to be already
- initialized, and the caller is responsible for finishing and appending '\0'
- to it.
-
- The list of cores that threads are running on is assigned to *CORES, if it
- is not NULL. If no cores are found, *CORES will be set to NULL. Caller
- should free *CORES. */
-
-static void
-list_threads (int pid, struct buffer *buffer, char **cores)
-{
- int count = 0;
- int allocated = 10;
- int *core_numbers = xmalloc (sizeof (int) * allocated);
- char pathname[128];
- DIR *dir;
- struct dirent *dp;
- struct stat statbuf;
-
- sprintf (pathname, "/proc/%d/task", pid);
- if (stat (pathname, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
- {
- dir = opendir (pathname);
- if (!dir)
- {
- free (core_numbers);
- return;
- }
-
- while ((dp = readdir (dir)) != NULL)
- {
- unsigned long lwp = strtoul (dp->d_name, NULL, 10);
-
- if (lwp != 0)
- {
- unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
-
- if (core != -1)
- {
- char s[sizeof ("4294967295")];
- sprintf (s, "%u", core);
-
- if (count == allocated)
- {
- allocated *= 2;
- core_numbers = realloc (core_numbers,
- sizeof (int) * allocated);
- }
- core_numbers[count++] = core;
- if (buffer)
- buffer_xml_printf (buffer,
- "<item>"
- "<column name=\"pid\">%d</column>"
- "<column name=\"tid\">%s</column>"
- "<column name=\"core\">%s</column>"
- "</item>", pid, dp->d_name, s);
- }
- else
- {
- if (buffer)
- buffer_xml_printf (buffer,
- "<item>"
- "<column name=\"pid\">%d</column>"
- "<column name=\"tid\">%s</column>"
- "</item>", pid, dp->d_name);
- }
- }
- }
- }
-
- if (cores)
- {
- *cores = NULL;
- if (count > 0)
- {
- struct buffer buffer2;
- int *b;
- int *e;
- qsort (core_numbers, count, sizeof (int), compare_ints);
-
- /* Remove duplicates. */
- b = core_numbers;
- e = unique (b, core_numbers + count);
-
- buffer_init (&buffer2);
-
- for (b = core_numbers; b != e; ++b)
- {
- char number[sizeof ("4294967295")];
- sprintf (number, "%u", *b);
- buffer_xml_printf (&buffer2, "%s%s",
- (b == core_numbers) ? "" : ",", number);
- }
- buffer_grow_str0 (&buffer2, "");
-
- *cores = buffer_finish (&buffer2);
- }
- }
- free (core_numbers);
-}
-
-static void
-show_process (int pid, const char *username, struct buffer *buffer)
-{
- char pathname[128];
- FILE *f;
- char cmd[MAXPATHLEN + 1];
-
- sprintf (pathname, "/proc/%d/cmdline", pid);
-
- if ((f = fopen (pathname, "r")) != NULL)
- {
- size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
- if (len > 0)
- {
- char *cores = 0;
- int i;
- for (i = 0; i < len; i++)
- if (cmd[i] == '\0')
- cmd[i] = ' ';
- cmd[len] = '\0';
-
- buffer_xml_printf (buffer,
- "<item>"
- "<column name=\"pid\">%d</column>"
- "<column name=\"user\">%s</column>"
- "<column name=\"command\">%s</column>",
- pid,
- username,
- cmd);
-
- /* This only collects core numbers, and does not print threads. */
- list_threads (pid, NULL, &cores);
-
- if (cores)
- {
- buffer_xml_printf (buffer,
- "<column name=\"cores\">%s</column>", cores);
- free (cores);
- }
-
- buffer_xml_printf (buffer, "</item>");
- }
- fclose (f);
- }
-}
-
static int
linux_qxfer_osdata (const char *annex,
unsigned char *readbuf, unsigned const char *writebuf,
CORE_ADDR offset, int len)
{
- /* We make the process list snapshot when the object starts to be
- read. */
- static const char *buf;
- static long len_avail = -1;
- static struct buffer buffer;
- int processes = 0;
- int threads = 0;
-
- DIR *dirp;
-
- if (strcmp (annex, "processes") == 0)
- processes = 1;
- else if (strcmp (annex, "threads") == 0)
- threads = 1;
- else
- return 0;
-
- if (!readbuf || writebuf)
- return 0;
-
- if (offset == 0)
- {
- if (len_avail != -1 && len_avail != 0)
- buffer_free (&buffer);
- len_avail = 0;
- buf = NULL;
- buffer_init (&buffer);
- if (processes)
- buffer_grow_str (&buffer, "<osdata type=\"processes\">");
- else if (threads)
- buffer_grow_str (&buffer, "<osdata type=\"threads\">");
-
- dirp = opendir ("/proc");
- if (dirp)
- {
- struct dirent *dp;
- while ((dp = readdir (dirp)) != NULL)
- {
- struct stat statbuf;
- char procentry[sizeof ("/proc/4294967295")];
-
- if (!isdigit (dp->d_name[0])
- || strlen (dp->d_name) > sizeof ("4294967295") - 1)
- continue;
-
- sprintf (procentry, "/proc/%s", dp->d_name);
- if (stat (procentry, &statbuf) == 0
- && S_ISDIR (statbuf.st_mode))
- {
- int pid = (int) strtoul (dp->d_name, NULL, 10);
-
- if (processes)
- {
- struct passwd *entry = getpwuid (statbuf.st_uid);
- show_process (pid, entry ? entry->pw_name : "?", &buffer);
- }
- else if (threads)
- {
- list_threads (pid, &buffer, NULL);
- }
- }
- }
-
- closedir (dirp);
- }
- buffer_grow_str0 (&buffer, "</osdata>\n");
- buf = buffer_finish (&buffer);
- len_avail = strlen (buf);
- }
-
- if (offset >= len_avail)
- {
- /* Done. Get rid of the data. */
- buffer_free (&buffer);
- buf = NULL;
- len_avail = 0;
- return 0;
- }
-
- if (len > len_avail - offset)
- len = len_avail - offset;
- memcpy (readbuf, buf + offset, len);
-
- return len;
+ return linux_common_xfer_osdata (annex, readbuf, offset, len);
}
/* Convert a native/host siginfo object, into/from the siginfo in the
readbuf != NULL ? "Reading" : "Writing",
pid);
- if (offset > sizeof (siginfo))
+ if (offset >= sizeof (siginfo))
return -1;
if (ptrace (PTRACE_GETSIGINFO, pid, 0, &siginfo) != 0)
return 1;
}
+static int
+linux_supports_disable_randomization (void)
+{
+#ifdef HAVE_PERSONALITY
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+static int
+linux_supports_agent (void)
+{
+ return 1;
+}
/* Enumerate spufs IDs for process PID. */
static int
return ret;
}
-static int
-linux_core_of_thread (ptid_t ptid)
-{
- char filename[sizeof ("/proc//task//stat")
- + 2 * 20 /* decimal digits for 2 numbers, max 2^64 bit each */
- + 1];
- FILE *f;
- char *content = NULL;
- char *p;
- char *ts = 0;
- int content_read = 0;
- int i;
- int core;
-
- sprintf (filename, "/proc/%d/task/%ld/stat",
- ptid_get_pid (ptid), ptid_get_lwp (ptid));
- f = fopen (filename, "r");
- if (!f)
- return -1;
+#if defined PT_GETDSBT || defined PTRACE_GETFDPIC
+struct target_loadseg
+{
+ /* Core address to which the segment is mapped. */
+ Elf32_Addr addr;
+ /* VMA recorded in the program header. */
+ Elf32_Addr p_vaddr;
+ /* Size of this segment in memory. */
+ Elf32_Word p_memsz;
+};
- for (;;)
- {
- int n;
- content = realloc (content, content_read + 1024);
- n = fread (content + content_read, 1, 1024, f);
- content_read += n;
- if (n < 1024)
- {
- content[content_read] = '\0';
- break;
- }
- }
+# if defined PT_GETDSBT
+struct target_loadmap
+{
+ /* Protocol version number, must be zero. */
+ Elf32_Word version;
+ /* Pointer to the DSBT table, its size, and the DSBT index. */
+ unsigned *dsbt_table;
+ unsigned dsbt_size, dsbt_index;
+ /* Number of segments in this map. */
+ Elf32_Word nsegs;
+ /* The actual memory map. */
+ struct target_loadseg segs[/*nsegs*/];
+};
+# define LINUX_LOADMAP PT_GETDSBT
+# define LINUX_LOADMAP_EXEC PTRACE_GETDSBT_EXEC
+# define LINUX_LOADMAP_INTERP PTRACE_GETDSBT_INTERP
+# else
+struct target_loadmap
+{
+ /* Protocol version number, must be zero. */
+ Elf32_Half version;
+ /* Number of segments in this map. */
+ Elf32_Half nsegs;
+ /* The actual memory map. */
+ struct target_loadseg segs[/*nsegs*/];
+};
+# define LINUX_LOADMAP PTRACE_GETFDPIC
+# define LINUX_LOADMAP_EXEC PTRACE_GETFDPIC_EXEC
+# define LINUX_LOADMAP_INTERP PTRACE_GETFDPIC_INTERP
+# endif
- p = strchr (content, '(');
+static int
+linux_read_loadmap (const char *annex, CORE_ADDR offset,
+ unsigned char *myaddr, unsigned int len)
+{
+ int pid = lwpid_of (get_thread_lwp (current_inferior));
+ int addr = -1;
+ struct target_loadmap *data = NULL;
+ unsigned int actual_length, copy_length;
+
+ if (strcmp (annex, "exec") == 0)
+ addr = (int) LINUX_LOADMAP_EXEC;
+ else if (strcmp (annex, "interp") == 0)
+ addr = (int) LINUX_LOADMAP_INTERP;
+ else
+ return -1;
- /* Skip ")". */
- if (p != NULL)
- p = strchr (p, ')');
- if (p != NULL)
- p++;
+ if (ptrace (LINUX_LOADMAP, pid, addr, &data) != 0)
+ return -1;
- /* If the first field after program name has index 0, then core number is
- the field with index 36. There's no constant for that anywhere. */
- if (p != NULL)
- p = strtok_r (p, " ", &ts);
- for (i = 0; p != NULL && i != 36; ++i)
- p = strtok_r (NULL, " ", &ts);
+ if (data == NULL)
+ return -1;
- if (p == NULL || sscanf (p, "%d", &core) == 0)
- core = -1;
+ actual_length = sizeof (struct target_loadmap)
+ + sizeof (struct target_loadseg) * data->nsegs;
- free (content);
- fclose (f);
+ if (offset < 0 || offset > actual_length)
+ return -1;
- return core;
+ copy_length = actual_length - offset < len ? actual_length - offset : len;
+ memcpy (myaddr, (char *) data + offset, copy_length);
+ return copy_length;
}
+#else
+# define linux_read_loadmap NULL
+#endif /* defined PT_GETDSBT || defined PTRACE_GETFDPIC */
static void
linux_process_qsupported (const char *query)
CORE_ADDR lockaddr,
ULONGEST orig_size,
CORE_ADDR *jump_entry,
+ CORE_ADDR *trampoline,
+ ULONGEST *trampoline_size,
unsigned char *jjump_pad_insn,
ULONGEST *jjump_pad_insn_size,
CORE_ADDR *adjusted_insn_addr,
- CORE_ADDR *adjusted_insn_addr_end)
+ CORE_ADDR *adjusted_insn_addr_end,
+ char *err)
{
return (*the_low_target.install_fast_tracepoint_jump_pad)
(tpoint, tpaddr, collector, lockaddr, orig_size,
- jump_entry, jjump_pad_insn, jjump_pad_insn_size,
- adjusted_insn_addr, adjusted_insn_addr_end);
+ jump_entry, trampoline, trampoline_size,
+ jjump_pad_insn, jjump_pad_insn_size,
+ adjusted_insn_addr, adjusted_insn_addr_end,
+ err);
}
static struct emit_ops *
return NULL;
}
+static int
+linux_get_min_fast_tracepoint_insn_len (void)
+{
+ return (*the_low_target.get_min_fast_tracepoint_insn_len) ();
+}
+
+/* Extract &phdr and num_phdr in the inferior. Return 0 on success. */
+
+static int
+get_phdr_phnum_from_proc_auxv (const int pid, const int is_elf64,
+ CORE_ADDR *phdr_memaddr, int *num_phdr)
+{
+ char filename[PATH_MAX];
+ int fd;
+ const int auxv_size = is_elf64
+ ? sizeof (Elf64_auxv_t) : sizeof (Elf32_auxv_t);
+ char buf[sizeof (Elf64_auxv_t)]; /* The larger of the two. */
+
+ xsnprintf (filename, sizeof filename, "/proc/%d/auxv", pid);
+
+ fd = open (filename, O_RDONLY);
+ if (fd < 0)
+ return 1;
+
+ *phdr_memaddr = 0;
+ *num_phdr = 0;
+ while (read (fd, buf, auxv_size) == auxv_size
+ && (*phdr_memaddr == 0 || *num_phdr == 0))
+ {
+ if (is_elf64)
+ {
+ Elf64_auxv_t *const aux = (Elf64_auxv_t *) buf;
+
+ switch (aux->a_type)
+ {
+ case AT_PHDR:
+ *phdr_memaddr = aux->a_un.a_val;
+ break;
+ case AT_PHNUM:
+ *num_phdr = aux->a_un.a_val;
+ break;
+ }
+ }
+ else
+ {
+ Elf32_auxv_t *const aux = (Elf32_auxv_t *) buf;
+
+ switch (aux->a_type)
+ {
+ case AT_PHDR:
+ *phdr_memaddr = aux->a_un.a_val;
+ break;
+ case AT_PHNUM:
+ *num_phdr = aux->a_un.a_val;
+ break;
+ }
+ }
+ }
+
+ close (fd);
+
+ if (*phdr_memaddr == 0 || *num_phdr == 0)
+ {
+ warning ("Unexpected missing AT_PHDR and/or AT_PHNUM: "
+ "phdr_memaddr = %ld, phdr_num = %d",
+ (long) *phdr_memaddr, *num_phdr);
+ return 2;
+ }
+
+ return 0;
+}
+
+/* Return &_DYNAMIC (via PT_DYNAMIC) in the inferior, or 0 if not present. */
+
+static CORE_ADDR
+get_dynamic (const int pid, const int is_elf64)
+{
+ CORE_ADDR phdr_memaddr, relocation;
+ int num_phdr, i;
+ unsigned char *phdr_buf;
+ const int phdr_size = is_elf64 ? sizeof (Elf64_Phdr) : sizeof (Elf32_Phdr);
+
+ if (get_phdr_phnum_from_proc_auxv (pid, is_elf64, &phdr_memaddr, &num_phdr))
+ return 0;
+
+ gdb_assert (num_phdr < 100); /* Basic sanity check. */
+ phdr_buf = alloca (num_phdr * phdr_size);
+
+ if (linux_read_memory (phdr_memaddr, phdr_buf, num_phdr * phdr_size))
+ return 0;
+
+ /* Compute relocation: it is expected to be 0 for "regular" executables,
+ non-zero for PIE ones. */
+ relocation = -1;
+ for (i = 0; relocation == -1 && i < num_phdr; i++)
+ if (is_elf64)
+ {
+ Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size);
+
+ if (p->p_type == PT_PHDR)
+ relocation = phdr_memaddr - p->p_vaddr;
+ }
+ else
+ {
+ Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size);
+
+ if (p->p_type == PT_PHDR)
+ relocation = phdr_memaddr - p->p_vaddr;
+ }
+
+ if (relocation == -1)
+ {
+ warning ("Unexpected missing PT_PHDR");
+ return 0;
+ }
+
+ for (i = 0; i < num_phdr; i++)
+ {
+ if (is_elf64)
+ {
+ Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size);
+
+ if (p->p_type == PT_DYNAMIC)
+ return p->p_vaddr + relocation;
+ }
+ else
+ {
+ Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size);
+
+ if (p->p_type == PT_DYNAMIC)
+ return p->p_vaddr + relocation;
+ }
+ }
+
+ return 0;
+}
+
+/* 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. */
+
+static CORE_ADDR
+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. */
+
+ dynamic_memaddr = get_dynamic (pid, is_elf64);
+ if (dynamic_memaddr == 0)
+ return (CORE_ADDR) -1;
+
+ while (linux_read_memory (dynamic_memaddr, buf, dyn_size) == 0)
+ {
+ if (is_elf64)
+ {
+ Elf64_Dyn *const dyn = (Elf64_Dyn *) buf;
+
+ if (dyn->d_tag == DT_DEBUG)
+ return dyn->d_un.d_val;
+
+ if (dyn->d_tag == DT_NULL)
+ break;
+ }
+ else
+ {
+ Elf32_Dyn *const dyn = (Elf32_Dyn *) buf;
+
+ if (dyn->d_tag == DT_DEBUG)
+ return dyn->d_un.d_val;
+
+ if (dyn->d_tag == DT_NULL)
+ break;
+ }
+
+ dynamic_memaddr += dyn_size;
+ }
+
+ return (CORE_ADDR) -1;
+}
+
+/* Read one pointer from MEMADDR in the inferior. */
+
+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);
+}
+
+struct link_map_offsets
+ {
+ /* Offset and size of r_debug.r_version. */
+ int r_version_offset;
+
+ /* Offset and size of r_debug.r_map. */
+ int r_map_offset;
+
+ /* Offset to l_addr field in struct link_map. */
+ int l_addr_offset;
+
+ /* Offset to l_name field in struct link_map. */
+ int l_name_offset;
+
+ /* Offset to l_ld field in struct link_map. */
+ int l_ld_offset;
+
+ /* Offset to l_next field in struct link_map. */
+ int l_next_offset;
+
+ /* Offset to l_prev field in struct link_map. */
+ int l_prev_offset;
+ };
+
+/* Construct qXfer:libraries:read reply. */
+
+static int
+linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
+ unsigned const char *writebuf,
+ CORE_ADDR offset, int len)
+{
+ char *document;
+ unsigned document_len;
+ struct process_info_private *const priv = current_process ()->private;
+ char filename[PATH_MAX];
+ int pid, is_elf64;
+
+ static const struct link_map_offsets lmo_32bit_offsets =
+ {
+ 0, /* r_version offset. */
+ 4, /* r_debug.r_map offset. */
+ 0, /* l_addr offset in link_map. */
+ 4, /* l_name offset in link_map. */
+ 8, /* l_ld offset in link_map. */
+ 12, /* l_next offset in link_map. */
+ 16 /* l_prev offset in link_map. */
+ };
+
+ static const struct link_map_offsets lmo_64bit_offsets =
+ {
+ 0, /* r_version offset. */
+ 8, /* r_debug.r_map offset. */
+ 0, /* l_addr offset in link_map. */
+ 8, /* l_name offset in link_map. */
+ 16, /* l_ld offset in link_map. */
+ 24, /* l_next offset in link_map. */
+ 32 /* l_prev offset in link_map. */
+ };
+ const struct link_map_offsets *lmo;
+
+ if (writebuf != NULL)
+ return -2;
+ if (readbuf == NULL)
+ return -1;
+
+ pid = lwpid_of (get_thread_lwp (current_inferior));
+ xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid);
+ is_elf64 = elf_64_file_p (filename);
+ 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)
+ {
+ document = xstrdup ("<library-list-svr4 version=\"1.0\"/>\n");
+ }
+ else
+ {
+ int allocated = 1024;
+ char *p;
+ const int ptr_size = is_elf64 ? 8 : 4;
+ CORE_ADDR lm_addr, lm_prev, l_name, l_addr, l_ld, l_next, l_prev;
+ int r_version, header_done = 0;
+
+ document = xmalloc (allocated);
+ strcpy (document, "<library-list-svr4 version=\"1.0\"");
+ p = document + strlen (document);
+
+ r_version = 0;
+ if (linux_read_memory (priv->r_debug + lmo->r_version_offset,
+ (unsigned char *) &r_version,
+ sizeof (r_version)) != 0
+ || r_version != 1)
+ {
+ warning ("unexpected r_debug version %d", r_version);
+ goto done;
+ }
+
+ if (read_one_ptr (priv->r_debug + lmo->r_map_offset,
+ &lm_addr, ptr_size) != 0)
+ {
+ warning ("unable to read r_map from 0x%lx",
+ (long) priv->r_debug + lmo->r_map_offset);
+ goto done;
+ }
+
+ lm_prev = 0;
+ while (read_one_ptr (lm_addr + lmo->l_name_offset,
+ &l_name, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_addr_offset,
+ &l_addr, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_ld_offset,
+ &l_ld, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_prev_offset,
+ &l_prev, ptr_size) == 0
+ && read_one_ptr (lm_addr + lmo->l_next_offset,
+ &l_next, ptr_size) == 0)
+ {
+ unsigned char libname[PATH_MAX];
+
+ if (lm_prev != l_prev)
+ {
+ warning ("Corrupted shared library list: 0x%lx != 0x%lx",
+ (long) lm_prev, (long) l_prev);
+ break;
+ }
+
+ /* Not checking for error because reading may stop before
+ we've got PATH_MAX worth of characters. */
+ libname[0] = '\0';
+ linux_read_memory (l_name, libname, sizeof (libname) - 1);
+ libname[sizeof (libname) - 1] = '\0';
+ if (libname[0] != '\0')
+ {
+ /* 6x the size for xml_escape_text below. */
+ size_t len = 6 * strlen ((char *) libname);
+ char *name;
+
+ if (!header_done)
+ {
+ /* Terminate `<library-list-svr4'. */
+ *p++ = '>';
+ header_done = 1;
+ }
+
+ while (allocated < p - document + len + 200)
+ {
+ /* Expand to guarantee sufficient storage. */
+ uintptr_t document_len = p - document;
+
+ document = xrealloc (document, 2 * allocated);
+ allocated *= 2;
+ p = document + document_len;
+ }
+
+ name = xml_escape_text ((char *) libname);
+ p += sprintf (p, "<library name=\"%s\" lm=\"0x%lx\" "
+ "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
+ name, (unsigned long) lm_addr,
+ (unsigned long) l_addr, (unsigned long) l_ld);
+ free (name);
+ }
+ else if (lm_prev == 0)
+ {
+ sprintf (p, " main-lm=\"0x%lx\"", (unsigned long) lm_addr);
+ p = p + strlen (p);
+ }
+
+ if (l_next == 0)
+ break;
+
+ lm_prev = lm_addr;
+ lm_addr = l_next;
+ }
+ done:
+ strcpy (p, "</library-list-svr4>");
+ }
+
+ document_len = strlen (document);
+ if (offset < document_len)
+ document_len -= offset;
+ else
+ document_len = 0;
+ if (len > document_len)
+ len = document_len;
+
+ memcpy (readbuf, document + offset, len);
+ xfree (document);
+
+ return len;
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
#else
NULL,
#endif
- linux_core_of_thread,
+ linux_common_core_of_thread,
+ linux_read_loadmap,
linux_process_qsupported,
linux_supports_tracepoints,
linux_read_pc,
linux_cancel_breakpoints,
linux_stabilize_threads,
linux_install_fast_tracepoint_jump_pad,
- linux_emit_ops
+ linux_emit_ops,
+ linux_supports_disable_randomization,
+ linux_get_min_fast_tracepoint_insn_len,
+ linux_qxfer_libraries_svr4,
+ linux_supports_agent,
};
static void