449 lines
9.2 KiB
C
449 lines
9.2 KiB
C
/* Base/prototype target for default child (native) targets.
|
|
|
|
Copyright (C) 1988-2018 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 <sys/stat.h>
|
|
#include "inf-child.h"
|
|
#include "fileio.h"
|
|
#include "agent.h"
|
|
#include "gdb_wait.h"
|
|
#include "filestuff.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
static const target_info inf_child_target_info = {
|
|
"native",
|
|
N_("Native process"),
|
|
N_("Native process (started by the \"run\" command).")
|
|
};
|
|
|
|
const target_info &
|
|
inf_child_target::info () const
|
|
{
|
|
return inf_child_target_info;
|
|
}
|
|
|
|
/* 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));
|
|
}
|
|
}
|
|
|
|
inf_child_target::~inf_child_target ()
|
|
{}
|
|
|
|
void
|
|
inf_child_target::post_attach (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. */
|
|
|
|
void
|
|
inf_child_target::prepare_to_store (struct regcache *regcache)
|
|
{
|
|
}
|
|
|
|
bool
|
|
inf_child_target::supports_terminal_ours ()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void
|
|
inf_child_target::terminal_init ()
|
|
{
|
|
child_terminal_init (this);
|
|
}
|
|
|
|
void
|
|
inf_child_target::terminal_inferior ()
|
|
{
|
|
child_terminal_inferior (this);
|
|
}
|
|
|
|
void
|
|
inf_child_target::terminal_ours_for_output ()
|
|
{
|
|
child_terminal_ours_for_output (this);
|
|
}
|
|
|
|
void
|
|
inf_child_target::terminal_ours ()
|
|
{
|
|
child_terminal_ours (this);
|
|
}
|
|
|
|
void
|
|
inf_child_target::interrupt ()
|
|
{
|
|
child_interrupt (this);
|
|
}
|
|
|
|
void
|
|
inf_child_target::pass_ctrlc ()
|
|
{
|
|
child_pass_ctrlc (this);
|
|
}
|
|
|
|
void
|
|
inf_child_target::terminal_info (const char *args, int from_tty)
|
|
{
|
|
child_terminal_info (this, args, from_tty);
|
|
}
|
|
|
|
/* True if the user did "target native". In that case, we won't
|
|
unpush the child target automatically when the last inferior is
|
|
gone. */
|
|
static int inf_child_explicitly_opened;
|
|
|
|
/* See inf-child.h. */
|
|
|
|
void
|
|
inf_child_open_target (const char *arg, int from_tty)
|
|
{
|
|
target_ops *target = get_native_target ();
|
|
|
|
/* There's always only ever one native target, and if we get here,
|
|
it better be an inf-child target. */
|
|
gdb_assert (dynamic_cast<inf_child_target *> (target) != NULL);
|
|
|
|
target_preopen (from_tty);
|
|
push_target (target);
|
|
inf_child_explicitly_opened = 1;
|
|
if (from_tty)
|
|
printf_filtered ("Done. Use the \"run\" command to start a process.\n");
|
|
}
|
|
|
|
/* Implement the to_disconnect target_ops method. */
|
|
|
|
void
|
|
inf_child_target::disconnect (const char *args, int from_tty)
|
|
{
|
|
if (args != NULL)
|
|
error (_("Argument given to \"disconnect\"."));
|
|
|
|
/* This offers to detach/kill current inferiors, and then pops all
|
|
targets. */
|
|
target_preopen (from_tty);
|
|
}
|
|
|
|
/* Implement the to_close target_ops method. */
|
|
|
|
void
|
|
inf_child_target::close ()
|
|
{
|
|
/* In case we were forcibly closed. */
|
|
inf_child_explicitly_opened = 0;
|
|
}
|
|
|
|
void
|
|
inf_child_target::mourn_inferior ()
|
|
{
|
|
generic_mourn_inferior ();
|
|
maybe_unpush_target ();
|
|
}
|
|
|
|
/* See inf-child.h. */
|
|
|
|
void
|
|
inf_child_target::maybe_unpush_target ()
|
|
{
|
|
if (!inf_child_explicitly_opened && !have_inferiors ())
|
|
unpush_target (this);
|
|
}
|
|
|
|
void
|
|
inf_child_target::post_startup_inferior (ptid_t ptid)
|
|
{
|
|
/* This target doesn't require a meaningful "post startup inferior"
|
|
operation by a debugger. */
|
|
}
|
|
|
|
bool
|
|
inf_child_target::can_run ()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
inf_child_target::can_create_inferior ()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
inf_child_target::can_attach ()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
char *
|
|
inf_child_target::pid_to_exec_file (int pid)
|
|
{
|
|
/* This target doesn't support translation of a process ID to the
|
|
filename of the executable file. */
|
|
return NULL;
|
|
}
|
|
|
|
bool
|
|
inf_child_target::has_all_memory ()
|
|
{
|
|
return default_child_has_all_memory ();
|
|
}
|
|
|
|
bool
|
|
inf_child_target::has_memory ()
|
|
{
|
|
return default_child_has_memory ();
|
|
}
|
|
|
|
bool
|
|
inf_child_target::has_stack ()
|
|
{
|
|
return default_child_has_stack ();
|
|
}
|
|
|
|
bool
|
|
inf_child_target::has_registers ()
|
|
{
|
|
return default_child_has_registers ();
|
|
}
|
|
|
|
bool
|
|
inf_child_target::has_execution (ptid_t ptid)
|
|
{
|
|
return default_child_has_execution (ptid);
|
|
}
|
|
|
|
/* Implementation of to_fileio_open. */
|
|
|
|
int
|
|
inf_child_target::fileio_open (struct inferior *inf, const char *filename,
|
|
int flags, int mode, int warn_if_slow,
|
|
int *target_errno)
|
|
{
|
|
int nat_flags;
|
|
mode_t nat_mode;
|
|
int fd;
|
|
|
|
if (fileio_to_host_openflags (flags, &nat_flags) == -1
|
|
|| fileio_to_host_mode (mode, &nat_mode) == -1)
|
|
{
|
|
*target_errno = FILEIO_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
fd = gdb_open_cloexec (filename, nat_flags, nat_mode);
|
|
if (fd == -1)
|
|
*target_errno = host_to_fileio_error (errno);
|
|
|
|
return fd;
|
|
}
|
|
|
|
/* Implementation of to_fileio_pwrite. */
|
|
|
|
int
|
|
inf_child_target::fileio_pwrite (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 = host_to_fileio_error (errno);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Implementation of to_fileio_pread. */
|
|
|
|
int
|
|
inf_child_target::fileio_pread (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 = host_to_fileio_error (errno);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Implementation of to_fileio_fstat. */
|
|
|
|
int
|
|
inf_child_target::fileio_fstat (int fd, struct stat *sb, int *target_errno)
|
|
{
|
|
int ret;
|
|
|
|
ret = fstat (fd, sb);
|
|
if (ret == -1)
|
|
*target_errno = host_to_fileio_error (errno);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Implementation of to_fileio_close. */
|
|
|
|
int
|
|
inf_child_target::fileio_close (int fd, int *target_errno)
|
|
{
|
|
int ret;
|
|
|
|
ret = ::close (fd);
|
|
if (ret == -1)
|
|
*target_errno = host_to_fileio_error (errno);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Implementation of to_fileio_unlink. */
|
|
|
|
int
|
|
inf_child_target::fileio_unlink (struct inferior *inf, const char *filename,
|
|
int *target_errno)
|
|
{
|
|
int ret;
|
|
|
|
ret = unlink (filename);
|
|
if (ret == -1)
|
|
*target_errno = host_to_fileio_error (errno);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Implementation of to_fileio_readlink. */
|
|
|
|
gdb::optional<std::string>
|
|
inf_child_target::fileio_readlink (struct inferior *inf, 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 (PATH_MAX)
|
|
char buf[PATH_MAX];
|
|
int len;
|
|
|
|
len = readlink (filename, buf, sizeof buf);
|
|
if (len < 0)
|
|
{
|
|
*target_errno = host_to_fileio_error (errno);
|
|
return {};
|
|
}
|
|
|
|
return std::string (buf, len);
|
|
#else
|
|
*target_errno = FILEIO_ENOSYS;
|
|
return {};
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
inf_child_target::use_agent (bool use)
|
|
{
|
|
if (agent_loaded_p ())
|
|
{
|
|
::use_agent = use;
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
inf_child_target::can_use_agent ()
|
|
{
|
|
return agent_loaded_p ();
|
|
}
|
|
|
|
inf_child_target::inf_child_target ()
|
|
{
|
|
this->to_stratum = process_stratum;
|
|
}
|
|
|
|
/* See inf-child.h. */
|
|
|
|
void
|
|
add_inf_child_target (inf_child_target *target)
|
|
{
|
|
set_native_target (target);
|
|
add_target (inf_child_target_info, inf_child_open_target);
|
|
}
|