binutils-gdb/gdb/gdbserver/server.c
Daniel Jacobowitz 0d62e5e807 2002-06-11 Daniel Jacobowitz <drow@mvista.com>
* gdbserver/thread-db.c: New file.
	* gdbserver/proc-service.c: New file.
	* gdbserver/acinclude.m4: New file.
	* gdbserver/Makefile.in: Add GDBSERVER_LIBS, gdb_proc_service_h,
	proc-service.o, and thread-db.o.
	(linux-low.o): Add USE_THREAD_DB.
	* gdbserver/acconfig.h: Add HAVE_PRGREGSET_T, HAVE_PRFPREGSET_T,
	HAVE_LWPID_T, HAVE_PSADDR_T, and PRFPREGSET_T_BROKEN.
	* gdbserver/aclocal.m4: Regenerated.
	* gdbserver/config.in: Regenerated.
	* gdbserver/configure: Regenerated.
	* gdbserver/configure.in: Check for proc_service.h, sys/procfs.h,
	thread_db.h, and linux/elf.h headrs.
	Check for lwpid_t, psaddr_t, prgregset_t, prfpregset_t, and
	PRFPREGSET_T_BROKEN.  Introduce srv_thread_depfiles and USE_THREAD_DB.
	Check for -lthread_db and thread support.
	* gdbserver/configure.srv: Enable thread_db support for ARM, i386, MIPS,
	PowerPC, and SuperH.
	* gdbserver/i387-fp.c: Constify arguments.
	* gdbserver/i387-fp.h: Likewise.
	* gdbserver/inferiors.c: (struct thread_info): Renamed from
	`struct inferior_info'.  Remove PID member.  Use generic inferior
	list header.  All uses updated.
	(inferiors, signal_pid): Removed.
	(all_threads): New variable.
	(get_thread): Define.
	(add_inferior_to_list): New function.
	(for_each_inferior): New function.
	(change_inferior_id): New function.
	(add_inferior): Removed.
	(remove_inferior): New function.
	(add_thread): New function.
	(free_one_thread): New function.
	(remove_thread): New function.
	(clear_inferiors): Use for_each_inferior and free_one_thread.
	(find_inferior): New function.
	(find_inferior_id): New function.
	(inferior_target_data): Update argument type.
	(set_inferior_target_data): Likewise.
	(inferior_regcache_data): Likewise.
	(set_inferior_regcache_data): Likewise.
	* gdbserver/linux-low.c (linux_bp_reinsert): Remove.
	(all_processes, stopping_threads, using_thrads)
	(struct pending_signals, debug_threads, pid_of): New.
	(inferior_pid): Replace with macro.
	(struct inferior_linux_data): Remove.
	(get_stop_pc, add_process): New functions.
	(linux_create_inferior): Restore SIGRTMIN+1 before calling exec.
	Use add_process and add_thread.
	(linux_attach_lwp): New function, based on old linux_attach.  Use
	add_process and add_thread.  Set stop_expected for new threads.
	(linux_attach): New function.
	(linux_kill_one_process): New function.
	(linux_kill): Kill all LWPs.
	(linux_thread_alive): Use find_inferior_id.
	(check_removed_breakpoints, status_pending_p): New functions.
	(linux_wait_for_process): Renamed from linux_wait_for_one_inferior.
	Update.  Use WNOHANG.  Wait for cloned processes also.  Update process
	struct for the found process.
	(linux_wait_for_event): New function.
	(linux_wait): Use it.  Support LWPs.
	(send_sigstop, wait_for_sigstop, stop_all_processes)
	(linux_resume_one_process, linux_continue_one_process): New functions.
	(linux_resume): Support LWPs.
	(REGISTER_RAW_SIZE): Remove.
	(fetch_register): Use register_size instead.  Call supply_register.
	(usr_store_inferior_registers): Likewise.  Call collect_register.
	Fix recursive case.
	(regsets_fetch_inferior_registers): Improve error message.
	(regsets_store_inferior_registers): Add debugging.
	(linux_look_up_symbols): Call thread_db_init if USE_THREAD_DB.
	(unstopped_p, linux_signal_pid): New functions.
	(linux_target_ops): Add linux_signal_pid.
	(linux_init_signals): New function.
	(initialize_low): Call it.  Initialize using_threads.
	* gdbserver/regcache.c (inferior_regcache_data): Add valid
	flag.
	(get_regcache): Fetch registers lazily.  Add fetch argument
	and update all callers.
	(regcache_invalidate_one, regcache_invalidate): New
	functions.
	(new_register_cache): Renamed from create_register_cache.
	Return the new regcache.
	(free_register_cache): Change argument to a void *.
	(registers_to_string, registers_from_string): Call get_regcache
	with fetch flag set.
	(register_data): Make static.  Pass fetch flag to get_regcache.
	(supply_register): Call get_regcache with fetch flag clear.
	(collect_register): Call get_regcache with fetch flag set.
	(collect_register_as_string): New function.
	* gdbserver/regcache.h: Update.
	* gdbserver/remote-utils.c (putpkt): Flush after debug output and use
	stderr.
	Handle input interrupts while waiting for an ACK.
	(input_interrupt): Use signal_pid method.
	(getpkt): Flush after debug output and use stderr.
	(outreg): Use collect_register_as_string.
	(new_thread_notify, dead_thread_notify): New functions.
	(prepare_resume_reply): Check using_threads.  Set thread_from_wait
	and general_thread.
	(look_up_one_symbol): Flush after debug output.
	* gdbserver/server.c (step_thread, server_waiting): New variables.
	(start_inferior): Don't use signal_pid.  Update call to mywait.
	(attach_inferior): Update call to mywait.
	(handle_query): Handle qfThreadInfo and qsThreadInfo.
	(main): Don't fetch/store registers explicitly.  Use
	set_desired_inferior.  Support proposed ``Hs'' packet.  Update
	calls to mywait.
	* gdbserver/server.h: Update.
	(struct inferior_list, struct_inferior_list_entry): New.
	* gdbserver/target.c (set_desired_inferior): New.
	(write_inferior_memory): Constify.
	(mywait): New function.
	* gdbserver/target.h: Update.
	(struct target_ops): New signal_pid method.
	(mywait): Removed macro, added prototype.

	* gdbserver/linux-low.h (regset_func): Removed.
	(regset_fill_func, regset_store_func): New.
	(enum regset_type): New.
	(struct regset_info): Add type field.  Use new operation types.
	(struct linux_target_ops): stop_pc renamed to get_pc.
	Add decr_pc_after_break and breakpoint_at.
	(get_process, get_thread_proess, get_process_thread)
	(strut process_info, all_processes, linux_attach_lwp)
	(thread_db_init): New.

	* gdbserver/linux-arm-low.c (arm_get_pc, arm_set_pc,
	arm_breakpoint, arm_breakpoint_len, arm_breakpoint_at): New.
	(the_low_target): Add new members.
	* gdbserver/linux-i386-low.c (i386_store_gregset, i386_store_fpregset)
	(i386_store_fpxregset): Constify.
	(target_regsets): Add new kind identifier.
	(i386_get_pc): Renamed from i386_stop_pc.  Simplify.
	(i386_set_pc): Add debugging.
	(i386_breakpoint_at): New function.
	(the_low_target): Add new members.
	* gdbserver/linux-mips-low.c (mips_get_pc, mips_set_pc)
	(mips_breakpoint, mips_breakpoint_len, mips_reinsert_addr)
	(mips_breakpoint_at): New.
	(the_low_target): Add new members.
	* gdbserver/linux-ppc-low.c (ppc_get_pc, ppc_set_pc)
	(ppc_breakpoint, ppc_breakpoint_len, ppc_breakpoint_at): New.
	(the_low_target): Add new members.
	* gdbserver/linux-sh-low.c (sh_get_pc, sh_set_pc)
	(sh_breakpoint, sh_breakpoint_len, sh_breakpoint_at): New.
	(the_low_target): Add new members.
	* gdbserver/linux-x86-64-low.c (target_regsets): Add new kind
	identifier.
2002-06-11 17:32:40 +00:00

398 lines
9.3 KiB
C

/* Main code for remote server for GDB.
Copyright 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "server.h"
int cont_thread;
int general_thread;
int step_thread;
int thread_from_wait;
int old_thread_from_wait;
int extended_protocol;
int server_waiting;
jmp_buf toplevel;
static unsigned char
start_inferior (char *argv[], char *statusptr)
{
/* FIXME Check error? Or turn to void. */
create_inferior (argv[0], argv);
fprintf (stderr, "Process %s created; pid = %d\n", argv[0],
all_threads.head->id);
/* Wait till we are at 1st instruction in program, return signal number. */
return mywait (statusptr, 0);
}
static int
attach_inferior (int pid, char *statusptr, unsigned char *sigptr)
{
/* myattach should return -1 if attaching is unsupported,
0 if it succeeded, and call error() otherwise. */
if (myattach (pid) != 0)
return -1;
*sigptr = mywait (statusptr, 0);
return 0;
}
extern int remote_debug;
/* Handle all of the extended 'q' packets. */
void
handle_query (char *own_buf)
{
static struct inferior_list_entry *thread_ptr;
if (strcmp ("qSymbol::", own_buf) == 0)
{
if (the_target->look_up_symbols != NULL)
(*the_target->look_up_symbols) ();
strcpy (own_buf, "OK");
return;
}
if (strcmp ("qfThreadInfo", own_buf) == 0)
{
thread_ptr = all_threads.head;
sprintf (own_buf, "m%x", thread_ptr->id);
thread_ptr = thread_ptr->next;
return;
}
if (strcmp ("qsThreadInfo", own_buf) == 0)
{
if (thread_ptr != NULL)
{
sprintf (own_buf, "m%x", thread_ptr->id);
thread_ptr = thread_ptr->next;
return;
}
else
{
sprintf (own_buf, "l");
return;
}
}
/* Otherwise we didn't know what packet it was. Say we didn't
understand it. */
own_buf[0] = 0;
}
static int attached;
static void
gdbserver_usage (void)
{
error ("Usage:\tgdbserver COMM PROG [ARGS ...]\n"
"\tgdbserver COMM --attach PID\n"
"\n"
"COMM may either be a tty device (for serial debugging), or \n"
"HOST:PORT to listen for a TCP connection.\n");
}
int
main (int argc, char *argv[])
{
char ch, status, *own_buf, mem_buf[2000];
int i = 0;
unsigned char signal;
unsigned int len;
CORE_ADDR mem_addr;
int bad_attach;
int pid;
char *arg_end;
if (setjmp (toplevel))
{
fprintf (stderr, "Exiting\n");
exit (1);
}
bad_attach = 0;
pid = 0;
attached = 0;
if (argc >= 3 && strcmp (argv[2], "--attach") == 0)
{
if (argc == 4
&& argv[3] != '\0'
&& (pid = strtoul (argv[3], &arg_end, 10)) != 0
&& *arg_end == '\0')
{
;
}
else
bad_attach = 1;
}
if (argc < 3 || bad_attach)
gdbserver_usage();
initialize_low ();
own_buf = malloc (PBUFSIZ);
if (pid == 0)
{
/* Wait till we are at first instruction in program. */
signal = start_inferior (&argv[2], &status);
/* We are now stopped at the first instruction of the target process */
}
else
{
switch (attach_inferior (pid, &status, &signal))
{
case -1:
error ("Attaching not supported on this target");
break;
default:
attached = 1;
break;
}
}
while (1)
{
remote_open (argv[1]);
restart:
setjmp (toplevel);
while (getpkt (own_buf) > 0)
{
unsigned char sig;
i = 0;
ch = own_buf[i++];
switch (ch)
{
case 'q':
handle_query (own_buf);
break;
case 'd':
remote_debug = !remote_debug;
break;
case '!':
if (attached == 0)
{
extended_protocol = 1;
prepare_resume_reply (own_buf, status, signal);
}
else
{
/* We can not use the extended protocol if we are
attached, because we can not restart the running
program. So return unrecognized. */
own_buf[0] = '\0';
}
break;
case '?':
prepare_resume_reply (own_buf, status, signal);
break;
case 'H':
switch (own_buf[1])
{
case 'g':
general_thread = strtol (&own_buf[2], NULL, 16);
write_ok (own_buf);
set_desired_inferior (1);
break;
case 'c':
cont_thread = strtol (&own_buf[2], NULL, 16);
write_ok (own_buf);
break;
case 's':
step_thread = strtol (&own_buf[2], NULL, 16);
write_ok (own_buf);
break;
default:
/* Silently ignore it so that gdb can extend the protocol
without compatibility headaches. */
own_buf[0] = '\0';
break;
}
break;
case 'g':
set_desired_inferior (1);
registers_to_string (own_buf);
break;
case 'G':
set_desired_inferior (1);
registers_from_string (&own_buf[1]);
write_ok (own_buf);
break;
case 'm':
decode_m_packet (&own_buf[1], &mem_addr, &len);
read_inferior_memory (mem_addr, mem_buf, len);
convert_int_to_ascii (mem_buf, own_buf, len);
break;
case 'M':
decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf);
if (write_inferior_memory (mem_addr, mem_buf, len) == 0)
write_ok (own_buf);
else
write_enn (own_buf);
break;
case 'C':
convert_ascii_to_int (own_buf + 1, &sig, 1);
if (target_signal_to_host_p (sig))
signal = target_signal_to_host (sig);
else
signal = 0;
set_desired_inferior (0);
myresume (0, signal);
signal = mywait (&status, 1);
prepare_resume_reply (own_buf, status, signal);
break;
case 'S':
convert_ascii_to_int (own_buf + 1, &sig, 1);
if (target_signal_to_host_p (sig))
signal = target_signal_to_host (sig);
else
signal = 0;
set_desired_inferior (0);
myresume (1, signal);
signal = mywait (&status, 1);
prepare_resume_reply (own_buf, status, signal);
break;
case 'c':
set_desired_inferior (0);
myresume (0, 0);
signal = mywait (&status, 1);
prepare_resume_reply (own_buf, status, signal);
break;
case 's':
set_desired_inferior (0);
myresume (1, 0);
signal = mywait (&status, 1);
prepare_resume_reply (own_buf, status, signal);
break;
case 'k':
fprintf (stderr, "Killing inferior\n");
kill_inferior ();
/* When using the extended protocol, we start up a new
debugging session. The traditional protocol will
exit instead. */
if (extended_protocol)
{
write_ok (own_buf);
fprintf (stderr, "GDBserver restarting\n");
/* Wait till we are at 1st instruction in prog. */
signal = start_inferior (&argv[2], &status);
goto restart;
break;
}
else
{
exit (0);
break;
}
case 'T':
if (mythread_alive (strtol (&own_buf[1], NULL, 16)))
write_ok (own_buf);
else
write_enn (own_buf);
break;
case 'R':
/* Restarting the inferior is only supported in the
extended protocol. */
if (extended_protocol)
{
kill_inferior ();
write_ok (own_buf);
fprintf (stderr, "GDBserver restarting\n");
/* Wait till we are at 1st instruction in prog. */
signal = start_inferior (&argv[2], &status);
goto restart;
break;
}
else
{
/* It is a request we don't understand. Respond with an
empty packet so that gdb knows that we don't support this
request. */
own_buf[0] = '\0';
break;
}
default:
/* It is a request we don't understand. Respond with an
empty packet so that gdb knows that we don't support this
request. */
own_buf[0] = '\0';
break;
}
putpkt (own_buf);
if (status == 'W')
fprintf (stderr,
"\nChild exited with status %d\n", sig);
if (status == 'X')
fprintf (stderr, "\nChild terminated with signal = 0x%x\n", sig);
if (status == 'W' || status == 'X')
{
if (extended_protocol)
{
fprintf (stderr, "Killing inferior\n");
kill_inferior ();
write_ok (own_buf);
fprintf (stderr, "GDBserver restarting\n");
/* Wait till we are at 1st instruction in prog. */
signal = start_inferior (&argv[2], &status);
goto restart;
break;
}
else
{
fprintf (stderr, "GDBserver exiting\n");
exit (0);
}
}
}
/* We come here when getpkt fails.
For the extended remote protocol we exit (and this is the only
way we gracefully exit!).
For the traditional remote protocol close the connection,
and re-open it at the top of the loop. */
if (extended_protocol)
{
remote_close ();
exit (0);
}
else
{
fprintf (stderr, "Remote side has terminated connection. "
"GDBserver will reopen the connection.\n");
remote_close ();
}
}
}