Sergio Durigan Junior 2090129c36 Share fork_inferior et al with gdbserver
This is the most important (and the biggest, sorry) patch of the
series.  It moves fork_inferior from gdb/fork-child.c to
nat/fork-inferior.c and makes all the necessary adjustments to both
GDB and gdbserver to make sure everything works OK.

There is no "most important change" with this patch; all changes are
made in a progressive way, making sure that gdbserver had the
necessary features while not breaking GDB at the same time.

I decided to go ahead and implement a partial support for starting the
inferior with a shell on gdbserver, although the full feature comes in
the next patch.  The user won't have the option to disable the
startup-with-shell, and also won't be able to change which shell
gdbserver will use (other than setting the $SHELL environment
variable, that is).

Everything is working as expected, and no regressions were present
during the tests.

gdb/ChangeLog:
2017-06-07  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Pedro Alves  <palves@redhat.com>

	* Makefile.in (HFILES_NO_SRCDIR): Add "common/common-inferior.h"
	and "nat/fork-inferior.h".
	* common/common-inferior.h: New file, with contents from
	"gdb/inferior.h".
	* commom/common-utils.c: Include "common-utils.h".
	(stringify_argv): New function.
	* common/common-utils.h (stringify_argv): New prototype.
	* configure.nat: Add "fork-inferior.o" as a dependency for
	"*linux*", "fbsd*" and "nbsd*" hosts.
	* corefile.c (get_exec_file): Update comment.
	* darwin-nat.c (darwin_ptrace_him): Call "gdb_startup_inferior"
	instead of "startup_inferior".
	(darwin_create_inferior): Call "add_thread_silent" after
	"fork_inferior".
	* fork-child.c: Cleanup unnecessary includes.
	(SHELL_FILE): Move to "common/common-fork-child.c".
	(environ): Likewise.
	(exec_wrapper): Initialize.
	(get_exec_wrapper): New function.
	(breakup_args): Move to "common/common-fork-child.c"; rename to
	"breakup_args_for_exec".
	(escape_bang_in_quoted_argument): Move to
	"common/common-fork-child.c".
	(saved_ui): New variable.
	(prefork_hook): New function.
	(postfork_hook): Likewise.
	(postfork_child_hook): Likewise.
	(gdb_startup_inferior): Likewise.
	(fork_inferior): Move to "common/common-fork-child.c".  Update
	function to support gdbserver.
	(startup_inferior): Likewise.
	* gdbcore.h (get_exec_file): Remove declaration.
	* gnu-nat.c (gnu_create_inferior): Call "gdb_startup_inferior"
	instead of "startup_inferior".  Call "add_thread_silent" after
	"fork_inferior".
	* inf-ptrace.c: Include "nat/fork-inferior.h" and "utils.h".
	(inf_ptrace_create_inferior): Call "gdb_startup_inferior"
	instead of "startup_inferior".  Call "add_thread_silent" after
	"fork_inferior".
	* inferior.h: Include "common-inferior.h".
	(trace_start_error): Move to "common/common-utils.h".
	(trace_start_error_with_name): Likewise.
	(fork_inferior): Move prototype to "nat/fork-inferior.h".
	(startup_inferior): Likewise.
	(gdb_startup_inferior): New prototype.
	* nat/fork-inferior.c: New file, with contents from "fork-child.c".
	* nat/fork-inferior.h: New file.
	* procfs.c (procfs_init_inferior): Call "gdb_startup_inferior"
	instead of "startup_inferior".  Call "add_thread_silent" after
	"fork_inferior".
	* target.h (target_terminal_init): Move prototype to
	"target/target.h".
	(target_terminal_inferior): Likewise.
	(target_terminal_ours): Likewise.
	* target/target.h (target_terminal_init): New prototype, moved
	from "target.h".
	(target_terminal_inferior): Likewise.
	(target_terminal_ours): Likewise.
	* utils.c (gdb_flush_out_err): New function.

