forked from Imagelibrary/binutils-gdb
A patch in the target cleanup series caused a regression when using
record with target-async. Version 4 of the patch is here:
https://sourceware.org/ml/gdb-patches/2014-03/msg00159.html
The immediate problem is that record supplies to_can_async_p and
to_is_async_p methods, but does not supply a to_async method. So,
when target-async is set, record claims to support async -- but if the
underlying target does not support async, then the to_async method
call will end up in that method's default implementation, namely
tcomplain.
This worked previously because the record target used to provide a
to_async method; one that (erroneously, only at push time) checked the
other members of the target stack, and then simply dropped to_async
calls in the "does not implement async" case.
My first thought was to simply drop tcomplain as the default for
to_async. This works, but Pedro pointed out that the only reason
record has to supply to_can_async_p and to_is_async_p is that these
default to using the find_default_run_target machinery -- and these
defaults are only needed by "run" and "attach".
So, a nicer solution presents itself: change run and attach to
explicitly call into the default run target when needed; and change
to_is_async_p and to_can_async_p to default to "return 0". This makes
the target stack simpler to use and lets us remove the method
implementations from record. This is also in harmony with other plans
for the target stack; namely trying to reduce the impact of
find_default_run_target. This approach makes it clear that
find_default_is_async_p is not needed -- it is asking whether a target
that may not even be pushed is actually async, which seems like a
nonsensical question.
While an improvement, this approach proved to introduce the same bug
when using the core target. Looking a bit deeper, the issue is that
code in "attach" and "run" may need to use either the current target
stack or the default run target -- but different calls into the target
API in those functions could wind up querying different targets.
This new patch makes the target to use more explicit in "run" and
"attach". Then these commands explicitly make the needed calls
against that target. This ensures that a single target is used for
all relevant operations. This lets us remove a couple find_default_*
functions from various targets, including the dummy target. I think
this is a decent understandability improvement.
One issue I see with this patch is that the new calls in "run" and
"attach" are not very much like the rest of the target API. I think
fundamentally this is due to bad factoring in the target API, which
may need to be fixed for multi-target. Tackling that seemed ambitious
for a regression fix.
While working on this I noticed that there don't seem to be any test
cases that involve both target-async and record, so this patch changes
break-precsave.exp to add some. It also changes corefile.exp to add
some target-async tests; these pass with current trunk and with this
patch applied, but fail with the v1 patch.
This patch differs from v4 in that it moves initialization of
to_can_async_p and to_supports_non_stop into inf-child, adds some
assertions to complete_target_initialization, and adds some comments
to target.h.
Built and regtested on x86-64 Fedora 20.
2014-03-12 Tom Tromey <tromey@redhat.com>
* inf-child.c (return_zero): New function.
(inf_child_target): Set to_can_async_p, to_supports_non_stop.
* aix-thread.c (aix_thread_inferior_created): New function.
(aix_thread_attach): Remove.
(init_aix_thread_ops): Don't set to_attach.
(_initialize_aix_thread): Register inferior_created observer.
* corelow.c (init_core_ops): Don't set to_attach or
to_create_inferior.
* exec.c (init_exec_ops): Don't set to_attach or
to_create_inferior.
* infcmd.c (run_command_1): Use find_run_target. Make direct
target calls.
(attach_command): Use find_attach_target. Make direct target
calls.
* record-btrace.c (init_record_btrace_ops): Don't set
to_create_inferior.
* record-full.c (record_full_can_async_p, record_full_is_async_p):
Remove.
(init_record_full_ops, init_record_full_core_ops): Update. Don't
set to_create_inferior.
* target.c (complete_target_initialization): Add assertion.
(target_create_inferior): Remove.
(find_default_attach, find_default_create_inferior): Remove.
(find_attach_target, find_run_target): New functions.
(find_default_is_async_p, find_default_can_async_p)
(target_supports_non_stop, target_attach): Remove.
(init_dummy_target): Don't set to_create_inferior or
to_supports_non_stop.
* target.h (struct target_ops) <to_attach>: Add comment. Remove
TARGET_DEFAULT_FUNC.
<to_create_inferior>: Add comment.
<to_can_async_p, to_is_async_p, to_supports_non_stop>: Use
TARGET_DEFAULT_RETURN.
<to_can_async_p, to_supports_non_stop, to_can_run>: Add comments.
(find_attach_target, find_run_target): Declare.
(target_create_inferior): Remove.
(target_has_execution_1): Update comment.
(target_supports_non_stop): Remove.
* target-delegates.c: Rebuild.
2014-03-12 Tom Tromey <tromey@redhat.com>
* gdb.base/corefile.exp (corefile_test_run, corefile_test_attach):
New procs. Add target-async tests.
* gdb.reverse/break-precsave.exp (precsave_tests): New proc.
Add target-async tests.
450 lines
12 KiB
C
450 lines
12 KiB
C
/* Base/prototype target for default child (native) targets.
|
|
|
|
Copyright (C) 1988-2014 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
/* This file provides a common base class/target that all native
|
|
target implementations extend, by calling inf_child_target to get a
|
|
new prototype target and then overriding target methods as
|
|
necessary. */
|
|
|
|
#include "defs.h"
|
|
#include "regcache.h"
|
|
#include "memattr.h"
|
|
#include "symtab.h"
|
|
#include "target.h"
|
|
#include "inferior.h"
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include "inf-child.h"
|
|
#include "gdb/fileio.h"
|
|
#include "agent.h"
|
|
#include "gdb_wait.h"
|
|
#include "filestuff.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
/* Helper function for child_wait and the derivatives of child_wait.
|
|
HOSTSTATUS is the waitstatus from wait() or the equivalent; store our
|
|
translation of that in OURSTATUS. */
|
|
void
|
|
store_waitstatus (struct target_waitstatus *ourstatus, int hoststatus)
|
|
{
|
|
if (WIFEXITED (hoststatus))
|
|
{
|
|
ourstatus->kind = TARGET_WAITKIND_EXITED;
|
|
ourstatus->value.integer = WEXITSTATUS (hoststatus);
|
|
}
|
|
else if (!WIFSTOPPED (hoststatus))
|
|
{
|
|
ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
|
|
ourstatus->value.sig = gdb_signal_from_host (WTERMSIG (hoststatus));
|
|
}
|
|
else
|
|
{
|
|
ourstatus->kind = TARGET_WAITKIND_STOPPED;
|
|
ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (hoststatus));
|
|
}
|
|
}
|
|
|
|
/* Fetch register REGNUM from the inferior. If REGNUM is -1, do this
|
|
for all registers. */
|
|
|
|
static void
|
|
inf_child_fetch_inferior_registers (struct target_ops *ops,
|
|
struct regcache *regcache, int regnum)
|
|
{
|
|
if (regnum == -1)
|
|
{
|
|
for (regnum = 0;
|
|
regnum < gdbarch_num_regs (get_regcache_arch (regcache));
|
|
regnum++)
|
|
regcache_raw_supply (regcache, regnum, NULL);
|
|
}
|
|
else
|
|
regcache_raw_supply (regcache, regnum, NULL);
|
|
}
|
|
|
|
/* Store register REGNUM back into the inferior. If REGNUM is -1, do
|
|
this for all registers (including the floating point registers). */
|
|
|
|
static void
|
|
inf_child_store_inferior_registers (struct target_ops *ops,
|
|
struct regcache *regcache, int regnum)
|
|
{
|
|
}
|
|
|
|
static void
|
|
inf_child_post_attach (struct target_ops *self, int pid)
|
|
{
|
|
/* This target doesn't require a meaningful "post attach" operation
|
|
by a debugger. */
|
|
}
|
|
|
|
/* Get ready to modify the registers array. On machines which store
|
|
individual registers, this doesn't need to do anything. On
|
|
machines which store all the registers in one fell swoop, this
|
|
makes sure that registers contains all the registers from the
|
|
program being debugged. */
|
|
|
|
static void
|
|
inf_child_prepare_to_store (struct target_ops *self,
|
|
struct regcache *regcache)
|
|
{
|
|
}
|
|
|
|
static void
|
|
inf_child_open (char *arg, int from_tty)
|
|
{
|
|
error (_("Use the \"run\" command to start a Unix child process."));
|
|
}
|
|
|
|
static void
|
|
inf_child_post_startup_inferior (struct target_ops *self, ptid_t ptid)
|
|
{
|
|
/* This target doesn't require a meaningful "post startup inferior"
|
|
operation by a debugger. */
|
|
}
|
|
|
|
static int
|
|
inf_child_follow_fork (struct target_ops *ops, int follow_child,
|
|
int detach_fork)
|
|
{
|
|
/* This target doesn't support following fork or vfork events. */
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
inf_child_can_run (struct target_ops *self)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static char *
|
|
inf_child_pid_to_exec_file (struct target_ops *self, int pid)
|
|
{
|
|
/* This target doesn't support translation of a process ID to the
|
|
filename of the executable file. */
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Target file operations. */
|
|
|
|
static int
|
|
inf_child_fileio_open_flags_to_host (int fileio_open_flags, int *open_flags_p)
|
|
{
|
|
int open_flags = 0;
|
|
|
|
if (fileio_open_flags & ~FILEIO_O_SUPPORTED)
|
|
return -1;
|
|
|
|
if (fileio_open_flags & FILEIO_O_CREAT)
|
|
open_flags |= O_CREAT;
|
|
if (fileio_open_flags & FILEIO_O_EXCL)
|
|
open_flags |= O_EXCL;
|
|
if (fileio_open_flags & FILEIO_O_TRUNC)
|
|
open_flags |= O_TRUNC;
|
|
if (fileio_open_flags & FILEIO_O_APPEND)
|
|
open_flags |= O_APPEND;
|
|
if (fileio_open_flags & FILEIO_O_RDONLY)
|
|
open_flags |= O_RDONLY;
|
|
if (fileio_open_flags & FILEIO_O_WRONLY)
|
|
open_flags |= O_WRONLY;
|
|
if (fileio_open_flags & FILEIO_O_RDWR)
|
|
open_flags |= O_RDWR;
|
|
/* On systems supporting binary and text mode, always open files in
|
|
binary mode. */
|
|
#ifdef O_BINARY
|
|
open_flags |= O_BINARY;
|
|
#endif
|
|
|
|
*open_flags_p = open_flags;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
inf_child_errno_to_fileio_error (int errnum)
|
|
{
|
|
switch (errnum)
|
|
{
|
|
case EPERM:
|
|
return FILEIO_EPERM;
|
|
case ENOENT:
|
|
return FILEIO_ENOENT;
|
|
case EINTR:
|
|
return FILEIO_EINTR;
|
|
case EIO:
|
|
return FILEIO_EIO;
|
|
case EBADF:
|
|
return FILEIO_EBADF;
|
|
case EACCES:
|
|
return FILEIO_EACCES;
|
|
case EFAULT:
|
|
return FILEIO_EFAULT;
|
|
case EBUSY:
|
|
return FILEIO_EBUSY;
|
|
case EEXIST:
|
|
return FILEIO_EEXIST;
|
|
case ENODEV:
|
|
return FILEIO_ENODEV;
|
|
case ENOTDIR:
|
|
return FILEIO_ENOTDIR;
|
|
case EISDIR:
|
|
return FILEIO_EISDIR;
|
|
case EINVAL:
|
|
return FILEIO_EINVAL;
|
|
case ENFILE:
|
|
return FILEIO_ENFILE;
|
|
case EMFILE:
|
|
return FILEIO_EMFILE;
|
|
case EFBIG:
|
|
return FILEIO_EFBIG;
|
|
case ENOSPC:
|
|
return FILEIO_ENOSPC;
|
|
case ESPIPE:
|
|
return FILEIO_ESPIPE;
|
|
case EROFS:
|
|
return FILEIO_EROFS;
|
|
case ENOSYS:
|
|
return FILEIO_ENOSYS;
|
|
case ENAMETOOLONG:
|
|
return FILEIO_ENAMETOOLONG;
|
|
}
|
|
return FILEIO_EUNKNOWN;
|
|
}
|
|
|
|
/* Open FILENAME on the target, using FLAGS and MODE. Return a
|
|
target file descriptor, or -1 if an error occurs (and set
|
|
*TARGET_ERRNO). */
|
|
static int
|
|
inf_child_fileio_open (struct target_ops *self,
|
|
const char *filename, int flags, int mode,
|
|
int *target_errno)
|
|
{
|
|
int nat_flags;
|
|
int fd;
|
|
|
|
if (inf_child_fileio_open_flags_to_host (flags, &nat_flags) == -1)
|
|
{
|
|
*target_errno = FILEIO_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
/* We do not need to convert MODE, since the fileio protocol uses
|
|
the standard values. */
|
|
fd = gdb_open_cloexec (filename, nat_flags, mode);
|
|
if (fd == -1)
|
|
*target_errno = inf_child_errno_to_fileio_error (errno);
|
|
|
|
return fd;
|
|
}
|
|
|
|
/* Write up to LEN bytes from WRITE_BUF to FD on the target.
|
|
Return the number of bytes written, or -1 if an error occurs
|
|
(and set *TARGET_ERRNO). */
|
|
static int
|
|
inf_child_fileio_pwrite (struct target_ops *self,
|
|
int fd, const gdb_byte *write_buf, int len,
|
|
ULONGEST offset, int *target_errno)
|
|
{
|
|
int ret;
|
|
|
|
#ifdef HAVE_PWRITE
|
|
ret = pwrite (fd, write_buf, len, (long) offset);
|
|
#else
|
|
ret = -1;
|
|
#endif
|
|
/* If we have no pwrite or it failed for this file, use lseek/write. */
|
|
if (ret == -1)
|
|
{
|
|
ret = lseek (fd, (long) offset, SEEK_SET);
|
|
if (ret != -1)
|
|
ret = write (fd, write_buf, len);
|
|
}
|
|
|
|
if (ret == -1)
|
|
*target_errno = inf_child_errno_to_fileio_error (errno);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Read up to LEN bytes FD on the target into READ_BUF.
|
|
Return the number of bytes read, or -1 if an error occurs
|
|
(and set *TARGET_ERRNO). */
|
|
static int
|
|
inf_child_fileio_pread (struct target_ops *self,
|
|
int fd, gdb_byte *read_buf, int len,
|
|
ULONGEST offset, int *target_errno)
|
|
{
|
|
int ret;
|
|
|
|
#ifdef HAVE_PREAD
|
|
ret = pread (fd, read_buf, len, (long) offset);
|
|
#else
|
|
ret = -1;
|
|
#endif
|
|
/* If we have no pread or it failed for this file, use lseek/read. */
|
|
if (ret == -1)
|
|
{
|
|
ret = lseek (fd, (long) offset, SEEK_SET);
|
|
if (ret != -1)
|
|
ret = read (fd, read_buf, len);
|
|
}
|
|
|
|
if (ret == -1)
|
|
*target_errno = inf_child_errno_to_fileio_error (errno);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Close FD on the target. Return 0, or -1 if an error occurs
|
|
(and set *TARGET_ERRNO). */
|
|
static int
|
|
inf_child_fileio_close (struct target_ops *self, int fd, int *target_errno)
|
|
{
|
|
int ret;
|
|
|
|
ret = close (fd);
|
|
if (ret == -1)
|
|
*target_errno = inf_child_errno_to_fileio_error (errno);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Unlink FILENAME on the target. Return 0, or -1 if an error
|
|
occurs (and set *TARGET_ERRNO). */
|
|
static int
|
|
inf_child_fileio_unlink (struct target_ops *self,
|
|
const char *filename, int *target_errno)
|
|
{
|
|
int ret;
|
|
|
|
ret = unlink (filename);
|
|
if (ret == -1)
|
|
*target_errno = inf_child_errno_to_fileio_error (errno);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Read value of symbolic link FILENAME on the target. Return a
|
|
null-terminated string allocated via xmalloc, or NULL if an error
|
|
occurs (and set *TARGET_ERRNO). */
|
|
static char *
|
|
inf_child_fileio_readlink (struct target_ops *self,
|
|
const char *filename, int *target_errno)
|
|
{
|
|
/* We support readlink only on systems that also provide a compile-time
|
|
maximum path length (PATH_MAX), at least for now. */
|
|
#if defined (HAVE_READLINK) && defined (PATH_MAX)
|
|
char buf[PATH_MAX];
|
|
int len;
|
|
char *ret;
|
|
|
|
len = readlink (filename, buf, sizeof buf);
|
|
if (len < 0)
|
|
{
|
|
*target_errno = inf_child_errno_to_fileio_error (errno);
|
|
return NULL;
|
|
}
|
|
|
|
ret = xmalloc (len + 1);
|
|
memcpy (ret, buf, len);
|
|
ret[len] = '\0';
|
|
return ret;
|
|
#else
|
|
*target_errno = FILEIO_ENOSYS;
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
inf_child_use_agent (struct target_ops *self, int use)
|
|
{
|
|
if (agent_loaded_p ())
|
|
{
|
|
use_agent = use;
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
inf_child_can_use_agent (struct target_ops *self)
|
|
{
|
|
return agent_loaded_p ();
|
|
}
|
|
|
|
/* Default implementation of the to_can_async_p and
|
|
to_supports_non_stop methods. */
|
|
|
|
static int
|
|
return_zero (struct target_ops *ignore)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
struct target_ops *
|
|
inf_child_target (void)
|
|
{
|
|
struct target_ops *t = XCNEW (struct target_ops);
|
|
|
|
t->to_shortname = "child";
|
|
t->to_longname = "Unix child process";
|
|
t->to_doc = "Unix child process (started by the \"run\" command).";
|
|
t->to_open = inf_child_open;
|
|
t->to_post_attach = inf_child_post_attach;
|
|
t->to_fetch_registers = inf_child_fetch_inferior_registers;
|
|
t->to_store_registers = inf_child_store_inferior_registers;
|
|
t->to_prepare_to_store = inf_child_prepare_to_store;
|
|
t->to_insert_breakpoint = memory_insert_breakpoint;
|
|
t->to_remove_breakpoint = memory_remove_breakpoint;
|
|
t->to_terminal_init = terminal_init_inferior;
|
|
t->to_terminal_inferior = terminal_inferior;
|
|
t->to_terminal_ours_for_output = terminal_ours_for_output;
|
|
t->to_terminal_save_ours = terminal_save_ours;
|
|
t->to_terminal_ours = terminal_ours;
|
|
t->to_terminal_info = child_terminal_info;
|
|
t->to_post_startup_inferior = inf_child_post_startup_inferior;
|
|
t->to_follow_fork = inf_child_follow_fork;
|
|
t->to_can_run = inf_child_can_run;
|
|
/* We must default these because they must be implemented by any
|
|
target that can run. */
|
|
t->to_can_async_p = return_zero;
|
|
t->to_supports_non_stop = return_zero;
|
|
t->to_pid_to_exec_file = inf_child_pid_to_exec_file;
|
|
t->to_stratum = process_stratum;
|
|
t->to_has_all_memory = default_child_has_all_memory;
|
|
t->to_has_memory = default_child_has_memory;
|
|
t->to_has_stack = default_child_has_stack;
|
|
t->to_has_registers = default_child_has_registers;
|
|
t->to_has_execution = default_child_has_execution;
|
|
t->to_fileio_open = inf_child_fileio_open;
|
|
t->to_fileio_pwrite = inf_child_fileio_pwrite;
|
|
t->to_fileio_pread = inf_child_fileio_pread;
|
|
t->to_fileio_close = inf_child_fileio_close;
|
|
t->to_fileio_unlink = inf_child_fileio_unlink;
|
|
t->to_fileio_readlink = inf_child_fileio_readlink;
|
|
t->to_magic = OPS_MAGIC;
|
|
t->to_use_agent = inf_child_use_agent;
|
|
t->to_can_use_agent = inf_child_can_use_agent;
|
|
return t;
|
|
}
|