From: nobody <> Date: Sat, 19 Aug 2006 15:15:19 +0000 (+0000) Subject: This commit was manufactured by cvs2svn to create branch 'nickrob- X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0951c781d633adfd4c7edba344cb6037f3deea59;p=thirdparty%2Fbinutils-gdb.git This commit was manufactured by cvs2svn to create branch 'nickrob- async-20060513-branch'. Cherrypick from master 2006-08-19 15:15:18 UTC Daniel Jacobowitz 'gdb/': gdb/alphaobsd-tdep.c gdb/amd64-linux-tdep.h gdb/arm-linux-tdep.h gdb/armbsd-tdep.c gdb/armobsd-tdep.c gdb/config/alpha/obsd.mt gdb/config/arm/obsd.mt gdb/config/sparc/embed.mt gdb/gdbserver/win32-i386-low.c gdb/regformats/reg-x86-64-linux.dat gdb/testsuite/gdb.arch/i386-size.c gdb/testsuite/gdb.arch/i386-size.exp gdb/testsuite/gdb.base/ifelse.exp gdb/testsuite/gdb.base/step-bt.c gdb/testsuite/gdb.base/step-bt.exp gdb/testsuite/gdb.base/trace-commands.exp gdb/testsuite/gdb.cp/ref-params.cc gdb/testsuite/gdb.cp/ref-params.exp --- diff --git a/gdb/alphaobsd-tdep.c b/gdb/alphaobsd-tdep.c new file mode 100644 index 00000000000..58b4f8aa2b5 --- /dev/null +++ b/gdb/alphaobsd-tdep.c @@ -0,0 +1,137 @@ +/* Target-dependent code for OpenBSD/alpha. + + Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "defs.h" +#include "frame.h" +#include "gdbcore.h" +#include "osabi.h" + +#include "obsd-tdep.h" +#include "alpha-tdep.h" +#include "alphabsd-tdep.h" +#include "solib-svr4.h" + +/* Signal trampolines. */ + +/* The OpenBSD kernel maps the signal trampoline at some random + location in user space, which means that the traditional BSD way of + detecting it won't work. + + The signal trampoline will be mapped at an address that is page + aligned. We recognize the signal trampoline by looking for the + sigreturn system call. */ + +static const int alphaobsd_page_size = 8192; + +static LONGEST +alphaobsd_sigtramp_offset (CORE_ADDR pc) +{ + return (pc & (alphaobsd_page_size - 1)); +} + +static int +alphaobsd_pc_in_sigtramp (CORE_ADDR pc, char *name) +{ + CORE_ADDR start_pc = (pc & ~(alphaobsd_page_size - 1)); + unsigned insn; + + if (name) + return 0; + + /* Check for "". */ + insn = alpha_read_insn (start_pc + 5 * ALPHA_INSN_SIZE); + if (insn != 0x201f0067) + return 0; + + /* Check for "". */ + insn = alpha_read_insn (start_pc + 6 * ALPHA_INSN_SIZE); + if (insn != 0x00000083) + return 0; + + return 1; +} + +static CORE_ADDR +alphaobsd_sigcontext_addr (struct frame_info *next_frame) +{ + CORE_ADDR pc = frame_pc_unwind (next_frame); + + if (alphaobsd_sigtramp_offset (pc) < 3 * ALPHA_INSN_SIZE) + { + /* On entry, a pointer the `struct sigcontext' is passed in %a2. */ + return frame_unwind_register_unsigned (next_frame, ALPHA_A0_REGNUM + 2); + } + else if (alphaobsd_sigtramp_offset (pc) < 4 * ALPHA_INSN_SIZE) + { + /* It is stored on the stack Before calling the signal handler. */ + CORE_ADDR sp; + sp = frame_unwind_register_unsigned (next_frame, ALPHA_SP_REGNUM); + return get_frame_memory_unsigned (next_frame, sp, 8); + } + else + { + /* It is reloaded into %a0 for the sigreturn(2) call. */ + return frame_unwind_register_unsigned (next_frame, ALPHA_A0_REGNUM); + } +} + + +static void +alphaobsd_init_abi(struct gdbarch_info info, struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + /* Hook into the DWARF CFI frame unwinder. */ + alpha_dwarf2_init_abi (info, gdbarch); + + /* Hook into the MDEBUG frame unwinder. */ + alpha_mdebug_init_abi (info, gdbarch); + + /* OpenBSD/alpha 3.0 and earlier does not provide single step + support via ptrace(2); use software single-stepping for now. */ + set_gdbarch_software_single_step (gdbarch, alpha_software_single_step); + + /* OpenBSD/alpha has SVR4-style shared libraries. */ + set_solib_svr4_fetch_link_map_offsets + (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_gdbarch_skip_solib_resolver (gdbarch, obsd_skip_solib_resolver); + + tdep->dynamic_sigtramp_offset = alphaobsd_sigtramp_offset; + tdep->pc_in_sigtramp = alphaobsd_pc_in_sigtramp; + tdep->sigcontext_addr = alphaobsd_sigcontext_addr; + + tdep->jb_pc = 2; + tdep->jb_elt_size = 8; + + set_gdbarch_regset_from_core_section + (gdbarch, alphanbsd_regset_from_core_section); +} + + +/* Provide a prototype to silence -Wmissing-prototypes. */ +void _initialize_alphaobsd_tdep (void); + +void +_initialize_alphaobsd_tdep (void) +{ + gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_OPENBSD_ELF, + alphaobsd_init_abi); +} diff --git a/gdb/amd64-linux-tdep.h b/gdb/amd64-linux-tdep.h new file mode 100644 index 00000000000..4a5778d0ca5 --- /dev/null +++ b/gdb/amd64-linux-tdep.h @@ -0,0 +1,36 @@ +/* Target-dependent code for GNU/Linux AMD64. + + Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef AMD64_LINUX_TDEP_H +#define AMD64_LINUX_TDEP_H + +/* Like for i386 GNU/Linux, there is an extra "register" + used to control syscall restarting. */ + +/* Register number for the "orig_rax" register. If this register + contains a value >= 0 it is interpreted as the system call number + that the kernel is supposed to restart. */ +#define AMD64_LINUX_ORIG_RAX_REGNUM (AMD64_MXCSR_REGNUM + 1) + +/* Total number of registers for GNU/Linux. */ +#define AMD64_LINUX_NUM_REGS (AMD64_LINUX_ORIG_RAX_REGNUM + 1) + +#endif /* amd64-linux-tdep.h */ diff --git a/gdb/arm-linux-tdep.h b/gdb/arm-linux-tdep.h new file mode 100644 index 00000000000..31d6ba03fc9 --- /dev/null +++ b/gdb/arm-linux-tdep.h @@ -0,0 +1,62 @@ +/* GNU/Linux on ARM target support, prototypes. + + Copyright (C) 2006 + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +struct regset; +struct regcache; + +#define ARM_CPSR_REGNUM 16 + +#define ARM_LINUX_SIZEOF_NWFPE (8 * FP_REGISTER_SIZE \ + + 2 * INT_REGISTER_SIZE \ + + 8 + INT_REGISTER_SIZE) + +/* Support for register format used by the NWFPE FPA emulator. Each + register takes three words, where either the first one, two, or + three hold a single, double, or extended precision value (depending + on the corresponding tag). The register set is eight registers, + followed by the fpsr and fpcr, followed by eight tag bytes, and a + final word flag which indicates whether NWFPE has been + initialized. */ + +#define NWFPE_FPSR_OFFSET (8 * FP_REGISTER_SIZE) +#define NWFPE_FPCR_OFFSET (NWFPE_FPSR_OFFSET + INT_REGISTER_SIZE) +#define NWFPE_TAGS_OFFSET (NWFPE_FPCR_OFFSET + INT_REGISTER_SIZE) +#define NWFPE_INITFLAG_OFFSET (NWFPE_TAGS_OFFSET + 8) + +void arm_linux_supply_gregset (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *gregs_buf, size_t len); +void arm_linux_collect_gregset (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *gregs_buf, size_t len); + +void supply_nwfpe_register (struct regcache *regcache, int regno, + const gdb_byte *regs); +void collect_nwfpe_register (const struct regcache *regcache, int regno, + gdb_byte *regs); + +void arm_linux_supply_nwfpe (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *regs_buf, size_t len); +void arm_linux_collect_nwfpe (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *regs_buf, size_t len); diff --git a/gdb/armbsd-tdep.c b/gdb/armbsd-tdep.c new file mode 100644 index 00000000000..95ecd425dff --- /dev/null +++ b/gdb/armbsd-tdep.c @@ -0,0 +1,129 @@ +/* Target-dependent code for ARM BSD's. + + Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "defs.h" +#include "osabi.h" +#include "regcache.h" +#include "regset.h" + +#include "gdb_assert.h" +#include "gdb_string.h" + +#include "arm-tdep.h" + +/* Core file support. */ + +/* Sizeof `struct reg' in . */ +#define ARMBSD_SIZEOF_GREGS (17 * 4) + +/* Sizeof `struct fpreg' in = ARMBSD_SIZEOF_FPREGS); + + for (i = ARM_F0_REGNUM; i <= ARM_FPS_REGNUM; i++) + { + if (regnum == i || regnum == -1) + regcache_raw_supply (regcache, i, regs + armbsd_fpreg_offset (i)); + } +} + +/* Supply register REGNUM from the buffer specified by GREGS and LEN + in the general-purpose register set REGSET to register cache + REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */ + +static void +armbsd_supply_gregset (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *gregs, size_t len) +{ + const gdb_byte *regs = gregs; + int i; + + gdb_assert (len >= ARMBSD_SIZEOF_GREGS); + + for (i = ARM_A1_REGNUM; i <= ARM_PC_REGNUM; i++) + { + if (regnum == i || regnum == -1) + regcache_raw_supply (regcache, i, regs + i * 4); + } + + if (regnum == ARM_PS_REGNUM || regnum == -1) + regcache_raw_supply (regcache, i, regs + 16 * 4); + + if (len >= ARMBSD_SIZEOF_GREGS + ARMBSD_SIZEOF_FPREGS) + { + regs += ARMBSD_SIZEOF_GREGS; + len -= ARMBSD_SIZEOF_GREGS; + armbsd_supply_fpregset (regset, regcache, regnum, regs, len); + } +} + +/* ARM register sets. */ + +static struct regset armbsd_gregset = +{ + NULL, + armbsd_supply_gregset +}; + +static struct regset armbsd_fpregset = +{ + NULL, + armbsd_supply_fpregset +}; + +/* Return the appropriate register set for the core section identified + by SECT_NAME and SECT_SIZE. */ + +const struct regset * +armbsd_regset_from_core_section (struct gdbarch *gdbarch, + const char *sect_name, size_t sect_size) +{ + if (strcmp (sect_name, ".reg") == 0 && sect_size >= ARMBSD_SIZEOF_GREGS) + return &armbsd_gregset; + + if (strcmp (sect_name, ".reg2") == 0 && sect_size >= ARMBSD_SIZEOF_FPREGS) + return &armbsd_fpregset; + + return NULL; +} diff --git a/gdb/armobsd-tdep.c b/gdb/armobsd-tdep.c new file mode 100644 index 00000000000..cc769b70486 --- /dev/null +++ b/gdb/armobsd-tdep.c @@ -0,0 +1,120 @@ +/* Target-dependent code for OpenBSD/arm. + + Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "defs.h" +#include "osabi.h" +#include "trad-frame.h" +#include "tramp-frame.h" + +#include "gdb_string.h" + +#include "obsd-tdep.h" +#include "arm-tdep.h" +#include "solib-svr4.h" + +/* Signal trampolines. */ + +static void +armobsd_sigframe_init (const struct tramp_frame *self, + struct frame_info *next_frame, + struct trad_frame_cache *cache, + CORE_ADDR func) +{ + CORE_ADDR sp, sigcontext_addr, addr; + int regnum; + + /* We find the appropriate instance of `struct sigcontext' at a + fixed offset in the signal frame. */ + sp = frame_unwind_register_signed (next_frame, ARM_SP_REGNUM); + sigcontext_addr = sp + 16; + + /* PC. */ + trad_frame_set_reg_addr (cache, ARM_PC_REGNUM, sigcontext_addr + 76); + + /* GPRs. */ + for (regnum = ARM_A1_REGNUM, addr = sigcontext_addr + 12; + regnum <= ARM_LR_REGNUM; regnum++, addr += 4) + trad_frame_set_reg_addr (cache, regnum, addr); + + trad_frame_set_id (cache, frame_id_build (sp, func)); +} + +static const struct tramp_frame armobsd_sigframe = +{ + SIGTRAMP_FRAME, + 4, + { + { 0xe28d0010, -1 }, /* add r0, sp, #16 */ + { 0xef000067, -1 }, /* swi SYS_sigreturn */ + { 0xef000001, -1 }, /* swi SYS_exit */ + { 0xeafffffc, -1 }, /* b . - 8 */ + { TRAMP_SENTINEL_INSN, -1 } + }, + armobsd_sigframe_init +}; + + +static void +armobsd_init_abi (struct gdbarch_info info, + struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + if (tdep->fp_model == ARM_FLOAT_AUTO) + tdep->fp_model = ARM_FLOAT_SOFT_VFP; + + tramp_frame_prepend_unwinder (gdbarch, &armobsd_sigframe); + + /* OpenBSD/arm uses SVR4-style shared libraries. */ + set_solib_svr4_fetch_link_map_offsets + (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_gdbarch_skip_solib_resolver (gdbarch, obsd_skip_solib_resolver); + + tdep->jb_pc = 24; + tdep->jb_elt_size = 4; + + set_gdbarch_regset_from_core_section + (gdbarch, armbsd_regset_from_core_section); + + /* OpenBSD/arm uses -fpcc-struct-return by default. */ + tdep->struct_return = pcc_struct_return; +} + + +static enum gdb_osabi +armobsd_core_osabi_sniffer (bfd *abfd) +{ + if (strcmp (bfd_get_target (abfd), "netbsd-core") == 0) + return GDB_OSABI_OPENBSD_ELF; + + return GDB_OSABI_UNKNOWN; +} + +void +_initialize_armobsd_tdep (void) +{ + /* BFD doesn't set a flavour for NetBSD style a.out core files. */ + gdbarch_register_osabi_sniffer (bfd_arch_arm, bfd_target_unknown_flavour, + armobsd_core_osabi_sniffer); + + gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_OPENBSD_ELF, + armobsd_init_abi); +} diff --git a/gdb/config/alpha/obsd.mt b/gdb/config/alpha/obsd.mt new file mode 100644 index 00000000000..093d50c875a --- /dev/null +++ b/gdb/config/alpha/obsd.mt @@ -0,0 +1,4 @@ +# Target: OpenBSD/alpha +TDEPFILES= alpha-tdep.o alpha-mdebug-tdep.o alphabsd-tdep.o \ + alphanbsd-tdep.o alphaobsd-tdep.o nbsd-tdep.o obsd-tdep.o \ + corelow.o solib.o solib-svr4.o diff --git a/gdb/config/arm/obsd.mt b/gdb/config/arm/obsd.mt new file mode 100644 index 00000000000..fcb1a761d55 --- /dev/null +++ b/gdb/config/arm/obsd.mt @@ -0,0 +1,3 @@ +# Target: OpenBSD/arm +TDEPFILES= arm-tdep.o armbsd-tdep.o armobsd-tdep.o obsd-tdep.o \ + corelow.o solib.o solib-svr4.o diff --git a/gdb/config/sparc/embed.mt b/gdb/config/sparc/embed.mt new file mode 100644 index 00000000000..5d3039c0120 --- /dev/null +++ b/gdb/config/sparc/embed.mt @@ -0,0 +1,5 @@ +# Target: SPARC embedded with simulator +TDEPFILES= sparc-tdep.o + +SIM_OBS = remote-sim.o +SIM = ../sim/erc32/libsim.a diff --git a/gdb/gdbserver/win32-i386-low.c b/gdb/gdbserver/win32-i386-low.c new file mode 100644 index 00000000000..de6ef0b8461 --- /dev/null +++ b/gdb/gdbserver/win32-i386-low.c @@ -0,0 +1,1077 @@ +/* Low level interface to Windows debugging, for gdbserver. + Copyright (C) 2006 + Free Software Foundation, Inc. + + Contributed by Leo Zayas. Based on "win32-nat.c" from GDB. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "server.h" +#include "regcache.h" +#include "gdb/signals.h" + +#include +#include +#include +#include +#include +#include + +#ifndef USE_WIN32API +#include +#endif + +#define LOG 0 + +#define OUTMSG(X) do { printf X; fflush (stdout); } while (0) +#if LOG +#define OUTMSG2(X) do { printf X; fflush (stdout); } while (0) +#else +#define OUTMSG2(X) +#endif + +int debug_threads; +int using_threads = 1; + +/* Globals. */ +static HANDLE current_process_handle = NULL; +static DWORD current_process_id = 0; +static enum target_signal last_sig = TARGET_SIGNAL_0; + +/* The current debug event from WaitForDebugEvent. */ +static DEBUG_EVENT current_event; + +static int debug_registers_changed = 0; +static int debug_registers_used = 0; +static unsigned dr[8]; + +typedef BOOL winapi_DebugActiveProcessStop (DWORD dwProcessId); +typedef BOOL winapi_DebugSetProcessKillOnExit (BOOL KillOnExit); + +#define FLAG_TRACE_BIT 0x100 +#define CONTEXT_DEBUGGER (CONTEXT_FULL | CONTEXT_FLOATING_POINT) +#define CONTEXT_DEBUGGER_DR CONTEXT_DEBUGGER | CONTEXT_DEBUG_REGISTERS \ + | CONTEXT_EXTENDED_REGISTERS + +/* Thread information structure used to track extra information about + each thread. */ +typedef struct thread_info_struct +{ + DWORD tid; + HANDLE h; + int suspend_count; + CONTEXT context; +} thread_info; +static DWORD main_thread_id = 0; + +/* Get the thread ID from the current selected inferior (the current + thread). */ +static DWORD +current_inferior_tid (void) +{ + thread_info *th = inferior_target_data (current_inferior); + return th->tid; +} + +/* Find a thread record given a thread id. If GET_CONTEXT is set then + also retrieve the context for this thread. */ +static thread_info * +thread_rec (DWORD id, int get_context) +{ + struct thread_info *thread; + thread_info *th; + + thread = (struct thread_info *) find_inferior_id (&all_threads, id); + if (thread == NULL) + return NULL; + + th = inferior_target_data (thread); + if (!th->suspend_count && get_context) + { + if (get_context > 0 && id != current_event.dwThreadId) + th->suspend_count = SuspendThread (th->h) + 1; + else if (get_context < 0) + th->suspend_count = -1; + + th->context.ContextFlags = CONTEXT_DEBUGGER_DR; + + GetThreadContext (th->h, &th->context); + + if (id == current_event.dwThreadId) + { + /* Copy dr values from that thread. */ + dr[0] = th->context.Dr0; + dr[1] = th->context.Dr1; + dr[2] = th->context.Dr2; + dr[3] = th->context.Dr3; + dr[6] = th->context.Dr6; + dr[7] = th->context.Dr7; + } + } + + return th; +} + +/* Add a thread to the thread list. */ +static thread_info * +child_add_thread (DWORD tid, HANDLE h) +{ + thread_info *th; + + if ((th = thread_rec (tid, FALSE))) + return th; + + th = (thread_info *) malloc (sizeof (*th)); + memset (th, 0, sizeof (*th)); + th->tid = tid; + th->h = h; + + add_thread (tid, th, (unsigned int) tid); + set_inferior_regcache_data ((struct thread_info *) + find_inferior_id (&all_threads, tid), + new_register_cache ()); + + /* Set the debug registers for the new thread if they are used. */ + if (debug_registers_used) + { + /* Only change the value of the debug registers. */ + th->context.ContextFlags = CONTEXT_DEBUGGER_DR; + + GetThreadContext (th->h, &th->context); + + th->context.Dr0 = dr[0]; + th->context.Dr1 = dr[1]; + th->context.Dr2 = dr[2]; + th->context.Dr3 = dr[3]; + /* th->context.Dr6 = dr[6]; + FIXME: should we set dr6 also ?? */ + th->context.Dr7 = dr[7]; + SetThreadContext (th->h, &th->context); + th->context.ContextFlags = 0; + } + + return th; +} + +/* Delete a thread from the list of threads. */ +static void +delete_thread_info (struct inferior_list_entry *thread) +{ + thread_info *th = inferior_target_data ((struct thread_info *) thread); + + remove_thread ((struct thread_info *) thread); + CloseHandle (th->h); + free (th); +} + +/* Delete a thread from the list of threads. */ +static void +child_delete_thread (DWORD id) +{ + struct inferior_list_entry *thread; + + /* If the last thread is exiting, just return. */ + if (all_threads.head == all_threads.tail) + return; + + thread = find_inferior_id (&all_threads, id); + if (thread == NULL) + return; + + delete_thread_info (thread); +} + +/* Transfer memory from/to the debugged process. */ +static int +child_xfer_memory (CORE_ADDR memaddr, char *our, int len, + int write, struct target_ops *target) +{ + SIZE_T done; + long addr = (long) memaddr; + + if (write) + { + WriteProcessMemory (current_process_handle, (LPVOID) addr, + (LPCVOID) our, len, &done); + FlushInstructionCache (current_process_handle, (LPCVOID) addr, len); + } + else + { + ReadProcessMemory (current_process_handle, (LPCVOID) addr, (LPVOID) our, + len, &done); + } + return done; +} + +/* Generally, what has the program done? */ +enum target_waitkind +{ + /* The program has exited. The exit status is in value.integer. */ + TARGET_WAITKIND_EXITED, + + /* The program has stopped with a signal. Which signal is in + value.sig. */ + TARGET_WAITKIND_STOPPED, + + /* The program is letting us know that it dynamically loaded something + (e.g. it called load(2) on AIX). */ + TARGET_WAITKIND_LOADED, + + /* The program has exec'ed a new executable file. The new file's + pathname is pointed to by value.execd_pathname. */ + + TARGET_WAITKIND_EXECD, + + /* Nothing happened, but we stopped anyway. This perhaps should be handled + within target_wait, but I'm not sure target_wait should be resuming the + inferior. */ + TARGET_WAITKIND_SPURIOUS, +}; + +struct target_waitstatus +{ + enum target_waitkind kind; + + /* Forked child pid, execd pathname, exit status or signal number. */ + union + { + int integer; + enum target_signal sig; + int related_pid; + char *execd_pathname; + int syscall_id; + } + value; +}; + +#define NUM_REGS 41 +#define FCS_REGNUM 27 +#define FOP_REGNUM 31 + +#define context_offset(x) ((int)&(((CONTEXT *)NULL)->x)) +static const int mappings[] = { + context_offset (Eax), + context_offset (Ecx), + context_offset (Edx), + context_offset (Ebx), + context_offset (Esp), + context_offset (Ebp), + context_offset (Esi), + context_offset (Edi), + context_offset (Eip), + context_offset (EFlags), + context_offset (SegCs), + context_offset (SegSs), + context_offset (SegDs), + context_offset (SegEs), + context_offset (SegFs), + context_offset (SegGs), + context_offset (FloatSave.RegisterArea[0 * 10]), + context_offset (FloatSave.RegisterArea[1 * 10]), + context_offset (FloatSave.RegisterArea[2 * 10]), + context_offset (FloatSave.RegisterArea[3 * 10]), + context_offset (FloatSave.RegisterArea[4 * 10]), + context_offset (FloatSave.RegisterArea[5 * 10]), + context_offset (FloatSave.RegisterArea[6 * 10]), + context_offset (FloatSave.RegisterArea[7 * 10]), + context_offset (FloatSave.ControlWord), + context_offset (FloatSave.StatusWord), + context_offset (FloatSave.TagWord), + context_offset (FloatSave.ErrorSelector), + context_offset (FloatSave.ErrorOffset), + context_offset (FloatSave.DataSelector), + context_offset (FloatSave.DataOffset), + context_offset (FloatSave.ErrorSelector), + /* XMM0-7 */ + context_offset (ExtendedRegisters[10 * 16]), + context_offset (ExtendedRegisters[11 * 16]), + context_offset (ExtendedRegisters[12 * 16]), + context_offset (ExtendedRegisters[13 * 16]), + context_offset (ExtendedRegisters[14 * 16]), + context_offset (ExtendedRegisters[15 * 16]), + context_offset (ExtendedRegisters[16 * 16]), + context_offset (ExtendedRegisters[17 * 16]), + /* MXCSR */ + context_offset (ExtendedRegisters[24]) +}; + +#undef context_offset + +/* Clear out any old thread list and reintialize it to a pristine + state. */ +static void +child_init_thread_list (void) +{ + for_each_inferior (&all_threads, delete_thread_info); +} + +static void +do_initial_child_stuff (DWORD pid) +{ + int i; + + last_sig = TARGET_SIGNAL_0; + + debug_registers_changed = 0; + debug_registers_used = 0; + for (i = 0; i < sizeof (dr) / sizeof (dr[0]); i++) + dr[i] = 0; + memset (¤t_event, 0, sizeof (current_event)); + + child_init_thread_list (); +} + +/* Resume all artificially suspended threads if we are continuing + execution. */ +static int +continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr) +{ + struct thread_info *thread = (struct thread_info *) this_thread; + int thread_id = * (int *) id_ptr; + thread_info *th = inferior_target_data (thread); + int i; + + if ((thread_id == -1 || thread_id == th->tid) + && th->suspend_count) + { + for (i = 0; i < th->suspend_count; i++) + (void) ResumeThread (th->h); + th->suspend_count = 0; + if (debug_registers_changed) + { + /* Only change the value of the debug registers. */ + th->context.ContextFlags = CONTEXT_DEBUG_REGISTERS; + th->context.Dr0 = dr[0]; + th->context.Dr1 = dr[1]; + th->context.Dr2 = dr[2]; + th->context.Dr3 = dr[3]; + /* th->context.Dr6 = dr[6]; + FIXME: should we set dr6 also ?? */ + th->context.Dr7 = dr[7]; + SetThreadContext (th->h, &th->context); + th->context.ContextFlags = 0; + } + } + + return 0; +} + +static BOOL +child_continue (DWORD continue_status, int thread_id) +{ + BOOL res; + + res = ContinueDebugEvent (current_event.dwProcessId, + current_event.dwThreadId, continue_status); + continue_status = 0; + if (res) + find_inferior (&all_threads, continue_one_thread, &thread_id); + + debug_registers_changed = 0; + return res; +} + +/* Fetch register(s) from gdbserver regcache data. */ +static void +do_child_fetch_inferior_registers (thread_info *th, int r) +{ + char *context_offset = ((char *) &th->context) + mappings[r]; + long l; + if (r == FCS_REGNUM) + { + l = *((long *) context_offset) & 0xffff; + supply_register (r, (char *) &l); + } + else if (r == FOP_REGNUM) + { + l = (*((long *) context_offset) >> 16) & ((1 << 11) - 1); + supply_register (r, (char *) &l); + } + else + supply_register (r, context_offset); +} + +/* Fetch register(s) from the current thread context. */ +static void +child_fetch_inferior_registers (int r) +{ + int regno; + thread_info *th = thread_rec (current_inferior_tid (), TRUE); + if (r == -1 || r == 0 || r > NUM_REGS) + child_fetch_inferior_registers (NUM_REGS); + else + for (regno = 0; regno < r; regno++) + do_child_fetch_inferior_registers (th, regno); +} + +/* Get register from gdbserver regcache data. */ +static void +do_child_store_inferior_registers (thread_info *th, int r) +{ + collect_register (r, ((char *) &th->context) + mappings[r]); +} + +/* Store a new register value into the current thread context. We don't + change the program's context until later, when we resume it. */ +static void +child_store_inferior_registers (int r) +{ + int regno; + thread_info *th = thread_rec (current_inferior_tid (), TRUE); + if (r == -1 || r == 0 || r > NUM_REGS) + child_store_inferior_registers (NUM_REGS); + else + for (regno = 0; regno < r; regno++) + do_child_store_inferior_registers (th, regno); +} + +/* Start a new process. + PROGRAM is a path to the program to execute. + ARGS is a standard NULL-terminated array of arguments, + to be passed to the inferior as ``argv''. + Returns the new PID on success, -1 on failure. Registers the new + process with the process list. */ +static int +win32_create_inferior (char *program, char **program_args) +{ +#ifndef USE_WIN32API + char real_path[MAXPATHLEN]; + char *orig_path, *new_path, *path_ptr; +#endif + char *winenv = NULL; + STARTUPINFO si; + PROCESS_INFORMATION pi; + BOOL ret; + DWORD flags; + char *args; + int argslen; + int argc; + + if (!program) + error ("No executable specified, specify executable to debug.\n"); + + memset (&si, 0, sizeof (si)); + si.cb = sizeof (si); + + flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; + +#ifndef USE_WIN32API + orig_path = NULL; + path_ptr = getenv ("PATH"); + if (path_ptr) + { + orig_path = alloca (strlen (path_ptr) + 1); + new_path = alloca (cygwin_posix_to_win32_path_list_buf_size (path_ptr)); + strcpy (orig_path, path_ptr); + cygwin_posix_to_win32_path_list (path_ptr, new_path); + setenv ("PATH", new_path, 1); + } + cygwin_conv_to_win32_path (program, real_path); + program = real_path; +#endif + + argslen = strlen (program) + 1; + for (argc = 1; program_args[argc]; argc++) + argslen += strlen (program_args[argc]) + 1; + args = alloca (argslen); + strcpy (args, program); + for (argc = 1; program_args[argc]; argc++) + { + /* FIXME: Can we do better about quoting? How does Cygwin + handle this? */ + strcat (args, " "); + strcat (args, program_args[argc]); + } + OUTMSG2 (("Command line is %s\n", args)); + + flags |= CREATE_NEW_PROCESS_GROUP; + + ret = CreateProcess (0, args, /* command line */ + NULL, /* Security */ + NULL, /* thread */ + TRUE, /* inherit handles */ + flags, /* start flags */ + winenv, NULL, /* current directory */ + &si, &pi); + +#ifndef USE_WIN32API + if (orig_path) + setenv ("PATH", orig_path, 1); +#endif + + if (!ret) + { + error ("Error creating process %s, (error %d): %s\n", args, + (int) GetLastError (), strerror (GetLastError ())); + } + else + { + OUTMSG2 (("Process created: %s\n", (char *) args)); + } + + CloseHandle (pi.hThread); + + current_process_handle = pi.hProcess; + current_process_id = pi.dwProcessId; + + do_initial_child_stuff (current_process_id); + + return current_process_id; +} + +/* Attach to a running process. + PID is the process ID to attach to, specified by the user + or a higher layer. */ +static int +win32_attach (unsigned long pid) +{ + int res = 0; + HMODULE kernel32 = LoadLibrary ("KERNEL32.DLL"); + winapi_DebugActiveProcessStop *DebugActiveProcessStop = NULL; + winapi_DebugSetProcessKillOnExit *DebugSetProcessKillOnExit = NULL; + + DebugActiveProcessStop = + (winapi_DebugActiveProcessStop *) GetProcAddress (kernel32, + "DebugActiveProcessStop"); + DebugSetProcessKillOnExit = + (winapi_DebugSetProcessKillOnExit *) GetProcAddress (kernel32, + "DebugSetProcessKillOnExit"); + + res = DebugActiveProcess (pid) ? 1 : 0; + + if (!res) + error ("Attach to process failed."); + + if (DebugSetProcessKillOnExit != NULL) + DebugSetProcessKillOnExit (FALSE); + + current_process_id = pid; + current_process_handle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); + + if (current_process_handle == NULL) + { + res = 0; + if (DebugActiveProcessStop != NULL) + DebugActiveProcessStop (current_process_id); + } + + if (res) + do_initial_child_stuff (pid); + + FreeLibrary (kernel32); + + return res; +} + +/* Kill all inferiors. */ +static void +win32_kill (void) +{ + TerminateProcess (current_process_handle, 0); + for (;;) + { + if (!child_continue (DBG_CONTINUE, -1)) + break; + if (!WaitForDebugEvent (¤t_event, INFINITE)) + break; + if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) + break; + } +} + +/* Detach from all inferiors. */ +static void +win32_detach (void) +{ + HMODULE kernel32 = LoadLibrary ("KERNEL32.DLL"); + winapi_DebugActiveProcessStop *DebugActiveProcessStop = NULL; + winapi_DebugSetProcessKillOnExit *DebugSetProcessKillOnExit = NULL; + + DebugActiveProcessStop = + (winapi_DebugActiveProcessStop *) GetProcAddress (kernel32, + "DebugActiveProcessStop"); + DebugSetProcessKillOnExit = + (winapi_DebugSetProcessKillOnExit *) GetProcAddress (kernel32, + "DebugSetProcessKillOnExit"); + + if (DebugSetProcessKillOnExit != NULL) + DebugSetProcessKillOnExit (FALSE); + + if (DebugActiveProcessStop != NULL) + DebugActiveProcessStop (current_process_id); + else + win32_kill (); + + FreeLibrary (kernel32); +} + +/* Return 1 iff the thread with thread ID TID is alive. */ +static int +win32_thread_alive (unsigned long tid) +{ + int res; + + /* Our thread list is reliable; don't bother to poll target + threads. */ + if (find_inferior_id (&all_threads, tid) != NULL) + res = 1; + else + res = 0; + return res; +} + +/* Resume the inferior process. RESUME_INFO describes how we want + to resume. */ +static void +win32_resume (struct thread_resume *resume_info) +{ + DWORD tid; + enum target_signal sig; + int step; + thread_info *th; + DWORD continue_status = DBG_CONTINUE; + + /* This handles the very limited set of resume packets that GDB can + currently produce. */ + + if (resume_info[0].thread == -1) + tid = -1; + else if (resume_info[1].thread == -1 && !resume_info[1].leave_stopped) + tid = -1; + else + /* Yes, we're ignoring resume_info[0].thread. It'd be tricky to make + the Windows resume code do the right thing for thread switching. */ + tid = current_event.dwThreadId; + + if (resume_info[0].thread != -1) + { + sig = resume_info[0].sig; + step = resume_info[0].step; + } + else + { + sig = 0; + step = 0; + } + + if (sig != TARGET_SIGNAL_0) + { + if (current_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) + { + OUTMSG (("Cannot continue with signal %d here.\n", sig)); + } + else if (sig == last_sig) + continue_status = DBG_EXCEPTION_NOT_HANDLED; + else + OUTMSG (("Can only continue with recieved signal %d.\n", last_sig)); + } + + last_sig = TARGET_SIGNAL_0; + + /* Get context for the currently selected thread. */ + th = thread_rec (current_event.dwThreadId, FALSE); + if (th) + { + if (th->context.ContextFlags) + { + if (debug_registers_changed) + { + th->context.Dr0 = dr[0]; + th->context.Dr1 = dr[1]; + th->context.Dr2 = dr[2]; + th->context.Dr3 = dr[3]; + /* th->context.Dr6 = dr[6]; + FIXME: should we set dr6 also ?? */ + th->context.Dr7 = dr[7]; + } + + /* Move register values from the inferior into the thread + context structure. */ + regcache_invalidate (); + + if (step) + th->context.EFlags |= FLAG_TRACE_BIT; + + SetThreadContext (th->h, &th->context); + th->context.ContextFlags = 0; + } + } + + /* Allow continuing with the same signal that interrupted us. + Otherwise complain. */ + + child_continue (continue_status, tid); +} + +static int +handle_exception (struct target_waitstatus *ourstatus) +{ + thread_info *th; + DWORD code = current_event.u.Exception.ExceptionRecord.ExceptionCode; + + ourstatus->kind = TARGET_WAITKIND_STOPPED; + + /* Record the context of the current thread. */ + th = thread_rec (current_event.dwThreadId, -1); + + switch (code) + { + case EXCEPTION_ACCESS_VIOLATION: + OUTMSG2 (("EXCEPTION_ACCESS_VIOLATION")); + ourstatus->value.sig = TARGET_SIGNAL_SEGV; + break; + case STATUS_STACK_OVERFLOW: + OUTMSG2 (("STATUS_STACK_OVERFLOW")); + ourstatus->value.sig = TARGET_SIGNAL_SEGV; + break; + case STATUS_FLOAT_DENORMAL_OPERAND: + OUTMSG2 (("STATUS_FLOAT_DENORMAL_OPERAND")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + OUTMSG2 (("EXCEPTION_ARRAY_BOUNDS_EXCEEDED")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_FLOAT_INEXACT_RESULT: + OUTMSG2 (("STATUS_FLOAT_INEXACT_RESULT")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_FLOAT_INVALID_OPERATION: + OUTMSG2 (("STATUS_FLOAT_INVALID_OPERATION")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_FLOAT_OVERFLOW: + OUTMSG2 (("STATUS_FLOAT_OVERFLOW")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_FLOAT_STACK_CHECK: + OUTMSG2 (("STATUS_FLOAT_STACK_CHECK")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_FLOAT_UNDERFLOW: + OUTMSG2 (("STATUS_FLOAT_UNDERFLOW")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_FLOAT_DIVIDE_BY_ZERO: + OUTMSG2 (("STATUS_FLOAT_DIVIDE_BY_ZERO")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_INTEGER_DIVIDE_BY_ZERO: + OUTMSG2 (("STATUS_INTEGER_DIVIDE_BY_ZERO")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_INTEGER_OVERFLOW: + OUTMSG2 (("STATUS_INTEGER_OVERFLOW")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case EXCEPTION_BREAKPOINT: + OUTMSG2 (("EXCEPTION_BREAKPOINT")); + ourstatus->value.sig = TARGET_SIGNAL_TRAP; + break; + case DBG_CONTROL_C: + OUTMSG2 (("DBG_CONTROL_C")); + ourstatus->value.sig = TARGET_SIGNAL_INT; + break; + case DBG_CONTROL_BREAK: + OUTMSG2 (("DBG_CONTROL_BREAK")); + ourstatus->value.sig = TARGET_SIGNAL_INT; + break; + case EXCEPTION_SINGLE_STEP: + OUTMSG2 (("EXCEPTION_SINGLE_STEP")); + ourstatus->value.sig = TARGET_SIGNAL_TRAP; + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + OUTMSG2 (("EXCEPTION_ILLEGAL_INSTRUCTION")); + ourstatus->value.sig = TARGET_SIGNAL_ILL; + break; + case EXCEPTION_PRIV_INSTRUCTION: + OUTMSG2 (("EXCEPTION_PRIV_INSTRUCTION")); + ourstatus->value.sig = TARGET_SIGNAL_ILL; + break; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + OUTMSG2 (("EXCEPTION_NONCONTINUABLE_EXCEPTION")); + ourstatus->value.sig = TARGET_SIGNAL_ILL; + break; + default: + if (current_event.u.Exception.dwFirstChance) + return 0; + OUTMSG2 (("gdbserver: unknown target exception 0x%08lx at 0x%08lx", + current_event.u.Exception.ExceptionRecord.ExceptionCode, + (DWORD) current_event.u.Exception.ExceptionRecord. + ExceptionAddress)); + ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; + break; + } + OUTMSG2 (("\n")); + last_sig = ourstatus->value.sig; + return 1; +} + +/* Get the next event from the child. Return 1 if the event requires + handling. */ +static int +get_child_debug_event (struct target_waitstatus *ourstatus) +{ + BOOL debug_event; + DWORD continue_status, event_code; + thread_info *th = NULL; + static thread_info dummy_thread_info; + int retval = 0; + +in: + + last_sig = TARGET_SIGNAL_0; + ourstatus->kind = TARGET_WAITKIND_SPURIOUS; + + if (!(debug_event = WaitForDebugEvent (¤t_event, 1000))) + goto out; + + current_inferior = + (struct thread_info *) find_inferior_id (&all_threads, + current_event.dwThreadId); + + continue_status = DBG_CONTINUE; + event_code = current_event.dwDebugEventCode; + + switch (event_code) + { + case CREATE_THREAD_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event CREATE_THREAD_DEBUG_EVENT " + "for pid=%d tid=%x)\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + + /* Record the existence of this thread. */ + th = child_add_thread (current_event.dwThreadId, + current_event.u.CreateThread.hThread); + + retval = current_event.dwThreadId; + break; + + case EXIT_THREAD_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event EXIT_THREAD_DEBUG_EVENT " + "for pid=%d tid=%x\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + child_delete_thread (current_event.dwThreadId); + th = &dummy_thread_info; + break; + + case CREATE_PROCESS_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT " + "for pid=%d tid=%x\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + CloseHandle (current_event.u.CreateProcessInfo.hFile); + + current_process_handle = current_event.u.CreateProcessInfo.hProcess; + main_thread_id = current_event.dwThreadId; + + ourstatus->kind = TARGET_WAITKIND_EXECD; + ourstatus->value.execd_pathname = "Main executable"; + + /* Add the main thread. */ + th = + child_add_thread (main_thread_id, + current_event.u.CreateProcessInfo.hThread); + + retval = ourstatus->value.related_pid = current_event.dwThreadId; + break; + + case EXIT_PROCESS_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event EXIT_PROCESS_DEBUG_EVENT " + "for pid=%d tid=%x\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + ourstatus->kind = TARGET_WAITKIND_EXITED; + ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode; + CloseHandle (current_process_handle); + retval = main_thread_id; + break; + + case LOAD_DLL_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event LOAD_DLL_DEBUG_EVENT " + "for pid=%d tid=%x\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + CloseHandle (current_event.u.LoadDll.hFile); + + ourstatus->kind = TARGET_WAITKIND_LOADED; + ourstatus->value.integer = 0; + retval = main_thread_id; + break; + + case UNLOAD_DLL_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event UNLOAD_DLL_DEBUG_EVENT " + "for pid=%d tid=%x\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + break; + + case EXCEPTION_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event EXCEPTION_DEBUG_EVENT " + "for pid=%d tid=%x\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + retval = handle_exception (ourstatus); + break; + + case OUTPUT_DEBUG_STRING_EVENT: + /* A message from the kernel (or Cygwin). */ + OUTMSG2 (("gdbserver: kernel event OUTPUT_DEBUG_STRING_EVENT " + "for pid=%d tid=%x\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + break; + + default: + OUTMSG2 (("gdbserver: kernel event unknown " + "for pid=%d tid=%x code=%ld\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId, + current_event.dwDebugEventCode)); + break; + } + + current_inferior = + (struct thread_info *) find_inferior_id (&all_threads, + current_event.dwThreadId); + + if (!retval || (event_code != EXCEPTION_DEBUG_EVENT && event_code != EXIT_PROCESS_DEBUG_EVENT)) + { + child_continue (continue_status, -1); + goto in; + } + + if (th == NULL) + thread_rec (current_event.dwThreadId, TRUE); + +out: + return retval; +} + +/* Wait for the inferior process to change state. + STATUS will be filled in with a response code to send to GDB. + Returns the signal which caused the process to stop. */ +static unsigned char +win32_wait (char *status) +{ + struct target_waitstatus our_status; + + *status = 'T'; + + while (1) + { + get_child_debug_event (&our_status); + + if (our_status.kind == TARGET_WAITKIND_EXITED) + { + OUTMSG2 (("Child exited with retcode = %x\n", + our_status.value.integer)); + + *status = 'W'; + + child_fetch_inferior_registers (-1); + + return our_status.value.integer; + } + else if (our_status.kind == TARGET_WAITKIND_STOPPED) + { + OUTMSG2 (("Child Stopped with signal = %x \n", + WSTOPSIG (our_status.value.sig))); + + *status = 'T'; + + child_fetch_inferior_registers (-1); + + return our_status.value.sig; + } + else + OUTMSG (("Ignoring unknown internal event, %d\n", our_status.kind)); + + { + struct thread_resume resume; + resume.thread = -1; + resume.step = 0; + resume.sig = 0; + resume.leave_stopped = 0; + win32_resume (&resume); + } + } +} + +/* Fetch registers from the inferior process. + If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */ +static void +win32_fetch_inferior_registers (int regno) +{ + child_fetch_inferior_registers (regno); +} + +/* Store registers to the inferior process. + If REGNO is -1, store all registers; otherwise, store at least REGNO. */ +static void +win32_store_inferior_registers (int regno) +{ + child_store_inferior_registers (regno); +} + +/* Read memory from the inferior process. This should generally be + called through read_inferior_memory, which handles breakpoint shadowing. + Read LEN bytes at MEMADDR into a buffer at MYADDR. */ +static int +win32_read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) +{ + return child_xfer_memory (memaddr, myaddr, len, 0, 0) != len; +} + +/* Write memory to the inferior process. This should generally be + called through write_inferior_memory, which handles breakpoint shadowing. + Write LEN bytes from the buffer at MYADDR to MEMADDR. + Returns 0 on success and errno on failure. */ +static int +win32_write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr, + int len) +{ + return child_xfer_memory (memaddr, (char *) myaddr, len, 1, 0) != len; +} + +static struct target_ops win32_target_ops = { + win32_create_inferior, + win32_attach, + win32_kill, + win32_detach, + win32_thread_alive, + win32_resume, + win32_wait, + win32_fetch_inferior_registers, + win32_store_inferior_registers, + win32_read_inferior_memory, + win32_write_inferior_memory, + 0, + 0 +}; + +/* Initialize the Win32 backend. */ +void +initialize_low (void) +{ + set_target_ops (&win32_target_ops); + + init_registers (); +} diff --git a/gdb/regformats/reg-x86-64-linux.dat b/gdb/regformats/reg-x86-64-linux.dat new file mode 100644 index 00000000000..47d324a986f --- /dev/null +++ b/gdb/regformats/reg-x86-64-linux.dat @@ -0,0 +1,60 @@ +name:x86_64_linux +expedite:rbp,rsp,rip +64:rax +64:rbx +64:rcx +64:rdx +64:rsi +64:rdi +64:rbp +64:rsp +64:r8 +64:r9 +64:r10 +64:r11 +64:r12 +64:r13 +64:r14 +64:r15 +64:rip +32:eflags +32:cs +32:ss +32:ds +32:es +32:fs +32:gs +80:st0 +80:st1 +80:st2 +80:st3 +80:st4 +80:st5 +80:st6 +80:st7 +32:fctrl +32:fstat +32:ftag +32:fiseg +32:fioff +32:foseg +32:fooff +32:fop +128:xmm0 +128:xmm1 +128:xmm2 +128:xmm3 +128:xmm4 +128:xmm5 +128:xmm6 +128:xmm7 +128:xmm8 +128:xmm9 +128:xmm10 +128:xmm11 +128:xmm12 +128:xmm13 +128:xmm14 +128:xmm15 +32:mxcsr +64:orig_rax diff --git a/gdb/testsuite/gdb.arch/i386-size.c b/gdb/testsuite/gdb.arch/i386-size.c new file mode 100644 index 00000000000..80357ddbfa7 --- /dev/null +++ b/gdb/testsuite/gdb.arch/i386-size.c @@ -0,0 +1,50 @@ +/* Symbol size test program. + + Copyright 2006 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. */ + +#ifdef SYMBOL_PREFIX +#define SYMBOL(str) SYMBOL_PREFIX #str +#else +#define SYMBOL(str) #str +#endif + +void +trap (void) +{ + asm ("int $0x03"); +} + +/* Jump from a function with its symbol size set, to a function + named by a local label. If GDB does not look at the sizes of + symbols, we will still appear to be in the first function. */ + +asm(".text\n" + " .align 8\n" + " .globl " SYMBOL (main) "\n" + SYMBOL (main) ":\n" + " pushl %ebp\n" + " mov %esp, %ebp\n" + " call .Lfunc\n" + " ret\n" + " .size " SYMBOL (main) ", .-" SYMBOL (main) "\n" + ".Lfunc:\n" + " pushl %ebp\n" + " mov %esp, %ebp\n" + " call " SYMBOL (trap) "\n"); diff --git a/gdb/testsuite/gdb.arch/i386-size.exp b/gdb/testsuite/gdb.arch/i386-size.exp new file mode 100644 index 00000000000..dcf67e995e7 --- /dev/null +++ b/gdb/testsuite/gdb.arch/i386-size.exp @@ -0,0 +1,89 @@ +# Copyright 2006 Free Software Foundation, Inc. + +# 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. + +# Please email any bugs, comments, and/or additions to this file to: +# bug-gdb@gnu.org + +# This file is part of the gdb testsuite. + +if $tracelevel { + strace $tracelevel +} + +# Test that GDB can see the sizes of symbols. + +if ![istarget "i?86-*-*"] then { + verbose "Skipping i386 unwinder tests." + return +} + +set testfile "i386-size" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +# some targets have leading underscores on assembly symbols. +# TODO: detect this automatically +set additional_flags "" +if [istarget "i?86-*-cygwin*"] then { + set additional_flags "additional_flags=-DSYMBOL_PREFIX=\"_\"" +} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ + executable [list debug $additional_flags]] != "" } { + untested "i386-size" + return -1 +} + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +# We use gdb_run_cmd so this stands a chance to work for remote +# targets too. +gdb_run_cmd + +gdb_expect { + -re "Program received signal SIGTRAP.*$gdb_prompt $" { + pass "run past main" + } + -re ".*$gdb_prompt $" { + fail "run past main" + } + timeout { + fail "run past main (timeout)" + } +} + +set message "backtrace shows no function" +gdb_test_multiple "backtrace 10" $message { + -re "#1\[ \t]*$hex in main.*$gdb_prompt $" { + fail $message + } + -re "#1\[ \t]*$hex in \\?\\? \\(\\).*$gdb_prompt $" { + pass $message + } +} + +set message "disassemble stops at end of main" +gdb_test_multiple "disassemble main" $message { + -re "call.*.*$gdb_prompt $" { + fail $message + } + -re ":\[ \t\]+ret\[ \t\r\n\]+End of.*$gdb_prompt $" { + pass $message + } +} diff --git a/gdb/testsuite/gdb.base/ifelse.exp b/gdb/testsuite/gdb.base/ifelse.exp new file mode 100644 index 00000000000..1ad6520fb03 --- /dev/null +++ b/gdb/testsuite/gdb.base/ifelse.exp @@ -0,0 +1,105 @@ +# Copyright 2006 Free Software Foundation, Inc. + +# 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. + +# This test checks that the if .. else .. end construct works and may +# contain empty bodies without crashing. + +if $tracelevel then { + strace $tracelevel +} + +gdb_exit +gdb_start + +# First test that the if command works with an empty body +# Test with different conditions because the body is ignored +# if it is not executed. + +# with true condition +set message "if 1 with empty body" +gdb_test_multiple "if 1\nend" $message { + -re "$gdb_prompt $" {pass $message} + eof { + fail "$message (crashed)" + gdb_exit + gdb_start + } +} + +# with false condition +set message "if 0 with empty body" +gdb_test_multiple "if 0\nend" $message { + -re "$gdb_prompt $" {pass $message} + eof { + fail "$message (crashed)" + gdb_exit + gdb_start + } +} + +# Second, do the same tests with an empty else body. +# This fails in GDB <=6.5 + +# Unfortunately it was an uninitialised memory problem so +# sometimes it just works. Preceed it with an if else end with +# bodies and hopefully the memory with be dirty and the problem +# will show itself (this works at time of writing). + +gdb_test "if 1\necho true\\n\nelse\necho false\\n\nend" "true" "" + +# with true condition +set message "if 1 .. else with empty body" +gdb_test_multiple "if 1\nelse\nend" $message { + -re "$gdb_prompt $" {pass $message} + eof { + fail "$message (crashed)" + gdb_exit + gdb_start + } +} + +# dirty memory +gdb_test "if 1\necho true\\n\nelse\necho false\\n\nend" "true" "" + +# with false condition +set message "if 0 .. else with empty body" +gdb_test_multiple "if 0\nelse\nend" $message { + -re "$gdb_prompt $" {pass $message} + eof { + fail "$message (crashed)" + gdb_exit + gdb_start + } +} + +gdb_test "set confirm off" "" "" + +# Test that a define with an empty else can be replaced. +# If there is memory corruption then free will fail. +# dirty memory +gdb_test "if 1\necho true\\n\nelse\necho false\\n\nend" "true" "" +# create +gdb_test "define abc\nif 1\nelse\nend\nend" "" "create define with empty else" +# call (note that condition is 1 so should pass) +gdb_test "abc" "" "call original define" +# replace +set message "replace define with if .. else with empty body" +gdb_test_multiple "define abc\necho got here\\n\nend" $message { + -re "$gdb_prompt $" {pass $message} + eof {fail "$message (crashed)"} +} +# call +gdb_test "abc" "got here" "call replacement define" diff --git a/gdb/testsuite/gdb.base/step-bt.c b/gdb/testsuite/gdb.base/step-bt.c new file mode 100644 index 00000000000..95620e1c42f --- /dev/null +++ b/gdb/testsuite/gdb.base/step-bt.c @@ -0,0 +1,36 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2006 Free Software Foundation, Inc. + + 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. + + Please email any bugs, comments, and/or additions to this file to: + bug-gdb@prep.ai.mit.edu */ + +#include + +void +hello (void) +{ + printf ("Hello world.\n"); +} + +int +main (void) +{ + hello (); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/step-bt.exp b/gdb/testsuite/gdb.base/step-bt.exp new file mode 100644 index 00000000000..63e223deb9b --- /dev/null +++ b/gdb/testsuite/gdb.base/step-bt.exp @@ -0,0 +1,76 @@ +# Copyright 2006 Free Software Foundation, Inc. + +# 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. + +# The intent of this testcase it to verify that various aliases and +# shortcuts of the "delete" command never stop working. + +if $tracelevel then { + strace $tracelevel +} + +set prms_id 0 +set bug_id 0 + +set testfile step-bt +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested "Couldn't compile test program" + return -1 +} + +# Get things started. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +gdb_test "break *hello" \ + "Breakpoint.*at.* file .*$srcfile, line .*" \ + "breakpoint at first instruction of hello()" + +gdb_run_cmd +gdb_expect { + -re ".*Breakpoint.* hello .* at .*$srcfile:.*$gdb_prompt $" { + pass "run to hello()" + } + -re ".*$gdb_prompt $" { + fail "run to hello()" + return -1 + } + timeout { + fail "run to hello() (timeout)" + return -1 + } +} + +gdb_test "stepi" \ + "" \ + "step first instruction" + +gdb_test "bt" \ + "#0 +(0x\[0-9a-z\]+ in )?hello .*#1 +(0x\[0-9a-z\]* in )?main.*" \ + "backtrace after first instruction step" + +gdb_test "stepi" \ + "" \ + "step second instruction" + +gdb_test "bt" \ + "#0 +(0x\[0-9a-z\]+ in )?hello .*#1 +(0x\[0-9a-z\]* in )?main.*" \ + "backtrace after second instruction step" + diff --git a/gdb/testsuite/gdb.base/trace-commands.exp b/gdb/testsuite/gdb.base/trace-commands.exp new file mode 100644 index 00000000000..78f35ad9e3c --- /dev/null +++ b/gdb/testsuite/gdb.base/trace-commands.exp @@ -0,0 +1,129 @@ +# Copyright 2006 Free Software Foundation, Inc. + +# 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. + +# Test that the source command's verbose mode works, the 'set trace-commands' +# command works, and that the nest depth is correct in various circumstances. + +if $tracelevel then { + strace $tracelevel +} + +gdb_exit +gdb_start + +# Create a file to source +set fd [open "tracecommandsscript" w] +puts $fd "\ +echo in tracecommandsscript\\n +define func + echo in func \$arg0\\n +end +if 1 + if 2 + if 3 + if 4 + echo deep\\n + func 999 + end + end + end +end +" +close $fd + +# Make sure that the show trace-commands exists and the default is 'off'. +gdb_test "show trace-commands" "State of GDB CLI command tracing is off\\." \ + "show trace-commands says off" + +# Source the script with verbose mode. +send_gdb "source -v tracecommandsscript\n" +gdb_expect_list "source -v" ".*$gdb_prompt $" { + {[\r\n]\+echo in tracecommandsscript\\n} + {[\r\n]\+define func} + {[\r\n]\+if 1} + {[\r\n]\+\+if 2} + {[\r\n]\+\+\+if 3} + {[\r\n]\+\+\+\+if 4} + {[\r\n]\+\+\+\+\+echo deep\\n} + {[\r\n]\+\+\+\+\+func 999} + {[\r\n]\+\+\+\+\+\+echo in func 999\\n} +} + +# Turn on command tracing. +gdb_test "set trace-commands" "" "set trace-commands" + +# Make sure show trace-commands now gives 'on'. +gdb_test "show trace-commands" \ + {\+show trace-commands[\r\n]+State of GDB CLI command tracing is on\.} \ + "show trace-commands says on" + +# Simple test +gdb_test "echo hi\\n" {\+echo hi\\n[\r\n]+hi} "simple trace-commands test" + +# Nested test +send_gdb "if 1\nset \$i = 0\nwhile \$i < 5\nfunc \$i\nset \$i += 1\nend\nend\n" +gdb_expect_list "nested trace-commands test" ".*$gdb_prompt $" { + {[\r\n]\+if 1} + {[\r\n]\+\+set \$i = 0} + {[\r\n]\+\+while \$i < 5} + {[\r\n]\+\+\+func \$i} + {[\r\n]\+\+\+\+echo in func \$i\\n} + {[\r\n]\+\+\+set \$i \+= 1} + {[\r\n]\+\+\+func \$i} + {[\r\n]\+\+\+\+echo in func \$i\\n} + {[\r\n]\+\+\+set \$i \+= 1} + {[\r\n]\+\+\+func \$i} + {[\r\n]\+\+\+\+echo in func \$i\\n} + {[\r\n]\+\+\+set \$i \+= 1} + {[\r\n]\+\+\+func \$i} + {[\r\n]\+\+\+\+echo in func \$i\\n} + {[\r\n]\+\+\+set \$i \+= 1} + {[\r\n]\+\+\+func \$i} + {[\r\n]\+\+\+\+echo in func \$i\\n} + {[\r\n]\+\+\+set \$i \+= 1} +} + +# Function with source works +send_gdb "define topfunc\nsource tracecommandsscript\nend\n" +gdb_expect_list "define user command" ".*$gdb_prompt $" { + {[\r\n]\+define topfunc} +} +send_gdb "topfunc\n" +gdb_expect_list "nested trace-commands test with source" ".*$gdb_prompt $" { + {[\r\n]\+topfunc} + {[\r\n]\+\+source tracecommandsscript} + {[\r\n]\+\+echo in tracecommandsscript\\n} + {[\r\n]\+\+define func} + {[\r\n]\+\+if 1} + {[\r\n]\+\+\+if 2} + {[\r\n]\+\+\+\+if 3} + {[\r\n]\+\+\+\+\+if 4} + {[\r\n]\+\+\+\+\+\+echo deep\\n} + {[\r\n]\+\+\+\+\+\+func 999} + {[\r\n]\+\+\+\+\+\+\+echo in func 999\\n} +} + +# Test nest depth resets properly on error +send_gdb "if 1\nif 2\nload\necho should not get here\\n\nend\nend\n" +gdb_expect_list "depth resets on error part 1" ".*$gdb_prompt $" { + {[\r\n]\+if 1} + {[\r\n]\+\+if 2} + {[\r\n]\+\+\+load} + {[\r\n]No executable file specified\.} + {[\r\n]Use the "file" or "exec-file" command\.} +} +gdb_test "echo hi\\n" {[\r\n]\+echo hi\\n[\r\n]+hi} \ + "depth resets on error part 2" diff --git a/gdb/testsuite/gdb.cp/ref-params.cc b/gdb/testsuite/gdb.cp/ref-params.cc new file mode 100644 index 00000000000..9ccfe09c1a0 --- /dev/null +++ b/gdb/testsuite/gdb.cp/ref-params.cc @@ -0,0 +1,80 @@ +/* This test script is part of GDB, the GNU debugger. + + Copyright 2006 + Free Software Foundation, Inc. + + 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. + */ + +/* Author: Paul N. Hilfinger, AdaCore Inc. */ + +struct Parent { + Parent (int id0) : id(id0) { } + int id; +}; + +struct Child : public Parent { + Child (int id0) : Parent(id0) { } +}; + +int f1(Parent& R) +{ + return R.id; /* Set breakpoint marker3 here. */ +} + +int f2(Child& C) +{ + return f1(C); /* Set breakpoint marker2 here. */ +} + +struct OtherParent { + OtherParent (int other_id0) : other_id(other_id0) { } + int other_id; +}; + +struct MultiChild : public Parent, OtherParent { + MultiChild (int id0) : Parent(id0), OtherParent(id0 * 2) { } +}; + +int mf1(OtherParent& R) +{ + return R.other_id; +} + +int mf2(MultiChild& C) +{ + return mf1(C); +} + +int main(void) +{ + Child Q(42); + Child& QR = Q; + + #ifdef usestubs + set_debug_traps(); + breakpoint(); + #endif + + /* Set breakpoint marker1 here. */ + + f2(Q); + f2(QR); + + MultiChild MQ(53); + MultiChild& MQR = MQ; + + mf2(MQ); /* Set breakpoint MQ here. */ +} diff --git a/gdb/testsuite/gdb.cp/ref-params.exp b/gdb/testsuite/gdb.cp/ref-params.exp new file mode 100644 index 00000000000..8a8eb9071d2 --- /dev/null +++ b/gdb/testsuite/gdb.cp/ref-params.exp @@ -0,0 +1,80 @@ +# Tests for reference parameters of types and their subtypes in GDB. +# Copyright 2006 Free Software Foundation, Inc. + +# 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. + +# written by Paul N. Hilfinger (Hilfinger@adacore.com) + +if $tracelevel then { + strace $tracelevel + } + +# +# test running programs +# +set prms_id 0 +set bug_id 0 + +if { [skip_cplus_tests] } { continue } + +set testfile "ref-params" +set srcfile ${testfile}.cc +set binfile ${objdir}/${subdir}/${testfile} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } { + untested ref-params.exp + return -1 +} + +gdb_exit + +proc gdb_start_again { text } { + global srcdir + global subdir + global binfile + global srcfile + + gdb_start + gdb_reinitialize_dir $srcdir/$subdir + gdb_load ${binfile} + + runto ${srcfile}:[gdb_get_line_number $text] +} + +gdb_start_again "marker1 here" +gdb_test "print Q" ".*id = 42.*" "print value of a Child in main" +gdb_test "print f1(Q)" ".* = 42.*" "print value of f1 on Child in main" +gdb_test "print f2(Q)" ".* = 42.*" "print value of f2 on Child in main" + +gdb_start_again "marker1 here" +gdb_test "print f1(QR)" ".* = 42.*" "print value of f1 on (Child&) in main" + +gdb_start_again "marker1 here" +gdb_test "print f2(QR)" ".* = 42.*" "print value of f2 on (Child&) in main" + +gdb_start_again "marker2 here" +gdb_test "print C" ".*id = 42.*" "print value of Child& in f2" +gdb_test "print f1(C)" ".* = 42.*" "print value of f1 on Child& in f2" + +gdb_start_again "marker3 here" +gdb_test "print R" ".*id = 42.*" "print value of Parent& in f1" + +gdb_start_again "breakpoint MQ here" +gdb_test "print f1(MQ)" ".* = 53" +gdb_test "print mf1(MQ)" ".* = 106" +gdb_test "print mf2(MQ)" ".* = 106" +gdb_test "print f1(MQR)" ".* = 53" +gdb_test "print mf1(MQR)" ".* = 106" +gdb_test "print mf2(MQR)" ".* = 106"