gdb/gdbserver/ChangeLog:
2017-06-07  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Pedro Alves  <palves@redhat.com>

	* Makefile.in (SFILES): Add "nat/fork-inferior.o".
	* configure: Regenerate.
	* configure.srv (srv_linux_obj): Add "fork-child.o" and
	"fork-inferior.o".
	(i[34567]86-*-lynxos*): Likewise.
	(spu*-*-*): Likewise.
	* fork-child.c: New file.
	* linux-low.c: Include "common-inferior.h", "nat/fork-inferior.h"
	and "environ.h".
	(linux_ptrace_fun): New function.
	(linux_create_inferior): Adjust function prototype to reflect
	change on "target.h".  Adjust function code to use
	"fork_inferior".
	(linux_request_interrupt): Delete "signal_pid".
	* lynx-low.c: Include "common-inferior.h" and "nat/fork-inferior.h".
	(lynx_ptrace_fun): New function.
	(lynx_create_inferior): Adjust function prototype to reflect
	change on "target.h".  Adjust function code to use
	"fork_inferior".
	* nto-low.c (nto_create_inferior): Adjust function prototype and
	code to reflect change on "target.h".  Update comments.
	* server.c: Include "common-inferior.h", "nat/fork-inferior.h",
	"common-terminal.h" and "environ.h".
	(terminal_fd): Moved to fork-child.c.
	(old_foreground_pgrp): Likewise.
	(restore_old_foreground_pgrp): Likewise.
	(last_status): Make it global.
	(last_ptid): Likewise.
	(our_environ): New variable.
	(startup_with_shell): Likewise.
	(program_name): Likewise.
	(program_argv): Rename to...
	(program_args): ...this.
	(wrapper_argv): New variable.
	(start_inferior): Delete function.
	(get_exec_wrapper): New function.
	(get_exec_file): Likewise.
	(get_environ): Likewise.
	(prefork_hook): Likewise.
	(post_fork_inferior): Likewise.
	(postfork_hook): Likewise.
	(postfork_child_hook): Likewise.
	(handle_v_run): Update code to deal with arguments coming from the
	remote host.  Update calls from "start_inferior" to
	"create_inferior".
	(captured_main): Likewise.  Initialize environment variable.  Call
	"have_job_control".
	* server.h (post_fork_inferior): New prototype.
	(get_environ): Likewise.
	(last_status): Declare.
	(last_ptid): Likewise.
	(signal_pid): Likewise.
	* spu-low.c: Include "common-inferior.h" and "nat/fork-inferior.h".
	(spu_ptrace_fun): New function.
	(spu_create_inferior): Adjust function prototype to reflect change
	on "target.h".  Adjust function code to use "fork_inferior".
	* target.c (target_terminal_init): New function.
	(target_terminal_inferior): Likewise.
	(target_terminal_ours): Likewise.
	* target.h: Include <vector>.
	(struct target_ops) <create_inferior>: Update prototype.
	(create_inferior): Update macro.
	* utils.c (gdb_flush_out_err): New function.
	* win32-low.c (win32_create_inferior): Adjust function prototype
	and code to reflect change on "target.h".

gdb/testsuite/ChangeLog:
2017-06-07  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb.server/non-existing-program.exp: Update regex in order to
	reflect the fact that gdbserver is now using fork_inferior (with a
	shell) to startup the inferior.
2017-06-07 19:56:09 -04:00

417 lines
9.7 KiB
C

