/* GNU/Linux native-dependent code common to multiple platforms.
- Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.
This file is part of GDB.
#include "inf-loop.h"
#include "event-loop.h"
#include "event-top.h"
+#include <pwd.h>
+#include <sys/types.h>
+#include "gdb_dirent.h"
+#include "xml-support.h"
#ifdef HAVE_PERSONALITY
# include <sys/personality.h>
#endif
#ifndef PTRACE_GETSIGINFO
-#define PTRACE_GETSIGINFO 0x4202
+# define PTRACE_GETSIGINFO 0x4202
+# define PTRACE_SETSIGINFO 0x4203
#endif
/* The single-threaded native GNU/Linux target_ops. We save a pointer for
/* The method to call, if any, when a new thread is attached. */
static void (*linux_nat_new_thread) (ptid_t);
+/* The method to call, if any, when the siginfo object needs to be
+ converted between the layout returned by ptrace, and the layout in
+ the architecture of the inferior. */
+static int (*linux_nat_siginfo_fixup) (struct siginfo *,
+ gdb_byte *,
+ int);
+
/* The saved to_xfer_partial method, inherited from inf-ptrace.c.
Called by our to_xfer_partial. */
static LONGEST (*super_xfer_partial) (struct target_ops *,
else
{
struct fork_info *fp;
+ struct inferior *parent_inf, *child_inf;
/* Add process to GDB's tables. */
- add_inferior (child_pid);
+ child_inf = add_inferior (child_pid);
+
+ parent_inf = find_inferior_pid (GET_PID (last_ptid));
+ child_inf->attach_flag = parent_inf->attach_flag;
/* Retain child fork in ptrace (stopped) state. */
fp = find_fork_pid (child_pid);
struct thread_info *last_tp = find_thread_pid (last_ptid);
struct thread_info *tp;
char child_pid_spelling[40];
+ struct inferior *parent_inf, *child_inf;
/* Copy user stepping state to the new inferior thread. */
struct breakpoint *step_resume_breakpoint = last_tp->step_resume_breakpoint;
child_pid);
}
+ /* Add the new inferior first, so that the target_detach below
+ doesn't unpush the target. */
+
+ child_inf = add_inferior (child_pid);
+
+ parent_inf = find_inferior_pid (GET_PID (last_ptid));
+ child_inf->attach_flag = parent_inf->attach_flag;
+
/* If we're vforking, we may want to hold on to the parent until
the child exits or execs. At exec time we can remove the old
breakpoints from the parent and detach it; at exit time we
if (!fp)
fp = add_fork (parent_pid);
fork_save_infrun_state (fp, 0);
+
+ /* Also add an entry for the child fork. */
+ fp = find_fork_pid (child_pid);
+ if (!fp)
+ fp = add_fork (child_pid);
+ fork_save_infrun_state (fp, 0);
}
else
target_detach (NULL, 0);
inferior_ptid = ptid_build (child_pid, child_pid, 0);
- add_inferior (child_pid);
-
- /* Reinstall ourselves, since we might have been removed in
- target_detach (which does other necessary cleanup). */
- push_target (ops);
linux_nat_switch_fork (inferior_ptid);
check_for_thread_db ();
/* Prototypes for local functions. */
static int stop_wait_callback (struct lwp_info *lp, void *data);
-static int linux_nat_thread_alive (ptid_t ptid);
+static int linux_thread_alive (ptid_t ptid);
static char *linux_child_pid_to_exec_file (int pid);
static int cancel_breakpoint (struct lwp_info *lp);
}
static void
-linux_nat_create_inferior (char *exec_file, char *allargs, char **env,
+linux_nat_create_inferior (struct target_ops *ops,
+ char *exec_file, char *allargs, char **env,
int from_tty)
{
int saved_async = 0;
}
#endif /* HAVE_PERSONALITY */
- linux_ops->to_create_inferior (exec_file, allargs, env, from_tty);
+ linux_ops->to_create_inferior (ops, exec_file, allargs, env, from_tty);
#ifdef HAVE_PERSONALITY
if (personality_set)
}
static void
-linux_nat_attach (char *args, int from_tty)
+linux_nat_attach (struct target_ops *ops, char *args, int from_tty)
{
struct lwp_info *lp;
int status;
/* FIXME: We should probably accept a list of process id's, and
attach all of them. */
- linux_ops->to_attach (args, from_tty);
+ linux_ops->to_attach (ops, args, from_tty);
if (!target_can_async_p ())
{
}
static void
-linux_nat_detach (char *args, int from_tty)
+linux_nat_detach (struct target_ops *ops, char *args, int from_tty)
{
int pid;
int status;
/* Destroy LWP info; it's no longer valid. */
init_lwp_list ();
- pid = GET_PID (inferior_ptid);
- inferior_ptid = pid_to_ptid (pid);
- linux_ops->to_detach (args, from_tty);
+ pid = ptid_get_pid (inferior_ptid);
if (target_can_async_p ())
drain_queued_events (pid);
+
+ if (forks_exist_p ())
+ {
+ /* Multi-fork case. The current inferior_ptid is being detached
+ from, but there are other viable forks to debug. Detach from
+ the current fork, and context-switch to the first
+ available. */
+ linux_fork_detach (args, from_tty);
+
+ if (non_stop && target_can_async_p ())
+ target_async (inferior_event_handler, 0);
+ }
+ else
+ linux_ops->to_detach (ops, args, from_tty);
}
/* Resume LP. */
{
if (lp->stopped && lp->status == 0)
{
- linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ linux_ops->to_resume (linux_ops,
+ pid_to_ptid (GET_LWP (lp->ptid)),
0, TARGET_SIGNAL_0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
}
static void
-linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
+linux_nat_resume (struct target_ops *ops,
+ ptid_t ptid, int step, enum target_signal signo)
{
struct lwp_info *lp;
int resume_all;
if (resume_all)
iterate_over_lwps (resume_callback, NULL);
- linux_ops->to_resume (ptid, step, signo);
+ linux_ops->to_resume (linux_ops, ptid, step, signo);
memset (&lp->siginfo, 0, sizeof (lp->siginfo));
if (debug_linux_nat)
/* Check if the thread has exited. */
if ((WIFEXITED (status) || WIFSIGNALED (status)) && num_lwps > 1)
{
- /* If this is the main thread, we must stop all threads and
- verify if they are still alive. This is because in the nptl
- thread model, there is no signal issued for exiting LWPs
+ /* If this is the main thread, we must stop all threads and verify
+ if they are still alive. This is because in the nptl thread model
+ on Linux 2.4, there is no signal issued for exiting LWPs
other than the main thread. We only get the main thread exit
signal once all child threads have already exited. If we
stop all the threads and use the stop_wait_callback to check
"LLW: %s exited.\n",
target_pid_to_str (lp->ptid));
- exit_lwp (lp);
-
- /* If there is at least one more LWP, then the exit signal was
- not the end of the debugged application and should be
- ignored. */
- if (num_lwps > 0)
- return NULL;
+ if (num_lwps > 1)
+ {
+ /* If there is at least one more LWP, then the exit signal
+ was not the end of the debugged application and should be
+ ignored. */
+ exit_lwp (lp);
+ return NULL;
+ }
}
/* Check if the current LWP has previously exited. In the nptl
thread model, LWPs other than the main thread do not issue
signals when they exit so we must check whenever the thread has
stopped. A similar check is made in stop_wait_callback(). */
- if (num_lwps > 1 && !linux_nat_thread_alive (lp->ptid))
+ if (num_lwps > 1 && !linux_thread_alive (lp->ptid))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
registers_changed ();
- linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
lp->ignore_sigint = 0;
registers_changed ();
- linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
}
static ptid_t
-linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
+linux_nat_wait (struct target_ops *ops,
+ ptid_t ptid, struct target_waitstatus *ourstatus)
{
struct lwp_info *lp = NULL;
int options = 0;
/* Resume the thread. It should halt immediately returning the
pending SIGSTOP. */
registers_changed ();
- linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
{
/* Causes SIGINT to be passed on to the attached process. */
set_sigint_trap ();
- set_sigio_trap ();
}
while (status == 0)
}
if (!target_can_async_p ())
- {
- clear_sigio_trap ();
- clear_sigint_trap ();
- }
+ clear_sigint_trap ();
gdb_assert (lp);
newly attached threads may cause an unwanted delay in
getting them running. */
registers_changed ();
- linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, signo);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
}
static void
-linux_nat_kill (void)
+linux_nat_kill (struct target_ops *ops)
{
struct target_waitstatus last;
ptid_t last_ptid;
}
static void
-linux_nat_mourn_inferior (void)
+linux_nat_mourn_inferior (struct target_ops *ops)
{
/* Destroy LWP info; it's no longer valid. */
init_lwp_list ();
/* Normal case, no other forks available. */
if (target_can_async_p ())
linux_nat_async (NULL, 0);
- linux_ops->to_mourn_inferior ();
+ linux_ops->to_mourn_inferior (ops);
}
else
/* Multi-fork case. The current inferior_ptid has exited, but
linux_fork_mourn_inferior ();
}
+/* Convert a native/host siginfo object, into/from the siginfo in the
+ layout of the inferiors' architecture. */
+
+static void
+siginfo_fixup (struct siginfo *siginfo, gdb_byte *inf_siginfo, int direction)
+{
+ int done = 0;
+
+ if (linux_nat_siginfo_fixup != NULL)
+ done = linux_nat_siginfo_fixup (siginfo, inf_siginfo, direction);
+
+ /* If there was no callback, or the callback didn't do anything,
+ then just do a straight memcpy. */
+ if (!done)
+ {
+ if (direction == 1)
+ memcpy (siginfo, inf_siginfo, sizeof (struct siginfo));
+ else
+ memcpy (inf_siginfo, siginfo, sizeof (struct siginfo));
+ }
+}
+
+static LONGEST
+linux_xfer_siginfo (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
+{
+ int pid;
+ struct siginfo siginfo;
+ gdb_byte inf_siginfo[sizeof (struct siginfo)];
+
+ gdb_assert (object == TARGET_OBJECT_SIGNAL_INFO);
+ gdb_assert (readbuf || writebuf);
+
+ pid = GET_LWP (inferior_ptid);
+ if (pid == 0)
+ pid = GET_PID (inferior_ptid);
+
+ if (offset > sizeof (siginfo))
+ return -1;
+
+ errno = 0;
+ ptrace (PTRACE_GETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo);
+ if (errno != 0)
+ return -1;
+
+ /* When GDB is built as a 64-bit application, ptrace writes into
+ SIGINFO an object with 64-bit layout. Since debugging a 32-bit
+ inferior with a 64-bit GDB should look the same as debugging it
+ with a 32-bit GDB, we need to convert it. GDB core always sees
+ the converted layout, so any read/write will have to be done
+ post-conversion. */
+ siginfo_fixup (&siginfo, inf_siginfo, 0);
+
+ if (offset + len > sizeof (siginfo))
+ len = sizeof (siginfo) - offset;
+
+ if (readbuf != NULL)
+ memcpy (readbuf, inf_siginfo + offset, len);
+ else
+ {
+ memcpy (inf_siginfo + offset, writebuf, len);
+
+ /* Convert back to ptrace layout before flushing it out. */
+ siginfo_fixup (&siginfo, inf_siginfo, 1);
+
+ errno = 0;
+ ptrace (PTRACE_SETSIGINFO, pid, (PTRACE_TYPE_ARG3) 0, &siginfo);
+ if (errno != 0)
+ return -1;
+ }
+
+ return len;
+}
+
static LONGEST
linux_nat_xfer_partial (struct target_ops *ops, enum target_object object,
const char *annex, gdb_byte *readbuf,
const gdb_byte *writebuf,
ULONGEST offset, LONGEST len)
{
- struct cleanup *old_chain = save_inferior_ptid ();
+ struct cleanup *old_chain;
LONGEST xfer;
+ if (object == TARGET_OBJECT_SIGNAL_INFO)
+ return linux_xfer_siginfo (ops, object, annex, readbuf, writebuf,
+ offset, len);
+
+ old_chain = save_inferior_ptid ();
+
if (is_lwp (inferior_ptid))
inferior_ptid = pid_to_ptid (GET_LWP (inferior_ptid));
}
static int
-linux_nat_thread_alive (ptid_t ptid)
+linux_thread_alive (ptid_t ptid)
{
int err;
return 1;
}
+static int
+linux_nat_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+ return linux_thread_alive (ptid);
+}
+
static char *
-linux_nat_pid_to_str (ptid_t ptid)
+linux_nat_pid_to_str (struct target_ops *ops, ptid_t ptid)
{
static char buf[64];
unsigned long,
int, int, int, void *), void *obfd)
{
- long long pid = PIDGET (inferior_ptid);
+ int pid = PIDGET (inferior_ptid);
char mapsfilename[MAXPATHLEN];
FILE *mapsfile;
long long addr, endaddr, size, offset, inode;
char permissions[8], device[8], filename[MAXPATHLEN];
int read, write, exec;
int ret;
+ struct cleanup *cleanup;
/* Compose the filename for the /proc memory map, and open it. */
- sprintf (mapsfilename, "/proc/%lld/maps", pid);
+ sprintf (mapsfilename, "/proc/%d/maps", pid);
if ((mapsfile = fopen (mapsfilename, "r")) == NULL)
error (_("Could not open %s."), mapsfilename);
+ cleanup = make_cleanup_fclose (mapsfile);
if (info_verbose)
fprintf_filtered (gdb_stdout,
segment. */
func (addr, size, read, write, exec, obfd);
}
- fclose (mapsfile);
+ do_cleanups (cleanup);
return 0;
}
static void
linux_nat_info_proc_cmd (char *args, int from_tty)
{
- long long pid = PIDGET (inferior_ptid);
+ /* A long is used for pid instead of an int to avoid a loss of precision
+ compiler warning from the output of strtoul. */
+ long pid = PIDGET (inferior_ptid);
FILE *procfile;
char **argv = NULL;
char buffer[MAXPATHLEN];
if (pid == 0)
error (_("No current process: you must name one."));
- sprintf (fname1, "/proc/%lld", pid);
+ sprintf (fname1, "/proc/%ld", pid);
if (stat (fname1, &dummy) != 0)
error (_("No /proc directory: '%s'"), fname1);
- printf_filtered (_("process %lld\n"), pid);
+ printf_filtered (_("process %ld\n"), pid);
if (cmdline_f || all)
{
- sprintf (fname1, "/proc/%lld/cmdline", pid);
+ sprintf (fname1, "/proc/%ld/cmdline", pid);
if ((procfile = fopen (fname1, "r")) != NULL)
{
- fgets (buffer, sizeof (buffer), procfile);
- printf_filtered ("cmdline = '%s'\n", buffer);
- fclose (procfile);
+ struct cleanup *cleanup = make_cleanup_fclose (procfile);
+ if (fgets (buffer, sizeof (buffer), procfile))
+ printf_filtered ("cmdline = '%s'\n", buffer);
+ else
+ warning (_("unable to read '%s'"), fname1);
+ do_cleanups (cleanup);
}
else
warning (_("unable to open /proc file '%s'"), fname1);
}
if (cwd_f || all)
{
- sprintf (fname1, "/proc/%lld/cwd", pid);
+ sprintf (fname1, "/proc/%ld/cwd", pid);
memset (fname2, 0, sizeof (fname2));
if (readlink (fname1, fname2, sizeof (fname2)) > 0)
printf_filtered ("cwd = '%s'\n", fname2);
}
if (exe_f || all)
{
- sprintf (fname1, "/proc/%lld/exe", pid);
+ sprintf (fname1, "/proc/%ld/exe", pid);
memset (fname2, 0, sizeof (fname2));
if (readlink (fname1, fname2, sizeof (fname2)) > 0)
printf_filtered ("exe = '%s'\n", fname2);
}
if (mappings_f || all)
{
- sprintf (fname1, "/proc/%lld/maps", pid);
+ sprintf (fname1, "/proc/%ld/maps", pid);
if ((procfile = fopen (fname1, "r")) != NULL)
{
long long addr, endaddr, size, offset, inode;
char permissions[8], device[8], filename[MAXPATHLEN];
+ struct cleanup *cleanup;
+ cleanup = make_cleanup_fclose (procfile);
printf_filtered (_("Mapped address spaces:\n\n"));
if (gdbarch_addr_bit (current_gdbarch) == 32)
{
}
}
- fclose (procfile);
+ do_cleanups (cleanup);
}
else
warning (_("unable to open /proc file '%s'"), fname1);
}
if (status_f || all)
{
- sprintf (fname1, "/proc/%lld/status", pid);
+ sprintf (fname1, "/proc/%ld/status", pid);
if ((procfile = fopen (fname1, "r")) != NULL)
{
+ struct cleanup *cleanup = make_cleanup_fclose (procfile);
while (fgets (buffer, sizeof (buffer), procfile) != NULL)
puts_filtered (buffer);
- fclose (procfile);
+ do_cleanups (cleanup);
}
else
warning (_("unable to open /proc file '%s'"), fname1);
}
if (stat_f || all)
{
- sprintf (fname1, "/proc/%lld/stat", pid);
+ sprintf (fname1, "/proc/%ld/stat", pid);
if ((procfile = fopen (fname1, "r")) != NULL)
{
int itmp;
char ctmp;
long ltmp;
+ struct cleanup *cleanup = make_cleanup_fclose (procfile);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered (_("Process: %d\n"), itmp);
if (fscanf (procfile, "%lu ", <mp) > 0) /* FIXME arch? */
printf_filtered (_("wchan (system call): 0x%lx\n"), ltmp);
#endif
- fclose (procfile);
+ do_cleanups (cleanup);
}
else
warning (_("unable to open /proc file '%s'"), fname1);
FILE *procfile;
char buffer[MAXPATHLEN], fname[MAXPATHLEN];
int signum;
+ struct cleanup *cleanup;
sigemptyset (pending);
sigemptyset (blocked);
procfile = fopen (fname, "r");
if (procfile == NULL)
error (_("Could not open %s"), fname);
+ cleanup = make_cleanup_fclose (procfile);
while (fgets (buffer, MAXPATHLEN, procfile) != NULL)
{
add_line_to_sigset (buffer + 8, ignored);
}
- fclose (procfile);
+ do_cleanups (cleanup);
+}
+
+static LONGEST
+linux_nat_xfer_osdata (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
+{
+ /* We make the process list snapshot when the object starts to be
+ read. */
+ static const char *buf;
+ static LONGEST len_avail = -1;
+ static struct obstack obstack;
+
+ DIR *dirp;
+
+ gdb_assert (object == TARGET_OBJECT_OSDATA);
+
+ if (strcmp (annex, "processes") != 0)
+ return 0;
+
+ gdb_assert (readbuf && !writebuf);
+
+ if (offset == 0)
+ {
+ if (len_avail != -1 && len_avail != 0)
+ obstack_free (&obstack, NULL);
+ len_avail = 0;
+ buf = NULL;
+ obstack_init (&obstack);
+ obstack_grow_str (&obstack, "<osdata type=\"processes\">\n");
+
+ 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])
+ || NAMELEN (dp) > sizeof ("4294967295") - 1)
+ continue;
+
+ sprintf (procentry, "/proc/%s", dp->d_name);
+ if (stat (procentry, &statbuf) == 0
+ && S_ISDIR (statbuf.st_mode))
+ {
+ char *pathname;
+ FILE *f;
+ char cmd[MAXPATHLEN + 1];
+ struct passwd *entry;
+
+ pathname = xstrprintf ("/proc/%s/cmdline", dp->d_name);
+ entry = getpwuid (statbuf.st_uid);
+
+ if ((f = fopen (pathname, "r")) != NULL)
+ {
+ size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
+ if (len > 0)
+ {
+ int i;
+ for (i = 0; i < len; i++)
+ if (cmd[i] == '\0')
+ cmd[i] = ' ';
+ cmd[len] = '\0';
+
+ obstack_xml_printf (
+ &obstack,
+ "<item>"
+ "<column name=\"pid\">%s</column>"
+ "<column name=\"user\">%s</column>"
+ "<column name=\"command\">%s</column>"
+ "</item>",
+ dp->d_name,
+ entry ? entry->pw_name : "?",
+ cmd);
+ }
+ fclose (f);
+ }
+
+ xfree (pathname);
+ }
+ }
+
+ closedir (dirp);
+ }
+
+ obstack_grow_str0 (&obstack, "</osdata>\n");
+ buf = obstack_finish (&obstack);
+ len_avail = strlen (buf);
+ }
+
+ if (offset >= len_avail)
+ {
+ /* Done. Get rid of the obstack. */
+ obstack_free (&obstack, NULL);
+ buf = NULL;
+ len_avail = 0;
+ return 0;
+ }
+
+ if (len > len_avail - offset)
+ len = len_avail - offset;
+ memcpy (readbuf, buf + offset, len);
+
+ return len;
}
static LONGEST
return procfs_xfer_auxv (ops, object, annex, readbuf, writebuf,
offset, len);
+ if (object == TARGET_OBJECT_OSDATA)
+ return linux_nat_xfer_osdata (ops, object, annex, readbuf, writebuf,
+ offset, len);
+
xfer = linux_proc_xfer_partial (ops, object, annex, readbuf, writebuf,
offset, len);
if (xfer != 0)
/* target_terminal_ours implementation. */
-void
+static void
linux_nat_terminal_ours (void)
{
if (!target_is_async_p ())
also want to be used for single-threaded processes. */
add_target (t);
-
- /* TODO: Eliminate this and have libthread_db use
- find_target_beneath. */
- thread_db_init (t);
}
/* Register a method to call whenever a new thread is attached. */
linux_nat_new_thread = new_thread;
}
+/* Register a method that converts a siginfo object between the layout
+ that ptrace returns, and the layout in the architecture of the
+ inferior. */
+void
+linux_nat_set_siginfo_fixup (struct target_ops *t,
+ int (*siginfo_fixup) (struct siginfo *,
+ gdb_byte *,
+ int))
+{
+ /* Save the pointer. */
+ linux_nat_siginfo_fixup = siginfo_fixup;
+}
+
/* Return the saved siginfo associated with PTID. */
struct siginfo *
linux_nat_get_siginfo (ptid_t ptid)
fcntl (linux_nat_event_pipe[1], F_SETFL, O_NONBLOCK);
}
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_linux_nat;
+
void
_initialize_linux_nat (void)
{