#include "linux-low.h"
#include "nat/linux-osdata.h"
#include "agent.h"
+#include "tdesc.h"
+#include "rsp-low.h"
#include "nat/linux-nat.h"
#include "nat/linux-waitpid.h"
#include "gdb_wait.h"
-#include <sys/ptrace.h>
+#include "nat/gdb_ptrace.h"
#include "nat/linux-ptrace.h"
#include "nat/linux-procfs.h"
#include "nat/linux-personality.h"
definition of elf_fpregset_t. */
#include <elf.h>
#endif
+#include "nat/linux-namespaces.h"
#ifndef SPUFS_MAGIC
#define SPUFS_MAGIC 0x23c9b64e
} Elf64_auxv_t;
#endif
+/* Does the current host support PTRACE_GETREGSET? */
+int have_ptrace_getregset = -1;
+
/* LWP accessors. */
/* See nat/linux-nat.h. */
proc = add_process (pid, attached);
proc->priv = xcalloc (1, sizeof (*proc->priv));
- /* Set the arch when the first LWP stops. */
- proc->priv->new_inferior = 1;
-
if (the_low_target.new_process != NULL)
proc->priv->arch_private = the_low_target.new_process ();
static CORE_ADDR get_pc (struct lwp_info *lwp);
/* Handle a GNU/Linux extended wait response. If we see a clone
- event, we need to add the new LWP to our list (and not report the
- trap to higher layers). */
+ event, we need to add the new LWP to our list (and return 0 so as
+ not to report the trap to higher layers). */
-static void
-handle_extended_wait (struct lwp_info *event_child, int wstat)
+static int
+handle_extended_wait (struct lwp_info *event_lwp, int wstat)
{
int event = linux_ptrace_get_extended_event (wstat);
- struct thread_info *event_thr = get_lwp_thread (event_child);
+ struct thread_info *event_thr = get_lwp_thread (event_lwp);
struct lwp_info *new_lwp;
- if (event == PTRACE_EVENT_CLONE)
+ if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_VFORK)
+ || (event == PTRACE_EVENT_CLONE))
{
ptid_t ptid;
unsigned long new_pid;
int ret, status;
+ /* Get the pid of the new lwp. */
ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0,
&new_pid);
warning ("wait returned unexpected status 0x%x", status);
}
+ if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
+ {
+ struct process_info *parent_proc;
+ struct process_info *child_proc;
+ struct lwp_info *child_lwp;
+ struct thread_info *child_thr;
+ struct target_desc *tdesc;
+
+ ptid = ptid_build (new_pid, new_pid, 0);
+
+ if (debug_threads)
+ {
+ debug_printf ("HEW: Got fork event from LWP %ld, "
+ "new child is %d\n",
+ ptid_get_lwp (ptid_of (event_thr)),
+ ptid_get_pid (ptid));
+ }
+
+ /* Add the new process to the tables and clone the breakpoint
+ lists of the parent. We need to do this even if the new process
+ will be detached, since we will need the process object and the
+ breakpoints to remove any breakpoints from memory when we
+ detach, and the client side will access registers. */
+ child_proc = linux_add_process (new_pid, 0);
+ gdb_assert (child_proc != NULL);
+ child_lwp = add_lwp (ptid);
+ gdb_assert (child_lwp != NULL);
+ child_lwp->stopped = 1;
+ child_lwp->must_set_ptrace_flags = 1;
+ child_lwp->status_pending_p = 0;
+ child_thr = get_lwp_thread (child_lwp);
+ child_thr->last_resume_kind = resume_stop;
+ child_thr->last_status.kind = TARGET_WAITKIND_STOPPED;
+
+ parent_proc = get_thread_process (event_thr);
+ child_proc->attached = parent_proc->attached;
+ clone_all_breakpoints (&child_proc->breakpoints,
+ &child_proc->raw_breakpoints,
+ parent_proc->breakpoints);
+
+ tdesc = xmalloc (sizeof (struct target_desc));
+ copy_target_description (tdesc, parent_proc->tdesc);
+ child_proc->tdesc = tdesc;
+
+ /* Clone arch-specific process data. */
+ if (the_low_target.new_fork != NULL)
+ the_low_target.new_fork (parent_proc, child_proc);
+
+ /* Save fork info in the parent thread. */
+ if (event == PTRACE_EVENT_FORK)
+ event_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+ else if (event == PTRACE_EVENT_VFORK)
+ event_lwp->waitstatus.kind = TARGET_WAITKIND_VFORKED;
+
+ event_lwp->waitstatus.value.related_pid = ptid;
+
+ /* The status_pending field contains bits denoting the
+ extended event, so when the pending event is handled,
+ the handler will look at lwp->waitstatus. */
+ event_lwp->status_pending_p = 1;
+ event_lwp->status_pending = wstat;
+
+ /* Report the event. */
+ return 0;
+ }
+
if (debug_threads)
debug_printf ("HEW: Got clone event "
"from LWP %ld, new child is LWP %ld\n",
new_lwp->status_pending_p = 1;
new_lwp->status_pending = status;
}
+
+ /* Don't report the event. */
+ return 1;
+ }
+ else if (event == PTRACE_EVENT_VFORK_DONE)
+ {
+ event_lwp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+
+ /* Report the event. */
+ return 0;
}
+
+ internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
}
/* Return the PC as read from the regcache of LWP, without any
return pid;
}
+/* Implement the arch_setup target_ops method. */
+
+static void
+linux_arch_setup (void)
+{
+ the_low_target.arch_setup ();
+}
+
/* Attach to an inferior process. Returns 0 on success, ERRNO on
error. */
res = my_waitpid (lwpid, &wstat, __WCLONE);
} while (res > 0 && WIFSTOPPED (wstat));
- gdb_assert (res > 0);
+ /* Even if it was stopped, the child may have already disappeared.
+ E.g., if it was killed by SIGKILL. */
+ if (res < 0 && errno != ECHILD)
+ perror_with_name ("kill_wait_lwp");
}
/* Callback for `find_inferior'. Kills an lwp of a given process,
return child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
}
+/* Return the ptrace options that we want to try to enable. */
+
+static int
+linux_low_ptrace_options (int attached)
+{
+ int options = 0;
+
+ if (!attached)
+ options |= PTRACE_O_EXITKILL;
+
+ if (report_fork_events)
+ options |= PTRACE_O_TRACEFORK;
+
+ if (report_vfork_events)
+ options |= (PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE);
+
+ return options;
+}
+
/* Do low-level handling of the event, and check if we should go on
and pass it to caller code. Return the affected lwp if we are, or
NULL otherwise. */
{
struct process_info *proc;
- /* Architecture-specific setup after inferior is running. This
- needs to happen after we have attached to the inferior and it
- is stopped for the first time, but before we access any
- inferior registers. */
+ /* Architecture-specific setup after inferior is running. */
proc = find_process_pid (pid_of (thread));
- if (proc->priv->new_inferior)
+ if (proc->tdesc == NULL)
{
- struct thread_info *saved_thread;
-
- saved_thread = current_thread;
- current_thread = thread;
+ if (proc->attached)
+ {
+ struct thread_info *saved_thread;
- the_low_target.arch_setup ();
+ /* This needs to happen after we have attached to the
+ inferior and it is stopped for the first time, but
+ before we access any inferior registers. */
+ saved_thread = current_thread;
+ current_thread = thread;
- current_thread = saved_thread;
+ the_low_target.arch_setup ();
- proc->priv->new_inferior = 0;
+ current_thread = saved_thread;
+ }
+ else
+ {
+ /* The process is started, but GDBserver will do
+ architecture-specific setup after the program stops at
+ the first instruction. */
+ child->status_pending_p = 1;
+ child->status_pending = wstat;
+ return child;
+ }
}
}
if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
{
struct process_info *proc = find_process_pid (pid_of (thread));
+ int options = linux_low_ptrace_options (proc->attached);
- linux_enable_event_reporting (lwpid, proc->attached);
+ linux_enable_event_reporting (lwpid, options);
child->must_set_ptrace_flags = 0;
}
&& linux_is_extended_waitstatus (wstat))
{
child->stop_pc = get_pc (child);
- handle_extended_wait (child, wstat);
- return NULL;
+ if (handle_extended_wait (child, wstat))
+ {
+ /* The event has been handled, so just return without
+ reporting it. */
+ return NULL;
+ }
}
/* Check first whether this was a SW/HW breakpoint before checking
return null_ptid;
}
+/* Return non-zero if WAITSTATUS reflects an extended linux
+ event. Otherwise, return zero. */
+
+static int
+extended_event_reported (const struct target_waitstatus *waitstatus)
+{
+ if (waitstatus == NULL)
+ return 0;
+
+ return (waitstatus->kind == TARGET_WAITKIND_FORKED
+ || waitstatus->kind == TARGET_WAITKIND_VFORKED
+ || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE);
+}
+
/* Wait for process, returns status. */
static ptid_t
&& !bp_explains_trap && !trace_event)
|| (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)));
+ && gdb_no_commands_at_breakpoint (event_child->stop_pc))
+ || extended_event_reported (&event_child->waitstatus));
run_breakpoint_commands (event_child->stop_pc);
if (debug_threads)
{
+ if (extended_event_reported (&event_child->waitstatus))
+ {
+ char *str;
+
+ str = target_waitstatus_to_string (&event_child->waitstatus);
+ debug_printf ("LWP %ld: extended event with waitstatus %s\n",
+ lwpid_of (get_lwp_thread (event_child)), str);
+ xfree (str);
+ }
if (current_thread->last_resume_kind == resume_step)
{
if (event_child->step_range_start == event_child->step_range_end)
unstop_all_lwps (1, event_child);
}
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
+ if (extended_event_reported (&event_child->waitstatus))
+ {
+ /* If the reported event is a fork, vfork or exec, let GDB know. */
+ ourstatus->kind = event_child->waitstatus.kind;
+ ourstatus->value = event_child->waitstatus.value;
+
+ /* Clear the event lwp's waitstatus since we handled it already. */
+ event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE;
+ }
+ else
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
/* Now that we've selected our final event LWP, un-adjust its PC if
it was a software breakpoint, and the client doesn't know we can
but, it stopped for other reasons. */
ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
}
- else
+ else if (ourstatus->kind == TARGET_WAITKIND_STOPPED)
{
ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
}
struct thread_info *thread = get_lwp_thread (lwp);
struct thread_info *saved_thread;
int fast_tp_collecting;
+ struct process_info *proc = get_thread_process (thread);
+
+ /* Note that target description may not be initialised
+ (proc->tdesc == NULL) at this point because the program hasn't
+ stopped at the first instruction yet. It means GDBserver skips
+ the extra traps from the wrapper program (see option --wrapper).
+ Code in this function that requires register access should be
+ guarded by proc->tdesc == NULL or something else. */
if (lwp->stopped == 0)
return;
/* Cancel actions that rely on GDB not changing the PC (e.g., the
user used the "jump" command, or "set $pc = foo"). */
- if (lwp->stop_pc != get_pc (lwp))
+ if (thread->while_stepping != NULL && lwp->stop_pc != get_pc (lwp))
{
/* Collecting 'while-stepping' actions doesn't make sense
anymore. */
step = 1;
}
- if (the_low_target.get_pc != NULL)
+ if (proc->tdesc != NULL && the_low_target.get_pc != NULL)
{
struct regcache *regcache = get_thread_regcache (current_thread, 1);
struct lwp_info *lwp = get_thread_lwp (thread);
struct thread_info *saved_thread;
CORE_ADDR pc;
+ struct process_info *proc = get_thread_process (thread);
+
+ /* GDBserver is skipping the extra traps from the wrapper program,
+ don't have to do step over. */
+ if (proc->tdesc == NULL)
+ return 0;
/* LWPs which will not be resumed are not interesting, because we
might not wait for them next time through linux_wait. */
val = val & 0xffff;
else if (len == 3)
val = val & 0xffffff;
- debug_printf ("Writing %0*x to 0x%08lx\n", 2 * ((len < 4) ? len : 4),
- val, (long)memaddr);
+ debug_printf ("Writing %0*x to 0x%08lx in process %d\n",
+ 2 * ((len < 4) ? len : 4), val, (long)memaddr, pid);
}
/* Fill start and end extra bytes of buffer with existing memory data. */
return 1;
}
+/* Check if fork events are supported. */
+
+static int
+linux_supports_fork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Check if vfork events are supported. */
+
+static int
+linux_supports_vfork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Callback for 'find_inferior'. Set the (possibly changed) ptrace
+ options for the specified lwp. */
+
+static int
+reset_lwp_ptrace_options_callback (struct inferior_list_entry *entry,
+ void *args)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+ struct lwp_info *lwp = get_thread_lwp (thread);
+
+ if (!lwp->stopped)
+ {
+ /* Stop the lwp so we can modify its ptrace options. */
+ lwp->must_set_ptrace_flags = 1;
+ linux_stop_lwp (lwp);
+ }
+ else
+ {
+ /* Already stopped; go ahead and set the ptrace options. */
+ struct process_info *proc = find_process_pid (pid_of (thread));
+ int options = linux_low_ptrace_options (proc->attached);
+
+ linux_enable_event_reporting (lwpid_of (thread), options);
+ lwp->must_set_ptrace_flags = 0;
+ }
+
+ return 0;
+}
+
+/* Target hook for 'handle_new_gdb_connection'. Causes a reset of the
+ ptrace flags for all inferiors. This is in case the new GDB connection
+ doesn't support the same set of events that the previous one did. */
+
+static void
+linux_handle_new_gdb_connection (void)
+{
+ pid_t pid;
+
+ /* Request that all the lwps reset their ptrace options. */
+ find_inferior (&all_threads, reset_lwp_ptrace_options_callback , &pid);
+}
+
static int
linux_supports_disable_randomization (void)
{
return (err == BTRACE_ERR_NONE ? 0 : -1);
}
+/* Encode an Intel(R) Processor Trace configuration. */
+
+static void
+linux_low_encode_pt_config (struct buffer *buffer,
+ const struct btrace_data_pt_config *config)
+{
+ buffer_grow_str (buffer, "<pt-config>\n");
+
+ switch (config->cpu.vendor)
+ {
+ case CV_INTEL:
+ buffer_xml_printf (buffer, "<cpu vendor=\"GenuineIntel\" family=\"%u\" "
+ "model=\"%u\" stepping=\"%u\"/>\n",
+ config->cpu.family, config->cpu.model,
+ config->cpu.stepping);
+ break;
+
+ default:
+ break;
+ }
+
+ buffer_grow_str (buffer, "</pt-config>\n");
+}
+
+/* Encode a raw buffer. */
+
+static void
+linux_low_encode_raw (struct buffer *buffer, const gdb_byte *data,
+ unsigned int size)
+{
+ if (size == 0)
+ return;
+
+ /* We use hex encoding - see common/rsp-low.h. */
+ buffer_grow_str (buffer, "<raw>\n");
+
+ while (size-- > 0)
+ {
+ char elem[2];
+
+ elem[0] = tohex ((*data >> 4) & 0xf);
+ elem[1] = tohex (*data++ & 0xf);
+
+ buffer_grow (buffer, elem, 2);
+ }
+
+ buffer_grow_str (buffer, "</raw>\n");
+}
+
/* See to_read_btrace target method. */
static int
else
buffer_grow_str0 (buffer, "E.Generic Error.");
- btrace_data_fini (&btrace);
- return -1;
+ goto err;
}
switch (btrace.format)
{
case BTRACE_FORMAT_NONE:
buffer_grow_str0 (buffer, "E.No Trace.");
- break;
+ goto err;
case BTRACE_FORMAT_BTS:
buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
buffer_grow_str0 (buffer, "</btrace>\n");
break;
- default:
- buffer_grow_str0 (buffer, "E.Unknown Trace Format.");
+ case BTRACE_FORMAT_PT:
+ buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
+ buffer_grow_str (buffer, "<btrace version=\"1.0\">\n");
+ buffer_grow_str (buffer, "<pt>\n");
- btrace_data_fini (&btrace);
- return -1;
+ linux_low_encode_pt_config (buffer, &btrace.variant.pt.config);
+
+ linux_low_encode_raw (buffer, btrace.variant.pt.data,
+ btrace.variant.pt.size);
+
+ buffer_grow_str (buffer, "</pt>\n");
+ buffer_grow_str0 (buffer, "</btrace>\n");
+ break;
+
+ default:
+ buffer_grow_str0 (buffer, "E.Unsupported Trace Format.");
+ goto err;
}
btrace_data_fini (&btrace);
return 0;
+
+err:
+ btrace_data_fini (&btrace);
+ return -1;
}
/* See to_btrace_conf target method. */
buffer_xml_printf (buffer, " size=\"0x%x\"", conf->bts.size);
buffer_xml_printf (buffer, " />\n");
break;
+
+ case BTRACE_FORMAT_PT:
+ buffer_xml_printf (buffer, "<pt");
+ buffer_xml_printf (buffer, " size=\"0x%x\"", conf->pt.size);
+ buffer_xml_printf (buffer, "/>\n");
+ break;
}
}
static struct target_ops linux_target_ops = {
linux_create_inferior,
+ linux_arch_setup,
linux_attach,
linux_kill,
linux_detach,
linux_async,
linux_start_non_stop,
linux_supports_multi_process,
+ linux_supports_fork_events,
+ linux_supports_vfork_events,
+ linux_handle_new_gdb_connection,
#ifdef USE_THREAD_DB
thread_db_handle_monitor_command,
#else
#endif
linux_supports_range_stepping,
linux_proc_pid_to_exec_file,
+ linux_mntns_open_cloexec,
+ linux_mntns_unlink,
+ linux_mntns_readlink,
};
static void
sigaction (SIGCHLD, &sigchld_action, NULL);
initialize_low_arch ();
+
+ linux_check_ptrace_features ();
}