/* Target operations for the remote server for GDB.
Copyright (C) 2002-2017 Free Software Foundation, Inc.
Contributed by MontaVista Software.
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/>. */
#include "server.h"
#include "tracepoint.h"
struct target_ops *the_target;
int
set_desired_thread (int use_general)
{
struct thread_info *found;
if (use_general == 1)
found = find_thread_ptid (general_thread);
else
found = find_thread_ptid (cont_thread);
current_thread = found;
return (current_thread != NULL);
}
/* Structure used to look up a thread to use as current when accessing
memory. */
struct thread_search
{
/* The PTID of the current general thread. This is an input
parameter. */
ptid_t current_gen_ptid;
/* The first thread found. */
struct thread_info *first;
/* The first stopped thread found. */
struct thread_info *stopped;
/* The current general thread, if found. */
struct thread_info *current;
};
/* Callback for find_inferior. Search for a thread to use as current
when accessing memory. */
static int
thread_search_callback (struct inferior_list_entry *entry, void *args)
{
struct thread_info *thread = (struct thread_info *) entry;
struct thread_search *s = (struct thread_search *) args;
if (ptid_get_pid (entry->id) == ptid_get_pid (s->current_gen_ptid)
&& mythread_alive (ptid_of (thread)))
{
if (s->stopped == NULL
&& the_target->thread_stopped != NULL
&& thread_stopped (thread))
s->stopped = thread;
if (s->first == NULL)
s->first = thread;
if (s->current == NULL && ptid_equal (s->current_gen_ptid, entry->id))
s->current = thread;
}
return 0;
}
/* The thread that was current before prepare_to_access_memory was
called. done_accessing_memory uses this to restore the previous
selected thread. */
static ptid_t prev_general_thread;
/* See target.h. */
int
prepare_to_access_memory (void)
{
struct thread_search search;
struct thread_info *thread;
memset (&search, 0, sizeof (search));
search.current_gen_ptid = general_thread;
prev_general_thread = general_thread;
if (the_target->prepare_to_access_memory != NULL)
{
int res;
res = the_target->prepare_to_access_memory ();
if (res != 0)
return res;
}
find_inferior (&all_threads, thread_search_callback, &search);
/* Prefer a stopped thread. If none is found, try the current
thread. Otherwise, take the first thread in the process. If
none is found, undo the effects of
target->prepare_to_access_memory() and return error. */
if (search.stopped != NULL)
thread = search.stopped;
else if (search.current != NULL)
thread = search.current;
else if (search.first != NULL)
thread = search.first;
else
{
done_accessing_memory ();
return 1;
}
current_thread = thread;
general_thread = ptid_of (thread);
return 0;
}
/* See target.h. */
void
done_accessing_memory (void)
{
if (the_target->done_accessing_memory != NULL)
the_target->done_accessing_memory ();
/* Restore the previous selected thread. */
general_thread = prev_general_thread;
current_thread = find_thread_ptid (general_thread);
}
int
read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
{
int res;
res = (*the_target->read_memory) (memaddr, myaddr, len);
check_mem_read (memaddr, myaddr, len);
return res;
}
/* See target/target.h. */
int
target_read_memory (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len)
{
return read_inferior_memory (memaddr, myaddr, len);
}
/* See target/target.h. */
int
target_read_uint32 (CORE_ADDR memaddr, uint32_t *result)
{
return read_inferior_memory (memaddr, (gdb_byte *) result, sizeof (*result));
}
int
write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
int len)
{
/* Lacking cleanups, there is some potential for a memory leak if the
write fails and we go through error(). Make sure that no more than
one buffer is ever pending by making BUFFER static. */
static unsigned char *buffer = 0;
int res;
if (buffer != NULL)
free (buffer);
buffer = (unsigned char *) xmalloc (len);
memcpy (buffer, myaddr, len);
check_mem_write (memaddr, buffer, myaddr, len);
res = (*the_target->write_memory) (memaddr, buffer, len);
free (buffer);
buffer = NULL;
return res;
}
/* See target/target.h. */
int
target_write_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, ssize_t len)
{
return write_inferior_memory (memaddr, myaddr, len);
}
ptid_t
mywait (ptid_t ptid, struct target_waitstatus *ourstatus, int options,
int connected_wait)
{
ptid_t ret;
if (connected_wait)
server_waiting = 1;
ret = target_wait (ptid, ourstatus, options);
/* We don't expose _LOADED events to gdbserver core. See the
`dlls_changed' global. */
if (ourstatus->kind == TARGET_WAITKIND_LOADED)
ourstatus->kind = TARGET_WAITKIND_STOPPED;
/* If GDB is connected through TCP/serial, then GDBserver will most
probably be running on its own terminal/console, so it's nice to
print there why is GDBserver exiting. If however, GDB is
connected through stdio, then there's no need to spam the GDB
console with this -- the user will already see the exit through
regular GDB output, in that same terminal. */
if (!remote_connection_is_stdio ())
{
if (ourstatus->kind == TARGET_WAITKIND_EXITED)
fprintf (stderr,
"\nChild exited with status %d\n", ourstatus->value.integer);
else if (ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n",
gdb_signal_to_host (ourstatus->value.sig),
gdb_signal_to_name (ourstatus->value.sig));
}
if (connected_wait)
server_waiting = 0;
return ret;
}
/* See target/target.h. */
void
target_stop_and_wait (ptid_t ptid)
{
struct target_waitstatus status;
int was_non_stop = non_stop;
struct thread_resume resume_info;
resume_info.thread = ptid;
resume_info.kind = resume_stop;
resume_info.sig = GDB_SIGNAL_0;
(*the_target->resume) (&resume_info, 1);
non_stop = 1;
mywait (ptid, &status, 0, 0);
non_stop = was_non_stop;
}
/* See target/target.h. */
ptid_t
target_wait (ptid_t ptid, struct target_waitstatus *status, int options)
{
return (*the_target->wait) (ptid, status, options);
}
/* See target/target.h. */
void
target_mourn_inferior (ptid_t ptid)
{
(*the_target->mourn) (find_process_pid (ptid_get_pid (ptid)));
}
/* See target/target.h. */
void
target_continue_no_signal (ptid_t ptid)
{
struct thread_resume resume_info;
resume_info.thread = ptid;
resume_info.kind = resume_continue;
resume_info.sig = GDB_SIGNAL_0;
(*the_target->resume) (&resume_info, 1);
}
/* See target/target.h. */
void
target_continue (ptid_t ptid, enum gdb_signal signal)
{
struct thread_resume resume_info;
resume_info.thread = ptid;
resume_info.kind = resume_continue;
resume_info.sig = gdb_signal_to_host (signal);
(*the_target->resume) (&resume_info, 1);
}
/* See target/target.h. */
int
target_supports_multi_process (void)
{
return (the_target->supports_multi_process != NULL ?
(*the_target->supports_multi_process) () : 0);
}
int
start_non_stop (int nonstop)
{
if (the_target->start_non_stop == NULL)
{
if (nonstop)
return -1;
else
return 0;
}
return (*the_target->start_non_stop) (nonstop);
}
void
set_target_ops (struct target_ops *target)
{
the_target = XNEW (struct target_ops);
memcpy (the_target, target, sizeof (*the_target));
}
/* Convert pid to printable format. */
const char *
target_pid_to_str (ptid_t ptid)
{
static char buf[80];
if (ptid_equal (ptid, minus_one_ptid))
xsnprintf (buf, sizeof (buf), "<all threads>");
else if (ptid_equal (ptid, null_ptid))
xsnprintf (buf, sizeof (buf), "<null thread>");
else if (ptid_get_tid (ptid) != 0)
xsnprintf (buf, sizeof (buf), "Thread %d.0x%lx",
ptid_get_pid (ptid), ptid_get_tid (ptid));
else if (ptid_get_lwp (ptid) != 0)
xsnprintf (buf, sizeof (buf), "LWP %d.%ld",
ptid_get_pid (ptid), ptid_get_lwp (ptid));
else
xsnprintf (buf, sizeof (buf), "Process %d",
ptid_get_pid (ptid));
return buf;
}
int
kill_inferior (int pid)
{
gdb_agent_about_to_close (pid);
return (*the_target->kill) (pid);
}
/* Target can do hardware single step. */
int
target_can_do_hardware_single_step (void)
{
return 1;
}
/* Default implementation for breakpoint_kind_for_pc.
The default behavior for targets that don't implement breakpoint_kind_for_pc
is to use the size of a breakpoint as the kind. */
int
default_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
{
int size = 0;
gdb_assert (the_target->sw_breakpoint_from_kind != NULL);
(*the_target->sw_breakpoint_from_kind) (0, &size);
return size;
}
/* See target/target.h. */
void
target_terminal_init ()
{
/* Placeholder needed because of fork_inferior. Not necessary on
GDBserver. */
}
/* See target/target.h. */
void
target_terminal_inferior ()
{
/* Placeholder needed because of fork_inferior. Not necessary on
GDBserver. */
}
/* See target/target.h. */
void
target_terminal_ours ()
{
/* Placeholder needed because of fork_inferior. Not necessary on
GDBserver. */
}