/* GNU/Linux native-dependent code common to multiple platforms.
- Copyright (C) 2001-2022 Free Software Foundation, Inc.
+ Copyright (C) 2001-2023 Free Software Foundation, Inc.
This file is part of GDB.
#include <fcntl.h> /* for O_RDONLY */
#include "inf-loop.h"
#include "gdbsupport/event-loop.h"
-#include "gdbsupport/event-pipe.h"
#include "event-top.h"
#include <pwd.h>
#include <sys/types.h>
#include "symfile.h"
#include "gdbsupport/agent.h"
#include "tracepoint.h"
-#include "gdbsupport/buffer.h"
#include "target-descriptions.h"
#include "gdbsupport/filestuff.h"
#include "objfiles.h"
#include "nat/linux-namespaces.h"
+#include "gdbsupport/block-signals.h"
#include "gdbsupport/fileio.h"
#include "gdbsupport/scope-exit.h"
#include "gdbsupport/gdb-sigmask.h"
show_debug_linux_nat (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- fprintf_filtered (file, _("Debugging of GNU/Linux native targets is %s.\n"),
- value);
+ gdb_printf (file, _("Debugging of GNU/Linux native targets is %s.\n"),
+ value);
}
/* Print a linux-nat debug statement. */
/* Whether target_thread_events is in effect. */
static int report_thread_events;
-/* Async mode support. */
-
-/* The event pipe registered as a waitable file in the event loop. */
-static event_pipe linux_nat_event_pipe;
-
-/* True if we're currently in async mode. */
-#define linux_is_async_p() (linux_nat_event_pipe.is_open ())
-
-/* Flush the event pipe. */
-
-static void
-async_file_flush (void)
-{
- linux_nat_event_pipe.flush ();
-}
-
-/* Put something (anything, doesn't matter what, or how much) in event
- pipe, so that the select/poll in the event-loop realizes we have
- something to process. */
-
-static void
-async_file_mark (void)
-{
- linux_nat_event_pipe.mark ();
-}
-
static int kill_lwp (int lwpid, int signo);
static int stop_callback (struct lwp_info *lp);
static void save_stop_reason (struct lwp_info *lp);
+static bool proc_mem_file_is_writable ();
static void close_proc_mem_file (pid_t pid);
static void open_proc_mem_file (ptid_t ptid);
+/* Return TRUE if LWP is the leader thread of the process. */
+
+static bool
+is_leader (lwp_info *lp)
+{
+ return lp->ptid.pid () == lp->ptid.lwp ();
+}
+
\f
/* LWP accessors. */
linux_enable_event_reporting (pid, options);
linux_ptrace_init_warnings ();
linux_proc_init_warnings ();
+ proc_mem_file_is_writable ();
}
linux_nat_target::~linux_nat_target ()
if (th)
{
if (print_thread_events)
- printf_unfiltered (_("[%s exited]\n"),
- target_pid_to_str (lp->ptid).c_str ());
+ gdb_printf (_("[%s exited]\n"),
+ target_pid_to_str (lp->ptid).c_str ());
delete_thread (th);
}
gdb_signal_to_string (signo));
}
- internal_error (__FILE__, __LINE__,
- _("unexpected status %d for PID %ld"),
+ internal_error (_("unexpected status %d for PID %ld"),
status, (long) ptid.lwp ());
}
threads and associate pthread info with each LWP. */
linux_proc_attach_tgid_threads (lp->ptid.pid (),
attach_proc_task_lwp_callback);
-
- if (target_can_async_p ())
- target_async (1);
}
/* Ptrace-detach the thread with pid PID. */
if (tp->has_pending_waitstatus ())
{
/* If the thread has a pending event, and it was stopped with a
- signal, use that signal to resume it. If it has a pending
+ signal, use that signal to resume it. If it has a pending
event of another kind, it was not stopped with a signal, so
resume it without a signal. */
if (tp->pending_waitstatus ().kind () == TARGET_WAITKIND_STOPPED)
}
void
-linux_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signo)
+linux_nat_target::resume (ptid_t scope_ptid, int step, enum gdb_signal signo)
{
struct lwp_info *lp;
- int resume_many;
linux_nat_debug_printf ("Preparing to %s %s, %s, inferior_ptid %s",
step ? "step" : "resume",
- ptid.to_string ().c_str (),
+ scope_ptid.to_string ().c_str (),
(signo != GDB_SIGNAL_0
? strsignal (gdb_signal_to_host (signo)) : "0"),
inferior_ptid.to_string ().c_str ());
- /* A specific PTID means `step only this process id'. */
- resume_many = (minus_one_ptid == ptid
- || ptid.is_pid ());
-
/* Mark the lwps we're resuming as resumed and update their
last_resume_kind to resume_continue. */
- iterate_over_lwps (ptid, resume_set_callback);
+ iterate_over_lwps (scope_ptid, resume_set_callback);
- /* See if it's the current inferior that should be handled
- specially. */
- if (resume_many)
- lp = find_lwp_pid (inferior_ptid);
- else
- lp = find_lwp_pid (ptid);
+ lp = find_lwp_pid (inferior_ptid);
gdb_assert (lp != NULL);
/* Remember if we're stepping. */
if (target_can_async_p ())
{
- target_async (1);
+ target_async (true);
/* Tell the event loop we have something to process. */
async_file_mark ();
}
return;
}
- if (resume_many)
- iterate_over_lwps (ptid, [=] (struct lwp_info *info)
- {
- return linux_nat_resume_callback (info, lp);
- });
+ /* No use iterating unless we're resuming other threads. */
+ if (scope_ptid != lp->ptid)
+ iterate_over_lwps (scope_ptid, [=] (struct lwp_info *info)
+ {
+ return linux_nat_resume_callback (info, lp);
+ });
linux_nat_debug_printf ("%s %s, %s (resume event thread)",
step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT",
if (ret == -1)
perror_with_name (_("waiting for new child"));
else if (ret != new_pid)
- internal_error (__FILE__, __LINE__,
- _("wait returned unexpected PID %d"), ret);
+ internal_error (_("wait returned unexpected PID %d"), ret);
else if (!WIFSTOPPED (status))
- internal_error (__FILE__, __LINE__,
- _("wait returned unexpected status 0x%x"), status);
+ internal_error (_("wait returned unexpected status 0x%x"), status);
}
ptid_t child_ptid (new_pid, new_pid);
{
/* The process is not using thread_db. Add the LWP to
GDB's list. */
- target_post_attach (new_lp->ptid.lwp ());
add_thread (linux_target, new_lp->ptid);
}
if (event == PTRACE_EVENT_VFORK_DONE)
{
- if (current_inferior ()->waiting_for_vfork_done)
- {
- linux_nat_debug_printf
- ("Got expected PTRACE_EVENT_VFORK_DONE from LWP %ld: stopping",
- lp->ptid.lwp ());
-
- ourstatus->set_vfork_done ();
- return 0;
- }
-
linux_nat_debug_printf
- ("Got PTRACE_EVENT_VFORK_DONE from LWP %ld: ignoring", lp->ptid.lwp ());
-
- return 1;
+ ("Got PTRACE_EVENT_VFORK_DONE from LWP %ld",
+ lp->ptid.lwp ());
+ ourstatus->set_vfork_done ();
+ return 0;
}
- internal_error (__FILE__, __LINE__,
- _("unknown ptrace event %d"), event);
+ internal_error (_("unknown ptrace event %d"), event);
}
/* Suspend waiting for a signal. We're mostly interested in
if (!linux_target->low_status_is_event (lp->status))
return;
+ inferior *inf = find_inferior_ptid (linux_target, lp->ptid);
+ if (inf->starting_up)
+ return;
+
regcache = get_thread_regcache (linux_target, lp->ptid);
gdbarch = regcache->arch ();
lp = find_lwp_pid (ptid_t (lwpid));
- /* Check for stop events reported by a process we didn't already
- know about - anything not already in our LWP list.
-
- If we're expecting to receive stopped processes after
- fork, vfork, and clone events, then we'll just add the
- new one to our list and go back to waiting for the event
- to be reported - the stopped process might be returned
- from waitpid before or after the event is.
-
- But note the case of a non-leader thread exec'ing after the
- leader having exited, and gone from our lists. The non-leader
- thread changes its tid to the tgid. */
-
- if (WIFSTOPPED (status) && lp == NULL
- && (WSTOPSIG (status) == SIGTRAP && event == PTRACE_EVENT_EXEC))
+ /* Check for events reported by anything not in our LWP list. */
+ if (lp == nullptr)
{
- /* A multi-thread exec after we had seen the leader exiting. */
- linux_nat_debug_printf ("Re-adding thread group leader LWP %d.", lwpid);
+ if (WIFSTOPPED (status))
+ {
+ if (WSTOPSIG (status) == SIGTRAP && event == PTRACE_EVENT_EXEC)
+ {
+ /* A non-leader thread exec'ed after we've seen the
+ leader zombie, and removed it from our lists (in
+ check_zombie_leaders). The non-leader thread changes
+ its tid to the tgid. */
+ linux_nat_debug_printf
+ ("Re-adding thread group leader LWP %d after exec.",
+ lwpid);
- lp = add_lwp (ptid_t (lwpid, lwpid));
- lp->stopped = 1;
- lp->resumed = 1;
- add_thread (linux_target, lp->ptid);
- }
+ lp = add_lwp (ptid_t (lwpid, lwpid));
+ lp->stopped = 1;
+ lp->resumed = 1;
+ add_thread (linux_target, lp->ptid);
+ }
+ else
+ {
+ /* A process we are controlling has forked and the new
+ child's stop was reported to us by the kernel. Save
+ its PID and go back to waiting for the fork event to
+ be reported - the stopped process might be returned
+ from waitpid before or after the fork event is. */
+ linux_nat_debug_printf
+ ("Saving LWP %d status %s in stopped_pids list",
+ lwpid, status_to_str (status).c_str ());
+ add_to_pid_list (&stopped_pids, lwpid, status);
+ }
+ }
+ else
+ {
+ /* Don't report an event for the exit of an LWP not in our
+ list, i.e. not part of any inferior we're debugging.
+ This can happen if we detach from a program we originally
+ forked and then it exits. However, note that we may have
+ earlier deleted a leader of an inferior we're debugging,
+ in check_zombie_leaders. Re-add it back here if so. */
+ for (inferior *inf : all_inferiors (linux_target))
+ {
+ if (inf->pid == lwpid)
+ {
+ linux_nat_debug_printf
+ ("Re-adding thread group leader LWP %d after exit.",
+ lwpid);
+
+ lp = add_lwp (ptid_t (lwpid, lwpid));
+ lp->resumed = 1;
+ add_thread (linux_target, lp->ptid);
+ break;
+ }
+ }
+ }
- if (WIFSTOPPED (status) && !lp)
- {
- linux_nat_debug_printf ("saving LWP %ld status %s in stopped_pids list",
- (long) lwpid, status_to_str (status).c_str ());
- add_to_pid_list (&stopped_pids, lwpid, status);
- return;
+ if (lp == nullptr)
+ return;
}
- /* Make sure we don't report an event for the exit of an LWP not in
- our list, i.e. not part of the current process. This can happen
- if we detach from a program we originally forked and then it
- exits. */
- if (!WIFSTOPPED (status) && !lp)
- return;
-
/* This LWP is stopped now. (And if dead, this prevents it from
ever being continued.) */
lp->stopped = 1;
/* Check if the thread has exited. */
if (WIFEXITED (status) || WIFSIGNALED (status))
{
- if (!report_thread_events
- && num_lwps (lp->ptid.pid ()) > 1)
+ if (!report_thread_events && !is_leader (lp))
{
linux_nat_debug_printf ("%s exited.",
lp->ptid.to_string ().c_str ());
- /* If there is at least one more LWP, then the exit signal
+ /* If this was not the leader exiting, then the exit signal
was not the end of the debugged application and should be
ignored. */
exit_lwp (lp);
leader_lp = find_lwp_pid (ptid_t (inf->pid));
if (leader_lp != NULL
/* Check if there are other threads in the group, as we may
- have raced with the inferior simply exiting. */
+ have raced with the inferior simply exiting. Note this
+ isn't a watertight check. If the inferior is
+ multi-threaded and is exiting, it may be we see the
+ leader as zombie before we reap all the non-leader
+ threads. See comments below. */
&& num_lwps (inf->pid) > 1
&& linux_proc_pid_is_zombie (inf->pid))
{
+ /* A zombie leader in a multi-threaded program can mean one
+ of three things:
+
+ #1 - Only the leader exited, not the whole program, e.g.,
+ with pthread_exit. Since we can't reap the leader's exit
+ status until all other threads are gone and reaped too,
+ we want to delete the zombie leader right away, as it
+ can't be debugged, we can't read its registers, etc.
+ This is the main reason we check for zombie leaders
+ disappearing.
+
+ #2 - The whole thread-group/process exited (a group exit,
+ via e.g. exit(3), and there is (or will be shortly) an
+ exit reported for each thread in the process, and then
+ finally an exit for the leader once the non-leaders are
+ reaped.
+
+ #3 - There are 3 or more threads in the group, and a
+ thread other than the leader exec'd. See comments on
+ exec events at the top of the file.
+
+ Ideally we would never delete the leader for case #2.
+ Instead, we want to collect the exit status of each
+ non-leader thread, and then finally collect the exit
+ status of the leader as normal and use its exit code as
+ whole-process exit code. Unfortunately, there's no
+ race-free way to distinguish cases #1 and #2. We can't
+ assume the exit events for the non-leaders threads are
+ already pending in the kernel, nor can we assume the
+ non-leader threads are in zombie state already. Between
+ the leader becoming zombie and the non-leaders exiting
+ and becoming zombie themselves, there's a small time
+ window, so such a check would be racy. Temporarily
+ pausing all threads and checking to see if all threads
+ exit or not before re-resuming them would work in the
+ case that all threads are running right now, but it
+ wouldn't work if some thread is currently already
+ ptrace-stopped, e.g., due to scheduler-locking.
+
+ So what we do is we delete the leader anyhow, and then
+ later on when we see its exit status, we re-add it back.
+ We also make sure that we only report a whole-process
+ exit when we see the leader exiting, as opposed to when
+ the last LWP in the LWP list exits, which can be a
+ non-leader if we deleted the leader here. */
linux_nat_debug_printf ("Thread group leader %d zombie "
- "(it exited, or another thread execd).",
+ "(it exited, or another thread execd), "
+ "deleting it.",
inf->pid);
-
- /* A leader zombie can mean one of two things:
-
- - It exited, and there's an exit status pending
- available, or only the leader exited (not the whole
- program). In the latter case, we can't waitpid the
- leader's exit status until all other threads are gone.
-
- - There are 3 or more threads in the group, and a thread
- other than the leader exec'd. See comments on exec
- events at the top of the file. We could try
- distinguishing the exit and exec cases, by waiting once
- more, and seeing if something comes out, but it doesn't
- sound useful. The previous leader _does_ go away, and
- we'll re-add the new one once we see the exec event
- (which is just the same as what would happen if the
- previous leader did exit voluntarily before some other
- thread execs). */
-
- linux_nat_debug_printf ("Thread group leader %d vanished.", inf->pid);
exit_lwp (leader_lp);
}
}
{
ptid_t ptid = event_child->ptid;
- if (num_lwps (ptid.pid ()) > 1)
+ if (!is_leader (event_child))
{
if (report_thread_events)
ourstatus->set_thread_exited (0);
}
static enum target_xfer_status
-linux_xfer_siginfo (enum target_object object,
+linux_xfer_siginfo (ptid_t ptid, enum target_object object,
const char *annex, gdb_byte *readbuf,
const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
ULONGEST *xfered_len)
{
- int pid;
siginfo_t siginfo;
gdb_byte inf_siginfo[sizeof (siginfo_t)];
gdb_assert (object == TARGET_OBJECT_SIGNAL_INFO);
gdb_assert (readbuf || writebuf);
- pid = inferior_ptid.lwp ();
- if (pid == 0)
- pid = inferior_ptid.pid ();
-
if (offset > sizeof (siginfo))
return TARGET_XFER_E_IO;
- errno = 0;
- ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo);
- if (errno != 0)
+ if (!linux_nat_get_siginfo (ptid, &siginfo))
return TARGET_XFER_E_IO;
/* When GDB is built as a 64-bit application, ptrace writes into
/* Convert back to ptrace layout before flushing it out. */
siginfo_fixup (&siginfo, inf_siginfo, 1);
+ int pid = get_ptrace_pid (ptid);
errno = 0;
ptrace (PTRACE_SETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo);
if (errno != 0)
ULONGEST *xfered_len);
static enum target_xfer_status
-linux_proc_xfer_memory_partial (gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len, ULONGEST *xfered_len);
+linux_proc_xfer_memory_partial (int pid, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset,
+ LONGEST len, ULONGEST *xfered_len);
enum target_xfer_status
linux_nat_target::xfer_partial (enum target_object object,
ULONGEST offset, ULONGEST len, ULONGEST *xfered_len)
{
if (object == TARGET_OBJECT_SIGNAL_INFO)
- return linux_xfer_siginfo (object, annex, readbuf, writebuf,
+ return linux_xfer_siginfo (inferior_ptid, object, annex, readbuf, writebuf,
offset, len, xfered_len);
/* The target is connected but no live inferior is selected. Pass
if (addr_bit < (sizeof (ULONGEST) * HOST_CHAR_BIT))
offset &= ((ULONGEST) 1 << addr_bit) - 1;
- return linux_proc_xfer_memory_partial (readbuf, writebuf,
- offset, len, xfered_len);
+ /* If /proc/pid/mem is writable, don't fallback to ptrace. If
+ the write via /proc/pid/mem fails because the inferior execed
+ (and we haven't seen the exec event yet), a subsequent ptrace
+ poke would incorrectly write memory to the post-exec address
+ space, while the core was trying to write to the pre-exec
+ address space. */
+ if (proc_mem_file_is_writable ())
+ return linux_proc_xfer_memory_partial (inferior_ptid.pid (), readbuf,
+ writebuf, offset, len,
+ xfered_len);
}
return inf_ptrace_target::xfer_partial (object, annex, readbuf, writebuf,
/* Accepts an integer PID; Returns a string representing a file that
can be opened to get the symbols for the child process. */
-char *
+const char *
linux_nat_target::pid_to_exec_file (int pid)
{
return linux_proc_pid_to_exec_file (pid);
fd, ptid.pid (), ptid.lwp ());
}
-/* Implement the to_xfer_partial target method using /proc/PID/mem.
- Because we can use a single read/write call, this can be much more
- efficient than banging away at PTRACE_PEEKTEXT. Also, unlike
- PTRACE_PEEKTEXT/PTRACE_POKETEXT, this works with running
- threads. */
+/* Helper for linux_proc_xfer_memory_partial and
+ proc_mem_file_is_writable. FD is the already opened /proc/pid/mem
+ file, and PID is the pid of the corresponding process. The rest of
+ the arguments are like linux_proc_xfer_memory_partial's. */
static enum target_xfer_status
-linux_proc_xfer_memory_partial (gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len,
- ULONGEST *xfered_len)
+linux_proc_xfer_memory_partial_fd (int fd, int pid,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len,
+ ULONGEST *xfered_len)
{
ssize_t ret;
- auto iter = proc_mem_file_map.find (inferior_ptid.pid ());
- if (iter == proc_mem_file_map.end ())
- return TARGET_XFER_EOF;
-
- int fd = iter->second.fd ();
-
gdb_assert (fd != -1);
/* Use pread64/pwrite64 if available, since they save a syscall and can
if (ret == -1)
{
linux_nat_debug_printf ("accessing fd %d for pid %d failed: %s (%d)",
- fd, inferior_ptid.pid (),
- safe_strerror (errno), errno);
- return TARGET_XFER_EOF;
+ fd, pid, safe_strerror (errno), errno);
+ return TARGET_XFER_E_IO;
}
else if (ret == 0)
{
/* EOF means the address space is gone, the whole process exited
or execed. */
linux_nat_debug_printf ("accessing fd %d for pid %d got EOF",
- fd, inferior_ptid.pid ());
+ fd, pid);
return TARGET_XFER_EOF;
}
else
}
}
+/* Implement the to_xfer_partial target method using /proc/PID/mem.
+ Because we can use a single read/write call, this can be much more
+ efficient than banging away at PTRACE_PEEKTEXT. Also, unlike
+ PTRACE_PEEKTEXT/PTRACE_POKETEXT, this works with running
+ threads. */
+
+static enum target_xfer_status
+linux_proc_xfer_memory_partial (int pid, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset,
+ LONGEST len, ULONGEST *xfered_len)
+{
+ auto iter = proc_mem_file_map.find (pid);
+ if (iter == proc_mem_file_map.end ())
+ return TARGET_XFER_EOF;
+
+ int fd = iter->second.fd ();
+
+ return linux_proc_xfer_memory_partial_fd (fd, pid, readbuf, writebuf, offset,
+ len, xfered_len);
+}
+
+/* Check whether /proc/pid/mem is writable in the current kernel, and
+ return true if so. It wasn't writable before Linux 2.6.39, but
+ there's no way to know whether the feature was backported to older
+ kernels. So we check to see if it works. The result is cached,
+ and this is garanteed to be called once early during inferior
+ startup, so that any warning is printed out consistently between
+ GDB invocations. Note we don't call it during GDB startup instead
+ though, because then we might warn with e.g. just "gdb --version"
+ on sandboxed systems. See PR gdb/29907. */
+
+static bool
+proc_mem_file_is_writable ()
+{
+ static gdb::optional<bool> writable;
+
+ if (writable.has_value ())
+ return *writable;
+
+ writable.emplace (false);
+
+ /* We check whether /proc/pid/mem is writable by trying to write to
+ one of our variables via /proc/self/mem. */
+
+ int fd = gdb_open_cloexec ("/proc/self/mem", O_RDWR | O_LARGEFILE, 0).release ();
+
+ if (fd == -1)
+ {
+ warning (_("opening /proc/self/mem file failed: %s (%d)"),
+ safe_strerror (errno), errno);
+ return *writable;
+ }
+
+ SCOPE_EXIT { close (fd); };
+
+ /* This is the variable we try to write to. Note OFFSET below. */
+ volatile gdb_byte test_var = 0;
+
+ gdb_byte writebuf[] = {0x55};
+ ULONGEST offset = (uintptr_t) &test_var;
+ ULONGEST xfered_len;
+
+ enum target_xfer_status res
+ = linux_proc_xfer_memory_partial_fd (fd, getpid (), nullptr, writebuf,
+ offset, 1, &xfered_len);
+
+ if (res == TARGET_XFER_OK)
+ {
+ gdb_assert (xfered_len == 1);
+ gdb_assert (test_var == 0x55);
+ /* Success. */
+ *writable = true;
+ }
+
+ return *writable;
+}
+
/* Parse LINE as a signal set and add its set bits to SIGS. */
static void
/* Pause all */
target_stop (ptid);
- memcpy (s, "qTfSTM", sizeof ("qTfSTM"));
- s[sizeof ("qTfSTM")] = 0;
-
+ strcpy (s, "qTfSTM");
agent_run_command (pid, s, strlen (s) + 1);
/* Unpause all. */
}
while (*p++ == ','); /* comma-separated list */
- memcpy (s, "qTsSTM", sizeof ("qTsSTM"));
- s[sizeof ("qTsSTM")] = 0;
+ strcpy (s, "qTsSTM");
agent_run_command (pid, s, strlen (s) + 1);
p = s;
}
return markers;
}
-/* target_is_async_p implementation. */
-
-bool
-linux_nat_target::is_async_p ()
-{
- return linux_is_async_p ();
-}
-
/* target_can_async_p implementation. */
bool
if (debug_linux_nat)
gdb_stdlog->write_async_safe ("sigchld\n", sizeof ("sigchld\n") - 1);
- if (signo == SIGCHLD
- && linux_nat_event_pipe.is_open ())
- async_file_mark (); /* Let the event loop know that there are
- events to handle. */
+ if (signo == SIGCHLD)
+ {
+ /* Let the event loop know that there are events to handle. */
+ linux_nat_target::async_file_mark_if_open ();
+ }
errno = old_errno;
}
inferior_event_handler (INF_REG_EVENT);
}
-/* Create/destroy the target events pipe. Returns previous state. */
-
-static int
-linux_async_pipe (int enable)
-{
- int previous = linux_is_async_p ();
-
- if (previous != enable)
- {
- sigset_t prev_mask;
-
- /* Block child signals while we create/destroy the pipe, as
- their handler writes to it. */
- block_child_signals (&prev_mask);
-
- if (enable)
- {
- if (!linux_nat_event_pipe.open ())
- internal_error (__FILE__, __LINE__,
- "creating event pipe failed.");
- }
- else
- {
- linux_nat_event_pipe.close ();
- }
-
- restore_child_signals_mask (&prev_mask);
- }
-
- return previous;
-}
-
-int
-linux_nat_target::async_wait_fd ()
-{
- return linux_nat_event_pipe.event_fd ();
-}
-
/* target_async implementation. */
void
-linux_nat_target::async (int enable)
+linux_nat_target::async (bool enable)
{
+ if (enable == is_async_p ())
+ return;
+
+ /* Block child signals while we create/destroy the pipe, as their
+ handler writes to it. */
+ gdb::block_signals blocker;
+
if (enable)
{
- if (!linux_async_pipe (1))
- {
- add_file_handler (linux_nat_event_pipe.event_fd (),
- handle_target_event, NULL,
- "linux-nat");
- /* There may be pending events to handle. Tell the event loop
- to poll them. */
- async_file_mark ();
- }
+ if (!async_file_open ())
+ internal_error ("creating event pipe failed.");
+
+ add_file_handler (async_wait_fd (), handle_target_event, NULL,
+ "linux-nat");
+
+ /* There may be pending events to handle. Tell the event loop
+ to poll them. */
+ async_file_mark ();
}
else
{
- delete_file_handler (linux_nat_event_pipe.event_fd ());
- linux_async_pipe (0);
+ delete_file_handler (async_wait_fd ());
+ async_file_close ();
}
- return;
}
/* Stop an LWP, and push a GDB_SIGNAL_0 stop status if no other
iterate_over_lwps (ptid, linux_nat_stop_lwp);
}
-void
-linux_nat_target::close ()
-{
- /* Unregister from the event loop. */
- if (is_async_p ())
- async (0);
-
- inf_ptrace_target::close ();
-}
-
/* When requests are passed down from the linux-nat layer to the
single threaded inf-ptrace layer, ptids of (lwpid,0,0) form are
used. The address space pointer is stored in the inferior object,
int
linux_nat_target::fileio_open (struct inferior *inf, const char *filename,
int flags, int mode, int warn_if_slow,
- int *target_errno)
+ fileio_error *target_errno)
{
int nat_flags;
mode_t nat_mode;
gdb::optional<std::string>
linux_nat_target::fileio_readlink (struct inferior *inf, const char *filename,
- int *target_errno)
+ fileio_error *target_errno)
{
char buf[PATH_MAX];
int len;
int
linux_nat_target::fileio_unlink (struct inferior *inf, const char *filename,
- int *target_errno)
+ fileio_error *target_errno)
{
int ret;
/* See linux-nat.h. */
-int
+bool
linux_nat_get_siginfo (ptid_t ptid, siginfo_t *siginfo)
{
- int pid;
-
- pid = ptid.lwp ();
- if (pid == 0)
- pid = ptid.pid ();
-
- errno = 0;
- ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, siginfo);
- if (errno != 0)
- {
- memset (siginfo, 0, sizeof (*siginfo));
- return 0;
- }
- return 1;
+ int pid = get_ptrace_pid (ptid);
+ return ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, siginfo) == 0;
}
/* See nat/linux-nat.h. */