diff options
73 files changed, 46808 insertions, 0 deletions
diff --git a/contrib/gdb/gdb/abug-rom.c b/contrib/gdb/gdb/abug-rom.c new file mode 100644 index 0000000..543f702 --- /dev/null +++ b/contrib/gdb/gdb/abug-rom.c @@ -0,0 +1,182 @@ +/* Remote debugging interface for ABug Rom monitor for GDB, the GNU debugger. + Copyright 1995, 1996, 1998, 1999, 2000, 2001 + Free Software Foundation, Inc. + + Written by Rob Savoye of Cygnus Support + + 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 "defs.h" +#include "gdbcore.h" +#include "target.h" +#include "monitor.h" +#include "serial.h" +#include "regcache.h" + +#include "m68k-tdep.h" + +/* Prototypes for local functions. */ + +static void abug_open (char *args, int from_tty); + +static void +abug_supply_register (char *regname, int regnamelen, char *val, int vallen) +{ + int regno; + + if (regnamelen != 2) + return; + + switch (regname[0]) + { + case 'S': + if (regname[1] != 'R') + return; + regno = PS_REGNUM; + break; + case 'P': + if (regname[1] != 'C') + return; + regno = PC_REGNUM; + break; + case 'D': + if (regname[1] < '0' || regname[1] > '7') + return; + regno = regname[1] - '0' + M68K_D0_REGNUM; + break; + case 'A': + if (regname[1] < '0' || regname[1] > '7') + return; + regno = regname[1] - '0' + M68K_A0_REGNUM; + break; + default: + return; + } + + monitor_supply_register (regno, val); +} + +/* + * This array of registers needs to match the indexes used by GDB. The + * whole reason this exists is because the various ROM monitors use + * different names than GDB does, and don't support all the + * registers either. So, typing "info reg sp" becomes an "A7". + */ + +static const char * +abug_regname (int index) +{ + static char *regnames[] = + { + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", + "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", + "PC", + }; + + if ((index >= (sizeof (regnames) / sizeof (regnames[0]))) + || (index < 0) || (index >= NUM_REGS)) + return NULL; + else + return regnames[index]; +} + +/* + * Define the monitor command strings. Since these are passed directly + * through to a printf style function, we need can include formatting + * strings. We also need a CR or LF on the end. + */ + +static struct target_ops abug_ops; + +static char *abug_inits[] = +{"\r", NULL}; + +static struct monitor_ops abug_cmds; + +static void +init_abug_cmds (void) +{ + abug_cmds.flags = MO_CLR_BREAK_USES_ADDR; + abug_cmds.init = abug_inits; /* Init strings */ + abug_cmds.cont = "g\r"; /* continue command */ + abug_cmds.step = "t\r"; /* single step */ + abug_cmds.stop = NULL; /* interrupt command */ + abug_cmds.set_break = "br %x\r"; /* set a breakpoint */ + abug_cmds.clr_break = "nobr %x\r"; /* clear a breakpoint */ + abug_cmds.clr_all_break = "nobr\r"; /* clear all breakpoints */ + abug_cmds.fill = "bf %x:%x %x;b\r"; /* fill (start count val) */ + abug_cmds.setmem.cmdb = "ms %x %02x\r"; /* setmem.cmdb (addr, value) */ + abug_cmds.setmem.cmdw = "ms %x %04x\r"; /* setmem.cmdw (addr, value) */ + abug_cmds.setmem.cmdl = "ms %x %08x\r"; /* setmem.cmdl (addr, value) */ + abug_cmds.setmem.cmdll = NULL; /* setmem.cmdll (addr, value) */ + abug_cmds.setmem.resp_delim = NULL; /* setreg.resp_delim */ + abug_cmds.setmem.term = NULL; /* setreg.term */ + abug_cmds.setmem.term_cmd = NULL; /* setreg.term_cmd */ + abug_cmds.getmem.cmdb = "md %x:%x;b\r"; /* getmem.cmdb (addr, len) */ + abug_cmds.getmem.cmdw = "md %x:%x;b\r"; /* getmem.cmdw (addr, len) */ + abug_cmds.getmem.cmdl = "md %x:%x;b\r"; /* getmem.cmdl (addr, len) */ + abug_cmds.getmem.cmdll = NULL; /* getmem.cmdll (addr, len) */ + abug_cmds.getmem.resp_delim = " "; /* getmem.resp_delim */ + abug_cmds.getmem.term = NULL; /* getmem.term */ + abug_cmds.getmem.term_cmd = NULL; /* getmem.term_cmd */ + abug_cmds.setreg.cmd = "rm %s %x\r"; /* setreg.cmd (name, value) */ + abug_cmds.setreg.resp_delim = "="; /* setreg.resp_delim */ + abug_cmds.setreg.term = "? "; /* setreg.term */ + abug_cmds.setreg.term_cmd = ".\r"; /* setreg.term_cmd */ + abug_cmds.getreg.cmd = "rm %s\r"; /* getreg.cmd (name) */ + abug_cmds.getreg.resp_delim = "="; /* getreg.resp_delim */ + abug_cmds.getreg.term = "? "; /* getreg.term */ + abug_cmds.getreg.term_cmd = ".\r"; /* getreg.term_cmd */ + abug_cmds.dump_registers = "rd\r"; /* dump_registers */ + abug_cmds.register_pattern = "\\(\\w+\\) +=\\([0-9a-fA-F]+\\b\\)"; /* register_pattern */ + abug_cmds.supply_register = abug_supply_register; /* supply_register */ + abug_cmds.load_routine = NULL; /* load_routine (defaults to SRECs) */ + abug_cmds.load = "lo 0\r"; /* download command */ + abug_cmds.loadresp = "\n"; /* load response */ + abug_cmds.prompt = "135Bug>"; /* monitor command prompt */ + abug_cmds.line_term = "\r"; /* end-of-line terminator */ + abug_cmds.cmd_end = NULL; /* optional command terminator */ + abug_cmds.target = &abug_ops; /* target operations */ + abug_cmds.stopbits = SERIAL_1_STOPBITS; /* number of stop bits */ + abug_cmds.regnames = NULL; /* registers names */ + abug_cmds.regname = abug_regname; + abug_cmds.magic = MONITOR_OPS_MAGIC; /* magic */ +}; + +static void +abug_open (char *args, int from_tty) +{ + monitor_open (args, &abug_cmds, from_tty); +} + +extern initialize_file_ftype _initialize_abug_rom; /* -Wmissing-prototypes */ + +void +_initialize_abug_rom (void) +{ + init_abug_cmds (); + init_monitor_ops (&abug_ops); + + abug_ops.to_shortname = "abug"; + abug_ops.to_longname = "ABug monitor"; + abug_ops.to_doc = "Debug via the ABug monitor.\n\ +Specify the serial device it is connected to (e.g. /dev/ttya)."; + abug_ops.to_open = abug_open; + + add_target (&abug_ops); +} diff --git a/contrib/gdb/gdb/alpha-nat.c b/contrib/gdb/gdb/alpha-nat.c new file mode 100644 index 0000000..0a78d94 --- /dev/null +++ b/contrib/gdb/gdb/alpha-nat.c @@ -0,0 +1,272 @@ +/* Low level Alpha interface, for GDB when running native. + Copyright 1993, 1995, 1996, 1998, 1999, 2000, 2001, 2003 + 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 "defs.h" +#include "gdb_string.h" +#include "inferior.h" +#include "gdbcore.h" +#include "target.h" +#include "regcache.h" + +#include "alpha-tdep.h" + +#include <sys/ptrace.h> +#ifdef __linux__ +#include <asm/reg.h> +#include <alpha/ptrace.h> +#else +#include <alpha/coreregs.h> +#endif +#include <sys/user.h> + +/* Prototypes for local functions. */ + +static void fetch_osf_core_registers (char *, unsigned, int, CORE_ADDR); +static void fetch_elf_core_registers (char *, unsigned, int, CORE_ADDR); + +/* Extract the register values out of the core file and store + them where `read_register' will find them. + + CORE_REG_SECT points to the register values themselves, read into memory. + CORE_REG_SIZE is the size of that area. + WHICH says which set of registers we are handling (0 = int, 2 = float + on machines where they are discontiguous). + REG_ADDR is the offset from u.u_ar0 to the register values relative to + core_reg_sect. This is used with old-fashioned core files to + locate the registers in a large upage-plus-stack ".reg" section. + Original upage address X is at location core_reg_sect+x+reg_addr. + */ + +static void +fetch_osf_core_registers (char *core_reg_sect, unsigned core_reg_size, + int which, CORE_ADDR reg_addr) +{ + int regno; + int addr; + int bad_reg = -1; + + /* Table to map a gdb regnum to an index in the core register + section. The floating point register values are garbage in + OSF/1.2 core files. OSF5 uses different names for the register + enum list, need to handle two cases. The actual values are the + same. */ + static int const core_reg_mapping[ALPHA_NUM_REGS] = + { +#ifdef NCF_REGS +#define EFL NCF_REGS + CF_V0, CF_T0, CF_T1, CF_T2, CF_T3, CF_T4, CF_T5, CF_T6, + CF_T7, CF_S0, CF_S1, CF_S2, CF_S3, CF_S4, CF_S5, CF_S6, + CF_A0, CF_A1, CF_A2, CF_A3, CF_A4, CF_A5, CF_T8, CF_T9, + CF_T10, CF_T11, CF_RA, CF_T12, CF_AT, CF_GP, CF_SP, -1, + EFL + 0, EFL + 1, EFL + 2, EFL + 3, EFL + 4, EFL + 5, EFL + 6, EFL + 7, + EFL + 8, EFL + 9, EFL + 10, EFL + 11, EFL + 12, EFL + 13, EFL + 14, EFL + 15, + EFL + 16, EFL + 17, EFL + 18, EFL + 19, EFL + 20, EFL + 21, EFL + 22, EFL + 23, + EFL + 24, EFL + 25, EFL + 26, EFL + 27, EFL + 28, EFL + 29, EFL + 30, EFL + 31, + CF_PC, -1 +#else +#define EFL (EF_SIZE / 8) + EF_V0, EF_T0, EF_T1, EF_T2, EF_T3, EF_T4, EF_T5, EF_T6, + EF_T7, EF_S0, EF_S1, EF_S2, EF_S3, EF_S4, EF_S5, EF_S6, + EF_A0, EF_A1, EF_A2, EF_A3, EF_A4, EF_A5, EF_T8, EF_T9, + EF_T10, EF_T11, EF_RA, EF_T12, EF_AT, EF_GP, EF_SP, -1, + EFL + 0, EFL + 1, EFL + 2, EFL + 3, EFL + 4, EFL + 5, EFL + 6, EFL + 7, + EFL + 8, EFL + 9, EFL + 10, EFL + 11, EFL + 12, EFL + 13, EFL + 14, EFL + 15, + EFL + 16, EFL + 17, EFL + 18, EFL + 19, EFL + 20, EFL + 21, EFL + 22, EFL + 23, + EFL + 24, EFL + 25, EFL + 26, EFL + 27, EFL + 28, EFL + 29, EFL + 30, EFL + 31, + EF_PC, -1 +#endif + }; + + for (regno = 0; regno < ALPHA_NUM_REGS; regno++) + { + if (CANNOT_FETCH_REGISTER (regno)) + { + supply_register (regno, NULL); + continue; + } + addr = 8 * core_reg_mapping[regno]; + if (addr < 0 || addr >= core_reg_size) + { + /* ??? UNIQUE is a new addition. Don't generate an error. */ + if (regno == ALPHA_UNIQUE_REGNUM) + { + supply_register (regno, NULL); + continue; + } + if (bad_reg < 0) + bad_reg = regno; + } + else + { + supply_register (regno, core_reg_sect + addr); + } + } + if (bad_reg >= 0) + { + error ("Register %s not found in core file.", REGISTER_NAME (bad_reg)); + } +} + +static void +fetch_elf_core_registers (char *core_reg_sect, unsigned core_reg_size, + int which, CORE_ADDR reg_addr) +{ + if (core_reg_size < 32 * 8) + { + error ("Core file register section too small (%u bytes).", core_reg_size); + return; + } + + switch (which) + { + case 0: /* integer registers */ + /* PC is in slot 32; UNIQUE is in slot 33, if present. */ + alpha_supply_int_regs (-1, core_reg_sect, core_reg_sect + 31*8, + (core_reg_size >= 33 * 8 + ? core_reg_sect + 32*8 : NULL)); + break; + + case 2: /* floating-point registers */ + /* FPCR is in slot 32. */ + alpha_supply_fp_regs (-1, core_reg_sect, core_reg_sect + 31*8); + break; + + default: + break; + } +} + + +/* Map gdb internal register number to a ptrace ``address''. + These ``addresses'' are defined in <sys/ptrace.h>, with + the exception of ALPHA_UNIQUE_PTRACE_ADDR. */ + +#ifndef ALPHA_UNIQUE_PTRACE_ADDR +#define ALPHA_UNIQUE_PTRACE_ADDR 0 +#endif + +CORE_ADDR +register_addr (int regno, CORE_ADDR blockend) +{ + if (regno == PC_REGNUM) + return PC; + if (regno == ALPHA_UNIQUE_REGNUM) + return ALPHA_UNIQUE_PTRACE_ADDR; + if (regno < FP0_REGNUM) + return GPR_BASE + regno; + else + return FPR_BASE + regno - FP0_REGNUM; +} + +int +kernel_u_size (void) +{ + return (sizeof (struct user)); +} + +#if defined(USE_PROC_FS) || defined(HAVE_GREGSET_T) +#include <sys/procfs.h> + +/* Prototypes for supply_gregset etc. */ +#include "gregset.h" + +/* Locate the UNIQUE value within the gregset_t. */ +#ifndef ALPHA_REGSET_UNIQUE +#define ALPHA_REGSET_UNIQUE(ptr) NULL +#endif + +/* + * See the comment in m68k-tdep.c regarding the utility of these functions. + */ + +void +supply_gregset (gdb_gregset_t *gregsetp) +{ + long *regp = ALPHA_REGSET_BASE (gregsetp); + void *unique = ALPHA_REGSET_UNIQUE (gregsetp); + + /* PC is in slot 32. */ + alpha_supply_int_regs (-1, regp, regp + 31, unique); +} + +void +fill_gregset (gdb_gregset_t *gregsetp, int regno) +{ + long *regp = ALPHA_REGSET_BASE (gregsetp); + void *unique = ALPHA_REGSET_UNIQUE (gregsetp); + + /* PC is in slot 32. */ + alpha_fill_int_regs (regno, regp, regp + 31, unique); +} + +/* + * Now we do the same thing for floating-point registers. + * Again, see the comments in m68k-tdep.c. + */ + +void +supply_fpregset (gdb_fpregset_t *fpregsetp) +{ + long *regp = ALPHA_REGSET_BASE (fpregsetp); + + /* FPCR is in slot 32. */ + alpha_supply_fp_regs (-1, regp, regp + 31); +} + +void +fill_fpregset (gdb_fpregset_t *fpregsetp, int regno) +{ + long *regp = ALPHA_REGSET_BASE (fpregsetp); + + /* FPCR is in slot 32. */ + alpha_fill_fp_regs (regno, regp, regp + 31); +} +#endif + + +/* Register that we are able to handle alpha core file formats. */ + +static struct core_fns alpha_osf_core_fns = +{ + /* This really is bfd_target_unknown_flavour. */ + + bfd_target_unknown_flavour, /* core_flavour */ + default_check_format, /* check_format */ + default_core_sniffer, /* core_sniffer */ + fetch_osf_core_registers, /* core_read_registers */ + NULL /* next */ +}; + +static struct core_fns alpha_elf_core_fns = +{ + bfd_target_elf_flavour, /* core_flavour */ + default_check_format, /* check_format */ + default_core_sniffer, /* core_sniffer */ + fetch_elf_core_registers, /* core_read_registers */ + NULL /* next */ +}; + +void +_initialize_core_alpha (void) +{ + add_core_fns (&alpha_osf_core_fns); + add_core_fns (&alpha_elf_core_fns); +} diff --git a/contrib/gdb/gdb/arm-tdep.c b/contrib/gdb/gdb/arm-tdep.c new file mode 100644 index 0000000..0034690 --- /dev/null +++ b/contrib/gdb/gdb/arm-tdep.c @@ -0,0 +1,3018 @@ +/* Common target dependent code for GDB on ARM systems. + Copyright 1988, 1989, 1991, 1992, 1993, 1995, 1996, 1998, 1999, 2000, + 2001, 2002, 2003, 2004 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 <ctype.h> /* XXX for isupper () */ + +#include "defs.h" +#include "frame.h" +#include "inferior.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "gdb_string.h" +#include "dis-asm.h" /* For register styles. */ +#include "regcache.h" +#include "doublest.h" +#include "value.h" +#include "arch-utils.h" +#include "osabi.h" +#include "frame-unwind.h" +#include "frame-base.h" +#include "trad-frame.h" + +#include "arm-tdep.h" +#include "gdb/sim-arm.h" + +#include "elf-bfd.h" +#include "coff/internal.h" +#include "elf/arm.h" + +#include "gdb_assert.h" + +static int arm_debug; + +/* Each OS has a different mechanism for accessing the various + registers stored in the sigcontext structure. + + SIGCONTEXT_REGISTER_ADDRESS should be defined to the name (or + function pointer) which may be used to determine the addresses + of the various saved registers in the sigcontext structure. + + For the ARM target, there are three parameters to this function. + The first is the pc value of the frame under consideration, the + second the stack pointer of this frame, and the last is the + register number to fetch. + + If the tm.h file does not define this macro, then it's assumed that + no mechanism is needed and we define SIGCONTEXT_REGISTER_ADDRESS to + be 0. + + When it comes time to multi-arching this code, see the identically + named machinery in ia64-tdep.c for an example of how it could be + done. It should not be necessary to modify the code below where + this macro is used. */ + +#ifdef SIGCONTEXT_REGISTER_ADDRESS +#ifndef SIGCONTEXT_REGISTER_ADDRESS_P +#define SIGCONTEXT_REGISTER_ADDRESS_P() 1 +#endif +#else +#define SIGCONTEXT_REGISTER_ADDRESS(SP,PC,REG) 0 +#define SIGCONTEXT_REGISTER_ADDRESS_P() 0 +#endif + +/* Macros for setting and testing a bit in a minimal symbol that marks + it as Thumb function. The MSB of the minimal symbol's "info" field + is used for this purpose. + + MSYMBOL_SET_SPECIAL Actually sets the "special" bit. + MSYMBOL_IS_SPECIAL Tests the "special" bit in a minimal symbol. */ + +#define MSYMBOL_SET_SPECIAL(msym) \ + MSYMBOL_INFO (msym) = (char *) (((long) MSYMBOL_INFO (msym)) \ + | 0x80000000) + +#define MSYMBOL_IS_SPECIAL(msym) \ + (((long) MSYMBOL_INFO (msym) & 0x80000000) != 0) + +/* The list of available "set arm ..." and "show arm ..." commands. */ +static struct cmd_list_element *setarmcmdlist = NULL; +static struct cmd_list_element *showarmcmdlist = NULL; + +/* The type of floating-point to use. Keep this in sync with enum + arm_float_model, and the help string in _initialize_arm_tdep. */ +static const char *fp_model_strings[] = +{ + "auto", + "softfpa", + "fpa", + "softvfp", + "vfp" +}; + +/* A variable that can be configured by the user. */ +static enum arm_float_model arm_fp_model = ARM_FLOAT_AUTO; +static const char *current_fp_model = "auto"; + +/* Number of different reg name sets (options). */ +static int num_disassembly_options; + +/* We have more registers than the disassembler as gdb can print the value + of special registers as well. + The general register names are overwritten by whatever is being used by + the disassembler at the moment. We also adjust the case of cpsr and fps. */ + +/* Initial value: Register names used in ARM's ISA documentation. */ +static char * arm_register_name_strings[] = +{"r0", "r1", "r2", "r3", /* 0 1 2 3 */ + "r4", "r5", "r6", "r7", /* 4 5 6 7 */ + "r8", "r9", "r10", "r11", /* 8 9 10 11 */ + "r12", "sp", "lr", "pc", /* 12 13 14 15 */ + "f0", "f1", "f2", "f3", /* 16 17 18 19 */ + "f4", "f5", "f6", "f7", /* 20 21 22 23 */ + "fps", "cpsr" }; /* 24 25 */ +static char **arm_register_names = arm_register_name_strings; + +/* Valid register name styles. */ +static const char **valid_disassembly_styles; + +/* Disassembly style to use. Default to "std" register names. */ +static const char *disassembly_style; +/* Index to that option in the opcodes table. */ +static int current_option; + +/* This is used to keep the bfd arch_info in sync with the disassembly + style. */ +static void set_disassembly_style_sfunc(char *, int, + struct cmd_list_element *); +static void set_disassembly_style (void); + +static void convert_from_extended (const struct floatformat *, const void *, + void *); +static void convert_to_extended (const struct floatformat *, void *, + const void *); + +struct arm_prologue_cache +{ + /* The stack pointer at the time this frame was created; i.e. the + caller's stack pointer when this function was called. It is used + to identify this frame. */ + CORE_ADDR prev_sp; + + /* The frame base for this frame is just prev_sp + frame offset - + frame size. FRAMESIZE is the size of this stack frame, and + FRAMEOFFSET if the initial offset from the stack pointer (this + frame's stack pointer, not PREV_SP) to the frame base. */ + + int framesize; + int frameoffset; + + /* The register used to hold the frame pointer for this frame. */ + int framereg; + + /* Saved register offsets. */ + struct trad_frame_saved_reg *saved_regs; +}; + +/* Addresses for calling Thumb functions have the bit 0 set. + Here are some macros to test, set, or clear bit 0 of addresses. */ +#define IS_THUMB_ADDR(addr) ((addr) & 1) +#define MAKE_THUMB_ADDR(addr) ((addr) | 1) +#define UNMAKE_THUMB_ADDR(addr) ((addr) & ~1) + +/* Set to true if the 32-bit mode is in use. */ + +int arm_apcs_32 = 1; + +/* Flag set by arm_fix_call_dummy that tells whether the target + function is a Thumb function. This flag is checked by + arm_push_arguments. FIXME: Change the PUSH_ARGUMENTS macro (and + its use in valops.c) to pass the function address as an additional + parameter. */ + +static int target_is_thumb; + +/* Flag set by arm_fix_call_dummy that tells whether the calling + function is a Thumb function. This flag is checked by + arm_pc_is_thumb and arm_call_dummy_breakpoint_offset. */ + +static int caller_is_thumb; + +/* Determine if the program counter specified in MEMADDR is in a Thumb + function. */ + +int +arm_pc_is_thumb (CORE_ADDR memaddr) +{ + struct minimal_symbol *sym; + + /* If bit 0 of the address is set, assume this is a Thumb address. */ + if (IS_THUMB_ADDR (memaddr)) + return 1; + + /* Thumb functions have a "special" bit set in minimal symbols. */ + sym = lookup_minimal_symbol_by_pc (memaddr); + if (sym) + { + return (MSYMBOL_IS_SPECIAL (sym)); + } + else + { + return 0; + } +} + +/* Determine if the program counter specified in MEMADDR is in a call + dummy being called from a Thumb function. */ + +int +arm_pc_is_thumb_dummy (CORE_ADDR memaddr) +{ + CORE_ADDR sp = read_sp (); + + /* FIXME: Until we switch for the new call dummy macros, this heuristic + is the best we can do. We are trying to determine if the pc is on + the stack, which (hopefully) will only happen in a call dummy. + We hope the current stack pointer is not so far alway from the dummy + frame location (true if we have not pushed large data structures or + gone too many levels deep) and that our 1024 is not enough to consider + code regions as part of the stack (true for most practical purposes). */ + if (DEPRECATED_PC_IN_CALL_DUMMY (memaddr, sp, sp + 1024)) + return caller_is_thumb; + else + return 0; +} + +/* Remove useless bits from addresses in a running program. */ +static CORE_ADDR +arm_addr_bits_remove (CORE_ADDR val) +{ + if (arm_apcs_32) + return (val & (arm_pc_is_thumb (val) ? 0xfffffffe : 0xfffffffc)); + else + return (val & 0x03fffffc); +} + +/* When reading symbols, we need to zap the low bit of the address, + which may be set to 1 for Thumb functions. */ +static CORE_ADDR +arm_smash_text_address (CORE_ADDR val) +{ + return val & ~1; +} + +/* Immediately after a function call, return the saved pc. Can't + always go through the frames for this because on some machines the + new frame is not set up until the new function executes some + instructions. */ + +static CORE_ADDR +arm_saved_pc_after_call (struct frame_info *frame) +{ + return ADDR_BITS_REMOVE (read_register (ARM_LR_REGNUM)); +} + +/* Determine whether the function invocation represented by FI has a + frame on the stack associated with it. If it does return zero, + otherwise return 1. */ + +static int +arm_frameless_function_invocation (struct frame_info *fi) +{ + CORE_ADDR func_start, after_prologue; + int frameless; + + /* Sometimes we have functions that do a little setup (like saving the + vN registers with the stmdb instruction, but DO NOT set up a frame. + The symbol table will report this as a prologue. However, it is + important not to try to parse these partial frames as frames, or we + will get really confused. + + So I will demand 3 instructions between the start & end of the + prologue before I call it a real prologue, i.e. at least + mov ip, sp, + stmdb sp!, {} + sub sp, ip, #4. */ + + func_start = (get_frame_func (fi) + FUNCTION_START_OFFSET); + after_prologue = SKIP_PROLOGUE (func_start); + + /* There are some frameless functions whose first two instructions + follow the standard APCS form, in which case after_prologue will + be func_start + 8. */ + + frameless = (after_prologue < func_start + 12); + return frameless; +} + +/* A typical Thumb prologue looks like this: + push {r7, lr} + add sp, sp, #-28 + add r7, sp, #12 + Sometimes the latter instruction may be replaced by: + mov r7, sp + + or like this: + push {r7, lr} + mov r7, sp + sub sp, #12 + + or, on tpcs, like this: + sub sp,#16 + push {r7, lr} + (many instructions) + mov r7, sp + sub sp, #12 + + There is always one instruction of three classes: + 1 - push + 2 - setting of r7 + 3 - adjusting of sp + + When we have found at least one of each class we are done with the prolog. + Note that the "sub sp, #NN" before the push does not count. + */ + +static CORE_ADDR +thumb_skip_prologue (CORE_ADDR pc, CORE_ADDR func_end) +{ + CORE_ADDR current_pc; + /* findmask: + bit 0 - push { rlist } + bit 1 - mov r7, sp OR add r7, sp, #imm (setting of r7) + bit 2 - sub sp, #simm OR add sp, #simm (adjusting of sp) + */ + int findmask = 0; + + for (current_pc = pc; + current_pc + 2 < func_end && current_pc < pc + 40; + current_pc += 2) + { + unsigned short insn = read_memory_unsigned_integer (current_pc, 2); + + if ((insn & 0xfe00) == 0xb400) /* push { rlist } */ + { + findmask |= 1; /* push found */ + } + else if ((insn & 0xff00) == 0xb000) /* add sp, #simm OR + sub sp, #simm */ + { + if ((findmask & 1) == 0) /* before push ? */ + continue; + else + findmask |= 4; /* add/sub sp found */ + } + else if ((insn & 0xff00) == 0xaf00) /* add r7, sp, #imm */ + { + findmask |= 2; /* setting of r7 found */ + } + else if (insn == 0x466f) /* mov r7, sp */ + { + findmask |= 2; /* setting of r7 found */ + } + else if (findmask == (4+2+1)) + { + /* We have found one of each type of prologue instruction */ + break; + } + else + /* Something in the prolog that we don't care about or some + instruction from outside the prolog scheduled here for + optimization. */ + continue; + } + + return current_pc; +} + +/* Advance the PC across any function entry prologue instructions to + reach some "real" code. + + The APCS (ARM Procedure Call Standard) defines the following + prologue: + + mov ip, sp + [stmfd sp!, {a1,a2,a3,a4}] + stmfd sp!, {...,fp,ip,lr,pc} + [stfe f7, [sp, #-12]!] + [stfe f6, [sp, #-12]!] + [stfe f5, [sp, #-12]!] + [stfe f4, [sp, #-12]!] + sub fp, ip, #nn @@ nn == 20 or 4 depending on second insn */ + +static CORE_ADDR +arm_skip_prologue (CORE_ADDR pc) +{ + unsigned long inst; + CORE_ADDR skip_pc; + CORE_ADDR func_addr, func_end = 0; + char *func_name; + struct symtab_and_line sal; + + /* If we're in a dummy frame, don't even try to skip the prologue. */ + if (DEPRECATED_PC_IN_CALL_DUMMY (pc, 0, 0)) + return pc; + + /* See what the symbol table says. */ + + if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end)) + { + struct symbol *sym; + + /* Found a function. */ + sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL, NULL); + if (sym && SYMBOL_LANGUAGE (sym) != language_asm) + { + /* Don't use this trick for assembly source files. */ + sal = find_pc_line (func_addr, 0); + if ((sal.line != 0) && (sal.end < func_end)) + return sal.end; + } + } + + /* Check if this is Thumb code. */ + if (arm_pc_is_thumb (pc)) + return thumb_skip_prologue (pc, func_end); + + /* Can't find the prologue end in the symbol table, try it the hard way + by disassembling the instructions. */ + + /* Like arm_scan_prologue, stop no later than pc + 64. */ + if (func_end == 0 || func_end > pc + 64) + func_end = pc + 64; + + for (skip_pc = pc; skip_pc < func_end; skip_pc += 4) + { + inst = read_memory_integer (skip_pc, 4); + + /* "mov ip, sp" is no longer a required part of the prologue. */ + if (inst == 0xe1a0c00d) /* mov ip, sp */ + continue; + + if ((inst & 0xfffff000) == 0xe28dc000) /* add ip, sp #n */ + continue; + + if ((inst & 0xfffff000) == 0xe24dc000) /* sub ip, sp #n */ + continue; + + /* Some prologues begin with "str lr, [sp, #-4]!". */ + if (inst == 0xe52de004) /* str lr, [sp, #-4]! */ + continue; + + if ((inst & 0xfffffff0) == 0xe92d0000) /* stmfd sp!,{a1,a2,a3,a4} */ + continue; + + if ((inst & 0xfffff800) == 0xe92dd800) /* stmfd sp!,{fp,ip,lr,pc} */ + continue; + + /* Any insns after this point may float into the code, if it makes + for better instruction scheduling, so we skip them only if we + find them, but still consider the function to be frame-ful. */ + + /* We may have either one sfmfd instruction here, or several stfe + insns, depending on the version of floating point code we + support. */ + if ((inst & 0xffbf0fff) == 0xec2d0200) /* sfmfd fn, <cnt>, [sp]! */ + continue; + + if ((inst & 0xffff8fff) == 0xed6d0103) /* stfe fn, [sp, #-12]! */ + continue; + + if ((inst & 0xfffff000) == 0xe24cb000) /* sub fp, ip, #nn */ + continue; + + if ((inst & 0xfffff000) == 0xe24dd000) /* sub sp, sp, #nn */ + continue; + + if ((inst & 0xffffc000) == 0xe54b0000 || /* strb r(0123),[r11,#-nn] */ + (inst & 0xffffc0f0) == 0xe14b00b0 || /* strh r(0123),[r11,#-nn] */ + (inst & 0xffffc000) == 0xe50b0000) /* str r(0123),[r11,#-nn] */ + continue; + + if ((inst & 0xffffc000) == 0xe5cd0000 || /* strb r(0123),[sp,#nn] */ + (inst & 0xffffc0f0) == 0xe1cd00b0 || /* strh r(0123),[sp,#nn] */ + (inst & 0xffffc000) == 0xe58d0000) /* str r(0123),[sp,#nn] */ + continue; + + /* Un-recognized instruction; stop scanning. */ + break; + } + + return skip_pc; /* End of prologue */ +} + +/* *INDENT-OFF* */ +/* Function: thumb_scan_prologue (helper function for arm_scan_prologue) + This function decodes a Thumb function prologue to determine: + 1) the size of the stack frame + 2) which registers are saved on it + 3) the offsets of saved regs + 4) the offset from the stack pointer to the frame pointer + + A typical Thumb function prologue would create this stack frame + (offsets relative to FP) + old SP -> 24 stack parameters + 20 LR + 16 R7 + R7 -> 0 local variables (16 bytes) + SP -> -12 additional stack space (12 bytes) + The frame size would thus be 36 bytes, and the frame offset would be + 12 bytes. The frame register is R7. + + The comments for thumb_skip_prolog() describe the algorithm we use + to detect the end of the prolog. */ +/* *INDENT-ON* */ + +static void +thumb_scan_prologue (CORE_ADDR prev_pc, struct arm_prologue_cache *cache) +{ + CORE_ADDR prologue_start; + CORE_ADDR prologue_end; + CORE_ADDR current_pc; + /* Which register has been copied to register n? */ + int saved_reg[16]; + /* findmask: + bit 0 - push { rlist } + bit 1 - mov r7, sp OR add r7, sp, #imm (setting of r7) + bit 2 - sub sp, #simm OR add sp, #simm (adjusting of sp) + */ + int findmask = 0; + int i; + + if (find_pc_partial_function (prev_pc, NULL, &prologue_start, &prologue_end)) + { + struct symtab_and_line sal = find_pc_line (prologue_start, 0); + + if (sal.line == 0) /* no line info, use current PC */ + prologue_end = prev_pc; + else if (sal.end < prologue_end) /* next line begins after fn end */ + prologue_end = sal.end; /* (probably means no prologue) */ + } + else + /* We're in the boondocks: allow for + 16 pushes, an add, and "mv fp,sp". */ + prologue_end = prologue_start + 40; + + prologue_end = min (prologue_end, prev_pc); + + /* Initialize the saved register map. When register H is copied to + register L, we will put H in saved_reg[L]. */ + for (i = 0; i < 16; i++) + saved_reg[i] = i; + + /* Search the prologue looking for instructions that set up the + frame pointer, adjust the stack pointer, and save registers. + Do this until all basic prolog instructions are found. */ + + cache->framesize = 0; + for (current_pc = prologue_start; + (current_pc < prologue_end) && ((findmask & 7) != 7); + current_pc += 2) + { + unsigned short insn; + int regno; + int offset; + + insn = read_memory_unsigned_integer (current_pc, 2); + + if ((insn & 0xfe00) == 0xb400) /* push { rlist } */ + { + int mask; + findmask |= 1; /* push found */ + /* Bits 0-7 contain a mask for registers R0-R7. Bit 8 says + whether to save LR (R14). */ + mask = (insn & 0xff) | ((insn & 0x100) << 6); + + /* Calculate offsets of saved R0-R7 and LR. */ + for (regno = ARM_LR_REGNUM; regno >= 0; regno--) + if (mask & (1 << regno)) + { + cache->framesize += 4; + cache->saved_regs[saved_reg[regno]].addr = -cache->framesize; + /* Reset saved register map. */ + saved_reg[regno] = regno; + } + } + else if ((insn & 0xff00) == 0xb000) /* add sp, #simm OR + sub sp, #simm */ + { + if ((findmask & 1) == 0) /* before push? */ + continue; + else + findmask |= 4; /* add/sub sp found */ + + offset = (insn & 0x7f) << 2; /* get scaled offset */ + if (insn & 0x80) /* is it signed? (==subtracting) */ + { + cache->frameoffset += offset; + offset = -offset; + } + cache->framesize -= offset; + } + else if ((insn & 0xff00) == 0xaf00) /* add r7, sp, #imm */ + { + findmask |= 2; /* setting of r7 found */ + cache->framereg = THUMB_FP_REGNUM; + /* get scaled offset */ + cache->frameoffset = (insn & 0xff) << 2; + } + else if (insn == 0x466f) /* mov r7, sp */ + { + findmask |= 2; /* setting of r7 found */ + cache->framereg = THUMB_FP_REGNUM; + cache->frameoffset = 0; + saved_reg[THUMB_FP_REGNUM] = ARM_SP_REGNUM; + } + else if ((insn & 0xffc0) == 0x4640) /* mov r0-r7, r8-r15 */ + { + int lo_reg = insn & 7; /* dest. register (r0-r7) */ + int hi_reg = ((insn >> 3) & 7) + 8; /* source register (r8-15) */ + saved_reg[lo_reg] = hi_reg; /* remember hi reg was saved */ + } + else + /* Something in the prolog that we don't care about or some + instruction from outside the prolog scheduled here for + optimization. */ + continue; + } +} + +/* This function decodes an ARM function prologue to determine: + 1) the size of the stack frame + 2) which registers are saved on it + 3) the offsets of saved regs + 4) the offset from the stack pointer to the frame pointer + This information is stored in the "extra" fields of the frame_info. + + There are two basic forms for the ARM prologue. The fixed argument + function call will look like: + + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + sub fp, ip, #4 + [sub sp, sp, #4] + + Which would create this stack frame (offsets relative to FP): + IP -> 4 (caller's stack) + FP -> 0 PC (points to address of stmfd instruction + 8 in callee) + -4 LR (return address in caller) + -8 IP (copy of caller's SP) + -12 FP (caller's FP) + SP -> -28 Local variables + + The frame size would thus be 32 bytes, and the frame offset would be + 28 bytes. The stmfd call can also save any of the vN registers it + plans to use, which increases the frame size accordingly. + + Note: The stored PC is 8 off of the STMFD instruction that stored it + because the ARM Store instructions always store PC + 8 when you read + the PC register. + + A variable argument function call will look like: + + mov ip, sp + stmfd sp!, {a1, a2, a3, a4} + stmfd sp!, {fp, ip, lr, pc} + sub fp, ip, #20 + + Which would create this stack frame (offsets relative to FP): + IP -> 20 (caller's stack) + 16 A4 + 12 A3 + 8 A2 + 4 A1 + FP -> 0 PC (points to address of stmfd instruction + 8 in callee) + -4 LR (return address in caller) + -8 IP (copy of caller's SP) + -12 FP (caller's FP) + SP -> -28 Local variables + + The frame size would thus be 48 bytes, and the frame offset would be + 28 bytes. + + There is another potential complication, which is that the optimizer + will try to separate the store of fp in the "stmfd" instruction from + the "sub fp, ip, #NN" instruction. Almost anything can be there, so + we just key on the stmfd, and then scan for the "sub fp, ip, #NN"... + + Also, note, the original version of the ARM toolchain claimed that there + should be an + + instruction at the end of the prologue. I have never seen GCC produce + this, and the ARM docs don't mention it. We still test for it below in + case it happens... + + */ + +static void +arm_scan_prologue (struct frame_info *next_frame, struct arm_prologue_cache *cache) +{ + int regno, sp_offset, fp_offset, ip_offset; + CORE_ADDR prologue_start, prologue_end, current_pc; + CORE_ADDR prev_pc = frame_pc_unwind (next_frame); + + /* Assume there is no frame until proven otherwise. */ + cache->framereg = ARM_SP_REGNUM; + cache->framesize = 0; + cache->frameoffset = 0; + + /* Check for Thumb prologue. */ + if (arm_pc_is_thumb (prev_pc)) + { + thumb_scan_prologue (prev_pc, cache); + return; + } + + /* Find the function prologue. If we can't find the function in + the symbol table, peek in the stack frame to find the PC. */ + if (find_pc_partial_function (prev_pc, NULL, &prologue_start, &prologue_end)) + { + /* One way to find the end of the prologue (which works well + for unoptimized code) is to do the following: + + struct symtab_and_line sal = find_pc_line (prologue_start, 0); + + if (sal.line == 0) + prologue_end = prev_pc; + else if (sal.end < prologue_end) + prologue_end = sal.end; + + This mechanism is very accurate so long as the optimizer + doesn't move any instructions from the function body into the + prologue. If this happens, sal.end will be the last + instruction in the first hunk of prologue code just before + the first instruction that the scheduler has moved from + the body to the prologue. + + In order to make sure that we scan all of the prologue + instructions, we use a slightly less accurate mechanism which + may scan more than necessary. To help compensate for this + lack of accuracy, the prologue scanning loop below contains + several clauses which'll cause the loop to terminate early if + an implausible prologue instruction is encountered. + + The expression + + prologue_start + 64 + + is a suitable endpoint since it accounts for the largest + possible prologue plus up to five instructions inserted by + the scheduler. */ + + if (prologue_end > prologue_start + 64) + { + prologue_end = prologue_start + 64; /* See above. */ + } + } + else + { + /* We have no symbol information. Our only option is to assume this + function has a standard stack frame and the normal frame register. + Then, we can find the value of our frame pointer on entrance to + the callee (or at the present moment if this is the innermost frame). + The value stored there should be the address of the stmfd + 8. */ + CORE_ADDR frame_loc; + LONGEST return_value; + + frame_loc = frame_unwind_register_unsigned (next_frame, ARM_FP_REGNUM); + if (!safe_read_memory_integer (frame_loc, 4, &return_value)) + return; + else + { + prologue_start = ADDR_BITS_REMOVE (return_value) - 8; + prologue_end = prologue_start + 64; /* See above. */ + } + } + + if (prev_pc < prologue_end) + prologue_end = prev_pc; + + /* Now search the prologue looking for instructions that set up the + frame pointer, adjust the stack pointer, and save registers. + + Be careful, however, and if it doesn't look like a prologue, + don't try to scan it. If, for instance, a frameless function + begins with stmfd sp!, then we will tell ourselves there is + a frame, which will confuse stack traceback, as well as "finish" + and other operations that rely on a knowledge of the stack + traceback. + + In the APCS, the prologue should start with "mov ip, sp" so + if we don't see this as the first insn, we will stop. + + [Note: This doesn't seem to be true any longer, so it's now an + optional part of the prologue. - Kevin Buettner, 2001-11-20] + + [Note further: The "mov ip,sp" only seems to be missing in + frameless functions at optimization level "-O2" or above, + in which case it is often (but not always) replaced by + "str lr, [sp, #-4]!". - Michael Snyder, 2002-04-23] */ + + sp_offset = fp_offset = ip_offset = 0; + + for (current_pc = prologue_start; + current_pc < prologue_end; + current_pc += 4) + { + unsigned int insn = read_memory_unsigned_integer (current_pc, 4); + + if (insn == 0xe1a0c00d) /* mov ip, sp */ + { + ip_offset = 0; + continue; + } + else if ((insn & 0xfffff000) == 0xe28dc000) /* add ip, sp #n */ + { + unsigned imm = insn & 0xff; /* immediate value */ + unsigned rot = (insn & 0xf00) >> 7; /* rotate amount */ + imm = (imm >> rot) | (imm << (32 - rot)); + ip_offset = imm; + continue; + } + else if ((insn & 0xfffff000) == 0xe24dc000) /* sub ip, sp #n */ + { + unsigned imm = insn & 0xff; /* immediate value */ + unsigned rot = (insn & 0xf00) >> 7; /* rotate amount */ + imm = (imm >> rot) | (imm << (32 - rot)); + ip_offset = -imm; + continue; + } + else if (insn == 0xe52de004) /* str lr, [sp, #-4]! */ + { + sp_offset -= 4; + cache->saved_regs[ARM_LR_REGNUM].addr = sp_offset; + continue; + } + else if ((insn & 0xffff0000) == 0xe92d0000) + /* stmfd sp!, {..., fp, ip, lr, pc} + or + stmfd sp!, {a1, a2, a3, a4} */ + { + int mask = insn & 0xffff; + + /* Calculate offsets of saved registers. */ + for (regno = ARM_PC_REGNUM; regno >= 0; regno--) + if (mask & (1 << regno)) + { + sp_offset -= 4; + cache->saved_regs[regno].addr = sp_offset; + } + } + else if ((insn & 0xffffc000) == 0xe54b0000 || /* strb rx,[r11,#-n] */ + (insn & 0xffffc0f0) == 0xe14b00b0 || /* strh rx,[r11,#-n] */ + (insn & 0xffffc000) == 0xe50b0000) /* str rx,[r11,#-n] */ + { + /* No need to add this to saved_regs -- it's just an arg reg. */ + continue; + } + else if ((insn & 0xffffc000) == 0xe5cd0000 || /* strb rx,[sp,#n] */ + (insn & 0xffffc0f0) == 0xe1cd00b0 || /* strh rx,[sp,#n] */ + (insn & 0xffffc000) == 0xe58d0000) /* str rx,[sp,#n] */ + { + /* No need to add this to saved_regs -- it's just an arg reg. */ + continue; + } + else if ((insn & 0xfffff000) == 0xe24cb000) /* sub fp, ip #n */ + { + unsigned imm = insn & 0xff; /* immediate value */ + unsigned rot = (insn & 0xf00) >> 7; /* rotate amount */ + imm = (imm >> rot) | (imm << (32 - rot)); + fp_offset = -imm + ip_offset; + cache->framereg = ARM_FP_REGNUM; + } + else if ((insn & 0xfffff000) == 0xe24dd000) /* sub sp, sp #n */ + { + unsigned imm = insn & 0xff; /* immediate value */ + unsigned rot = (insn & 0xf00) >> 7; /* rotate amount */ + imm = (imm >> rot) | (imm << (32 - rot)); + sp_offset -= imm; + } + else if ((insn & 0xffff7fff) == 0xed6d0103) /* stfe f?, [sp, -#c]! */ + { + sp_offset -= 12; + regno = ARM_F0_REGNUM + ((insn >> 12) & 0x07); + cache->saved_regs[regno].addr = sp_offset; + } + else if ((insn & 0xffbf0fff) == 0xec2d0200) /* sfmfd f0, 4, [sp!] */ + { + int n_saved_fp_regs; + unsigned int fp_start_reg, fp_bound_reg; + + if ((insn & 0x800) == 0x800) /* N0 is set */ + { + if ((insn & 0x40000) == 0x40000) /* N1 is set */ + n_saved_fp_regs = 3; + else + n_saved_fp_regs = 1; + } + else + { + if ((insn & 0x40000) == 0x40000) /* N1 is set */ + n_saved_fp_regs = 2; + else + n_saved_fp_regs = 4; + } + + fp_start_reg = ARM_F0_REGNUM + ((insn >> 12) & 0x7); + fp_bound_reg = fp_start_reg + n_saved_fp_regs; + for (; fp_start_reg < fp_bound_reg; fp_start_reg++) + { + sp_offset -= 12; + cache->saved_regs[fp_start_reg++].addr = sp_offset; + } + } + else if ((insn & 0xf0000000) != 0xe0000000) + break; /* Condition not true, exit early */ + else if ((insn & 0xfe200000) == 0xe8200000) /* ldm? */ + break; /* Don't scan past a block load */ + else + /* The optimizer might shove anything into the prologue, + so we just skip what we don't recognize. */ + continue; + } + + /* The frame size is just the negative of the offset (from the + original SP) of the last thing thing we pushed on the stack. + The frame offset is [new FP] - [new SP]. */ + cache->framesize = -sp_offset; + if (cache->framereg == ARM_FP_REGNUM) + cache->frameoffset = fp_offset - sp_offset; + else + cache->frameoffset = 0; +} + +static struct arm_prologue_cache * +arm_make_prologue_cache (struct frame_info *next_frame) +{ + int reg; + struct arm_prologue_cache *cache; + CORE_ADDR unwound_fp; + + cache = frame_obstack_zalloc (sizeof (struct arm_prologue_cache)); + cache->saved_regs = trad_frame_alloc_saved_regs (next_frame); + + arm_scan_prologue (next_frame, cache); + + unwound_fp = frame_unwind_register_unsigned (next_frame, cache->framereg); + if (unwound_fp == 0) + return cache; + + cache->prev_sp = unwound_fp + cache->framesize - cache->frameoffset; + + /* Calculate actual addresses of saved registers using offsets + determined by arm_scan_prologue. */ + for (reg = 0; reg < NUM_REGS; reg++) + if (trad_frame_addr_p (cache->saved_regs, reg)) + cache->saved_regs[reg].addr += cache->prev_sp; + + return cache; +} + +/* Our frame ID for a normal frame is the current function's starting PC + and the caller's SP when we were called. */ + +static void +arm_prologue_this_id (struct frame_info *next_frame, + void **this_cache, + struct frame_id *this_id) +{ + struct arm_prologue_cache *cache; + struct frame_id id; + CORE_ADDR func; + + if (*this_cache == NULL) + *this_cache = arm_make_prologue_cache (next_frame); + cache = *this_cache; + + func = frame_func_unwind (next_frame); + + /* This is meant to halt the backtrace at "_start". Make sure we + don't halt it at a generic dummy frame. */ + if (func <= LOWEST_PC) + return; + + /* If we've hit a wall, stop. */ + if (cache->prev_sp == 0) + return; + + id = frame_id_build (cache->prev_sp, func); + + /* Check that we're not going round in circles with the same frame + ID (but avoid applying the test to sentinel frames which do go + round in circles). */ + if (frame_relative_level (next_frame) >= 0 + && get_frame_type (next_frame) == NORMAL_FRAME + && frame_id_eq (get_frame_id (next_frame), id)) + return; + + *this_id = id; +} + +static void +arm_prologue_prev_register (struct frame_info *next_frame, + void **this_cache, + int prev_regnum, + int *optimized, + enum lval_type *lvalp, + CORE_ADDR *addrp, + int *realnump, + void *valuep) +{ + struct arm_prologue_cache *cache; + + if (*this_cache == NULL) + *this_cache = arm_make_prologue_cache (next_frame); + cache = *this_cache; + + /* If we are asked to unwind the PC, then we need to return the LR + instead. The saved value of PC points into this frame's + prologue, not the next frame's resume location. */ + if (prev_regnum == ARM_PC_REGNUM) + prev_regnum = ARM_LR_REGNUM; + + /* SP is generally not saved to the stack, but this frame is + identified by NEXT_FRAME's stack pointer at the time of the call. + The value was already reconstructed into PREV_SP. */ + if (prev_regnum == ARM_SP_REGNUM) + { + *lvalp = not_lval; + if (valuep) + store_unsigned_integer (valuep, 4, cache->prev_sp); + return; + } + + trad_frame_prev_register (next_frame, cache->saved_regs, prev_regnum, + optimized, lvalp, addrp, realnump, valuep); +} + +struct frame_unwind arm_prologue_unwind = { + NORMAL_FRAME, + arm_prologue_this_id, + arm_prologue_prev_register +}; + +static const struct frame_unwind * +arm_prologue_unwind_sniffer (struct frame_info *next_frame) +{ + return &arm_prologue_unwind; +} + +static CORE_ADDR +arm_normal_frame_base (struct frame_info *next_frame, void **this_cache) +{ + struct arm_prologue_cache *cache; + + if (*this_cache == NULL) + *this_cache = arm_make_prologue_cache (next_frame); + cache = *this_cache; + + return cache->prev_sp + cache->frameoffset - cache->framesize; +} + +struct frame_base arm_normal_base = { + &arm_prologue_unwind, + arm_normal_frame_base, + arm_normal_frame_base, + arm_normal_frame_base +}; + +static struct arm_prologue_cache * +arm_make_sigtramp_cache (struct frame_info *next_frame) +{ + struct arm_prologue_cache *cache; + int reg; + + cache = frame_obstack_zalloc (sizeof (struct arm_prologue_cache)); + + cache->prev_sp = frame_unwind_register_unsigned (next_frame, ARM_SP_REGNUM); + + cache->saved_regs = trad_frame_alloc_saved_regs (next_frame); + + for (reg = 0; reg < NUM_REGS; reg++) + cache->saved_regs[reg].addr + = SIGCONTEXT_REGISTER_ADDRESS (cache->prev_sp, + frame_pc_unwind (next_frame), reg); + + /* FIXME: What about thumb mode? */ + cache->framereg = ARM_SP_REGNUM; + cache->prev_sp + = read_memory_integer (cache->saved_regs[cache->framereg].addr, + register_size (current_gdbarch, cache->framereg)); + + return cache; +} + +static void +arm_sigtramp_this_id (struct frame_info *next_frame, + void **this_cache, + struct frame_id *this_id) +{ + struct arm_prologue_cache *cache; + + if (*this_cache == NULL) + *this_cache = arm_make_sigtramp_cache (next_frame); + cache = *this_cache; + + /* FIXME drow/2003-07-07: This isn't right if we single-step within + the sigtramp frame; the PC should be the beginning of the trampoline. */ + *this_id = frame_id_build (cache->prev_sp, frame_pc_unwind (next_frame)); +} + +static void +arm_sigtramp_prev_register (struct frame_info *next_frame, + void **this_cache, + int prev_regnum, + int *optimized, + enum lval_type *lvalp, + CORE_ADDR *addrp, + int *realnump, + void *valuep) +{ + struct arm_prologue_cache *cache; + + if (*this_cache == NULL) + *this_cache = arm_make_sigtramp_cache (next_frame); + cache = *this_cache; + + trad_frame_prev_register (next_frame, cache->saved_regs, prev_regnum, + optimized, lvalp, addrp, realnump, valuep); +} + +struct frame_unwind arm_sigtramp_unwind = { + SIGTRAMP_FRAME, + arm_sigtramp_this_id, + arm_sigtramp_prev_register +}; + +static const struct frame_unwind * +arm_sigtramp_unwind_sniffer (struct frame_info *next_frame) +{ + /* Note: If an ARM PC_IN_SIGTRAMP method ever needs to compare + against the name of the function, the code below will have to be + changed to first fetch the name of the function and then pass + this name to PC_IN_SIGTRAMP. */ + + if (SIGCONTEXT_REGISTER_ADDRESS_P () + && PC_IN_SIGTRAMP (frame_pc_unwind (next_frame), (char *) 0)) + return &arm_sigtramp_unwind; + + return NULL; +} + +/* Assuming NEXT_FRAME->prev is a dummy, return the frame ID of that + dummy frame. The frame ID's base needs to match the TOS value + saved by save_dummy_frame_tos() and returned from + arm_push_dummy_call, and the PC needs to match the dummy frame's + breakpoint. */ + +static struct frame_id +arm_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame) +{ + return frame_id_build (frame_unwind_register_unsigned (next_frame, ARM_SP_REGNUM), + frame_pc_unwind (next_frame)); +} + +/* Given THIS_FRAME, find the previous frame's resume PC (which will + be used to construct the previous frame's ID, after looking up the + containing function). */ + +static CORE_ADDR +arm_unwind_pc (struct gdbarch *gdbarch, struct frame_info *this_frame) +{ + CORE_ADDR pc; + pc = frame_unwind_register_unsigned (this_frame, ARM_PC_REGNUM); + return IS_THUMB_ADDR (pc) ? UNMAKE_THUMB_ADDR (pc) : pc; +} + +static CORE_ADDR +arm_unwind_sp (struct gdbarch *gdbarch, struct frame_info *this_frame) +{ + return frame_unwind_register_unsigned (this_frame, ARM_SP_REGNUM); +} + +/* DEPRECATED_CALL_DUMMY_WORDS: + This sequence of words is the instructions + + mov lr,pc + mov pc,r4 + illegal + + Note this is 12 bytes. */ + +static LONGEST arm_call_dummy_words[] = +{ + 0xe1a0e00f, 0xe1a0f004, 0xe7ffdefe +}; + +/* When arguments must be pushed onto the stack, they go on in reverse + order. The code below implements a FILO (stack) to do this. */ + +struct stack_item +{ + int len; + struct stack_item *prev; + void *data; +}; + +static struct stack_item * +push_stack_item (struct stack_item *prev, void *contents, int len) +{ + struct stack_item *si; + si = xmalloc (sizeof (struct stack_item)); + si->data = xmalloc (len); + si->len = len; + si->prev = prev; + memcpy (si->data, contents, len); + return si; +} + +static struct stack_item * +pop_stack_item (struct stack_item *si) +{ + struct stack_item *dead = si; + si = si->prev; + xfree (dead->data); + xfree (dead); + return si; +} + +/* We currently only support passing parameters in integer registers. This + conforms with GCC's default model. Several other variants exist and + we should probably support some of them based on the selected ABI. */ + +static CORE_ADDR +arm_push_dummy_call (struct gdbarch *gdbarch, CORE_ADDR func_addr, + struct regcache *regcache, CORE_ADDR bp_addr, int nargs, + struct value **args, CORE_ADDR sp, int struct_return, + CORE_ADDR struct_addr) +{ + int argnum; + int argreg; + int nstack; + struct stack_item *si = NULL; + + /* Set the return address. For the ARM, the return breakpoint is + always at BP_ADDR. */ + /* XXX Fix for Thumb. */ + regcache_cooked_write_unsigned (regcache, ARM_LR_REGNUM, bp_addr); + + /* Walk through the list of args and determine how large a temporary + stack is required. Need to take care here as structs may be + passed on the stack, and we have to to push them. */ + nstack = 0; + + argreg = ARM_A1_REGNUM; + nstack = 0; + + /* Some platforms require a double-word aligned stack. Make sure sp + is correctly aligned before we start. We always do this even if + it isn't really needed -- it can never hurt things. */ + sp &= ~(CORE_ADDR)(2 * DEPRECATED_REGISTER_SIZE - 1); + + /* The struct_return pointer occupies the first parameter + passing register. */ + if (struct_return) + { + if (arm_debug) + fprintf_unfiltered (gdb_stdlog, "struct return in %s = 0x%s\n", + REGISTER_NAME (argreg), paddr (struct_addr)); + regcache_cooked_write_unsigned (regcache, argreg, struct_addr); + argreg++; + } + + for (argnum = 0; argnum < nargs; argnum++) + { + int len; + struct type *arg_type; + struct type *target_type; + enum type_code typecode; + char *val; + + arg_type = check_typedef (VALUE_TYPE (args[argnum])); + len = TYPE_LENGTH (arg_type); + target_type = TYPE_TARGET_TYPE (arg_type); + typecode = TYPE_CODE (arg_type); + val = VALUE_CONTENTS (args[argnum]); + + /* If the argument is a pointer to a function, and it is a + Thumb function, create a LOCAL copy of the value and set + the THUMB bit in it. */ + if (TYPE_CODE_PTR == typecode + && target_type != NULL + && TYPE_CODE_FUNC == TYPE_CODE (target_type)) + { + CORE_ADDR regval = extract_unsigned_integer (val, len); + if (arm_pc_is_thumb (regval)) + { + val = alloca (len); + store_unsigned_integer (val, len, MAKE_THUMB_ADDR (regval)); + } + } + + /* Copy the argument to general registers or the stack in + register-sized pieces. Large arguments are split between + registers and stack. */ + while (len > 0) + { + int partial_len = len < DEPRECATED_REGISTER_SIZE ? len : DEPRECATED_REGISTER_SIZE; + + if (argreg <= ARM_LAST_ARG_REGNUM) + { + /* The argument is being passed in a general purpose + register. */ + CORE_ADDR regval = extract_unsigned_integer (val, partial_len); + if (arm_debug) + fprintf_unfiltered (gdb_stdlog, "arg %d in %s = 0x%s\n", + argnum, REGISTER_NAME (argreg), + phex (regval, DEPRECATED_REGISTER_SIZE)); + regcache_cooked_write_unsigned (regcache, argreg, regval); + argreg++; + } + else + { + /* Push the arguments onto the stack. */ + if (arm_debug) + fprintf_unfiltered (gdb_stdlog, "arg %d @ sp + %d\n", + argnum, nstack); + si = push_stack_item (si, val, DEPRECATED_REGISTER_SIZE); + nstack += DEPRECATED_REGISTER_SIZE; + } + + len -= partial_len; + val += partial_len; + } + } + /* If we have an odd number of words to push, then decrement the stack + by one word now, so first stack argument will be dword aligned. */ + if (nstack & 4) + sp -= 4; + + while (si) + { + sp -= si->len; + write_memory (sp, si->data, si->len); + si = pop_stack_item (si); + } + + /* Finally, update teh SP register. */ + regcache_cooked_write_unsigned (regcache, ARM_SP_REGNUM, sp); + + return sp; +} + +static void +print_fpu_flags (int flags) +{ + if (flags & (1 << 0)) + fputs ("IVO ", stdout); + if (flags & (1 << 1)) + fputs ("DVZ ", stdout); + if (flags & (1 << 2)) + fputs ("OFL ", stdout); + if (flags & (1 << 3)) + fputs ("UFL ", stdout); + if (flags & (1 << 4)) + fputs ("INX ", stdout); + putchar ('\n'); +} + +/* Print interesting information about the floating point processor + (if present) or emulator. */ +static void +arm_print_float_info (struct gdbarch *gdbarch, struct ui_file *file, + struct frame_info *frame, const char *args) +{ + unsigned long status = read_register (ARM_FPS_REGNUM); + int type; + + type = (status >> 24) & 127; + printf ("%s FPU type %d\n", + (status & (1 << 31)) ? "Hardware" : "Software", + type); + fputs ("mask: ", stdout); + print_fpu_flags (status >> 16); + fputs ("flags: ", stdout); + print_fpu_flags (status); +} + +/* Return the GDB type object for the "standard" data type of data in + register N. */ + +static struct type * +arm_register_type (struct gdbarch *gdbarch, int regnum) +{ + if (regnum >= ARM_F0_REGNUM && regnum < ARM_F0_REGNUM + NUM_FREGS) + { + if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) + return builtin_type_arm_ext_big; + else + return builtin_type_arm_ext_littlebyte_bigword; + } + else + return builtin_type_int32; +} + +/* Index within `registers' of the first byte of the space for + register N. */ + +static int +arm_register_byte (int regnum) +{ + if (regnum < ARM_F0_REGNUM) + return regnum * INT_REGISTER_SIZE; + else if (regnum < ARM_PS_REGNUM) + return (NUM_GREGS * INT_REGISTER_SIZE + + (regnum - ARM_F0_REGNUM) * FP_REGISTER_SIZE); + else + return (NUM_GREGS * INT_REGISTER_SIZE + + NUM_FREGS * FP_REGISTER_SIZE + + (regnum - ARM_FPS_REGNUM) * STATUS_REGISTER_SIZE); +} + +/* Map GDB internal REGNUM onto the Arm simulator register numbers. */ +static int +arm_register_sim_regno (int regnum) +{ + int reg = regnum; + gdb_assert (reg >= 0 && reg < NUM_REGS); + + if (reg < NUM_GREGS) + return SIM_ARM_R0_REGNUM + reg; + reg -= NUM_GREGS; + + if (reg < NUM_FREGS) + return SIM_ARM_FP0_REGNUM + reg; + reg -= NUM_FREGS; + + if (reg < NUM_SREGS) + return SIM_ARM_FPS_REGNUM + reg; + reg -= NUM_SREGS; + + internal_error (__FILE__, __LINE__, "Bad REGNUM %d", regnum); +} + +/* NOTE: cagney/2001-08-20: Both convert_from_extended() and + convert_to_extended() use floatformat_arm_ext_littlebyte_bigword. + It is thought that this is is the floating-point register format on + little-endian systems. */ + +static void +convert_from_extended (const struct floatformat *fmt, const void *ptr, + void *dbl) +{ + DOUBLEST d; + if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) + floatformat_to_doublest (&floatformat_arm_ext_big, ptr, &d); + else + floatformat_to_doublest (&floatformat_arm_ext_littlebyte_bigword, + ptr, &d); + floatformat_from_doublest (fmt, &d, dbl); +} + +static void +convert_to_extended (const struct floatformat *fmt, void *dbl, const void *ptr) +{ + DOUBLEST d; + floatformat_to_doublest (fmt, ptr, &d); + if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) + floatformat_from_doublest (&floatformat_arm_ext_big, &d, dbl); + else + floatformat_from_doublest (&floatformat_arm_ext_littlebyte_bigword, + &d, dbl); +} + +static int +condition_true (unsigned long cond, unsigned long status_reg) +{ + if (cond == INST_AL || cond == INST_NV) + return 1; + + switch (cond) + { + case INST_EQ: + return ((status_reg & FLAG_Z) != 0); + case INST_NE: + return ((status_reg & FLAG_Z) == 0); + case INST_CS: + return ((status_reg & FLAG_C) != 0); + case INST_CC: + return ((status_reg & FLAG_C) == 0); + case INST_MI: + return ((status_reg & FLAG_N) != 0); + case INST_PL: + return ((status_reg & FLAG_N) == 0); + case INST_VS: + return ((status_reg & FLAG_V) != 0); + case INST_VC: + return ((status_reg & FLAG_V) == 0); + case INST_HI: + return ((status_reg & (FLAG_C | FLAG_Z)) == FLAG_C); + case INST_LS: + return ((status_reg & (FLAG_C | FLAG_Z)) != FLAG_C); + case INST_GE: + return (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0)); + case INST_LT: + return (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0)); + case INST_GT: + return (((status_reg & FLAG_Z) == 0) && + (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0))); + case INST_LE: + return (((status_reg & FLAG_Z) != 0) || + (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0))); + } + return 1; +} + +/* Support routines for single stepping. Calculate the next PC value. */ +#define submask(x) ((1L << ((x) + 1)) - 1) +#define bit(obj,st) (((obj) >> (st)) & 1) +#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st))) +#define sbits(obj,st,fn) \ + ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st)))) +#define BranchDest(addr,instr) \ + ((CORE_ADDR) (((long) (addr)) + 8 + (sbits (instr, 0, 23) << 2))) +#define ARM_PC_32 1 + +static unsigned long +shifted_reg_val (unsigned long inst, int carry, unsigned long pc_val, + unsigned long status_reg) +{ + unsigned long res, shift; + int rm = bits (inst, 0, 3); + unsigned long shifttype = bits (inst, 5, 6); + + if (bit (inst, 4)) + { + int rs = bits (inst, 8, 11); + shift = (rs == 15 ? pc_val + 8 : read_register (rs)) & 0xFF; + } + else + shift = bits (inst, 7, 11); + + res = (rm == 15 + ? ((pc_val | (ARM_PC_32 ? 0 : status_reg)) + + (bit (inst, 4) ? 12 : 8)) + : read_register (rm)); + + switch (shifttype) + { + case 0: /* LSL */ + res = shift >= 32 ? 0 : res << shift; + break; + + case 1: /* LSR */ + res = shift >= 32 ? 0 : res >> shift; + break; + + case 2: /* ASR */ + if (shift >= 32) + shift = 31; + res = ((res & 0x80000000L) + ? ~((~res) >> shift) : res >> shift); + break; + + case 3: /* ROR/RRX */ + shift &= 31; + if (shift == 0) + res = (res >> 1) | (carry ? 0x80000000L : 0); + else + res = (res >> shift) | (res << (32 - shift)); + break; + } + + return res & 0xffffffff; +} + +/* Return number of 1-bits in VAL. */ + +static int +bitcount (unsigned long val) +{ + int nbits; + for (nbits = 0; val != 0; nbits++) + val &= val - 1; /* delete rightmost 1-bit in val */ + return nbits; +} + +CORE_ADDR +thumb_get_next_pc (CORE_ADDR pc) +{ + unsigned long pc_val = ((unsigned long) pc) + 4; /* PC after prefetch */ + unsigned short inst1 = read_memory_integer (pc, 2); + CORE_ADDR nextpc = pc + 2; /* default is next instruction */ + unsigned long offset; + + if ((inst1 & 0xff00) == 0xbd00) /* pop {rlist, pc} */ + { + CORE_ADDR sp; + + /* Fetch the saved PC from the stack. It's stored above + all of the other registers. */ + offset = bitcount (bits (inst1, 0, 7)) * DEPRECATED_REGISTER_SIZE; + sp = read_register (ARM_SP_REGNUM); + nextpc = (CORE_ADDR) read_memory_integer (sp + offset, 4); + nextpc = ADDR_BITS_REMOVE (nextpc); + if (nextpc == pc) + error ("Infinite loop detected"); + } + else if ((inst1 & 0xf000) == 0xd000) /* conditional branch */ + { + unsigned long status = read_register (ARM_PS_REGNUM); + unsigned long cond = bits (inst1, 8, 11); + if (cond != 0x0f && condition_true (cond, status)) /* 0x0f = SWI */ + nextpc = pc_val + (sbits (inst1, 0, 7) << 1); + } + else if ((inst1 & 0xf800) == 0xe000) /* unconditional branch */ + { + nextpc = pc_val + (sbits (inst1, 0, 10) << 1); + } + else if ((inst1 & 0xf800) == 0xf000) /* long branch with link, and blx */ + { + unsigned short inst2 = read_memory_integer (pc + 2, 2); + offset = (sbits (inst1, 0, 10) << 12) + (bits (inst2, 0, 10) << 1); + nextpc = pc_val + offset; + /* For BLX make sure to clear the low bits. */ + if (bits (inst2, 11, 12) == 1) + nextpc = nextpc & 0xfffffffc; + } + else if ((inst1 & 0xff00) == 0x4700) /* bx REG, blx REG */ + { + if (bits (inst1, 3, 6) == 0x0f) + nextpc = pc_val; + else + nextpc = read_register (bits (inst1, 3, 6)); + + nextpc = ADDR_BITS_REMOVE (nextpc); + if (nextpc == pc) + error ("Infinite loop detected"); + } + + return nextpc; +} + +CORE_ADDR +arm_get_next_pc (CORE_ADDR pc) +{ + unsigned long pc_val; + unsigned long this_instr; + unsigned long status; + CORE_ADDR nextpc; + + if (arm_pc_is_thumb (pc)) + return thumb_get_next_pc (pc); + + pc_val = (unsigned long) pc; + this_instr = read_memory_integer (pc, 4); + status = read_register (ARM_PS_REGNUM); + nextpc = (CORE_ADDR) (pc_val + 4); /* Default case */ + + if (condition_true (bits (this_instr, 28, 31), status)) + { + switch (bits (this_instr, 24, 27)) + { + case 0x0: + case 0x1: /* data processing */ + case 0x2: + case 0x3: + { + unsigned long operand1, operand2, result = 0; + unsigned long rn; + int c; + + if (bits (this_instr, 12, 15) != 15) + break; + + if (bits (this_instr, 22, 25) == 0 + && bits (this_instr, 4, 7) == 9) /* multiply */ + error ("Illegal update to pc in instruction"); + + /* BX <reg>, BLX <reg> */ + if (bits (this_instr, 4, 28) == 0x12fff1 + || bits (this_instr, 4, 28) == 0x12fff3) + { + rn = bits (this_instr, 0, 3); + result = (rn == 15) ? pc_val + 8 : read_register (rn); + nextpc = (CORE_ADDR) ADDR_BITS_REMOVE (result); + + if (nextpc == pc) + error ("Infinite loop detected"); + + return nextpc; + } + + /* Multiply into PC */ + c = (status & FLAG_C) ? 1 : 0; + rn = bits (this_instr, 16, 19); + operand1 = (rn == 15) ? pc_val + 8 : read_register (rn); + + if (bit (this_instr, 25)) + { + unsigned long immval = bits (this_instr, 0, 7); + unsigned long rotate = 2 * bits (this_instr, 8, 11); + operand2 = ((immval >> rotate) | (immval << (32 - rotate))) + & 0xffffffff; + } + else /* operand 2 is a shifted register */ + operand2 = shifted_reg_val (this_instr, c, pc_val, status); + + switch (bits (this_instr, 21, 24)) + { + case 0x0: /*and */ + result = operand1 & operand2; + break; + + case 0x1: /*eor */ + result = operand1 ^ operand2; + break; + + case 0x2: /*sub */ + result = operand1 - operand2; + break; + + case 0x3: /*rsb */ + result = operand2 - operand1; + break; + + case 0x4: /*add */ + result = operand1 + operand2; + break; + + case 0x5: /*adc */ + result = operand1 + operand2 + c; + break; + + case 0x6: /*sbc */ + result = operand1 - operand2 + c; + break; + + case 0x7: /*rsc */ + result = operand2 - operand1 + c; + break; + + case 0x8: + case 0x9: + case 0xa: + case 0xb: /* tst, teq, cmp, cmn */ + result = (unsigned long) nextpc; + break; + + case 0xc: /*orr */ + result = operand1 | operand2; + break; + + case 0xd: /*mov */ + /* Always step into a function. */ + result = operand2; + break; + + case 0xe: /*bic */ + result = operand1 & ~operand2; + break; + + case 0xf: /*mvn */ + result = ~operand2; + break; + } + nextpc = (CORE_ADDR) ADDR_BITS_REMOVE (result); + + if (nextpc == pc) + error ("Infinite loop detected"); + break; + } + + case 0x4: + case 0x5: /* data transfer */ + case 0x6: + case 0x7: + if (bit (this_instr, 20)) + { + /* load */ + if (bits (this_instr, 12, 15) == 15) + { + /* rd == pc */ + unsigned long rn; + unsigned long base; + + if (bit (this_instr, 22)) + error ("Illegal update to pc in instruction"); + + /* byte write to PC */ + rn = bits (this_instr, 16, 19); + base = (rn == 15) ? pc_val + 8 : read_register (rn); + if (bit (this_instr, 24)) + { + /* pre-indexed */ + int c = (status & FLAG_C) ? 1 : 0; + unsigned long offset = + (bit (this_instr, 25) + ? shifted_reg_val (this_instr, c, pc_val, status) + : bits (this_instr, 0, 11)); + + if (bit (this_instr, 23)) + base += offset; + else + base -= offset; + } + nextpc = (CORE_ADDR) read_memory_integer ((CORE_ADDR) base, + 4); + + nextpc = ADDR_BITS_REMOVE (nextpc); + + if (nextpc == pc) + error ("Infinite loop detected"); + } + } + break; + + case 0x8: + case 0x9: /* block transfer */ + if (bit (this_instr, 20)) + { + /* LDM */ + if (bit (this_instr, 15)) + { + /* loading pc */ + int offset = 0; + + if (bit (this_instr, 23)) + { + /* up */ + unsigned long reglist = bits (this_instr, 0, 14); + offset = bitcount (reglist) * 4; + if (bit (this_instr, 24)) /* pre */ + offset += 4; + } + else if (bit (this_instr, 24)) + offset = -4; + + { + unsigned long rn_val = + read_register (bits (this_instr, 16, 19)); + nextpc = + (CORE_ADDR) read_memory_integer ((CORE_ADDR) (rn_val + + offset), + 4); + } + nextpc = ADDR_BITS_REMOVE (nextpc); + if (nextpc == pc) + error ("Infinite loop detected"); + } + } + break; + + case 0xb: /* branch & link */ + case 0xa: /* branch */ + { + nextpc = BranchDest (pc, this_instr); + + /* BLX */ + if (bits (this_instr, 28, 31) == INST_NV) + nextpc |= bit (this_instr, 24) << 1; + + nextpc = ADDR_BITS_REMOVE (nextpc); + if (nextpc == pc) + error ("Infinite loop detected"); + break; + } + + case 0xc: + case 0xd: + case 0xe: /* coproc ops */ + case 0xf: /* SWI */ + break; + + default: + fprintf_filtered (gdb_stderr, "Bad bit-field extraction\n"); + return (pc); + } + } + + return nextpc; +} + +/* single_step() is called just before we want to resume the inferior, + if we want to single-step it but there is no hardware or kernel + single-step support. We find the target of the coming instruction + and breakpoint it. + + single_step() is also called just after the inferior stops. If we + had set up a simulated single-step, we undo our damage. */ + +static void +arm_software_single_step (enum target_signal sig, int insert_bpt) +{ + static int next_pc; /* State between setting and unsetting. */ + static char break_mem[BREAKPOINT_MAX]; /* Temporary storage for mem@bpt */ + + if (insert_bpt) + { + next_pc = arm_get_next_pc (read_register (ARM_PC_REGNUM)); + target_insert_breakpoint (next_pc, break_mem); + } + else + target_remove_breakpoint (next_pc, break_mem); +} + +#include "bfd-in2.h" +#include "libcoff.h" + +static int +gdb_print_insn_arm (bfd_vma memaddr, disassemble_info *info) +{ + if (arm_pc_is_thumb (memaddr)) + { + static asymbol *asym; + static combined_entry_type ce; + static struct coff_symbol_struct csym; + static struct bfd fake_bfd; + static bfd_target fake_target; + + if (csym.native == NULL) + { + /* Create a fake symbol vector containing a Thumb symbol. + This is solely so that the code in print_insn_little_arm() + and print_insn_big_arm() in opcodes/arm-dis.c will detect + the presence of a Thumb symbol and switch to decoding + Thumb instructions. */ + + fake_target.flavour = bfd_target_coff_flavour; + fake_bfd.xvec = &fake_target; + ce.u.syment.n_sclass = C_THUMBEXTFUNC; + csym.native = &ce; + csym.symbol.the_bfd = &fake_bfd; + csym.symbol.name = "fake"; + asym = (asymbol *) & csym; + } + + memaddr = UNMAKE_THUMB_ADDR (memaddr); + info->symbols = &asym; + } + else + info->symbols = NULL; + + if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) + return print_insn_big_arm (memaddr, info); + else + return print_insn_little_arm (memaddr, info); +} + +/* The following define instruction sequences that will cause ARM + cpu's to take an undefined instruction trap. These are used to + signal a breakpoint to GDB. + + The newer ARMv4T cpu's are capable of operating in ARM or Thumb + modes. A different instruction is required for each mode. The ARM + cpu's can also be big or little endian. Thus four different + instructions are needed to support all cases. + + Note: ARMv4 defines several new instructions that will take the + undefined instruction trap. ARM7TDMI is nominally ARMv4T, but does + not in fact add the new instructions. The new undefined + instructions in ARMv4 are all instructions that had no defined + behaviour in earlier chips. There is no guarantee that they will + raise an exception, but may be treated as NOP's. In practice, it + may only safe to rely on instructions matching: + + 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + C C C C 0 1 1 x x x x x x x x x x x x x x x x x x x x 1 x x x x + + Even this may only true if the condition predicate is true. The + following use a condition predicate of ALWAYS so it is always TRUE. + + There are other ways of forcing a breakpoint. GNU/Linux, RISC iX, + and NetBSD all use a software interrupt rather than an undefined + instruction to force a trap. This can be handled by by the + abi-specific code during establishment of the gdbarch vector. */ + + +/* NOTE rearnsha 2002-02-18: for now we allow a non-multi-arch gdb to + override these definitions. */ +#ifndef ARM_LE_BREAKPOINT +#define ARM_LE_BREAKPOINT {0xFE,0xDE,0xFF,0xE7} +#endif +#ifndef ARM_BE_BREAKPOINT +#define ARM_BE_BREAKPOINT {0xE7,0xFF,0xDE,0xFE} +#endif +#ifndef THUMB_LE_BREAKPOINT +#define THUMB_LE_BREAKPOINT {0xfe,0xdf} +#endif +#ifndef THUMB_BE_BREAKPOINT +#define THUMB_BE_BREAKPOINT {0xdf,0xfe} +#endif + +static const char arm_default_arm_le_breakpoint[] = ARM_LE_BREAKPOINT; +static const char arm_default_arm_be_breakpoint[] = ARM_BE_BREAKPOINT; +static const char arm_default_thumb_le_breakpoint[] = THUMB_LE_BREAKPOINT; +static const char arm_default_thumb_be_breakpoint[] = THUMB_BE_BREAKPOINT; + +/* Determine the type and size of breakpoint to insert at PCPTR. Uses + the program counter value to determine whether a 16-bit or 32-bit + breakpoint should be used. It returns a pointer to a string of + bytes that encode a breakpoint instruction, stores the length of + the string to *lenptr, and adjusts the program counter (if + necessary) to point to the actual memory location where the + breakpoint should be inserted. */ + +/* XXX ??? from old tm-arm.h: if we're using RDP, then we're inserting + breakpoints and storing their handles instread of what was in + memory. It is nice that this is the same size as a handle - + otherwise remote-rdp will have to change. */ + +static const unsigned char * +arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + + if (arm_pc_is_thumb (*pcptr) || arm_pc_is_thumb_dummy (*pcptr)) + { + *pcptr = UNMAKE_THUMB_ADDR (*pcptr); + *lenptr = tdep->thumb_breakpoint_size; + return tdep->thumb_breakpoint; + } + else + { + *lenptr = tdep->arm_breakpoint_size; + return tdep->arm_breakpoint; + } +} + +/* Extract from an array REGBUF containing the (raw) register state a + function return value of type TYPE, and copy that, in virtual + format, into VALBUF. */ + +static void +arm_extract_return_value (struct type *type, + struct regcache *regs, + void *dst) +{ + bfd_byte *valbuf = dst; + + if (TYPE_CODE_FLT == TYPE_CODE (type)) + { + switch (arm_get_fp_model (current_gdbarch)) + { + case ARM_FLOAT_FPA: + { + /* The value is in register F0 in internal format. We need to + extract the raw value and then convert it to the desired + internal type. */ + bfd_byte tmpbuf[FP_REGISTER_SIZE]; + + regcache_cooked_read (regs, ARM_F0_REGNUM, tmpbuf); + convert_from_extended (floatformat_from_type (type), tmpbuf, + valbuf); + } + break; + + case ARM_FLOAT_SOFT_FPA: + case ARM_FLOAT_SOFT_VFP: + regcache_cooked_read (regs, ARM_A1_REGNUM, valbuf); + if (TYPE_LENGTH (type) > 4) + regcache_cooked_read (regs, ARM_A1_REGNUM + 1, + valbuf + INT_REGISTER_SIZE); + break; + + default: + internal_error + (__FILE__, __LINE__, + "arm_extract_return_value: Floating point model not supported"); + break; + } + } + else if (TYPE_CODE (type) == TYPE_CODE_INT + || TYPE_CODE (type) == TYPE_CODE_CHAR + || TYPE_CODE (type) == TYPE_CODE_BOOL + || TYPE_CODE (type) == TYPE_CODE_PTR + || TYPE_CODE (type) == TYPE_CODE_REF + || TYPE_CODE (type) == TYPE_CODE_ENUM) + { + /* If the the type is a plain integer, then the access is + straight-forward. Otherwise we have to play around a bit more. */ + int len = TYPE_LENGTH (type); + int regno = ARM_A1_REGNUM; + ULONGEST tmp; + + while (len > 0) + { + /* By using store_unsigned_integer we avoid having to do + anything special for small big-endian values. */ + regcache_cooked_read_unsigned (regs, regno++, &tmp); + store_unsigned_integer (valbuf, + (len > INT_REGISTER_SIZE + ? INT_REGISTER_SIZE : len), + tmp); + len -= INT_REGISTER_SIZE; + valbuf += INT_REGISTER_SIZE; + } + } + else + { + /* For a structure or union the behaviour is as if the value had + been stored to word-aligned memory and then loaded into + registers with 32-bit load instruction(s). */ + int len = TYPE_LENGTH (type); + int regno = ARM_A1_REGNUM; + bfd_byte tmpbuf[INT_REGISTER_SIZE]; + + while (len > 0) + { + regcache_cooked_read (regs, regno++, tmpbuf); + memcpy (valbuf, tmpbuf, + len > INT_REGISTER_SIZE ? INT_REGISTER_SIZE : len); + len -= INT_REGISTER_SIZE; + valbuf += INT_REGISTER_SIZE; + } + } +} + +/* Extract from an array REGBUF containing the (raw) register state + the address in which a function should return its structure value. */ + +static CORE_ADDR +arm_extract_struct_value_address (struct regcache *regcache) +{ + ULONGEST ret; + + regcache_cooked_read_unsigned (regcache, ARM_A1_REGNUM, &ret); + return ret; +} + +/* Will a function return an aggregate type in memory or in a + register? Return 0 if an aggregate type can be returned in a + register, 1 if it must be returned in memory. */ + +static int +arm_use_struct_convention (int gcc_p, struct type *type) +{ + int nRc; + enum type_code code; + + CHECK_TYPEDEF (type); + + /* In the ARM ABI, "integer" like aggregate types are returned in + registers. For an aggregate type to be integer like, its size + must be less than or equal to DEPRECATED_REGISTER_SIZE and the + offset of each addressable subfield must be zero. Note that bit + fields are not addressable, and all addressable subfields of + unions always start at offset zero. + + This function is based on the behaviour of GCC 2.95.1. + See: gcc/arm.c: arm_return_in_memory() for details. + + Note: All versions of GCC before GCC 2.95.2 do not set up the + parameters correctly for a function returning the following + structure: struct { float f;}; This should be returned in memory, + not a register. Richard Earnshaw sent me a patch, but I do not + know of any way to detect if a function like the above has been + compiled with the correct calling convention. */ + + /* All aggregate types that won't fit in a register must be returned + in memory. */ + if (TYPE_LENGTH (type) > DEPRECATED_REGISTER_SIZE) + { + return 1; + } + + /* The only aggregate types that can be returned in a register are + structs and unions. Arrays must be returned in memory. */ + code = TYPE_CODE (type); + if ((TYPE_CODE_STRUCT != code) && (TYPE_CODE_UNION != code)) + { + return 1; + } + + /* Assume all other aggregate types can be returned in a register. + Run a check for structures, unions and arrays. */ + nRc = 0; + + if ((TYPE_CODE_STRUCT == code) || (TYPE_CODE_UNION == code)) + { + int i; + /* Need to check if this struct/union is "integer" like. For + this to be true, its size must be less than or equal to + DEPRECATED_REGISTER_SIZE and the offset of each addressable + subfield must be zero. Note that bit fields are not + addressable, and unions always start at offset zero. If any + of the subfields is a floating point type, the struct/union + cannot be an integer type. */ + + /* For each field in the object, check: + 1) Is it FP? --> yes, nRc = 1; + 2) Is it addressable (bitpos != 0) and + not packed (bitsize == 0)? + --> yes, nRc = 1 + */ + + for (i = 0; i < TYPE_NFIELDS (type); i++) + { + enum type_code field_type_code; + field_type_code = TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (type, i))); + + /* Is it a floating point type field? */ + if (field_type_code == TYPE_CODE_FLT) + { + nRc = 1; + break; + } + + /* If bitpos != 0, then we have to care about it. */ + if (TYPE_FIELD_BITPOS (type, i) != 0) + { + /* Bitfields are not addressable. If the field bitsize is + zero, then the field is not packed. Hence it cannot be + a bitfield or any other packed type. */ + if (TYPE_FIELD_BITSIZE (type, i) == 0) + { + nRc = 1; + break; + } + } + } + } + + return nRc; +} + +/* Write into appropriate registers a function return value of type + TYPE, given in virtual format. */ + +static void +arm_store_return_value (struct type *type, struct regcache *regs, + const void *src) +{ + const bfd_byte *valbuf = src; + + if (TYPE_CODE (type) == TYPE_CODE_FLT) + { + char buf[MAX_REGISTER_SIZE]; + + switch (arm_get_fp_model (current_gdbarch)) + { + case ARM_FLOAT_FPA: + + convert_to_extended (floatformat_from_type (type), buf, valbuf); + regcache_cooked_write (regs, ARM_F0_REGNUM, buf); + break; + + case ARM_FLOAT_SOFT_FPA: + case ARM_FLOAT_SOFT_VFP: + regcache_cooked_write (regs, ARM_A1_REGNUM, valbuf); + if (TYPE_LENGTH (type) > 4) + regcache_cooked_write (regs, ARM_A1_REGNUM + 1, + valbuf + INT_REGISTER_SIZE); + break; + + default: + internal_error + (__FILE__, __LINE__, + "arm_store_return_value: Floating point model not supported"); + break; + } + } + else if (TYPE_CODE (type) == TYPE_CODE_INT + || TYPE_CODE (type) == TYPE_CODE_CHAR + || TYPE_CODE (type) == TYPE_CODE_BOOL + || TYPE_CODE (type) == TYPE_CODE_PTR + || TYPE_CODE (type) == TYPE_CODE_REF + || TYPE_CODE (type) == TYPE_CODE_ENUM) + { + if (TYPE_LENGTH (type) <= 4) + { + /* Values of one word or less are zero/sign-extended and + returned in r0. */ + bfd_byte tmpbuf[INT_REGISTER_SIZE]; + LONGEST val = unpack_long (type, valbuf); + + store_signed_integer (tmpbuf, INT_REGISTER_SIZE, val); + regcache_cooked_write (regs, ARM_A1_REGNUM, tmpbuf); + } + else + { + /* Integral values greater than one word are stored in consecutive + registers starting with r0. This will always be a multiple of + the regiser size. */ + int len = TYPE_LENGTH (type); + int regno = ARM_A1_REGNUM; + + while (len > 0) + { + regcache_cooked_write (regs, regno++, valbuf); + len -= INT_REGISTER_SIZE; + valbuf += INT_REGISTER_SIZE; + } + } + } + else + { + /* For a structure or union the behaviour is as if the value had + been stored to word-aligned memory and then loaded into + registers with 32-bit load instruction(s). */ + int len = TYPE_LENGTH (type); + int regno = ARM_A1_REGNUM; + bfd_byte tmpbuf[INT_REGISTER_SIZE]; + + while (len > 0) + { + memcpy (tmpbuf, valbuf, + len > INT_REGISTER_SIZE ? INT_REGISTER_SIZE : len); + regcache_cooked_write (regs, regno++, tmpbuf); + len -= INT_REGISTER_SIZE; + valbuf += INT_REGISTER_SIZE; + } + } +} + +static int +arm_get_longjmp_target (CORE_ADDR *pc) +{ + CORE_ADDR jb_addr; + char buf[INT_REGISTER_SIZE]; + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + + jb_addr = read_register (ARM_A1_REGNUM); + + if (target_read_memory (jb_addr + tdep->jb_pc * tdep->jb_elt_size, buf, + INT_REGISTER_SIZE)) + return 0; + + *pc = extract_unsigned_integer (buf, INT_REGISTER_SIZE); + return 1; +} + +/* Return non-zero if the PC is inside a thumb call thunk. */ + +int +arm_in_call_stub (CORE_ADDR pc, char *name) +{ + CORE_ADDR start_addr; + + /* Find the starting address of the function containing the PC. If + the caller didn't give us a name, look it up at the same time. */ + if (0 == find_pc_partial_function (pc, name ? NULL : &name, + &start_addr, NULL)) + return 0; + + return strncmp (name, "_call_via_r", 11) == 0; +} + +/* If PC is in a Thumb call or return stub, return the address of the + target PC, which is in a register. The thunk functions are called + _called_via_xx, where x is the register name. The possible names + are r0-r9, sl, fp, ip, sp, and lr. */ + +CORE_ADDR +arm_skip_stub (CORE_ADDR pc) +{ + char *name; + CORE_ADDR start_addr; + + /* Find the starting address and name of the function containing the PC. */ + if (find_pc_partial_function (pc, &name, &start_addr, NULL) == 0) + return 0; + + /* Call thunks always start with "_call_via_". */ + if (strncmp (name, "_call_via_", 10) == 0) + { + /* Use the name suffix to determine which register contains the + target PC. */ + static char *table[15] = + {"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "sl", "fp", "ip", "sp", "lr" + }; + int regno; + + for (regno = 0; regno <= 14; regno++) + if (strcmp (&name[10], table[regno]) == 0) + return read_register (regno); + } + + return 0; /* not a stub */ +} + +static void +set_arm_command (char *args, int from_tty) +{ + printf_unfiltered ("\"set arm\" must be followed by an apporpriate subcommand.\n"); + help_list (setarmcmdlist, "set arm ", all_commands, gdb_stdout); +} + +static void +show_arm_command (char *args, int from_tty) +{ + cmd_show_list (showarmcmdlist, from_tty, ""); +} + +enum arm_float_model +arm_get_fp_model (struct gdbarch *gdbarch) +{ + if (arm_fp_model == ARM_FLOAT_AUTO) + return gdbarch_tdep (gdbarch)->fp_model; + + return arm_fp_model; +} + +static void +arm_set_fp (struct gdbarch *gdbarch) +{ + enum arm_float_model fp_model = arm_get_fp_model (gdbarch); + + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE + && (fp_model == ARM_FLOAT_SOFT_FPA || fp_model == ARM_FLOAT_FPA)) + { + set_gdbarch_double_format (gdbarch, + &floatformat_ieee_double_littlebyte_bigword); + set_gdbarch_long_double_format + (gdbarch, &floatformat_ieee_double_littlebyte_bigword); + } + else + { + set_gdbarch_double_format (gdbarch, &floatformat_ieee_double_little); + set_gdbarch_long_double_format (gdbarch, + &floatformat_ieee_double_little); + } +} + +static void +set_fp_model_sfunc (char *args, int from_tty, + struct cmd_list_element *c) +{ + enum arm_float_model fp_model; + + for (fp_model = ARM_FLOAT_AUTO; fp_model != ARM_FLOAT_LAST; fp_model++) + if (strcmp (current_fp_model, fp_model_strings[fp_model]) == 0) + { + arm_fp_model = fp_model; + break; + } + + if (fp_model == ARM_FLOAT_LAST) + internal_error (__FILE__, __LINE__, "Invalid fp model accepted: %s.", + current_fp_model); + + if (gdbarch_bfd_arch_info (current_gdbarch)->arch == bfd_arch_arm) + arm_set_fp (current_gdbarch); +} + +static void +show_fp_model (char *args, int from_tty, + struct cmd_list_element *c) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + + if (arm_fp_model == ARM_FLOAT_AUTO + && gdbarch_bfd_arch_info (current_gdbarch)->arch == bfd_arch_arm) + printf_filtered (" - the default for the current ABI is \"%s\".\n", + fp_model_strings[tdep->fp_model]); +} + +/* If the user changes the register disassembly style used for info + register and other commands, we have to also switch the style used + in opcodes for disassembly output. This function is run in the "set + arm disassembly" command, and does that. */ + +static void +set_disassembly_style_sfunc (char *args, int from_tty, + struct cmd_list_element *c) +{ + set_disassembly_style (); +} + +/* Return the ARM register name corresponding to register I. */ +static const char * +arm_register_name (int i) +{ + return arm_register_names[i]; +} + +static void +set_disassembly_style (void) +{ + const char *setname, *setdesc, **regnames; + int numregs, j; + + /* Find the style that the user wants in the opcodes table. */ + int current = 0; + numregs = get_arm_regnames (current, &setname, &setdesc, ®names); + while ((disassembly_style != setname) + && (current < num_disassembly_options)) + get_arm_regnames (++current, &setname, &setdesc, ®names); + current_option = current; + + /* Fill our copy. */ + for (j = 0; j < numregs; j++) + arm_register_names[j] = (char *) regnames[j]; + + /* Adjust case. */ + if (isupper (*regnames[ARM_PC_REGNUM])) + { + arm_register_names[ARM_FPS_REGNUM] = "FPS"; + arm_register_names[ARM_PS_REGNUM] = "CPSR"; + } + else + { + arm_register_names[ARM_FPS_REGNUM] = "fps"; + arm_register_names[ARM_PS_REGNUM] = "cpsr"; + } + + /* Synchronize the disassembler. */ + set_arm_regname_option (current); +} + +/* arm_othernames implements the "othernames" command. This is deprecated + by the "set arm disassembly" command. */ + +static void +arm_othernames (char *names, int n) +{ + /* Circle through the various flavors. */ + current_option = (current_option + 1) % num_disassembly_options; + + disassembly_style = valid_disassembly_styles[current_option]; + set_disassembly_style (); +} + +/* Test whether the coff symbol specific value corresponds to a Thumb + function. */ + +static int +coff_sym_is_thumb (int val) +{ + return (val == C_THUMBEXT || + val == C_THUMBSTAT || + val == C_THUMBEXTFUNC || + val == C_THUMBSTATFUNC || + val == C_THUMBLABEL); +} + +/* arm_coff_make_msymbol_special() + arm_elf_make_msymbol_special() + + These functions test whether the COFF or ELF symbol corresponds to + an address in thumb code, and set a "special" bit in a minimal + symbol to indicate that it does. */ + +static void +arm_elf_make_msymbol_special(asymbol *sym, struct minimal_symbol *msym) +{ + /* Thumb symbols are of type STT_LOPROC, (synonymous with + STT_ARM_TFUNC). */ + if (ELF_ST_TYPE (((elf_symbol_type *)sym)->internal_elf_sym.st_info) + == STT_LOPROC) + MSYMBOL_SET_SPECIAL (msym); +} + +static void +arm_coff_make_msymbol_special(int val, struct minimal_symbol *msym) +{ + if (coff_sym_is_thumb (val)) + MSYMBOL_SET_SPECIAL (msym); +} + +static void +arm_write_pc (CORE_ADDR pc, ptid_t ptid) +{ + write_register_pid (ARM_PC_REGNUM, pc, ptid); + + /* If necessary, set the T bit. */ + if (arm_apcs_32) + { + CORE_ADDR val = read_register_pid (ARM_PS_REGNUM, ptid); + if (arm_pc_is_thumb (pc)) + write_register_pid (ARM_PS_REGNUM, val | 0x20, ptid); + else + write_register_pid (ARM_PS_REGNUM, val & ~(CORE_ADDR) 0x20, ptid); + } +} + +static enum gdb_osabi +arm_elf_osabi_sniffer (bfd *abfd) +{ + unsigned int elfosabi, eflags; + enum gdb_osabi osabi = GDB_OSABI_UNKNOWN; + + elfosabi = elf_elfheader (abfd)->e_ident[EI_OSABI]; + + switch (elfosabi) + { + case ELFOSABI_NONE: + /* When elfosabi is ELFOSABI_NONE (0), then the ELF structures in the + file are conforming to the base specification for that machine + (there are no OS-specific extensions). In order to determine the + real OS in use we must look for OS notes that have been added. */ + bfd_map_over_sections (abfd, + generic_elf_osabi_sniff_abi_tag_sections, + &osabi); + if (osabi == GDB_OSABI_UNKNOWN) + { + /* Existing ARM tools don't set this field, so look at the EI_FLAGS + field for more information. */ + eflags = EF_ARM_EABI_VERSION(elf_elfheader(abfd)->e_flags); + switch (eflags) + { + case EF_ARM_EABI_VER1: + osabi = GDB_OSABI_ARM_EABI_V1; + break; + + case EF_ARM_EABI_VER2: + osabi = GDB_OSABI_ARM_EABI_V2; + break; + + case EF_ARM_EABI_UNKNOWN: + /* Assume GNU tools. */ + osabi = GDB_OSABI_ARM_APCS; + break; + + default: + internal_error (__FILE__, __LINE__, + "arm_elf_osabi_sniffer: Unknown ARM EABI " + "version 0x%x", eflags); + } + } + break; + + case ELFOSABI_ARM: + /* GNU tools use this value. Check note sections in this case, + as well. */ + bfd_map_over_sections (abfd, + generic_elf_osabi_sniff_abi_tag_sections, + &osabi); + if (osabi == GDB_OSABI_UNKNOWN) + { + /* Assume APCS ABI. */ + osabi = GDB_OSABI_ARM_APCS; + } + break; + + case ELFOSABI_FREEBSD: + osabi = GDB_OSABI_FREEBSD_ELF; + break; + + case ELFOSABI_NETBSD: + osabi = GDB_OSABI_NETBSD_ELF; + break; + + case ELFOSABI_LINUX: + osabi = GDB_OSABI_LINUX; + break; + } + + return osabi; +} + + +/* Initialize the current architecture based on INFO. If possible, + re-use an architecture from ARCHES, which is a list of + architectures already created during this debugging session. + + Called e.g. at program startup, when reading a core file, and when + reading a binary file. */ + +static struct gdbarch * +arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) +{ + struct gdbarch_tdep *tdep; + struct gdbarch *gdbarch; + + /* Try to deterimine the ABI of the object we are loading. */ + + if (info.abfd != NULL && info.osabi == GDB_OSABI_UNKNOWN) + { + switch (bfd_get_flavour (info.abfd)) + { + case bfd_target_aout_flavour: + /* Assume it's an old APCS-style ABI. */ + info.osabi = GDB_OSABI_ARM_APCS; + break; + + case bfd_target_coff_flavour: + /* Assume it's an old APCS-style ABI. */ + /* XXX WinCE? */ + info.osabi = GDB_OSABI_ARM_APCS; + break; + + default: + /* Leave it as "unknown". */ + break; + } + } + + /* If there is already a candidate, use it. */ + arches = gdbarch_list_lookup_by_info (arches, &info); + if (arches != NULL) + return arches->gdbarch; + + tdep = xmalloc (sizeof (struct gdbarch_tdep)); + gdbarch = gdbarch_alloc (&info, tdep); + + /* We used to default to FPA for generic ARM, but almost nobody uses that + now, and we now provide a way for the user to force the model. So + default to the most useful variant. */ + tdep->fp_model = ARM_FLOAT_SOFT_FPA; + + /* Breakpoints. */ + switch (info.byte_order) + { + case BFD_ENDIAN_BIG: + tdep->arm_breakpoint = arm_default_arm_be_breakpoint; + tdep->arm_breakpoint_size = sizeof (arm_default_arm_be_breakpoint); + tdep->thumb_breakpoint = arm_default_thumb_be_breakpoint; + tdep->thumb_breakpoint_size = sizeof (arm_default_thumb_be_breakpoint); + + break; + + case BFD_ENDIAN_LITTLE: + tdep->arm_breakpoint = arm_default_arm_le_breakpoint; + tdep->arm_breakpoint_size = sizeof (arm_default_arm_le_breakpoint); + tdep->thumb_breakpoint = arm_default_thumb_le_breakpoint; + tdep->thumb_breakpoint_size = sizeof (arm_default_thumb_le_breakpoint); + + break; + + default: + internal_error (__FILE__, __LINE__, + "arm_gdbarch_init: bad byte order for float format"); + } + + /* On ARM targets char defaults to unsigned. */ + set_gdbarch_char_signed (gdbarch, 0); + + /* This should be low enough for everything. */ + tdep->lowest_pc = 0x20; + tdep->jb_pc = -1; /* Longjump support not enabled by default. */ + + set_gdbarch_deprecated_call_dummy_words (gdbarch, arm_call_dummy_words); + set_gdbarch_deprecated_sizeof_call_dummy_words (gdbarch, 0); + + set_gdbarch_push_dummy_call (gdbarch, arm_push_dummy_call); + + set_gdbarch_write_pc (gdbarch, arm_write_pc); + + /* Frame handling. */ + set_gdbarch_unwind_dummy_id (gdbarch, arm_unwind_dummy_id); + set_gdbarch_unwind_pc (gdbarch, arm_unwind_pc); + set_gdbarch_unwind_sp (gdbarch, arm_unwind_sp); + + set_gdbarch_deprecated_frameless_function_invocation (gdbarch, arm_frameless_function_invocation); + + frame_base_set_default (gdbarch, &arm_normal_base); + + /* Address manipulation. */ + set_gdbarch_smash_text_address (gdbarch, arm_smash_text_address); + set_gdbarch_addr_bits_remove (gdbarch, arm_addr_bits_remove); + + /* Advance PC across function entry code. */ + set_gdbarch_skip_prologue (gdbarch, arm_skip_prologue); + + /* Get the PC when a frame might not be available. */ + set_gdbarch_deprecated_saved_pc_after_call (gdbarch, arm_saved_pc_after_call); + + /* The stack grows downward. */ + set_gdbarch_inner_than (gdbarch, core_addr_lessthan); + + /* Breakpoint manipulation. */ + set_gdbarch_breakpoint_from_pc (gdbarch, arm_breakpoint_from_pc); + + /* Information about registers, etc. */ + set_gdbarch_print_float_info (gdbarch, arm_print_float_info); + set_gdbarch_deprecated_fp_regnum (gdbarch, ARM_FP_REGNUM); /* ??? */ + set_gdbarch_sp_regnum (gdbarch, ARM_SP_REGNUM); + set_gdbarch_pc_regnum (gdbarch, ARM_PC_REGNUM); + set_gdbarch_deprecated_register_byte (gdbarch, arm_register_byte); + set_gdbarch_deprecated_register_bytes (gdbarch, + (NUM_GREGS * INT_REGISTER_SIZE + + NUM_FREGS * FP_REGISTER_SIZE + + NUM_SREGS * STATUS_REGISTER_SIZE)); + set_gdbarch_num_regs (gdbarch, NUM_GREGS + NUM_FREGS + NUM_SREGS); + set_gdbarch_register_type (gdbarch, arm_register_type); + + /* Internal <-> external register number maps. */ + set_gdbarch_register_sim_regno (gdbarch, arm_register_sim_regno); + + /* Integer registers are 4 bytes. */ + set_gdbarch_deprecated_register_size (gdbarch, 4); + set_gdbarch_register_name (gdbarch, arm_register_name); + + /* Returning results. */ + set_gdbarch_extract_return_value (gdbarch, arm_extract_return_value); + set_gdbarch_store_return_value (gdbarch, arm_store_return_value); + set_gdbarch_use_struct_convention (gdbarch, arm_use_struct_convention); + set_gdbarch_deprecated_extract_struct_value_address (gdbarch, arm_extract_struct_value_address); + + /* Single stepping. */ + /* XXX For an RDI target we should ask the target if it can single-step. */ + set_gdbarch_software_single_step (gdbarch, arm_software_single_step); + + /* Disassembly. */ + set_gdbarch_print_insn (gdbarch, gdb_print_insn_arm); + + /* Minsymbol frobbing. */ + set_gdbarch_elf_make_msymbol_special (gdbarch, arm_elf_make_msymbol_special); + set_gdbarch_coff_make_msymbol_special (gdbarch, + arm_coff_make_msymbol_special); + + /* Hook in the ABI-specific overrides, if they have been registered. */ + gdbarch_init_osabi (info, gdbarch); + + /* Add some default predicates. */ + frame_unwind_append_sniffer (gdbarch, arm_sigtramp_unwind_sniffer); + frame_unwind_append_sniffer (gdbarch, arm_prologue_unwind_sniffer); + + /* Now we have tuned the configuration, set a few final things, + based on what the OS ABI has told us. */ + + if (tdep->jb_pc >= 0) + set_gdbarch_get_longjmp_target (gdbarch, arm_get_longjmp_target); + + /* Floating point sizes and format. */ + switch (info.byte_order) + { + case BFD_ENDIAN_BIG: + set_gdbarch_float_format (gdbarch, &floatformat_ieee_single_big); + set_gdbarch_double_format (gdbarch, &floatformat_ieee_double_big); + set_gdbarch_long_double_format (gdbarch, &floatformat_ieee_double_big); + + break; + + case BFD_ENDIAN_LITTLE: + set_gdbarch_float_format (gdbarch, &floatformat_ieee_single_little); + arm_set_fp (gdbarch); + break; + + default: + internal_error (__FILE__, __LINE__, + "arm_gdbarch_init: bad byte order for float format"); + } + + return gdbarch; +} + +static void +arm_dump_tdep (struct gdbarch *current_gdbarch, struct ui_file *file) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + + if (tdep == NULL) + return; + + fprintf_unfiltered (file, "arm_dump_tdep: Lowest pc = 0x%lx", + (unsigned long) tdep->lowest_pc); +} + +static void +arm_init_abi_eabi_v1 (struct gdbarch_info info, + struct gdbarch *gdbarch) +{ + /* Place-holder. */ +} + +static void +arm_init_abi_eabi_v2 (struct gdbarch_info info, + struct gdbarch *gdbarch) +{ + /* Place-holder. */ +} + +static void +arm_init_abi_apcs (struct gdbarch_info info, + struct gdbarch *gdbarch) +{ + /* Place-holder. */ +} + +extern initialize_file_ftype _initialize_arm_tdep; /* -Wmissing-prototypes */ + +void +_initialize_arm_tdep (void) +{ + struct ui_file *stb; + long length; + struct cmd_list_element *new_set, *new_show; + const char *setname; + const char *setdesc; + const char **regnames; + int numregs, i, j; + static char *helptext; + + gdbarch_register (bfd_arch_arm, arm_gdbarch_init, arm_dump_tdep); + + /* Register an ELF OS ABI sniffer for ARM binaries. */ + gdbarch_register_osabi_sniffer (bfd_arch_arm, + bfd_target_elf_flavour, + arm_elf_osabi_sniffer); + + /* Register some ABI variants for embedded systems. */ + gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_ARM_EABI_V1, + arm_init_abi_eabi_v1); + gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_ARM_EABI_V2, + arm_init_abi_eabi_v2); + gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_ARM_APCS, + arm_init_abi_apcs); + + /* Get the number of possible sets of register names defined in opcodes. */ + num_disassembly_options = get_arm_regname_num_options (); + + /* Add root prefix command for all "set arm"/"show arm" commands. */ + add_prefix_cmd ("arm", no_class, set_arm_command, + "Various ARM-specific commands.", + &setarmcmdlist, "set arm ", 0, &setlist); + + add_prefix_cmd ("arm", no_class, show_arm_command, + "Various ARM-specific commands.", + &showarmcmdlist, "show arm ", 0, &showlist); + + /* Sync the opcode insn printer with our register viewer. */ + parse_arm_disassembler_option ("reg-names-std"); + + /* Begin creating the help text. */ + stb = mem_fileopen (); + fprintf_unfiltered (stb, "Set the disassembly style.\n" + "The valid values are:\n"); + + /* Initialize the array that will be passed to add_set_enum_cmd(). */ + valid_disassembly_styles + = xmalloc ((num_disassembly_options + 1) * sizeof (char *)); + for (i = 0; i < num_disassembly_options; i++) + { + numregs = get_arm_regnames (i, &setname, &setdesc, ®names); + valid_disassembly_styles[i] = setname; + fprintf_unfiltered (stb, "%s - %s\n", setname, + setdesc); + /* Copy the default names (if found) and synchronize disassembler. */ + if (!strcmp (setname, "std")) + { + disassembly_style = setname; + current_option = i; + for (j = 0; j < numregs; j++) + arm_register_names[j] = (char *) regnames[j]; + set_arm_regname_option (i); + } + } + /* Mark the end of valid options. */ + valid_disassembly_styles[num_disassembly_options] = NULL; + + /* Finish the creation of the help text. */ + fprintf_unfiltered (stb, "The default is \"std\"."); + helptext = ui_file_xstrdup (stb, &length); + ui_file_delete (stb); + + /* Add the deprecated disassembly-flavor command. */ + new_set = add_set_enum_cmd ("disassembly-flavor", no_class, + valid_disassembly_styles, + &disassembly_style, + helptext, + &setlist); + set_cmd_sfunc (new_set, set_disassembly_style_sfunc); + deprecate_cmd (new_set, "set arm disassembly"); + deprecate_cmd (add_show_from_set (new_set, &showlist), + "show arm disassembly"); + + /* And now add the new interface. */ + new_set = add_set_enum_cmd ("disassembler", no_class, + valid_disassembly_styles, &disassembly_style, + helptext, &setarmcmdlist); + + set_cmd_sfunc (new_set, set_disassembly_style_sfunc); + add_show_from_set (new_set, &showarmcmdlist); + + add_setshow_cmd_full ("apcs32", no_class, + var_boolean, (char *) &arm_apcs_32, + "Set usage of ARM 32-bit mode.", + "Show usage of ARM 32-bit mode.", + NULL, NULL, + &setlist, &showlist, &new_set, &new_show); + deprecate_cmd (new_set, "set arm apcs32"); + deprecate_cmd (new_show, "show arm apcs32"); + + add_setshow_boolean_cmd ("apcs32", no_class, &arm_apcs_32, + "Set usage of ARM 32-bit mode. " + "When off, a 26-bit PC will be used.", + "Show usage of ARM 32-bit mode. " + "When off, a 26-bit PC will be used.", + NULL, NULL, + &setarmcmdlist, &showarmcmdlist); + + /* Add a command to allow the user to force the FPU model. */ + new_set = add_set_enum_cmd + ("fpu", no_class, fp_model_strings, ¤t_fp_model, + "Set the floating point type.\n" + "auto - Determine the FP typefrom the OS-ABI.\n" + "softfpa - Software FP, mixed-endian doubles on little-endian ARMs.\n" + "fpa - FPA co-processor (GCC compiled).\n" + "softvfp - Software FP with pure-endian doubles.\n" + "vfp - VFP co-processor.", + &setarmcmdlist); + set_cmd_sfunc (new_set, set_fp_model_sfunc); + set_cmd_sfunc (add_show_from_set (new_set, &showarmcmdlist), show_fp_model); + + /* Add the deprecated "othernames" command. */ + deprecate_cmd (add_com ("othernames", class_obscure, arm_othernames, + "Switch to the next set of register names."), + "set arm disassembly"); + + /* Debugging flag. */ + add_setshow_boolean_cmd ("arm", class_maintenance, &arm_debug, + "Set ARM debugging. " + "When on, arm-specific debugging is enabled.", + "Show ARM debugging. " + "When on, arm-specific debugging is enabled.", + NULL, NULL, + &setdebuglist, &showdebuglist); +} diff --git a/contrib/gdb/gdb/coff-solib.c b/contrib/gdb/gdb/coff-solib.c new file mode 100644 index 0000000..64dca7b --- /dev/null +++ b/contrib/gdb/gdb/coff-solib.c @@ -0,0 +1,134 @@ +/* Handle COFF SVR3 shared libraries for GDB, the GNU Debugger. + Copyright 1993, 1994, 1998, 1999, 2000 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 "defs.h" + +#include "frame.h" +#include "bfd.h" +#include "gdbcore.h" +#include "symtab.h" +#include "symfile.h" +#include "objfiles.h" + +/* + + GLOBAL FUNCTION + + coff_solib_add -- add a shared library files to the symtab list. We + examine the `.lib' section of the exec file and determine the names of + the shared libraries. + + This function is responsible for discovering those names and + addresses, and saving sufficient information about them to allow + their symbols to be read at a later time. + + SYNOPSIS + + void coff_solib_add (char *arg_string, int from_tty, + struct target_ops *target, int readsyms) + + DESCRIPTION + + */ + +void +coff_solib_add (char *arg_string, int from_tty, struct target_ops *target, int readsyms) +{ + asection *libsect; + + if (!readsyms) + return; + + libsect = bfd_get_section_by_name (exec_bfd, ".lib"); + + if (libsect) + { + int libsize; + unsigned char *lib; + struct libent + { + bfd_byte len[4]; + bfd_byte nameoffset[4]; + }; + + libsize = bfd_section_size (exec_bfd, libsect); + + lib = (unsigned char *) alloca (libsize); + + bfd_get_section_contents (exec_bfd, libsect, lib, 0, libsize); + + while (libsize > 0) + { + struct libent *ent; + struct objfile *objfile; + int len, nameoffset; + char *filename; + + ent = (struct libent *) lib; + + len = bfd_get_32 (exec_bfd, ent->len); + + nameoffset = bfd_get_32 (exec_bfd, ent->nameoffset); + + if (len <= 0) + break; + + filename = (char *) ent + nameoffset * 4; + + objfile = symbol_file_add (filename, from_tty, + NULL, /* no offsets */ + 0, /* not mainline */ + OBJF_SHARED); /* flags */ + + libsize -= len * 4; + lib += len * 4; + } + + /* Getting new symbols may change our opinion about what is + frameless. */ + reinit_frame_cache (); + } +} + +/* + + GLOBAL FUNCTION + + coff_solib_create_inferior_hook -- shared library startup support + + SYNOPSIS + + void coff_solib_create_inferior_hook() + + DESCRIPTION + + When gdb starts up the inferior, the kernel maps in the shared + libraries. We get here with the target stopped at it's first + instruction, and the libraries already mapped. At this point, this + function gets called via expansion of the macro + SOLIB_CREATE_INFERIOR_HOOK. + */ + +void +coff_solib_create_inferior_hook (void) +{ + coff_solib_add ((char *) 0, 0, (struct target_ops *) 0, auto_solib_add); +} diff --git a/contrib/gdb/gdb/coff-solib.h b/contrib/gdb/gdb/coff-solib.h new file mode 100644 index 0000000..d29f96a --- /dev/null +++ b/contrib/gdb/gdb/coff-solib.h @@ -0,0 +1,186 @@ +/* COFF (SVR3) Shared library declarations for GDB, the GNU Debugger. + Copyright 1992, 1993, 1998, 1999, 2000, 2003 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. */ + +/* Forward decl's for prototypes */ +struct target_ops; + +/* Called when we free all symtabs, to free the shared library information + as well. */ + +#if 0 +#define CLEAR_SOLIB coff_clear_solib + +extern void coff_clear_solib (void); +#endif + +/* Called to add symbols from a shared library to gdb's symbol table. */ + +#define SOLIB_ADD(filename, from_tty, targ, readsyms) \ + coff_solib_add (filename, from_tty, targ, readsyms) + +extern void coff_solib_add (char *, int, struct target_ops *, int); + +/* Function to be called when the inferior starts up, to discover the names + of shared libraries that are dynamically linked, the base addresses to + which they are linked, and sufficient information to read in their symbols + at a later time. */ + +#define SOLIB_CREATE_INFERIOR_HOOK(PID) coff_solib_create_inferior_hook() + +extern void coff_solib_create_inferior_hook (void); /* solib.c */ + +/* Function to be called to remove the connection between debugger and + dynamic linker that was established by SOLIB_CREATE_INFERIOR_HOOK. + (This operation does not remove shared library information from + the debugger, as CLEAR_SOLIB does.) + + This functionality is presently not implemented for this target. + */ +#define SOLIB_REMOVE_INFERIOR_HOOK(PID) (0) + +/* This function is called by the "catch load" command. It allows + the debugger to be notified by the dynamic linker when a specified + library file (or any library file, if filename is NULL) is loaded. + + Presently, this functionality is not implemented. + */ +#define SOLIB_CREATE_CATCH_LOAD_HOOK(pid,tempflag,filename,cond_string) \ + error("catch of library loads/unloads not yet implemented on this platform") + +/* This function is called by the "catch unload" command. It allows + the debugger to be notified by the dynamic linker when a specified + library file (or any library file, if filename is NULL) is unloaded. + + Presently, this functionality is not implemented. + */ +#define SOLIB_CREATE_CATCH_UNLOAD_HOOK(pid,tempflag,filename,cond_string) \ + error("catch of library loads/unloads not yet implemented on this platform") + +/* This function returns TRUE if the dynamic linker has just reported + a load of a library. + + This function must be used only when the inferior has stopped in + the dynamic linker hook, or undefined results are guaranteed. + + Presently, this functionality is not implemented. + */ +/* + #define SOLIB_HAVE_LOAD_EVENT(pid) \ + error("catch of library loads/unloads not yet implemented on this platform") + */ + +#define SOLIB_HAVE_LOAD_EVENT(pid) \ +(0) + +/* This function returns a pointer to the string representation of the + pathname of the dynamically-linked library that has just been loaded. + + This function must be used only when SOLIB_HAVE_LOAD_EVENT is TRUE, + or undefined results are guaranteed. + + This string's contents are only valid immediately after the inferior + has stopped in the dynamic linker hook, and becomes invalid as soon + as the inferior is continued. Clients should make a copy of this + string if they wish to continue the inferior and then access the string. + + Presently, this functionality is not implemented. + */ + +/* + #define SOLIB_LOADED_LIBRARY_PATHNAME(pid) \ + error("catch of library loads/unloads not yet implemented on this platform") + */ + +#define SOLIB_LOADED_LIBRARY_PATHNAME(pid) \ +"" + +/* This function returns TRUE if the dynamic linker has just reported + an unload of a library. + + This function must be used only when the inferior has stopped in + the dynamic linker hook, or undefined results are guaranteed. + + Presently, this functionality is not implemented. + */ +/* + #define SOLIB_HAVE_UNLOAD_EVENT(pid) \ + error("catch of library loads/unloads not yet implemented on this platform") + */ + +#define SOLIB_HAVE_UNLOAD_EVENT(pid) \ +(0) + +/* This function returns a pointer to the string representation of the + pathname of the dynamically-linked library that has just been unloaded. + + This function must be used only when SOLIB_HAVE_UNLOAD_EVENT is TRUE, + or undefined results are guaranteed. + + This string's contents are only valid immediately after the inferior + has stopped in the dynamic linker hook, and becomes invalid as soon + as the inferior is continued. Clients should make a copy of this + string if they wish to continue the inferior and then access the string. + + Presently, this functionality is not implemented. + */ +/* + #define SOLIB_UNLOADED_LIBRARY_PATHNAME(pid) \ + error("catch of library loads/unloads not yet implemented on this platform") + */ + +#define SOLIB_UNLOADED_LIBRARY_PATHNAME(pid) \ +(0) + +/* This function returns TRUE if pc is the address of an instruction that + lies within the dynamic linker (such as the event hook, or the dld + itself). + + This function must be used only when a dynamic linker event has been + caught, and the inferior is being stepped out of the hook, or undefined + results are guaranteed. + + Presently, this functionality is not implemented. + */ + +/* + #define SOLIB_IN_DYNAMIC_LINKER(pid,pc) \ + error("catch of library loads/unloads not yet implemented on this platform") + */ + +#define SOLIB_IN_DYNAMIC_LINKER(pid,pc) \ +(0) + +/* This function must be called when the inferior is killed, and the program + restarted. This is not the same as CLEAR_SOLIB, in that it doesn't discard + any symbol tables. + + Presently, this functionality is not implemented. + */ +#define SOLIB_RESTART() \ + (0) + +/* If we can't set a breakpoint, and it's in a shared library, just + disable it. */ + +#if 0 +#define DISABLE_UNSETTABLE_BREAK(addr) coff_solib_address(addr) + +extern int solib_address (CORE_ADDR); /* solib.c */ +#endif diff --git a/contrib/gdb/gdb/config/nm-gnu.h b/contrib/gdb/gdb/config/nm-gnu.h new file mode 100644 index 0000000..73a4180 --- /dev/null +++ b/contrib/gdb/gdb/config/nm-gnu.h @@ -0,0 +1,43 @@ +/* Common declarations for the GNU Hurd + + Copyright 1995, 1996, 1998, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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. */ + +#ifndef __NM_GNU_H__ +#define __NM_GNU_H__ + +#include <unistd.h> +#include <mach.h> +#include <mach/exception.h> +#include "regcache.h" + +extern char *gnu_target_pid_to_str (int pid); + +/* Before storing, we need to read all the registers. */ +#define CHILD_PREPARE_TO_STORE() deprecated_read_register_bytes (0, NULL, DEPRECATED_REGISTER_BYTES) + +/* Don't do wait_for_inferior on attach. */ +#define ATTACH_NO_WAIT + +/* Use SVR4 style shared library support */ +#define SVR4_SHARED_LIBS +#include "solib.h" +#define NO_CORE_OPS + +#endif /* __NM_GNU_H__ */ diff --git a/contrib/gdb/gdb/config/nm-lynx.h b/contrib/gdb/gdb/config/nm-lynx.h new file mode 100644 index 0000000..4a55a13 --- /dev/null +++ b/contrib/gdb/gdb/config/nm-lynx.h @@ -0,0 +1,86 @@ +/* Native-dependent definitions for LynxOS. + + Copyright 1993, 1994, 1995, 1996, 1999, 2000, 2003 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. */ + +#ifndef NM_LYNX_H +#define NM_LYNX_H + +struct target_waitstatus; + +#include <sys/conf.h> +#include <sys/kernel.h> +/* sys/kernel.h should define this, but doesn't always, sigh. */ +#ifndef __LYNXOS +#define __LYNXOS +#endif +#include <sys/mem.h> +#include <sys/signal.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/itimer.h> +#include <sys/file.h> +#include <sys/proc.h> +#include "gdbthread.h" + +/* This is the amount to subtract from u.u_ar0 to get the offset in + the core file of the register values. */ + +#define KERNEL_U_ADDR USRSTACK + +/* As of LynxOS 2.2.2 (beta 8/15/94), this is int. Previous versions seem to + have had no prototype, so I'm not sure why GDB used to define this to + char *. */ +#define PTRACE_ARG3_TYPE int + +/* Override copies of {fetch,store}_inferior_registers in infptrace.c. */ + +#define FETCH_INFERIOR_REGISTERS + +/* Thread ID of stopped thread. */ + +#define WIFTID(x) (((union wait *)&x)->w_tid) + +/* Override child_wait in inftarg.c */ + +#define CHILD_WAIT + +/* Override child_resume in infptrace.c */ + +#define CHILD_RESUME + +/* Override child_thread_alive in intarg.c */ + +#define CHILD_THREAD_ALIVE + +#include "target.h" + +extern ptid_t child_wait (ptid_t ptid, + struct target_waitstatus *status); + +/* Lynx needs a special definition of this so that we can + print out the pid and thread number seperately. */ + + +/* override child_pid_to_str in inftarg.c */ +#define CHILD_PID_TO_STR +extern char *lynx_pid_to_str (ptid_t ptid); + +#endif /* NM_LYNX_H */ diff --git a/contrib/gdb/gdb/config/nm-nbsd.h b/contrib/gdb/gdb/config/nm-nbsd.h new file mode 100644 index 0000000..5078c56 --- /dev/null +++ b/contrib/gdb/gdb/config/nm-nbsd.h @@ -0,0 +1,27 @@ +/* Native-dependent definitions for NetBSD. + Copyright 1994, 1996, 1999 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. */ + +#define PTRACE_ARG3_TYPE char* + +#define FETCH_INFERIOR_REGISTERS + +#define ATTACH_DETACH + +#include "solib.h" /* Support for shared libraries. */ diff --git a/contrib/gdb/gdb/config/nm-sysv4.h b/contrib/gdb/gdb/config/nm-sysv4.h new file mode 100644 index 0000000..4b4f098 --- /dev/null +++ b/contrib/gdb/gdb/config/nm-sysv4.h @@ -0,0 +1,34 @@ +/* Definitions for running gdb on a host machine running any flavor of SVR4. + Copyright 1991, 1992, 1993, 1998 Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support (fnf@cygnus.com). + + 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. */ + +/* Use SVR4 style shared library support */ + +#define SVR4_SHARED_LIBS +#include "solib.h" + +/* SVR4 has /proc support, so use it instead of ptrace. */ + +#define USE_PROC_FS + +/* SVR4 machines can easily do attach and detach via /proc (procfs.c) + support */ + +#define ATTACH_DETACH diff --git a/contrib/gdb/gdb/config/tm-lynx.h b/contrib/gdb/gdb/config/tm-lynx.h new file mode 100644 index 0000000..7fbc06f --- /dev/null +++ b/contrib/gdb/gdb/config/tm-lynx.h @@ -0,0 +1,32 @@ +/* Macro definitions for LynxOS targets. + Copyright 1993, 1995 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. */ + +#ifndef TM_LYNX_H +#define TM_LYNX_H + +#include "coff-solib.h" /* COFF shared library support */ + +/* Lynx's signal.h doesn't seem to have any macros for what signal numbers + the real-time events are. */ +#define REALTIME_LO 33 +/* One more than the last one. */ +#define REALTIME_HI 64 + +#endif /* TM_LYNX_H */ diff --git a/contrib/gdb/gdb/config/tm-sunos.h b/contrib/gdb/gdb/config/tm-sunos.h new file mode 100644 index 0000000..c8db07e --- /dev/null +++ b/contrib/gdb/gdb/config/tm-sunos.h @@ -0,0 +1,32 @@ +/* Target machine sub-description for SunOS version 4. + This is included by other tm-*.h files to specify SunOS-specific stuff. + Copyright 1990, 1991, 1992, 1993, 1994 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 "solib.h" /* Support for shared libraries. */ + +/* Return non-zero if we are in a shared library trampoline code stub. */ + +#define IN_SOLIB_CALL_TRAMPOLINE(pc, name) \ + lookup_solib_trampoline_symbol_by_pc (pc) + +/* If PC is in a shared library trampoline code, return the PC + where the function itself actually starts. If not, return 0. */ + +#define SKIP_TRAMPOLINE_CODE(pc) find_solib_trampoline_target (pc) diff --git a/contrib/gdb/gdb/config/tm-sysv4.h b/contrib/gdb/gdb/config/tm-sysv4.h new file mode 100644 index 0000000..9a39af2 --- /dev/null +++ b/contrib/gdb/gdb/config/tm-sysv4.h @@ -0,0 +1,37 @@ +/* Macro definitions for GDB on all SVR4 target systems. + Copyright 1991, 1992, 1993, 1994, 1996, 1997, 2000 + Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support (fnf@cygnus.com). + + 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. */ + +/* For SVR4 shared libraries, each call to a library routine goes through + a small piece of trampoline code in the ".plt" section. + The horribly ugly wait_for_inferior() routine uses this macro to detect + when we have stepped into one of these fragments. + We do not use lookup_solib_trampoline_symbol_by_pc, because + we cannot always find the shared library trampoline symbols + (e.g. on Irix5). */ + +#define IN_SOLIB_CALL_TRAMPOLINE(pc, name) in_plt_section((pc), (name)) +extern int in_plt_section (CORE_ADDR, char *); + +/* If PC is in a shared library trampoline code, return the PC + where the function itself actually starts. If not, return 0. */ + +#define SKIP_TRAMPOLINE_CODE(pc) find_solib_trampoline_target (pc) diff --git a/contrib/gdb/gdb/config/xm-nbsd.h b/contrib/gdb/gdb/config/xm-nbsd.h new file mode 100644 index 0000000..c8d00f6 --- /dev/null +++ b/contrib/gdb/gdb/config/xm-nbsd.h @@ -0,0 +1,26 @@ +/* Host-dependent definitions for any CPU running NetBSD. + Copyright 1993, 1994, 1995, 1996, 1999 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 this to get things like NGROUPS which <limits.h> doesn't + define on some systems. */ +#include <sys/param.h> + +/* NetBSD has termios facilities. */ +#define HAVE_TERMIOS diff --git a/contrib/gdb/gdb/config/xm-sysv4.h b/contrib/gdb/gdb/config/xm-sysv4.h new file mode 100644 index 0000000..614d403 --- /dev/null +++ b/contrib/gdb/gdb/config/xm-sysv4.h @@ -0,0 +1,29 @@ +/* Definitions for running gdb on a host machine running any flavor of SVR4. + Copyright 1991, 1992, 1993, 1995, 1998 Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support (fnf@cygnus.com). + + 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. */ + +/* SVR4 has termios facilities. */ + +#undef HAVE_TERMIO +#define HAVE_TERMIOS + +/* SVR4 is a derivative of System V Release 3 (USG) */ + +#define USG diff --git a/contrib/gdb/gdb/cpu32bug-rom.c b/contrib/gdb/gdb/cpu32bug-rom.c new file mode 100644 index 0000000..03b3132 --- /dev/null +++ b/contrib/gdb/gdb/cpu32bug-rom.c @@ -0,0 +1,180 @@ +/* Remote debugging interface for CPU32Bug Rom monitor for GDB, the GNU debugger. + Copyright 1995, 1996, 1998, 1999, 2000, 2001 + Free Software Foundation, Inc. + + Written by Stu Grossman of Cygnus Support + + 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 "defs.h" +#include "gdbcore.h" +#include "target.h" +#include "monitor.h" +#include "serial.h" +#include "regcache.h" + +#include "m68k-tdep.h" + +static void cpu32bug_open (char *args, int from_tty); + +static void +cpu32bug_supply_register (char *regname, int regnamelen, char *val, int vallen) +{ + int regno; + + if (regnamelen != 2) + return; + + switch (regname[0]) + { + case 'S': + if (regname[1] != 'R') + return; + regno = PS_REGNUM; + break; + case 'P': + if (regname[1] != 'C') + return; + regno = PC_REGNUM; + break; + case 'D': + if (regname[1] < '0' || regname[1] > '7') + return; + regno = regname[1] - '0' + M68K_D0_REGNUM; + break; + case 'A': + if (regname[1] < '0' || regname[1] > '7') + return; + regno = regname[1] - '0' + M68K_A0_REGNUM; + break; + default: + return; + } + + monitor_supply_register (regno, val); +} + +/* + * This array of registers needs to match the indexes used by GDB. The + * whole reason this exists is because the various ROM monitors use + * different names than GDB does, and don't support all the + * registers either. So, typing "info reg sp" becomes an "A7". + */ + +static const char * +cpu32bug_regname (int index) +{ + static char *regnames[] = + { + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", + "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", + "SR", "PC" + }; + + if ((index >= (sizeof (regnames) / sizeof (regnames[0]))) + || (index < 0) || (index >= NUM_REGS)) + return NULL; + else + return regnames[index]; +} + +/* + * Define the monitor command strings. Since these are passed directly + * through to a printf style function, we need can include formatting + * strings. We also need a CR or LF on the end. + */ + +static struct target_ops cpu32bug_ops; + +static char *cpu32bug_inits[] = +{"\r", NULL}; + +static struct monitor_ops cpu32bug_cmds; + +static void +init_cpu32bug_cmds (void) +{ + cpu32bug_cmds.flags = MO_CLR_BREAK_USES_ADDR; + cpu32bug_cmds.init = cpu32bug_inits; /* Init strings */ + cpu32bug_cmds.cont = "g\r"; /* continue command */ + cpu32bug_cmds.step = "t\r"; /* single step */ + cpu32bug_cmds.stop = NULL; /* interrupt command */ + cpu32bug_cmds.set_break = "br %x\r"; /* set a breakpoint */ + cpu32bug_cmds.clr_break = "nobr %x\r"; /* clear a breakpoint */ + cpu32bug_cmds.clr_all_break = "nobr\r"; /* clear all breakpoints */ + cpu32bug_cmds.fill = "bf %x:%x %x;b\r"; /* fill (start count val) */ + cpu32bug_cmds.setmem.cmdb = "ms %x %02x\r"; /* setmem.cmdb (addr, value) */ + cpu32bug_cmds.setmem.cmdw = "ms %x %04x\r"; /* setmem.cmdw (addr, value) */ + cpu32bug_cmds.setmem.cmdl = "ms %x %08x\r"; /* setmem.cmdl (addr, value) */ + cpu32bug_cmds.setmem.cmdll = NULL; /* setmem.cmdll (addr, value) */ + cpu32bug_cmds.setmem.resp_delim = NULL; /* setreg.resp_delim */ + cpu32bug_cmds.setmem.term = NULL; /* setreg.term */ + cpu32bug_cmds.setmem.term_cmd = NULL; /* setreg.term_cmd */ + cpu32bug_cmds.getmem.cmdb = "md %x:%x;b\r"; /* getmem.cmdb (addr, len) */ + cpu32bug_cmds.getmem.cmdw = "md %x:%x;b\r"; /* getmem.cmdw (addr, len) */ + cpu32bug_cmds.getmem.cmdl = "md %x:%x;b\r"; /* getmem.cmdl (addr, len) */ + cpu32bug_cmds.getmem.cmdll = NULL; /* getmem.cmdll (addr, len) */ + cpu32bug_cmds.getmem.resp_delim = " "; /* getmem.resp_delim */ + cpu32bug_cmds.getmem.term = NULL; /* getmem.term */ + cpu32bug_cmds.getmem.term_cmd = NULL; /* getmem.term_cmd */ + cpu32bug_cmds.setreg.cmd = "rs %s %x\r"; /* setreg.cmd (name, value) */ + cpu32bug_cmds.setreg.resp_delim = NULL; /* setreg.resp_delim */ + cpu32bug_cmds.setreg.term = NULL; /* setreg.term */ + cpu32bug_cmds.setreg.term_cmd = NULL; /* setreg.term_cmd */ + cpu32bug_cmds.getreg.cmd = "rs %s\r"; /* getreg.cmd (name) */ + cpu32bug_cmds.getreg.resp_delim = "="; /* getreg.resp_delim */ + cpu32bug_cmds.getreg.term = NULL; /* getreg.term */ + cpu32bug_cmds.getreg.term_cmd = NULL; /* getreg.term_cmd */ + cpu32bug_cmds.dump_registers = "rd\r"; /* dump_registers */ + cpu32bug_cmds.register_pattern = "\\(\\w+\\) +=\\([0-9a-fA-F]+\\b\\)"; /* register_pattern */ + cpu32bug_cmds.supply_register = cpu32bug_supply_register; /* supply_register */ + cpu32bug_cmds.load_routine = NULL; /* load_routine (defaults to SRECs) */ + cpu32bug_cmds.load = "lo\r"; /* download command */ + cpu32bug_cmds.loadresp = "\n"; /* load response */ + cpu32bug_cmds.prompt = "CPU32Bug>"; /* monitor command prompt */ + cpu32bug_cmds.line_term = "\r"; /* end-of-line terminator */ + cpu32bug_cmds.cmd_end = NULL; /* optional command terminator */ + cpu32bug_cmds.target = &cpu32bug_ops; /* target operations */ + cpu32bug_cmds.stopbits = SERIAL_1_STOPBITS; /* number of stop bits */ + cpu32bug_cmds.regnames = NULL; /* registers names */ + cpu32bug_cmds.regname = cpu32bug_regname; + cpu32bug_cmds.magic = MONITOR_OPS_MAGIC; /* magic */ +}; /* init_cpu32bug_cmds */ + +static void +cpu32bug_open (char *args, int from_tty) +{ + monitor_open (args, &cpu32bug_cmds, from_tty); +} + +extern initialize_file_ftype _initialize_cpu32bug_rom; /* -Wmissing-prototypes */ + +void +_initialize_cpu32bug_rom (void) +{ + init_cpu32bug_cmds (); + init_monitor_ops (&cpu32bug_ops); + + cpu32bug_ops.to_shortname = "cpu32bug"; + cpu32bug_ops.to_longname = "CPU32Bug monitor"; + cpu32bug_ops.to_doc = "Debug via the CPU32Bug monitor.\n\ +Specify the serial device it is connected to (e.g. /dev/ttya)."; + cpu32bug_ops.to_open = cpu32bug_open; + + add_target (&cpu32bug_ops); +} diff --git a/contrib/gdb/gdb/gnu-nat.c b/contrib/gdb/gdb/gnu-nat.c new file mode 100644 index 0000000..a61d577 --- /dev/null +++ b/contrib/gdb/gdb/gnu-nat.c @@ -0,0 +1,3409 @@ +/* Interface GDB to the GNU Hurd. + Copyright 1992, 1995, 1996, 1997, 1998, 1999, 2000, 2001 + Free Software Foundation, Inc. + + This file is part of GDB. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + Some code and ideas from m3-nat.c by Jukka Virtanen <jtv@hut.fi> + + 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 <ctype.h> +#include <errno.h> +#include <limits.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include "gdb_string.h" +#include <sys/ptrace.h> + +#include <mach.h> +#include <mach_error.h> +#include <mach/exception.h> +#include <mach/message.h> +#include <mach/notify.h> +#include <mach/vm_attributes.h> + +#include <hurd.h> +#include <hurd/interrupt.h> +#include <hurd/msg.h> +#include <hurd/msg_request.h> +#include <hurd/process.h> +#include <hurd/process_request.h> +#include <hurd/signal.h> +#include <hurd/sigpreempt.h> + +#include <portinfo.h> + +#include "defs.h" +#include "inferior.h" +#include "symtab.h" +#include "value.h" +#include "language.h" +#include "target.h" +#include "gdb_wait.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "gdbthread.h" +#include "gdb_assert.h" +#include "gdb_obstack.h" + +#include "gnu-nat.h" + +#include "exc_request_S.h" +#include "notify_S.h" +#include "process_reply_S.h" +#include "msg_reply_S.h" +#include "exc_request_U.h" +#include "msg_U.h" + +static process_t proc_server = MACH_PORT_NULL; + +/* If we've sent a proc_wait_request to the proc server, the pid of the + process we asked about. We can only ever have one outstanding. */ +int proc_wait_pid = 0; + +/* The number of wait requests we've sent, and expect replies from. */ +int proc_waits_pending = 0; + +int gnu_debug_flag = 0; + +/* Forward decls */ + +extern struct target_ops gnu_ops; + +struct inf *make_inf (); +void inf_clear_wait (struct inf *inf); +void inf_cleanup (struct inf *inf); +void inf_startup (struct inf *inf, int pid); +int inf_update_suspends (struct inf *inf); +void inf_set_pid (struct inf *inf, pid_t pid); +void inf_validate_procs (struct inf *inf); +void inf_steal_exc_ports (struct inf *inf); +void inf_restore_exc_ports (struct inf *inf); +struct proc *inf_tid_to_proc (struct inf *inf, int tid); +void inf_set_threads_resume_sc (struct inf *inf, + struct proc *run_thread, + int run_others); +int inf_set_threads_resume_sc_for_signal_thread (struct inf *inf); +void inf_suspend (struct inf *inf); +void inf_resume (struct inf *inf); +void inf_set_step_thread (struct inf *inf, struct proc *proc); +void inf_detach (struct inf *inf); +void inf_attach (struct inf *inf, int pid); +void inf_signal (struct inf *inf, enum target_signal sig); +void inf_continue (struct inf *inf); + +#define inf_debug(_inf, msg, args...) \ + do { struct inf *__inf = (_inf); \ + debug ("{inf %d %p}: " msg, __inf->pid, __inf , ##args); } while (0) + +void proc_abort (struct proc *proc, int force); +struct proc *make_proc (struct inf *inf, mach_port_t port, int tid); +struct proc *_proc_free (struct proc *proc); +int proc_update_sc (struct proc *proc); +error_t proc_get_exception_port (struct proc *proc, mach_port_t * port); +error_t proc_set_exception_port (struct proc *proc, mach_port_t port); +static mach_port_t _proc_get_exc_port (struct proc *proc); +void proc_steal_exc_port (struct proc *proc, mach_port_t exc_port); +void proc_restore_exc_port (struct proc *proc); +int proc_trace (struct proc *proc, int set); + +/* Evaluate RPC_EXPR in a scope with the variables MSGPORT and REFPORT bound + to INF's msg port and task port respectively. If it has no msg port, + EIEIO is returned. INF must refer to a running process! */ +#define INF_MSGPORT_RPC(inf, rpc_expr) \ + HURD_MSGPORT_RPC (proc_getmsgport (proc_server, inf->pid, &msgport), \ + (refport = inf->task->port, 0), 0, \ + msgport ? (rpc_expr) : EIEIO) + +/* Like INF_MSGPORT_RPC, but will also resume the signal thread to ensure + there's someone around to deal with the RPC (and resuspend things + afterwards). This effects INF's threads' resume_sc count. */ +#define INF_RESUME_MSGPORT_RPC(inf, rpc_expr) \ + (inf_set_threads_resume_sc_for_signal_thread (inf) \ + ? ({ error_t __e; \ + inf_resume (inf); \ + __e = INF_MSGPORT_RPC (inf, rpc_expr); \ + inf_suspend (inf); \ + __e; }) \ + : EIEIO) + + +/* The state passed by an exception message. */ +struct exc_state + { + int exception; /* The exception code */ + int code, subcode; + mach_port_t handler; /* The real exception port to handle this. */ + mach_port_t reply; /* The reply port from the exception call. */ + }; + +/* The results of the last wait an inf did. */ +struct inf_wait + { + struct target_waitstatus status; /* The status returned to gdb. */ + struct exc_state exc; /* The exception that caused us to return. */ + struct proc *thread; /* The thread in question. */ + int suppress; /* Something trivial happened. */ + }; + +/* The state of an inferior. */ +struct inf + { + /* Fields describing the current inferior. */ + + struct proc *task; /* The mach task. */ + struct proc *threads; /* A linked list of all threads in TASK. */ + + /* True if THREADS needn't be validated by querying the task. We assume that + we and the task in question are the only ones frobbing the thread list, + so as long as we don't let any code run, we don't have to worry about + THREADS changing. */ + int threads_up_to_date; + + pid_t pid; /* The real system PID. */ + + struct inf_wait wait; /* What to return from target_wait. */ + + /* One thread proc in INF may be in `single-stepping mode'. This is it. */ + struct proc *step_thread; + + /* The thread we think is the signal thread. */ + struct proc *signal_thread; + + mach_port_t event_port; /* Where we receive various msgs. */ + + /* True if we think at least one thread in the inferior could currently be + running. */ + unsigned int running:1; + + /* True if the process has stopped (in the proc server sense). Note that + since a proc server `stop' leaves the signal thread running, the inf can + be RUNNING && STOPPED... */ + unsigned int stopped:1; + + /* True if the inferior has no message port. */ + unsigned int nomsg:1; + + /* True if the inferior is traced. */ + unsigned int traced:1; + + /* True if we shouldn't try waiting for the inferior, usually because we + can't for some reason. */ + unsigned int no_wait:1; + + /* When starting a new inferior, we don't try to validate threads until all + the proper execs have been done. This is a count of how many execs we + expect to happen. */ + unsigned pending_execs; + + /* Fields describing global state */ + + /* The task suspend count used when gdb has control. This is normally 1 to + make things easier for us, but sometimes (like when attaching to vital + system servers) it may be desirable to let the task continue to run + (pausing individual threads as necessary). */ + int pause_sc; + + /* The task suspend count left when detaching from a task. */ + int detach_sc; + + /* The initial values used for the run_sc and pause_sc of newly discovered + threads -- see the definition of those fields in struct proc. */ + int default_thread_run_sc; + int default_thread_pause_sc; + int default_thread_detach_sc; + + /* True if the process should be traced when started/attached. Newly + started processes *must* be traced at first to exec them properly, but + if this is false, tracing is turned off as soon it has done so. */ + int want_signals; + + /* True if exceptions from the inferior process should be trapped. This + must be on to use breakpoints. */ + int want_exceptions; + }; + + +int +__proc_pid (struct proc *proc) +{ + return proc->inf->pid; +} + + +/* Update PROC's real suspend count to match it's desired one. Returns true + if we think PROC is now in a runnable state. */ +int +proc_update_sc (struct proc *proc) +{ + int running; + int err = 0; + int delta = proc->sc - proc->cur_sc; + + if (delta) + proc_debug (proc, "sc: %d --> %d", proc->cur_sc, proc->sc); + + if (proc->sc == 0 && proc->state_changed) + /* Since PROC may start running, we must write back any state changes. */ + { + gdb_assert (proc_is_thread (proc)); + proc_debug (proc, "storing back changed thread state"); + err = thread_set_state (proc->port, THREAD_STATE_FLAVOR, + (thread_state_t) &proc->state, THREAD_STATE_SIZE); + if (!err) + proc->state_changed = 0; + } + + if (delta > 0) + { + while (delta-- > 0 && !err) + { + if (proc_is_task (proc)) + err = task_suspend (proc->port); + else + err = thread_suspend (proc->port); + } + } + else + { + while (delta++ < 0 && !err) + { + if (proc_is_task (proc)) + err = task_resume (proc->port); + else + err = thread_resume (proc->port); + } + } + if (!err) + proc->cur_sc = proc->sc; + + /* If we got an error, then the task/thread has disappeared. */ + running = !err && proc->sc == 0; + + proc_debug (proc, "is %s", err ? "dead" : running ? "running" : "suspended"); + if (err) + proc_debug (proc, "err = %s", safe_strerror (err)); + + if (running) + { + proc->aborted = 0; + proc->state_valid = proc->state_changed = 0; + proc->fetched_regs = 0; + } + + return running; +} + + +/* Thread_abort is called on PROC if needed. PROC must be a thread proc. + If PROC is deemed `precious', then nothing is done unless FORCE is true. + In particular, a thread is precious if it's running (in which case forcing + it includes suspending it first), or if it has an exception pending. */ +void +proc_abort (struct proc *proc, int force) +{ + gdb_assert (proc_is_thread (proc)); + + if (!proc->aborted) + { + struct inf *inf = proc->inf; + int running = (proc->cur_sc == 0 && inf->task->cur_sc == 0); + + if (running && force) + { + proc->sc = 1; + inf_update_suspends (proc->inf); + running = 0; + warning ("Stopped %s.", proc_string (proc)); + } + else if (proc == inf->wait.thread && inf->wait.exc.reply && !force) + /* An exception is pending on PROC, which don't mess with. */ + running = 1; + + if (!running) + /* We only abort the thread if it's not actually running. */ + { + thread_abort (proc->port); + proc_debug (proc, "aborted"); + proc->aborted = 1; + } + else + proc_debug (proc, "not aborting"); + } +} + +/* Make sure that the state field in PROC is up to date, and return a pointer + to it, or 0 if something is wrong. If WILL_MODIFY is true, makes sure + that the thread is stopped and aborted first, and sets the state_changed + field in PROC to true. */ +thread_state_t +proc_get_state (struct proc *proc, int will_modify) +{ + int was_aborted = proc->aborted; + + proc_debug (proc, "updating state info%s", + will_modify ? " (with intention to modify)" : ""); + + proc_abort (proc, will_modify); + + if (!was_aborted && proc->aborted) + /* PROC's state may have changed since we last fetched it. */ + proc->state_valid = 0; + + if (!proc->state_valid) + { + mach_msg_type_number_t state_size = THREAD_STATE_SIZE; + error_t err = + thread_get_state (proc->port, THREAD_STATE_FLAVOR, + (thread_state_t) &proc->state, &state_size); + proc_debug (proc, "getting thread state"); + proc->state_valid = !err; + } + + if (proc->state_valid) + { + if (will_modify) + proc->state_changed = 1; + return (thread_state_t) &proc->state; + } + else + return 0; +} + + +/* Set PORT to PROC's exception port. */ +error_t +proc_get_exception_port (struct proc * proc, mach_port_t * port) +{ + if (proc_is_task (proc)) + return task_get_exception_port (proc->port, port); + else + return thread_get_exception_port (proc->port, port); +} + +/* Set PROC's exception port to PORT. */ +error_t +proc_set_exception_port (struct proc * proc, mach_port_t port) +{ + proc_debug (proc, "setting exception port: %d", port); + if (proc_is_task (proc)) + return task_set_exception_port (proc->port, port); + else + return thread_set_exception_port (proc->port, port); +} + +/* Get PROC's exception port, cleaning up a bit if proc has died. */ +static mach_port_t +_proc_get_exc_port (struct proc *proc) +{ + mach_port_t exc_port; + error_t err = proc_get_exception_port (proc, &exc_port); + + if (err) + /* PROC must be dead. */ + { + if (proc->exc_port) + mach_port_deallocate (mach_task_self (), proc->exc_port); + proc->exc_port = MACH_PORT_NULL; + if (proc->saved_exc_port) + mach_port_deallocate (mach_task_self (), proc->saved_exc_port); + proc->saved_exc_port = MACH_PORT_NULL; + } + + return exc_port; +} + +/* Replace PROC's exception port with EXC_PORT, unless it's already been + done. Stash away any existing exception port so we can restore it later. */ +void +proc_steal_exc_port (struct proc *proc, mach_port_t exc_port) +{ + mach_port_t cur_exc_port = _proc_get_exc_port (proc); + + if (cur_exc_port) + { + error_t err = 0; + + proc_debug (proc, "inserting exception port: %d", exc_port); + + if (cur_exc_port != exc_port) + /* Put in our exception port. */ + err = proc_set_exception_port (proc, exc_port); + + if (err || cur_exc_port == proc->exc_port) + /* We previously set the exception port, and it's still set. So we + just keep the old saved port which is what the proc set. */ + { + if (cur_exc_port) + mach_port_deallocate (mach_task_self (), cur_exc_port); + } + else + /* Keep a copy of PROC's old exception port so it can be restored. */ + { + if (proc->saved_exc_port) + mach_port_deallocate (mach_task_self (), proc->saved_exc_port); + proc->saved_exc_port = cur_exc_port; + } + + proc_debug (proc, "saved exception port: %d", proc->saved_exc_port); + + if (!err) + proc->exc_port = exc_port; + else + warning ("Error setting exception port for %s: %s", + proc_string (proc), safe_strerror (err)); + } +} + +/* If we previously replaced PROC's exception port, put back what we + found there at the time, unless *our* exception port has since been + overwritten, in which case who knows what's going on. */ +void +proc_restore_exc_port (struct proc *proc) +{ + mach_port_t cur_exc_port = _proc_get_exc_port (proc); + + if (cur_exc_port) + { + error_t err = 0; + + proc_debug (proc, "restoring real exception port"); + + if (proc->exc_port == cur_exc_port) + /* Our's is still there. */ + err = proc_set_exception_port (proc, proc->saved_exc_port); + + if (proc->saved_exc_port) + mach_port_deallocate (mach_task_self (), proc->saved_exc_port); + proc->saved_exc_port = MACH_PORT_NULL; + + if (!err) + proc->exc_port = MACH_PORT_NULL; + else + warning ("Error setting exception port for %s: %s", + proc_string (proc), safe_strerror (err)); + } +} + + +/* Turns hardware tracing in PROC on or off when SET is true or false, + respectively. Returns true on success. */ +int +proc_trace (struct proc *proc, int set) +{ + thread_state_t state = proc_get_state (proc, 1); + + if (!state) + return 0; /* the thread must be dead. */ + + proc_debug (proc, "tracing %s", set ? "on" : "off"); + + if (set) + { + /* XXX We don't get the exception unless the thread has its own + exception port???? */ + if (proc->exc_port == MACH_PORT_NULL) + proc_steal_exc_port (proc, proc->inf->event_port); + THREAD_STATE_SET_TRACED (state); + } + else + THREAD_STATE_CLEAR_TRACED (state); + + return 1; +} + + +/* A variable from which to assign new TIDs. */ +static int next_thread_id = 1; + +/* Returns a new proc structure with the given fields. Also adds a + notification for PORT becoming dead to be sent to INF's notify port. */ +struct proc * +make_proc (struct inf *inf, mach_port_t port, int tid) +{ + error_t err; + mach_port_t prev_port = MACH_PORT_NULL; + struct proc *proc = xmalloc (sizeof (struct proc)); + + proc->port = port; + proc->tid = tid; + proc->inf = inf; + proc->next = 0; + proc->saved_exc_port = MACH_PORT_NULL; + proc->exc_port = MACH_PORT_NULL; + + proc->sc = 0; + proc->cur_sc = 0; + + /* Note that these are all the values for threads; the task simply uses the + corresponding field in INF directly. */ + proc->run_sc = inf->default_thread_run_sc; + proc->pause_sc = inf->default_thread_pause_sc; + proc->detach_sc = inf->default_thread_detach_sc; + proc->resume_sc = proc->run_sc; + + proc->aborted = 0; + proc->dead = 0; + proc->state_valid = 0; + proc->state_changed = 0; + + proc_debug (proc, "is new"); + + /* Get notified when things die. */ + err = + mach_port_request_notification (mach_task_self (), port, + MACH_NOTIFY_DEAD_NAME, 1, + inf->event_port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &prev_port); + if (err) + warning ("Couldn't request notification for port %d: %s", + port, safe_strerror (err)); + else + { + proc_debug (proc, "notifications to: %d", inf->event_port); + if (prev_port != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), prev_port); + } + + if (inf->want_exceptions) + { + if (proc_is_task (proc)) + /* Make the task exception port point to us. */ + proc_steal_exc_port (proc, inf->event_port); + else + /* Just clear thread exception ports -- they default to the + task one. */ + proc_steal_exc_port (proc, MACH_PORT_NULL); + } + + return proc; +} + +/* Frees PROC and any resources it uses, and returns the value of PROC's + next field. */ +struct proc * +_proc_free (struct proc *proc) +{ + struct inf *inf = proc->inf; + struct proc *next = proc->next; + + proc_debug (proc, "freeing..."); + + if (proc == inf->step_thread) + /* Turn off single stepping. */ + inf_set_step_thread (inf, 0); + if (proc == inf->wait.thread) + inf_clear_wait (inf); + if (proc == inf->signal_thread) + inf->signal_thread = 0; + + if (proc->port != MACH_PORT_NULL) + { + if (proc->exc_port != MACH_PORT_NULL) + /* Restore the original exception port. */ + proc_restore_exc_port (proc); + if (proc->cur_sc != 0) + /* Resume the thread/task. */ + { + proc->sc = 0; + proc_update_sc (proc); + } + mach_port_deallocate (mach_task_self (), proc->port); + } + + xfree (proc); + return next; +} + + +struct inf * +make_inf (void) +{ + struct inf *inf = xmalloc (sizeof (struct inf)); + + inf->task = 0; + inf->threads = 0; + inf->threads_up_to_date = 0; + inf->pid = 0; + inf->wait.status.kind = TARGET_WAITKIND_SPURIOUS; + inf->wait.thread = 0; + inf->wait.exc.handler = MACH_PORT_NULL; + inf->wait.exc.reply = MACH_PORT_NULL; + inf->step_thread = 0; + inf->signal_thread = 0; + inf->event_port = MACH_PORT_NULL; + inf->running = 0; + inf->stopped = 0; + inf->nomsg = 1; + inf->traced = 0; + inf->no_wait = 0; + inf->pending_execs = 0; + inf->pause_sc = 1; + inf->detach_sc = 0; + inf->default_thread_run_sc = 0; + inf->default_thread_pause_sc = 0; + inf->default_thread_detach_sc = 0; + inf->want_signals = 1; /* By default */ + inf->want_exceptions = 1; /* By default */ + + return inf; +} + +/* Clear INF's target wait status. */ +void +inf_clear_wait (struct inf *inf) +{ + inf_debug (inf, "clearing wait"); + inf->wait.status.kind = TARGET_WAITKIND_SPURIOUS; + inf->wait.thread = 0; + inf->wait.suppress = 0; + if (inf->wait.exc.handler != MACH_PORT_NULL) + { + mach_port_deallocate (mach_task_self (), inf->wait.exc.handler); + inf->wait.exc.handler = MACH_PORT_NULL; + } + if (inf->wait.exc.reply != MACH_PORT_NULL) + { + mach_port_deallocate (mach_task_self (), inf->wait.exc.reply); + inf->wait.exc.reply = MACH_PORT_NULL; + } +} + + +void +inf_cleanup (struct inf *inf) +{ + inf_debug (inf, "cleanup"); + + inf_clear_wait (inf); + + inf_set_pid (inf, -1); + inf->pid = 0; + inf->running = 0; + inf->stopped = 0; + inf->nomsg = 1; + inf->traced = 0; + inf->no_wait = 0; + inf->pending_execs = 0; + + if (inf->event_port) + { + mach_port_destroy (mach_task_self (), inf->event_port); + inf->event_port = MACH_PORT_NULL; + } +} + +void +inf_startup (struct inf *inf, int pid) +{ + error_t err; + + inf_debug (inf, "startup: pid = %d", pid); + + inf_cleanup (inf); + + /* Make the port on which we receive all events. */ + err = mach_port_allocate (mach_task_self (), + MACH_PORT_RIGHT_RECEIVE, &inf->event_port); + if (err) + error ("Error allocating event port: %s", safe_strerror (err)); + + /* Make a send right for it, so we can easily copy it for other people. */ + mach_port_insert_right (mach_task_self (), inf->event_port, + inf->event_port, MACH_MSG_TYPE_MAKE_SEND); + inf_set_pid (inf, pid); +} + + +/* Close current process, if any, and attach INF to process PORT. */ +void +inf_set_pid (struct inf *inf, pid_t pid) +{ + task_t task_port; + struct proc *task = inf->task; + + inf_debug (inf, "setting pid: %d", pid); + + if (pid < 0) + task_port = MACH_PORT_NULL; + else + { + error_t err = proc_pid2task (proc_server, pid, &task_port); + if (err) + error ("Error getting task for pid %d: %s", pid, safe_strerror (err)); + } + + inf_debug (inf, "setting task: %d", task_port); + + if (inf->pause_sc) + task_suspend (task_port); + + if (task && task->port != task_port) + { + inf->task = 0; + inf_validate_procs (inf); /* Trash all the threads. */ + _proc_free (task); /* And the task. */ + } + + if (task_port != MACH_PORT_NULL) + { + inf->task = make_proc (inf, task_port, PROC_TID_TASK); + inf->threads_up_to_date = 0; + } + + if (inf->task) + { + inf->pid = pid; + if (inf->pause_sc) + /* Reflect task_suspend above. */ + inf->task->sc = inf->task->cur_sc = 1; + } + else + inf->pid = -1; +} + + +/* Validates INF's stopped, nomsg and traced field from the actual + proc server state. Note that the traced field is only updated from + the proc server state if we do not have a message port. If we do + have a message port we'd better look at the tracemask itself. */ +static void +inf_validate_procinfo (struct inf *inf) +{ + char *noise; + mach_msg_type_number_t noise_len = 0; + struct procinfo *pi; + mach_msg_type_number_t pi_len = 0; + int info_flags = 0; + error_t err = + proc_getprocinfo (proc_server, inf->pid, &info_flags, + (procinfo_t *) &pi, &pi_len, &noise, &noise_len); + + if (!err) + { + inf->stopped = !!(pi->state & PI_STOPPED); + inf->nomsg = !!(pi->state & PI_NOMSG); + if (inf->nomsg) + inf->traced = !!(pi->state & PI_TRACED); + vm_deallocate (mach_task_self (), (vm_address_t) pi, pi_len); + if (noise_len > 0) + vm_deallocate (mach_task_self (), (vm_address_t) noise, noise_len); + } +} + +/* Validates INF's task suspend count. If it's higher than we expect, + verify with the user before `stealing' the extra count. */ +static void +inf_validate_task_sc (struct inf *inf) +{ + char *noise; + mach_msg_type_number_t noise_len = 0; + struct procinfo *pi; + mach_msg_type_number_t pi_len = 0; + int info_flags = PI_FETCH_TASKINFO; + int suspend_count = -1; + error_t err; + + retry: + err = proc_getprocinfo (proc_server, inf->pid, &info_flags, + (procinfo_t *) &pi, &pi_len, &noise, &noise_len); + if (err) + { + inf->task->dead = 1; /* oh well */ + return; + } + + if (inf->task->cur_sc < pi->taskinfo.suspend_count && suspend_count == -1) + { + /* The proc server might have suspended the task while stopping + it. This happens when the task is handling a traced signal. + Refetch the suspend count. The proc server should be + finished stopping the task by now. */ + suspend_count = pi->taskinfo.suspend_count; + goto retry; + } + + suspend_count = pi->taskinfo.suspend_count; + + vm_deallocate (mach_task_self (), (vm_address_t) pi, pi_len); + if (noise_len > 0) + vm_deallocate (mach_task_self (), (vm_address_t) pi, pi_len); + + if (inf->task->cur_sc < suspend_count) + { + int abort; + + target_terminal_ours (); /* Allow I/O. */ + abort = !query ("Pid %d has an additional task suspend count of %d;" + " clear it? ", inf->pid, + suspend_count - inf->task->cur_sc); + target_terminal_inferior (); /* Give it back to the child. */ + + if (abort) + error ("Additional task suspend count left untouched."); + + inf->task->cur_sc = suspend_count; + } +} + +/* Turns tracing for INF on or off, depending on ON, unless it already + is. If INF is running, the resume_sc count of INF's threads will + be modified, and the signal thread will briefly be run to change + the trace state. */ +void +inf_set_traced (struct inf *inf, int on) +{ + if (on == inf->traced) + return; + + if (inf->task && !inf->task->dead) + /* Make it take effect immediately. */ + { + sigset_t mask = on ? ~(sigset_t) 0 : 0; + error_t err = + INF_RESUME_MSGPORT_RPC (inf, msg_set_init_int (msgport, refport, + INIT_TRACEMASK, mask)); + if (err == EIEIO) + { + if (on) + warning ("Can't modify tracing state for pid %d: %s", + inf->pid, "No signal thread"); + inf->traced = on; + } + else if (err) + warning ("Can't modify tracing state for pid %d: %s", + inf->pid, safe_strerror (err)); + else + inf->traced = on; + } + else + inf->traced = on; +} + + +/* Makes all the real suspend count deltas of all the procs in INF + match the desired values. Careful to always do thread/task suspend + counts in the safe order. Returns true if at least one thread is + thought to be running. */ +int +inf_update_suspends (struct inf *inf) +{ + struct proc *task = inf->task; + /* We don't have to update INF->threads even though we're iterating over it + because we'll change a thread only if it already has an existing proc + entry. */ + + inf_debug (inf, "updating suspend counts"); + + if (task) + { + struct proc *thread; + int task_running = (task->sc == 0), thread_running = 0; + + if (task->sc > task->cur_sc) + /* The task is becoming _more_ suspended; do before any threads. */ + task_running = proc_update_sc (task); + + if (inf->pending_execs) + /* When we're waiting for an exec, things may be happening behind our + back, so be conservative. */ + thread_running = 1; + + /* Do all the thread suspend counts. */ + for (thread = inf->threads; thread; thread = thread->next) + thread_running |= proc_update_sc (thread); + + if (task->sc != task->cur_sc) + /* We didn't do the task first, because we wanted to wait for the + threads; do it now. */ + task_running = proc_update_sc (task); + + inf_debug (inf, "%srunning...", + (thread_running && task_running) ? "" : "not "); + + inf->running = thread_running && task_running; + + /* Once any thread has executed some code, we can't depend on the + threads list any more. */ + if (inf->running) + inf->threads_up_to_date = 0; + + return inf->running; + } + + return 0; +} + + +/* Converts a GDB pid to a struct proc. */ +struct proc * +inf_tid_to_thread (struct inf *inf, int tid) +{ + struct proc *thread = inf->threads; + + while (thread) + if (thread->tid == tid) + return thread; + else + thread = thread->next; + return 0; +} + +/* Converts a thread port to a struct proc. */ +struct proc * +inf_port_to_thread (struct inf *inf, mach_port_t port) +{ + struct proc *thread = inf->threads; + while (thread) + if (thread->port == port) + return thread; + else + thread = thread->next; + return 0; +} + + +/* Make INF's list of threads be consistent with reality of TASK. */ +void +inf_validate_procs (struct inf *inf) +{ + thread_array_t threads; + mach_msg_type_number_t num_threads, i; + struct proc *task = inf->task; + + /* If no threads are currently running, this function will guarantee that + things are up to date. The exception is if there are zero threads -- + then it is almost certainly in an odd state, and probably some outside + agent will create threads. */ + inf->threads_up_to_date = inf->threads ? !inf->running : 0; + + if (task) + { + error_t err = task_threads (task->port, &threads, &num_threads); + inf_debug (inf, "fetching threads"); + if (err) + /* TASK must be dead. */ + { + task->dead = 1; + task = 0; + } + } + + if (!task) + { + num_threads = 0; + inf_debug (inf, "no task"); + } + + { + /* Make things normally linear. */ + mach_msg_type_number_t search_start = 0; + /* Which thread in PROCS corresponds to each task thread, & the task. */ + struct proc *matched[num_threads + 1]; + /* The last thread in INF->threads, so we can add to the end. */ + struct proc *last = 0; + /* The current thread we're considering. */ + struct proc *thread = inf->threads; + + memset (matched, 0, sizeof (matched)); + + while (thread) + { + mach_msg_type_number_t left; + + for (i = search_start, left = num_threads; left; i++, left--) + { + if (i >= num_threads) + i -= num_threads; /* I wrapped around. */ + if (thread->port == threads[i]) + /* We already know about this thread. */ + { + matched[i] = thread; + last = thread; + thread = thread->next; + search_start++; + break; + } + } + + if (!left) + { + proc_debug (thread, "died!"); + thread->port = MACH_PORT_NULL; + thread = _proc_free (thread); /* THREAD is dead. */ + (last ? last->next : inf->threads) = thread; + } + } + + for (i = 0; i < num_threads; i++) + { + if (matched[i]) + /* Throw away the duplicate send right. */ + mach_port_deallocate (mach_task_self (), threads[i]); + else + /* THREADS[I] is a thread we don't know about yet! */ + { + thread = make_proc (inf, threads[i], next_thread_id++); + (last ? last->next : inf->threads) = thread; + last = thread; + proc_debug (thread, "new thread: %d", threads[i]); + add_thread (pid_to_ptid (thread->tid)); /* Tell GDB's generic thread code. */ + } + } + + vm_deallocate (mach_task_self (), + (vm_address_t) threads, (num_threads * sizeof (thread_t))); + } +} + + +/* Makes sure that INF's thread list is synced with the actual process. */ +int +inf_update_procs (struct inf *inf) +{ + if (!inf->task) + return 0; + if (!inf->threads_up_to_date) + inf_validate_procs (inf); + return !!inf->task; +} + +/* Sets the resume_sc of each thread in inf. That of RUN_THREAD is set to 0, + and others are set to their run_sc if RUN_OTHERS is true, and otherwise + their pause_sc. */ +void +inf_set_threads_resume_sc (struct inf *inf, + struct proc *run_thread, int run_others) +{ + struct proc *thread; + inf_update_procs (inf); + for (thread = inf->threads; thread; thread = thread->next) + if (thread == run_thread) + thread->resume_sc = 0; + else if (run_others) + thread->resume_sc = thread->run_sc; + else + thread->resume_sc = thread->pause_sc; +} + + +/* Cause INF to continue execution immediately; individual threads may still + be suspended (but their suspend counts will be updated). */ +void +inf_resume (struct inf *inf) +{ + struct proc *thread; + + inf_update_procs (inf); + + for (thread = inf->threads; thread; thread = thread->next) + thread->sc = thread->resume_sc; + + if (inf->task) + { + if (!inf->pending_execs) + /* Try to make sure our task count is correct -- in the case where + we're waiting for an exec though, things are too volatile, so just + assume things will be reasonable (which they usually will be). */ + inf_validate_task_sc (inf); + inf->task->sc = 0; + } + + inf_update_suspends (inf); +} + +/* Cause INF to stop execution immediately; individual threads may still + be running. */ +void +inf_suspend (struct inf *inf) +{ + struct proc *thread; + + inf_update_procs (inf); + + for (thread = inf->threads; thread; thread = thread->next) + thread->sc = thread->pause_sc; + + if (inf->task) + inf->task->sc = inf->pause_sc; + + inf_update_suspends (inf); +} + + +/* INF has one thread PROC that is in single-stepping mode. This + function changes it to be PROC, changing any old step_thread to be + a normal one. A PROC of 0 clears any existing value. */ +void +inf_set_step_thread (struct inf *inf, struct proc *thread) +{ + gdb_assert (!thread || proc_is_thread (thread)); + + if (thread) + inf_debug (inf, "setting step thread: %d/%d", inf->pid, thread->tid); + else + inf_debug (inf, "clearing step thread"); + + if (inf->step_thread != thread) + { + if (inf->step_thread && inf->step_thread->port != MACH_PORT_NULL) + if (!proc_trace (inf->step_thread, 0)) + return; + if (thread && proc_trace (thread, 1)) + inf->step_thread = thread; + else + inf->step_thread = 0; + } +} + + +/* Set up the thread resume_sc's so that only the signal thread is running + (plus whatever other thread are set to always run). Returns true if we + did so, or false if we can't find a signal thread. */ +int +inf_set_threads_resume_sc_for_signal_thread (struct inf *inf) +{ + if (inf->signal_thread) + { + inf_set_threads_resume_sc (inf, inf->signal_thread, 0); + return 1; + } + else + return 0; +} + +static void +inf_update_signal_thread (struct inf *inf) +{ + /* XXX for now we assume that if there's a msgport, the 2nd thread is + the signal thread. */ + inf->signal_thread = inf->threads ? inf->threads->next : 0; +} + + +/* Detachs from INF's inferior task, letting it run once again... */ +void +inf_detach (struct inf *inf) +{ + struct proc *task = inf->task; + + inf_debug (inf, "detaching..."); + + inf_clear_wait (inf); + inf_set_step_thread (inf, 0); + + if (task) + { + struct proc *thread; + + inf_validate_procinfo (inf); + + inf_set_traced (inf, 0); + if (inf->stopped) + { + if (inf->nomsg) + inf_continue (inf); + else + inf_signal (inf, TARGET_SIGNAL_0); + } + + proc_restore_exc_port (task); + task->sc = inf->detach_sc; + + for (thread = inf->threads; thread; thread = thread->next) + { + proc_restore_exc_port (thread); + thread->sc = thread->detach_sc; + } + + inf_update_suspends (inf); + } + + inf_cleanup (inf); +} + +/* Attaches INF to the process with process id PID, returning it in a + suspended state suitable for debugging. */ +void +inf_attach (struct inf *inf, int pid) +{ + inf_debug (inf, "attaching: %d", pid); + + if (inf->pid) + inf_detach (inf); + + inf_startup (inf, pid); +} + + +/* Makes sure that we've got our exception ports entrenched in the process. */ +void +inf_steal_exc_ports (struct inf *inf) +{ + struct proc *thread; + + inf_debug (inf, "stealing exception ports"); + + inf_set_step_thread (inf, 0); /* The step thread is special. */ + + proc_steal_exc_port (inf->task, inf->event_port); + for (thread = inf->threads; thread; thread = thread->next) + proc_steal_exc_port (thread, MACH_PORT_NULL); +} + +/* Makes sure the process has its own exception ports. */ +void +inf_restore_exc_ports (struct inf *inf) +{ + struct proc *thread; + + inf_debug (inf, "restoring exception ports"); + + inf_set_step_thread (inf, 0); /* The step thread is special. */ + + proc_restore_exc_port (inf->task); + for (thread = inf->threads; thread; thread = thread->next) + proc_restore_exc_port (thread); +} + + +/* Deliver signal SIG to INF. If INF is stopped, delivering a signal, even + signal 0, will continue it. INF is assumed to be in a paused state, and + the resume_sc's of INF's threads may be affected. */ +void +inf_signal (struct inf *inf, enum target_signal sig) +{ + error_t err = 0; + int host_sig = target_signal_to_host (sig); + +#define NAME target_signal_to_name (sig) + + if (host_sig >= _NSIG) + /* A mach exception. Exceptions are encoded in the signal space by + putting them after _NSIG; this assumes they're positive (and not + extremely large)! */ + { + struct inf_wait *w = &inf->wait; + if (w->status.kind == TARGET_WAITKIND_STOPPED + && w->status.value.sig == sig + && w->thread && !w->thread->aborted) + /* We're passing through the last exception we received. This is + kind of bogus, because exceptions are per-thread whereas gdb + treats signals as per-process. We just forward the exception to + the correct handler, even it's not for the same thread as TID -- + i.e., we pretend it's global. */ + { + struct exc_state *e = &w->exc; + inf_debug (inf, "passing through exception:" + " task = %d, thread = %d, exc = %d" + ", code = %d, subcode = %d", + w->thread->port, inf->task->port, + e->exception, e->code, e->subcode); + err = + exception_raise_request (e->handler, + e->reply, MACH_MSG_TYPE_MOVE_SEND_ONCE, + w->thread->port, inf->task->port, + e->exception, e->code, e->subcode); + } + else + error ("Can't forward spontaneous exception (%s).", NAME); + } + else + /* A Unix signal. */ + if (inf->stopped) + /* The process is stopped and expecting a signal. Just send off a + request and let it get handled when we resume everything. */ + { + inf_debug (inf, "sending %s to stopped process", NAME); + err = + INF_MSGPORT_RPC (inf, + msg_sig_post_untraced_request (msgport, + inf->event_port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + host_sig, 0, + refport)); + if (!err) + /* Posting an untraced signal automatically continues it. + We clear this here rather than when we get the reply + because we'd rather assume it's not stopped when it + actually is, than the reverse. */ + inf->stopped = 0; + } + else + /* It's not expecting it. We have to let just the signal thread + run, and wait for it to get into a reasonable state before we + can continue the rest of the process. When we finally resume the + process the signal we request will be the very first thing that + happens. */ + { + inf_debug (inf, "sending %s to unstopped process" + " (so resuming signal thread)", NAME); + err = + INF_RESUME_MSGPORT_RPC (inf, + msg_sig_post_untraced (msgport, host_sig, + 0, refport)); + } + + if (err == EIEIO) + /* Can't do too much... */ + warning ("Can't deliver signal %s: No signal thread.", NAME); + else if (err) + warning ("Delivering signal %s: %s", NAME, safe_strerror (err)); + +#undef NAME +} + + +/* Continue INF without delivering a signal. This is meant to be used + when INF does not have a message port. */ +void +inf_continue (struct inf *inf) +{ + process_t proc; + error_t err = proc_pid2proc (proc_server, inf->pid, &proc); + + if (!err) + { + inf_debug (inf, "continuing process"); + + err = proc_mark_cont (proc); + if (!err) + { + struct proc *thread; + + for (thread = inf->threads; thread; thread = thread->next) + thread_resume (thread->port); + + inf->stopped = 0; + } + } + + if (err) + warning ("Can't continue process: %s", safe_strerror (err)); +} + + +/* The inferior used for all gdb target ops. */ +struct inf *current_inferior = 0; + +/* The inferior being waited for by gnu_wait. Since GDB is decidely not + multi-threaded, we don't bother to lock this. */ +struct inf *waiting_inf; + +/* Wait for something to happen in the inferior, returning what in STATUS. */ +static ptid_t +gnu_wait (ptid_t tid, struct target_waitstatus *status) +{ + struct msg + { + mach_msg_header_t hdr; + mach_msg_type_t type; + int data[8000]; + } msg; + error_t err; + struct proc *thread; + struct inf *inf = current_inferior; + + extern int exc_server (mach_msg_header_t *, mach_msg_header_t *); + extern int msg_reply_server (mach_msg_header_t *, mach_msg_header_t *); + extern int notify_server (mach_msg_header_t *, mach_msg_header_t *); + extern int process_reply_server (mach_msg_header_t *, mach_msg_header_t *); + + gdb_assert (inf->task); + + if (!inf->threads && !inf->pending_execs) + /* No threads! Assume that maybe some outside agency is frobbing our + task, and really look for new threads. If we can't find any, just tell + the user to try again later. */ + { + inf_validate_procs (inf); + if (!inf->threads && !inf->task->dead) + error ("There are no threads; try again later."); + } + + waiting_inf = inf; + + inf_debug (inf, "waiting for: %d", PIDGET (tid)); + +rewait: + if (proc_wait_pid != inf->pid && !inf->no_wait) + /* Always get information on events from the proc server. */ + { + inf_debug (inf, "requesting wait on pid %d", inf->pid); + + if (proc_wait_pid) + /* The proc server is single-threaded, and only allows a single + outstanding wait request, so we have to cancel the previous one. */ + { + inf_debug (inf, "cancelling previous wait on pid %d", proc_wait_pid); + interrupt_operation (proc_server, 0); + } + + err = + proc_wait_request (proc_server, inf->event_port, inf->pid, WUNTRACED); + if (err) + warning ("wait request failed: %s", safe_strerror (err)); + else + { + inf_debug (inf, "waits pending: %d", proc_waits_pending); + proc_wait_pid = inf->pid; + /* Even if proc_waits_pending was > 0 before, we still won't + get any other replies, because it was either from a + different INF, or a different process attached to INF -- + and the event port, which is the wait reply port, changes + when you switch processes. */ + proc_waits_pending = 1; + } + } + + inf_clear_wait (inf); + + /* What can happen? (1) Dead name notification; (2) Exceptions arrive; + (3) wait reply from the proc server. */ + + inf_debug (inf, "waiting for an event..."); + err = mach_msg (&msg.hdr, MACH_RCV_MSG | MACH_RCV_INTERRUPT, + 0, sizeof (struct msg), inf->event_port, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + + /* Re-suspend the task. */ + inf_suspend (inf); + + if (!inf->task && inf->pending_execs) + /* When doing an exec, it's possible that the old task wasn't reused + (e.g., setuid execs). So if the task seems to have disappeared, + attempt to refetch it, as the pid should still be the same. */ + inf_set_pid (inf, inf->pid); + + if (err == EMACH_RCV_INTERRUPTED) + inf_debug (inf, "interrupted"); + else if (err) + error ("Couldn't wait for an event: %s", safe_strerror (err)); + else + { + struct + { + mach_msg_header_t hdr; + mach_msg_type_t err_type; + kern_return_t err; + char noise[200]; + } + reply; + + inf_debug (inf, "event: msgid = %d", msg.hdr.msgh_id); + + /* Handle what we got. */ + if (!notify_server (&msg.hdr, &reply.hdr) + && !exc_server (&msg.hdr, &reply.hdr) + && !process_reply_server (&msg.hdr, &reply.hdr) + && !msg_reply_server (&msg.hdr, &reply.hdr)) + /* Whatever it is, it's something strange. */ + error ("Got a strange event, msg id = %d.", msg.hdr.msgh_id); + + if (reply.err) + error ("Handling event, msgid = %d: %s", + msg.hdr.msgh_id, safe_strerror (reply.err)); + } + + if (inf->pending_execs) + /* We're waiting for the inferior to finish execing. */ + { + struct inf_wait *w = &inf->wait; + enum target_waitkind kind = w->status.kind; + + if (kind == TARGET_WAITKIND_SPURIOUS) + /* Since gdb is actually counting the number of times the inferior + stops, expecting one stop per exec, we only return major events + while execing. */ + { + w->suppress = 1; + inf_debug (inf, "pending_execs = %d, ignoring minor event", + inf->pending_execs); + } + else if (kind == TARGET_WAITKIND_STOPPED + && w->status.value.sig == TARGET_SIGNAL_TRAP) + /* Ah hah! A SIGTRAP from the inferior while starting up probably + means we've succesfully completed an exec! */ + { + if (--inf->pending_execs == 0) + /* We're done! */ + { +#if 0 /* do we need this? */ + prune_threads (1); /* Get rid of the old shell threads */ + renumber_threads (0); /* Give our threads reasonable names. */ +#endif + } + inf_debug (inf, "pending exec completed, pending_execs => %d", + inf->pending_execs); + } + else if (kind == TARGET_WAITKIND_STOPPED) + /* It's possible that this signal is because of a crashed process + being handled by the hurd crash server; in this case, the process + will have an extra task suspend, which we need to know about. + Since the code in inf_resume that normally checks for this is + disabled while INF->pending_execs, we do the check here instead. */ + inf_validate_task_sc (inf); + } + + if (inf->wait.suppress) + /* Some totally spurious event happened that we don't consider + worth returning to gdb. Just keep waiting. */ + { + inf_debug (inf, "suppressing return, rewaiting..."); + inf_resume (inf); + goto rewait; + } + + /* Pass back out our results. */ + bcopy (&inf->wait.status, status, sizeof (*status)); + + thread = inf->wait.thread; + if (thread) + tid = pid_to_ptid (thread->tid); + else + thread = inf_tid_to_thread (inf, PIDGET (tid)); + + if (!thread || thread->port == MACH_PORT_NULL) + { + /* TID is dead; try and find a new thread. */ + if (inf_update_procs (inf) && inf->threads) + tid = pid_to_ptid (inf->threads->tid); /* The first available thread. */ + else + tid = inferior_ptid; /* let wait_for_inferior handle exit case */ + } + + if (thread && PIDGET (tid) >= 0 && status->kind != TARGET_WAITKIND_SPURIOUS + && inf->pause_sc == 0 && thread->pause_sc == 0) + /* If something actually happened to THREAD, make sure we + suspend it. */ + { + thread->sc = 1; + inf_update_suspends (inf); + } + + inf_debug (inf, "returning tid = %d, status = %s (%d)", PIDGET (tid), + status->kind == TARGET_WAITKIND_EXITED ? "EXITED" + : status->kind == TARGET_WAITKIND_STOPPED ? "STOPPED" + : status->kind == TARGET_WAITKIND_SIGNALLED ? "SIGNALLED" + : status->kind == TARGET_WAITKIND_LOADED ? "LOADED" + : status->kind == TARGET_WAITKIND_SPURIOUS ? "SPURIOUS" + : "?", + status->value.integer); + + return tid; +} + + +/* The rpc handler called by exc_server. */ +error_t +S_exception_raise_request (mach_port_t port, mach_port_t reply_port, + thread_t thread_port, task_t task_port, + int exception, int code, int subcode) +{ + struct inf *inf = waiting_inf; + struct proc *thread = inf_port_to_thread (inf, thread_port); + + inf_debug (waiting_inf, + "thread = %d, task = %d, exc = %d, code = %d, subcode = %d", + thread_port, task_port, exception, code, subcode); + + if (!thread) + /* We don't know about thread? */ + { + inf_update_procs (inf); + thread = inf_port_to_thread (inf, thread_port); + if (!thread) + /* Give up, the generating thread is gone. */ + return 0; + } + + mach_port_deallocate (mach_task_self (), thread_port); + mach_port_deallocate (mach_task_self (), task_port); + + if (!thread->aborted) + /* THREAD hasn't been aborted since this exception happened (abortion + clears any exception state), so it must be real. */ + { + /* Store away the details; this will destroy any previous info. */ + inf->wait.thread = thread; + + inf->wait.status.kind = TARGET_WAITKIND_STOPPED; + + if (exception == EXC_BREAKPOINT) + /* GDB likes to get SIGTRAP for breakpoints. */ + { + inf->wait.status.value.sig = TARGET_SIGNAL_TRAP; + mach_port_deallocate (mach_task_self (), reply_port); + } + else + /* Record the exception so that we can forward it later. */ + { + if (thread->exc_port == port) + { + inf_debug (waiting_inf, "Handler is thread exception port <%d>", + thread->saved_exc_port); + inf->wait.exc.handler = thread->saved_exc_port; + } + else + { + inf_debug (waiting_inf, "Handler is task exception port <%d>", + inf->task->saved_exc_port); + inf->wait.exc.handler = inf->task->saved_exc_port; + gdb_assert (inf->task->exc_port == port); + } + if (inf->wait.exc.handler != MACH_PORT_NULL) + /* Add a reference to the exception handler. */ + mach_port_mod_refs (mach_task_self (), + inf->wait.exc.handler, MACH_PORT_RIGHT_SEND, + 1); + + inf->wait.exc.exception = exception; + inf->wait.exc.code = code; + inf->wait.exc.subcode = subcode; + inf->wait.exc.reply = reply_port; + + /* Exceptions are encoded in the signal space by putting them after + _NSIG; this assumes they're positive (and not extremely large)! */ + inf->wait.status.value.sig = + target_signal_from_host (_NSIG + exception); + } + } + else + /* A supppressed exception, which ignore. */ + { + inf->wait.suppress = 1; + mach_port_deallocate (mach_task_self (), reply_port); + } + + return 0; +} + + +/* Fill in INF's wait field after a task has died without giving us more + detailed information. */ +void +inf_task_died_status (struct inf *inf) +{ + warning ("Pid %d died with unknown exit status, using SIGKILL.", inf->pid); + inf->wait.status.kind = TARGET_WAITKIND_SIGNALLED; + inf->wait.status.value.sig = TARGET_SIGNAL_KILL; +} + +/* Notify server routines. The only real one is dead name notification. */ +error_t +do_mach_notify_dead_name (mach_port_t notify, mach_port_t dead_port) +{ + struct inf *inf = waiting_inf; + + inf_debug (waiting_inf, "port = %d", dead_port); + + if (inf->task && inf->task->port == dead_port) + { + proc_debug (inf->task, "is dead"); + inf->task->port = MACH_PORT_NULL; + if (proc_wait_pid == inf->pid) + /* We have a wait outstanding on the process, which will return more + detailed information, so delay until we get that. */ + inf->wait.suppress = 1; + else + /* We never waited for the process (maybe it wasn't a child), so just + pretend it got a SIGKILL. */ + inf_task_died_status (inf); + } + else + { + struct proc *thread = inf_port_to_thread (inf, dead_port); + if (thread) + { + proc_debug (thread, "is dead"); + thread->port = MACH_PORT_NULL; + } + + if (inf->task->dead) + /* Since the task is dead, its threads are dying with it. */ + inf->wait.suppress = 1; + } + + mach_port_deallocate (mach_task_self (), dead_port); + inf->threads_up_to_date = 0; /* Just in case */ + + return 0; +} + + +static error_t +ill_rpc (char *fun) +{ + warning ("illegal rpc: %s", fun); + return 0; +} + +error_t +do_mach_notify_no_senders (mach_port_t notify, mach_port_mscount_t count) +{ + return ill_rpc ("do_mach_notify_no_senders"); +} + +error_t +do_mach_notify_port_deleted (mach_port_t notify, mach_port_t name) +{ + return ill_rpc ("do_mach_notify_port_deleted"); +} + +error_t +do_mach_notify_msg_accepted (mach_port_t notify, mach_port_t name) +{ + return ill_rpc ("do_mach_notify_msg_accepted"); +} + +error_t +do_mach_notify_port_destroyed (mach_port_t notify, mach_port_t name) +{ + return ill_rpc ("do_mach_notify_port_destroyed"); +} + +error_t +do_mach_notify_send_once (mach_port_t notify) +{ + return ill_rpc ("do_mach_notify_send_once"); +} + + +/* Process_reply server routines. We only use process_wait_reply. */ + +error_t +S_proc_wait_reply (mach_port_t reply, error_t err, + int status, int sigcode, rusage_t rusage, pid_t pid) +{ + struct inf *inf = waiting_inf; + + inf_debug (inf, "err = %s, pid = %d, status = 0x%x, sigcode = %d", + err ? safe_strerror (err) : "0", pid, status, sigcode); + + if (err && proc_wait_pid && (!inf->task || !inf->task->port)) + /* Ack. The task has died, but the task-died notification code didn't + tell anyone because it thought a more detailed reply from the + procserver was forthcoming. However, we now learn that won't + happen... So we have to act like the task just died, and this time, + tell the world. */ + inf_task_died_status (inf); + + if (--proc_waits_pending == 0) + /* PROC_WAIT_PID represents the most recent wait. We will always get + replies in order because the proc server is single threaded. */ + proc_wait_pid = 0; + + inf_debug (inf, "waits pending now: %d", proc_waits_pending); + + if (err) + { + if (err != EINTR) + { + warning ("Can't wait for pid %d: %s", inf->pid, safe_strerror (err)); + inf->no_wait = 1; + + /* Since we can't see the inferior's signals, don't trap them. */ + inf_set_traced (inf, 0); + } + } + else if (pid == inf->pid) + { + store_waitstatus (&inf->wait.status, status); + if (inf->wait.status.kind == TARGET_WAITKIND_STOPPED) + /* The process has sent us a signal, and stopped itself in a sane + state pending our actions. */ + { + inf_debug (inf, "process has stopped itself"); + inf->stopped = 1; + } + } + else + inf->wait.suppress = 1; /* Something odd happened. Ignore. */ + + return 0; +} + +error_t +S_proc_setmsgport_reply (mach_port_t reply, error_t err, + mach_port_t old_msg_port) +{ + return ill_rpc ("S_proc_setmsgport_reply"); +} + +error_t +S_proc_getmsgport_reply (mach_port_t reply, error_t err, mach_port_t msg_port) +{ + return ill_rpc ("S_proc_getmsgport_reply"); +} + + +/* Msg_reply server routines. We only use msg_sig_post_untraced_reply. */ + +error_t +S_msg_sig_post_untraced_reply (mach_port_t reply, error_t err) +{ + struct inf *inf = waiting_inf; + + if (err == EBUSY) + /* EBUSY is what we get when the crash server has grabbed control of the + process and doesn't like what signal we tried to send it. Just act + like the process stopped (using a signal of 0 should mean that the + *next* time the user continues, it will pass signal 0, which the crash + server should like). */ + { + inf->wait.status.kind = TARGET_WAITKIND_STOPPED; + inf->wait.status.value.sig = TARGET_SIGNAL_0; + } + else if (err) + warning ("Signal delivery failed: %s", safe_strerror (err)); + + if (err) + /* We only get this reply when we've posted a signal to a process which we + thought was stopped, and which we expected to continue after the signal. + Given that the signal has failed for some reason, it's reasonable to + assume it's still stopped. */ + inf->stopped = 1; + else + inf->wait.suppress = 1; + + return 0; +} + +error_t +S_msg_sig_post_reply (mach_port_t reply, error_t err) +{ + return ill_rpc ("S_msg_sig_post_reply"); +} + + +/* Returns the number of messages queued for the receive right PORT. */ +static mach_port_msgcount_t +port_msgs_queued (mach_port_t port) +{ + struct mach_port_status status; + error_t err = + mach_port_get_receive_status (mach_task_self (), port, &status); + + if (err) + return 0; + else + return status.mps_msgcount; +} + + +/* Resume execution of the inferior process. + + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. + + TID STEP: + -1 true Single step the current thread allowing other threads to run. + -1 false Continue the current thread allowing other threads to run. + X true Single step the given thread, don't allow any others to run. + X false Continue the given thread, do not allow any others to run. + (Where X, of course, is anything except -1) + + Note that a resume may not `take' if there are pending exceptions/&c + still unprocessed from the last resume we did (any given resume may result + in multiple events returned by wait). + */ +static void +gnu_resume (ptid_t tid, int step, enum target_signal sig) +{ + struct proc *step_thread = 0; + struct inf *inf = current_inferior; + + inf_debug (inf, "tid = %d, step = %d, sig = %d", PIDGET (tid), step, sig); + + inf_validate_procinfo (inf); + + if (sig != TARGET_SIGNAL_0 || inf->stopped) + { + if (sig == TARGET_SIGNAL_0 && inf->nomsg) + inf_continue (inf); + else + inf_signal (inf, sig); + } + else if (inf->wait.exc.reply != MACH_PORT_NULL) + /* We received an exception to which we have chosen not to forward, so + abort the faulting thread, which will perhaps retake it. */ + { + proc_abort (inf->wait.thread, 1); + warning ("Aborting %s with unforwarded exception %s.", + proc_string (inf->wait.thread), + target_signal_to_name (inf->wait.status.value.sig)); + } + + if (port_msgs_queued (inf->event_port)) + /* If there are still messages in our event queue, don't bother resuming + the process, as we're just going to stop it right away anyway. */ + return; + + inf_update_procs (inf); + + if (PIDGET (tid) < 0) + /* Allow all threads to run, except perhaps single-stepping one. */ + { + inf_debug (inf, "running all threads; tid = %d", PIDGET (inferior_ptid)); + tid = inferior_ptid; /* What to step. */ + inf_set_threads_resume_sc (inf, 0, 1); + } + else + /* Just allow a single thread to run. */ + { + struct proc *thread = inf_tid_to_thread (inf, PIDGET (tid)); + if (!thread) + error ("Can't run single thread id %d: no such thread!"); + inf_debug (inf, "running one thread: %d/%d", inf->pid, thread->tid); + inf_set_threads_resume_sc (inf, thread, 0); + } + + if (step) + { + step_thread = inf_tid_to_thread (inf, PIDGET (tid)); + if (!step_thread) + warning ("Can't step thread id %d: no such thread.", PIDGET (tid)); + else + inf_debug (inf, "stepping thread: %d/%d", inf->pid, step_thread->tid); + } + if (step_thread != inf->step_thread) + inf_set_step_thread (inf, step_thread); + + inf_debug (inf, "here we go..."); + inf_resume (inf); +} + + +static void +gnu_kill_inferior (void) +{ + struct proc *task = current_inferior->task; + if (task) + { + proc_debug (task, "terminating..."); + task_terminate (task->port); + inf_set_pid (current_inferior, -1); + } + target_mourn_inferior (); +} + +/* Clean up after the inferior dies. */ +static void +gnu_mourn_inferior (void) +{ + inf_debug (current_inferior, "rip"); + inf_detach (current_inferior); + unpush_target (&gnu_ops); + generic_mourn_inferior (); +} + + +/* Fork an inferior process, and start debugging it. */ + +/* Set INFERIOR_PID to the first thread available in the child, if any. */ +static int +inf_pick_first_thread (void) +{ + if (current_inferior->task && current_inferior->threads) + /* The first thread. */ + return current_inferior->threads->tid; + else + /* What may be the next thread. */ + return next_thread_id; +} + +static struct inf * +cur_inf (void) +{ + if (!current_inferior) + current_inferior = make_inf (); + return current_inferior; +} + +static void +gnu_create_inferior (char *exec_file, char *allargs, char **env) +{ + struct inf *inf = cur_inf (); + + void trace_me () + { + /* We're in the child; make this process stop as soon as it execs. */ + inf_debug (inf, "tracing self"); + if (ptrace (PTRACE_TRACEME) != 0) + error ("ptrace (PTRACE_TRACEME) failed!"); + } + void attach_to_child (int pid) + { + /* Attach to the now stopped child, which is actually a shell... */ + inf_debug (inf, "attaching to child: %d", pid); + + inf_attach (inf, pid); + + attach_flag = 0; + push_target (&gnu_ops); + + inf->pending_execs = 2; + inf->nomsg = 1; + inf->traced = 1; + + /* Now let the child run again, knowing that it will stop immediately + because of the ptrace. */ + inf_resume (inf); + inferior_ptid = pid_to_ptid (inf_pick_first_thread ()); + + startup_inferior (inf->pending_execs); + } + + inf_debug (inf, "creating inferior"); + + fork_inferior (exec_file, allargs, env, trace_me, attach_to_child, + NULL, NULL); + + inf_validate_procinfo (inf); + inf_update_signal_thread (inf); + inf_set_traced (inf, inf->want_signals); + + /* Execing the process will have trashed our exception ports; steal them + back (or make sure they're restored if the user wants that). */ + if (inf->want_exceptions) + inf_steal_exc_ports (inf); + else + inf_restore_exc_ports (inf); + + /* Here we go! */ + proceed ((CORE_ADDR) -1, 0, 0); +} + +/* Mark our target-struct as eligible for stray "run" and "attach" + commands. */ +static int +gnu_can_run (void) +{ + return 1; +} + + +#ifdef ATTACH_DETACH + +/* Attach to process PID, then initialize for debugging it + and wait for the trace-trap that results from attaching. */ +static void +gnu_attach (char *args, int from_tty) +{ + int pid; + char *exec_file; + struct inf *inf = cur_inf (); + + if (!args) + error_no_arg ("process-id to attach"); + + pid = atoi (args); + + if (pid == getpid ()) /* Trying to masturbate? */ + error ("I refuse to debug myself!"); + + if (from_tty) + { + exec_file = (char *) get_exec_file (0); + + if (exec_file) + printf_unfiltered ("Attaching to program `%s', pid %d\n", + exec_file, pid); + else + printf_unfiltered ("Attaching to pid %d\n", pid); + + gdb_flush (gdb_stdout); + } + + inf_debug (inf, "attaching to pid: %d", pid); + + inf_attach (inf, pid); + inf_update_procs (inf); + + inferior_ptid = pid_to_ptid (inf_pick_first_thread ()); + + attach_flag = 1; + push_target (&gnu_ops); + + /* We have to initialize the terminal settings now, since the code + below might try to restore them. */ + target_terminal_init (); + + /* If the process was stopped before we attached, make it continue the next + time the user does a continue. */ + inf_validate_procinfo (inf); + + inf_update_signal_thread (inf); + inf_set_traced (inf, inf->want_signals); + +#if 0 /* Do we need this? */ + renumber_threads (0); /* Give our threads reasonable names. */ +#endif +} + + +/* Take a program previously attached to and detaches it. + The program resumes execution and will no longer stop + on signals, etc. We'd better not have left any breakpoints + in the program or it'll die when it hits one. For this + to work, it may be necessary for the process to have been + previously attached. It *might* work if the program was + started via fork. */ +static void +gnu_detach (char *args, int from_tty) +{ + if (from_tty) + { + char *exec_file = get_exec_file (0); + if (exec_file) + printf_unfiltered ("Detaching from program `%s' pid %d\n", + exec_file, current_inferior->pid); + else + printf_unfiltered ("Detaching from pid %d\n", current_inferior->pid); + gdb_flush (gdb_stdout); + } + + inf_detach (current_inferior); + + inferior_ptid = null_ptid; + + unpush_target (&gnu_ops); /* Pop out of handling an inferior */ +} +#endif /* ATTACH_DETACH */ + + +static void +gnu_terminal_init_inferior (void) +{ + gdb_assert (current_inferior); + terminal_init_inferior_with_pgrp (current_inferior->pid); +} + +/* Get ready to modify the registers array. On machines which store + individual registers, this doesn't need to do anything. On machines + which store all the registers in one fell swoop, this makes sure + that registers contains all the registers from the program being + debugged. */ +static void +gnu_prepare_to_store (void) +{ +#ifdef CHILD_PREPARE_TO_STORE + CHILD_PREPARE_TO_STORE (); +#endif +} + +static void +gnu_open (char *arg, int from_tty) +{ + error ("Use the \"run\" command to start a Unix child process."); +} + +static void +gnu_stop (void) +{ + error ("to_stop target function not implemented"); +} + +static char * +gnu_pid_to_exec_file (int pid) +{ + error ("to_pid_to_exec_file target function not implemented"); + return NULL; +} + + +static int +gnu_thread_alive (ptid_t tid) +{ + inf_update_procs (current_inferior); + return !!inf_tid_to_thread (current_inferior, PIDGET (tid)); +} + + +/* Read inferior task's LEN bytes from ADDR and copy it to MYADDR in + gdb's address space. Return 0 on failure; number of bytes read + otherwise. */ +int +gnu_read_inferior (task_t task, CORE_ADDR addr, char *myaddr, int length) +{ + error_t err; + vm_address_t low_address = (vm_address_t) trunc_page (addr); + vm_size_t aligned_length = + (vm_size_t) round_page (addr + length) - low_address; + pointer_t copied; + int copy_count; + + /* Get memory from inferior with page aligned addresses */ + err = vm_read (task, low_address, aligned_length, &copied, ©_count); + if (err) + return 0; + + err = hurd_safe_copyin (myaddr, (void *) addr - low_address + copied, length); + if (err) + { + warning ("Read from inferior faulted: %s", safe_strerror (err)); + length = 0; + } + + err = vm_deallocate (mach_task_self (), copied, copy_count); + if (err) + warning ("gnu_read_inferior vm_deallocate failed: %s", safe_strerror (err)); + + return length; +} + +#define CHK_GOTO_OUT(str,ret) \ + do if (ret != KERN_SUCCESS) { errstr = #str; goto out; } while(0) + +struct vm_region_list +{ + struct vm_region_list *next; + vm_prot_t protection; + vm_address_t start; + vm_size_t length; +}; + +struct obstack region_obstack; + +/* Write gdb's LEN bytes from MYADDR and copy it to ADDR in inferior + task's address space. */ +int +gnu_write_inferior (task_t task, CORE_ADDR addr, char *myaddr, int length) +{ + error_t err = 0; + vm_address_t low_address = (vm_address_t) trunc_page (addr); + vm_size_t aligned_length = + (vm_size_t) round_page (addr + length) - low_address; + pointer_t copied; + int copy_count; + int deallocate = 0; + + char *errstr = "Bug in gnu_write_inferior"; + + struct vm_region_list *region_element; + struct vm_region_list *region_head = (struct vm_region_list *) NULL; + + /* Get memory from inferior with page aligned addresses */ + err = vm_read (task, + low_address, + aligned_length, + &copied, + ©_count); + CHK_GOTO_OUT ("gnu_write_inferior vm_read failed", err); + + deallocate++; + + err = hurd_safe_copyout ((void *) addr - low_address + copied, + myaddr, length); + CHK_GOTO_OUT ("Write to inferior faulted", err); + + obstack_init (®ion_obstack); + + /* Do writes atomically. + First check for holes and unwritable memory. */ + { + vm_size_t remaining_length = aligned_length; + vm_address_t region_address = low_address; + + struct vm_region_list *scan; + + while (region_address < low_address + aligned_length) + { + vm_prot_t protection; + vm_prot_t max_protection; + vm_inherit_t inheritance; + boolean_t shared; + mach_port_t object_name; + vm_offset_t offset; + vm_size_t region_length = remaining_length; + vm_address_t old_address = region_address; + + err = vm_region (task, + ®ion_address, + ®ion_length, + &protection, + &max_protection, + &inheritance, + &shared, + &object_name, + &offset); + CHK_GOTO_OUT ("vm_region failed", err); + + /* Check for holes in memory */ + if (old_address != region_address) + { + warning ("No memory at 0x%x. Nothing written", + old_address); + err = KERN_SUCCESS; + length = 0; + goto out; + } + + if (!(max_protection & VM_PROT_WRITE)) + { + warning ("Memory at address 0x%x is unwritable. Nothing written", + old_address); + err = KERN_SUCCESS; + length = 0; + goto out; + } + + /* Chain the regions for later use */ + region_element = + (struct vm_region_list *) + obstack_alloc (®ion_obstack, sizeof (struct vm_region_list)); + + region_element->protection = protection; + region_element->start = region_address; + region_element->length = region_length; + + /* Chain the regions along with protections */ + region_element->next = region_head; + region_head = region_element; + + region_address += region_length; + remaining_length = remaining_length - region_length; + } + + /* If things fail after this, we give up. + Somebody is messing up inferior_task's mappings. */ + + /* Enable writes to the chained vm regions */ + for (scan = region_head; scan; scan = scan->next) + { + if (!(scan->protection & VM_PROT_WRITE)) + { + err = vm_protect (task, + scan->start, + scan->length, + FALSE, + scan->protection | VM_PROT_WRITE); + CHK_GOTO_OUT ("vm_protect: enable write failed", err); + } + } + + err = vm_write (task, + low_address, + copied, + aligned_length); + CHK_GOTO_OUT ("vm_write failed", err); + + /* Set up the original region protections, if they were changed */ + for (scan = region_head; scan; scan = scan->next) + { + if (!(scan->protection & VM_PROT_WRITE)) + { + err = vm_protect (task, + scan->start, + scan->length, + FALSE, + scan->protection); + CHK_GOTO_OUT ("vm_protect: enable write failed", err); + } + } + } + +out: + if (deallocate) + { + obstack_free (®ion_obstack, 0); + + (void) vm_deallocate (mach_task_self (), + copied, + copy_count); + } + + if (err != KERN_SUCCESS) + { + warning ("%s: %s", errstr, mach_error_string (err)); + return 0; + } + + return length; +} + + +/* Return 0 on failure, number of bytes handled otherwise. TARGET + is ignored. */ +static int +gnu_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write, + struct mem_attrib *attrib, + struct target_ops *target) +{ + task_t task = (current_inferior + ? (current_inferior->task + ? current_inferior->task->port : 0) + : 0); + + if (task == MACH_PORT_NULL) + return 0; + else + { + inf_debug (current_inferior, "%s %p[%d] %s %p", + write ? "writing" : "reading", (void *) memaddr, len, + write ? "<--" : "-->", myaddr); + if (write) + return gnu_write_inferior (task, memaddr, myaddr, len); + else + return gnu_read_inferior (task, memaddr, myaddr, len); + } +} + +/* Call FUNC on each memory region in the task. */ +static int +gnu_find_memory_regions (int (*func) (CORE_ADDR, + unsigned long, + int, int, int, + void *), + void *data) +{ + error_t err; + task_t task; + vm_address_t region_address, last_region_address, last_region_end; + vm_prot_t last_protection; + + if (current_inferior == 0 || current_inferior->task == 0) + return 0; + task = current_inferior->task->port; + if (task == MACH_PORT_NULL) + return 0; + + region_address = last_region_address = last_region_end = VM_MIN_ADDRESS; + last_protection = VM_PROT_NONE; + while (region_address < VM_MAX_ADDRESS) + { + vm_prot_t protection; + vm_prot_t max_protection; + vm_inherit_t inheritance; + boolean_t shared; + mach_port_t object_name; + vm_offset_t offset; + vm_size_t region_length = VM_MAX_ADDRESS - region_address; + vm_address_t old_address = region_address; + + err = vm_region (task, + ®ion_address, + ®ion_length, + &protection, + &max_protection, + &inheritance, + &shared, + &object_name, + &offset); + if (err == KERN_NO_SPACE) + break; + if (err != KERN_SUCCESS) + { + warning ("vm_region failed: %s", mach_error_string (err)); + return -1; + } + + if (protection == last_protection && region_address == last_region_end) + /* This region is contiguous with and indistinguishable from + the previous one, so we just extend that one. */ + last_region_end = region_address += region_length; + else + { + /* This region is distinct from the last one we saw, so report + that previous one. */ + if (last_protection != VM_PROT_NONE) + (*func) (last_region_address, + last_region_end - last_region_address, + last_protection & VM_PROT_READ, + last_protection & VM_PROT_WRITE, + last_protection & VM_PROT_EXECUTE, + data); + last_region_address = region_address; + last_region_end = region_address += region_length; + last_protection = protection; + } + } + + /* Report the final region. */ + if (last_region_end > last_region_address && last_protection != VM_PROT_NONE) + (*func) (last_region_address, last_region_end - last_region_address, + last_protection & VM_PROT_READ, + last_protection & VM_PROT_WRITE, + last_protection & VM_PROT_EXECUTE, + data); + + return 0; +} + + +/* Return printable description of proc. */ +char * +proc_string (struct proc *proc) +{ + static char tid_str[80]; + if (proc_is_task (proc)) + sprintf (tid_str, "process %d", proc->inf->pid); + else + sprintf (tid_str, "thread %d.%d", + proc->inf->pid, pid_to_thread_id (MERGEPID (proc->tid, 0))); + return tid_str; +} + +static char * +gnu_pid_to_str (ptid_t ptid) +{ + struct inf *inf = current_inferior; + int tid = PIDGET (ptid); + struct proc *thread = inf_tid_to_thread (inf, tid); + + if (thread) + return proc_string (thread); + else + { + static char tid_str[80]; + sprintf (tid_str, "bogus thread id %d", tid); + return tid_str; + } +} + + +extern void gnu_store_registers (int regno); +extern void gnu_fetch_registers (int regno); + +struct target_ops gnu_ops; + +static void +init_gnu_ops (void) +{ + gnu_ops.to_shortname = "GNU"; /* to_shortname */ + gnu_ops.to_longname = "GNU Hurd process"; /* to_longname */ + gnu_ops.to_doc = "GNU Hurd process"; /* to_doc */ + gnu_ops.to_open = gnu_open; /* to_open */ + gnu_ops.to_attach = gnu_attach; /* to_attach */ + gnu_ops.to_detach = gnu_detach; /* to_detach */ + gnu_ops.to_resume = gnu_resume; /* to_resume */ + gnu_ops.to_wait = gnu_wait; /* to_wait */ + gnu_ops.to_fetch_registers = gnu_fetch_registers; /* to_fetch_registers */ + gnu_ops.to_store_registers = gnu_store_registers; /* to_store_registers */ + gnu_ops.to_prepare_to_store = gnu_prepare_to_store; /* to_prepare_to_store */ + gnu_ops.to_xfer_memory = gnu_xfer_memory; /* to_xfer_memory */ + gnu_ops.to_find_memory_regions = gnu_find_memory_regions; + gnu_ops.to_insert_breakpoint = memory_insert_breakpoint; + gnu_ops.to_remove_breakpoint = memory_remove_breakpoint; + gnu_ops.to_terminal_init = gnu_terminal_init_inferior; + gnu_ops.to_terminal_inferior = terminal_inferior; + gnu_ops.to_terminal_ours_for_output = terminal_ours_for_output; + gnu_ops.to_terminal_save_ours = terminal_save_ours; + gnu_ops.to_terminal_ours = terminal_ours; + gnu_ops.to_terminal_info = child_terminal_info; + gnu_ops.to_kill = gnu_kill_inferior; /* to_kill */ + gnu_ops.to_create_inferior = gnu_create_inferior; /* to_create_inferior */ + gnu_ops.to_mourn_inferior = gnu_mourn_inferior; /* to_mourn_inferior */ + gnu_ops.to_can_run = gnu_can_run; /* to_can_run */ + gnu_ops.to_thread_alive = gnu_thread_alive; /* to_thread_alive */ + gnu_ops.to_pid_to_str = gnu_pid_to_str; /* to_pid_to_str */ + gnu_ops.to_stop = gnu_stop; /* to_stop */ + gnu_ops.to_pid_to_exec_file = gnu_pid_to_exec_file; /* to_pid_to_exec_file */ + gnu_ops.to_stratum = process_stratum; /* to_stratum */ + gnu_ops.to_has_all_memory = 1; /* to_has_all_memory */ + gnu_ops.to_has_memory = 1; /* to_has_memory */ + gnu_ops.to_has_stack = 1; /* to_has_stack */ + gnu_ops.to_has_registers = 1; /* to_has_registers */ + gnu_ops.to_has_execution = 1; /* to_has_execution */ + gnu_ops.to_magic = OPS_MAGIC; /* to_magic */ +} /* init_gnu_ops */ + + +/* User task commands. */ + +struct cmd_list_element *set_task_cmd_list = 0; +struct cmd_list_element *show_task_cmd_list = 0; +/* User thread commands. */ + +/* Commands with a prefix of `set/show thread'. */ +extern struct cmd_list_element *thread_cmd_list; +struct cmd_list_element *set_thread_cmd_list = NULL; +struct cmd_list_element *show_thread_cmd_list = NULL; + +/* Commands with a prefix of `set/show thread default'. */ +struct cmd_list_element *set_thread_default_cmd_list = NULL; +struct cmd_list_element *show_thread_default_cmd_list = NULL; + +static void +set_thread_cmd (char *args, int from_tty) +{ + printf_unfiltered ("\"set thread\" must be followed by the name of a thread property, or \"default\".\n"); +} + +static void +show_thread_cmd (char *args, int from_tty) +{ + printf_unfiltered ("\"show thread\" must be followed by the name of a thread property, or \"default\".\n"); +} + +static void +set_thread_default_cmd (char *args, int from_tty) +{ + printf_unfiltered ("\"set thread default\" must be followed by the name of a thread property.\n"); +} + +static void +show_thread_default_cmd (char *args, int from_tty) +{ + printf_unfiltered ("\"show thread default\" must be followed by the name of a thread property.\n"); +} + +static int +parse_int_arg (char *args, char *cmd_prefix) +{ + if (args) + { + char *arg_end; + int val = strtoul (args, &arg_end, 10); + if (*args && *arg_end == '\0') + return val; + } + error ("Illegal argument for \"%s\" command, should be an integer.", cmd_prefix); +} + +static int +_parse_bool_arg (char *args, char *t_val, char *f_val, char *cmd_prefix) +{ + if (!args || strcmp (args, t_val) == 0) + return 1; + else if (strcmp (args, f_val) == 0) + return 0; + else + error ("Illegal argument for \"%s\" command, should be \"%s\" or \"%s\".", + cmd_prefix, t_val, f_val); +} + +#define parse_bool_arg(args, cmd_prefix) \ + _parse_bool_arg (args, "on", "off", cmd_prefix) + +static void +check_empty (char *args, char *cmd_prefix) +{ + if (args) + error ("Garbage after \"%s\" command: `%s'", cmd_prefix, args); +} + +/* Returns the alive thread named by INFERIOR_PID, or signals an error. */ +static struct proc * +cur_thread (void) +{ + struct inf *inf = cur_inf (); + struct proc *thread = inf_tid_to_thread (inf, PIDGET (inferior_ptid)); + if (!thread) + error ("No current thread."); + return thread; +} + +/* Returns the current inferior, but signals an error if it has no task. */ +static struct inf * +active_inf (void) +{ + struct inf *inf = cur_inf (); + if (!inf->task) + error ("No current process."); + return inf; +} + + +static void +set_task_pause_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + int old_sc = inf->pause_sc; + + inf->pause_sc = parse_bool_arg (args, "set task pause"); + + if (old_sc == 0 && inf->pause_sc != 0) + /* If the task is currently unsuspended, immediately suspend it, + otherwise wait until the next time it gets control. */ + inf_suspend (inf); +} + +static void +show_task_pause_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + check_empty (args, "show task pause"); + printf_unfiltered ("The inferior task %s suspended while gdb has control.\n", + inf->task + ? (inf->pause_sc == 0 ? "isn't" : "is") + : (inf->pause_sc == 0 ? "won't be" : "will be")); +} + +static void +set_task_detach_sc_cmd (char *args, int from_tty) +{ + cur_inf ()->detach_sc = parse_int_arg (args, "set task detach-suspend-count"); +} + +static void +show_task_detach_sc_cmd (char *args, int from_tty) +{ + check_empty (args, "show task detach-suspend-count"); + printf_unfiltered ("The inferior task will be left with a suspend count of %d when detaching.\n", + cur_inf ()->detach_sc); +} + + +static void +set_thread_default_pause_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + inf->default_thread_pause_sc = + parse_bool_arg (args, "set thread default pause") ? 0 : 1; +} + +static void +show_thread_default_pause_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + int sc = inf->default_thread_pause_sc; + check_empty (args, "show thread default pause"); + printf_unfiltered ("New threads %s suspended while gdb has control%s.\n", + sc ? "are" : "aren't", + !sc && inf->pause_sc ? " (but the task is)" : ""); +} + +static void +set_thread_default_run_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + inf->default_thread_run_sc = + parse_bool_arg (args, "set thread default run") ? 0 : 1; +} + +static void +show_thread_default_run_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + check_empty (args, "show thread default run"); + printf_unfiltered ("New threads %s allowed to run.\n", + inf->default_thread_run_sc == 0 ? "are" : "aren't"); +} + +static void +set_thread_default_detach_sc_cmd (char *args, int from_tty) +{ + cur_inf ()->default_thread_detach_sc = + parse_int_arg (args, "set thread default detach-suspend-count"); +} + +static void +show_thread_default_detach_sc_cmd (char *args, int from_tty) +{ + check_empty (args, "show thread default detach-suspend-count"); + printf_unfiltered ("New threads will get a detach-suspend-count of %d.\n", + cur_inf ()->default_thread_detach_sc); +} + + +/* Steal a send right called NAME in the inferior task, and make it PROC's + saved exception port. */ +static void +steal_exc_port (struct proc *proc, mach_port_t name) +{ + error_t err; + mach_port_t port; + mach_msg_type_name_t port_type; + + if (!proc || !proc->inf->task) + error ("No inferior task."); + + err = mach_port_extract_right (proc->inf->task->port, + name, MACH_MSG_TYPE_COPY_SEND, + &port, &port_type); + if (err) + error ("Couldn't extract send right %d from inferior: %s", + name, safe_strerror (err)); + + if (proc->saved_exc_port) + /* Get rid of our reference to the old one. */ + mach_port_deallocate (mach_task_self (), proc->saved_exc_port); + + proc->saved_exc_port = port; + + if (!proc->exc_port) + /* If PROC is a thread, we may not have set its exception port before. + We can't use proc_steal_exc_port because it also sets saved_exc_port. */ + { + proc->exc_port = proc->inf->event_port; + err = proc_set_exception_port (proc, proc->exc_port); + error ("Can't set exception port for %s: %s", + proc_string (proc), safe_strerror (err)); + } +} + +static void +set_task_exc_port_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + if (!args) + error ("No argument to \"set task exception-port\" command."); + steal_exc_port (inf->task, parse_and_eval_address (args)); +} + +static void +set_stopped_cmd (char *args, int from_tty) +{ + cur_inf ()->stopped = _parse_bool_arg (args, "yes", "no", "set stopped"); +} + +static void +show_stopped_cmd (char *args, int from_tty) +{ + struct inf *inf = active_inf (); + check_empty (args, "show stopped"); + printf_unfiltered ("The inferior process %s stopped.\n", + inf->stopped ? "is" : "isn't"); +} + +static void +set_sig_thread_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + + if (!args || (!isdigit (*args) && strcmp (args, "none") != 0)) + error ("Illegal argument to \"set signal-thread\" command.\n" + "Should be an integer thread ID, or `none'."); + + if (strcmp (args, "none") == 0) + inf->signal_thread = 0; + else + { + int tid = PIDGET (thread_id_to_pid (atoi (args))); + if (tid < 0) + error ("Thread ID %s not known. Use the \"info threads\" command to\n" + "see the IDs of currently known threads.", args); + inf->signal_thread = inf_tid_to_thread (inf, tid); + } +} + +static void +show_sig_thread_cmd (char *args, int from_tty) +{ + struct inf *inf = active_inf (); + check_empty (args, "show signal-thread"); + if (inf->signal_thread) + printf_unfiltered ("The signal thread is %s.\n", + proc_string (inf->signal_thread)); + else + printf_unfiltered ("There is no signal thread.\n"); +} + + +static void +set_signals_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + + inf->want_signals = parse_bool_arg (args, "set signals"); + + if (inf->task && inf->want_signals != inf->traced) + /* Make this take effect immediately in a running process. */ + inf_set_traced (inf, inf->want_signals); +} + +static void +show_signals_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + check_empty (args, "show signals"); + printf_unfiltered ("The inferior process's signals %s intercepted.\n", + inf->task + ? (inf->traced ? "are" : "aren't") + : (inf->want_signals ? "will be" : "won't be")); +} + +static void +set_exceptions_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + int val = parse_bool_arg (args, "set exceptions"); + + if (inf->task && inf->want_exceptions != val) + /* Make this take effect immediately in a running process. */ + /* XXX */ ; + + inf->want_exceptions = val; +} + +static void +show_exceptions_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + check_empty (args, "show exceptions"); + printf_unfiltered ("Exceptions in the inferior %s trapped.\n", + inf->task + ? (inf->want_exceptions ? "are" : "aren't") + : (inf->want_exceptions ? "will be" : "won't be")); +} + + +static void +set_task_cmd (char *args, int from_tty) +{ + printf_unfiltered ("\"set task\" must be followed by the name" + " of a task property.\n"); +} + +static void +show_task_cmd (char *args, int from_tty) +{ + struct inf *inf = cur_inf (); + + check_empty (args, "show task"); + + show_signals_cmd (0, from_tty); + show_exceptions_cmd (0, from_tty); + show_task_pause_cmd (0, from_tty); + + if (inf->pause_sc == 0) + show_thread_default_pause_cmd (0, from_tty); + show_thread_default_run_cmd (0, from_tty); + + if (inf->task) + { + show_stopped_cmd (0, from_tty); + show_sig_thread_cmd (0, from_tty); + } + + if (inf->detach_sc != 0) + show_task_detach_sc_cmd (0, from_tty); + if (inf->default_thread_detach_sc != 0) + show_thread_default_detach_sc_cmd (0, from_tty); +} + + +static void +set_noninvasive_cmd (char *args, int from_tty) +{ + /* Invert the sense of the arg for each component. */ + char *inv_args = parse_bool_arg (args, "set noninvasive") ? "off" : "on"; + + set_task_pause_cmd (inv_args, from_tty); + set_signals_cmd (inv_args, from_tty); + set_exceptions_cmd (inv_args, from_tty); +} + + +static void +info_port_rights (char *args, mach_port_type_t only) +{ + struct inf *inf = active_inf (); + struct value *vmark = value_mark (); + + if (args) + /* Explicit list of port rights. */ + { + while (*args) + { + struct value *val = parse_to_comma_and_eval (&args); + long right = value_as_long (val); + error_t err = + print_port_info (right, 0, inf->task->port, PORTINFO_DETAILS, + stdout); + if (err) + error ("%ld: %s.", right, safe_strerror (err)); + } + } + else + /* Print all of them. */ + { + error_t err = + print_task_ports_info (inf->task->port, only, PORTINFO_DETAILS, + stdout); + if (err) + error ("%s.", safe_strerror (err)); + } + + value_free_to_mark (vmark); +} + +static void +info_send_rights_cmd (char *args, int from_tty) +{ + info_port_rights (args, MACH_PORT_TYPE_SEND); +} + +static void +info_recv_rights_cmd (char *args, int from_tty) +{ + info_port_rights (args, MACH_PORT_TYPE_RECEIVE); +} + +static void +info_port_sets_cmd (char *args, int from_tty) +{ + info_port_rights (args, MACH_PORT_TYPE_PORT_SET); +} + +static void +info_dead_names_cmd (char *args, int from_tty) +{ + info_port_rights (args, MACH_PORT_TYPE_DEAD_NAME); +} + +static void +info_port_rights_cmd (char *args, int from_tty) +{ + info_port_rights (args, ~0); +} + + +static void +add_task_commands (void) +{ + add_cmd ("pause", class_run, set_thread_default_pause_cmd, + "Set whether the new threads are suspended while gdb has control.\n\ +This property normally has no effect because the whole task is\n\ +suspended, however, that may be disabled with \"set task pause off\".\n\ +The default value is \"off\".", + &set_thread_default_cmd_list); + add_cmd ("pause", no_class, show_thread_default_pause_cmd, + "Show whether new threads are suspended while gdb has control.", + &show_thread_default_cmd_list); + + add_cmd ("run", class_run, set_thread_default_run_cmd, + "Set whether new threads are allowed to run \ +(once gdb has noticed them).", + &set_thread_default_cmd_list); + add_cmd ("run", no_class, show_thread_default_run_cmd, + "Show whether new threads are allowed to run \ +(once gdb has noticed them).", + &show_thread_default_cmd_list); + + add_cmd ("detach-suspend-count", class_run, set_thread_default_detach_sc_cmd, + "Set the default detach-suspend-count value for new threads.", + &set_thread_default_cmd_list); + add_cmd ("detach-suspend-count", no_class, show_thread_default_detach_sc_cmd, + "Show the default detach-suspend-count value for new threads.", + &show_thread_default_cmd_list); + + add_cmd ("signals", class_run, set_signals_cmd, + "Set whether the inferior process's signals will be intercepted.\n\ +Mach exceptions (such as breakpoint traps) are not affected.", + &setlist); + add_alias_cmd ("sigs", "signals", class_run, 1, &setlist); + add_cmd ("signals", no_class, show_signals_cmd, + "Show whether the inferior process's signals will be intercepted.", + &showlist); + add_alias_cmd ("sigs", "signals", no_class, 1, &showlist); + + add_cmd ("signal-thread", class_run, set_sig_thread_cmd, + "Set the thread that gdb thinks is the libc signal thread.\n\ +This thread is run when delivering a signal to a non-stopped process.", + &setlist); + add_alias_cmd ("sigthread", "signal-thread", class_run, 1, &setlist); + add_cmd ("signal-thread", no_class, show_sig_thread_cmd, + "Set the thread that gdb thinks is the libc signal thread.", + &showlist); + add_alias_cmd ("sigthread", "signal-thread", no_class, 1, &showlist); + + add_cmd ("stopped", class_run, set_stopped_cmd, + "Set whether gdb thinks the inferior process is stopped \ +as with SIGSTOP.\n\ +Stopped process will be continued by sending them a signal.", + &setlist); + add_cmd ("stopped", no_class, show_signals_cmd, + "Show whether gdb thinks the inferior process is stopped \ +as with SIGSTOP.", + &showlist); + + add_cmd ("exceptions", class_run, set_exceptions_cmd, + "Set whether exceptions in the inferior process will be trapped.\n\ +When exceptions are turned off, neither breakpoints nor single-stepping\n\ +will work.", + &setlist); + /* Allow `set exc' despite conflict with `set exception-port'. */ + add_alias_cmd ("exc", "exceptions", class_run, 1, &setlist); + add_cmd ("exceptions", no_class, show_exceptions_cmd, + "Show whether exceptions in the inferior process will be trapped.", + &showlist); + + add_prefix_cmd ("task", no_class, set_task_cmd, + "Command prefix for setting task attributes.", + &set_task_cmd_list, "set task ", 0, &setlist); + add_prefix_cmd ("task", no_class, show_task_cmd, + "Command prefix for showing task attributes.", + &show_task_cmd_list, "show task ", 0, &showlist); + + add_cmd ("pause", class_run, set_task_pause_cmd, + "Set whether the task is suspended while gdb has control.\n\ +A value of \"on\" takes effect immediately, otherwise nothing happens\n\ +until the next time the program is continued.\n\ +When setting this to \"off\", \"set thread default pause on\" can be\n\ +used to pause individual threads by default instead.", + &set_task_cmd_list); + add_cmd ("pause", no_class, show_task_pause_cmd, + "Show whether the task is suspended while gdb has control.", + &show_task_cmd_list); + + add_cmd ("detach-suspend-count", class_run, set_task_detach_sc_cmd, + "Set the suspend count will leave on the thread when detaching.", + &set_task_cmd_list); + add_cmd ("detach-suspend-count", no_class, show_task_detach_sc_cmd, + "Show the suspend count will leave on the thread when detaching.", + &show_task_cmd_list); + + add_cmd ("exception-port", no_class, set_task_exc_port_cmd, + "Set the task exception port to which we forward exceptions.\n\ +The argument should be the value of the send right in the task.", + &set_task_cmd_list); + add_alias_cmd ("excp", "exception-port", no_class, 1, &set_task_cmd_list); + add_alias_cmd ("exc-port", "exception-port", no_class, 1, + &set_task_cmd_list); + + /* A convenient way of turning on all options require to noninvasively + debug running tasks. */ + add_cmd ("noninvasive", no_class, set_noninvasive_cmd, + "Set task options so that we interfere as little as possible.\n\ +This is the same as setting `task pause', `exceptions', and\n\ +`signals' to the opposite value.", + &setlist); + + /* Commands to show information about the task's ports. */ + add_cmd ("send-rights", class_info, info_send_rights_cmd, + "Show information about the task's send rights", + &infolist); + add_cmd ("receive-rights", class_info, info_recv_rights_cmd, + "Show information about the task's receive rights", + &infolist); + add_cmd ("port-rights", class_info, info_port_rights_cmd, + "Show information about the task's port rights", + &infolist); + add_cmd ("port-sets", class_info, info_port_sets_cmd, + "Show information about the task's port sets", + &infolist); + add_cmd ("dead-names", class_info, info_dead_names_cmd, + "Show information about the task's dead names", + &infolist); + add_info_alias ("ports", "port-rights", 1); + add_info_alias ("port", "port-rights", 1); + add_info_alias ("psets", "port-sets", 1); +} + + +static void +set_thread_pause_cmd (char *args, int from_tty) +{ + struct proc *thread = cur_thread (); + int old_sc = thread->pause_sc; + thread->pause_sc = parse_bool_arg (args, "set thread pause"); + if (old_sc == 0 && thread->pause_sc != 0 && thread->inf->pause_sc == 0) + /* If the task is currently unsuspended, immediately suspend it, + otherwise wait until the next time it gets control. */ + inf_suspend (thread->inf); +} + +static void +show_thread_pause_cmd (char *args, int from_tty) +{ + struct proc *thread = cur_thread (); + int sc = thread->pause_sc; + check_empty (args, "show task pause"); + printf_unfiltered ("Thread %s %s suspended while gdb has control%s.\n", + proc_string (thread), + sc ? "is" : "isn't", + !sc && thread->inf->pause_sc ? " (but the task is)" : ""); +} + +static void +set_thread_run_cmd (char *args, int from_tty) +{ + struct proc *thread = cur_thread (); + thread->run_sc = parse_bool_arg (args, "set thread run") ? 0 : 1; +} + +static void +show_thread_run_cmd (char *args, int from_tty) +{ + struct proc *thread = cur_thread (); + check_empty (args, "show thread run"); + printf_unfiltered ("Thread %s %s allowed to run.", + proc_string (thread), + thread->run_sc == 0 ? "is" : "isn't"); +} + +static void +set_thread_detach_sc_cmd (char *args, int from_tty) +{ + cur_thread ()->detach_sc = parse_int_arg (args, + "set thread detach-suspend-count"); +} + +static void +show_thread_detach_sc_cmd (char *args, int from_tty) +{ + struct proc *thread = cur_thread (); + check_empty (args, "show thread detach-suspend-count"); + printf_unfiltered ("Thread %s will be left with a suspend count" + " of %d when detaching.\n", + proc_string (thread), + thread->detach_sc); +} + +static void +set_thread_exc_port_cmd (char *args, int from_tty) +{ + struct proc *thread = cur_thread (); + if (!args) + error ("No argument to \"set thread exception-port\" command."); + steal_exc_port (thread, parse_and_eval_address (args)); +} + +#if 0 +static void +show_thread_cmd (char *args, int from_tty) +{ + struct proc *thread = cur_thread (); + check_empty (args, "show thread"); + show_thread_run_cmd (0, from_tty); + show_thread_pause_cmd (0, from_tty); + if (thread->detach_sc != 0) + show_thread_detach_sc_cmd (0, from_tty); +} +#endif + +static void +thread_takeover_sc_cmd (char *args, int from_tty) +{ + struct proc *thread = cur_thread (); + thread_basic_info_data_t _info; + thread_basic_info_t info = &_info; + mach_msg_type_number_t info_len = THREAD_BASIC_INFO_COUNT; + error_t err = + thread_info (thread->port, THREAD_BASIC_INFO, (int *) &info, &info_len); + if (err) + error ("%s.", safe_strerror (err)); + thread->sc = info->suspend_count; + if (from_tty) + printf_unfiltered ("Suspend count was %d.\n", thread->sc); + if (info != &_info) + vm_deallocate (mach_task_self (), (vm_address_t) info, + info_len * sizeof (int)); +} + + +static void +add_thread_commands (void) +{ + add_prefix_cmd ("thread", no_class, set_thread_cmd, + "Command prefix for setting thread properties.", + &set_thread_cmd_list, "set thread ", 0, &setlist); + add_prefix_cmd ("default", no_class, show_thread_cmd, + "Command prefix for setting default thread properties.", + &set_thread_default_cmd_list, "set thread default ", 0, + &set_thread_cmd_list); + add_prefix_cmd ("thread", no_class, set_thread_default_cmd, + "Command prefix for showing thread properties.", + &show_thread_cmd_list, "show thread ", 0, &showlist); + add_prefix_cmd ("default", no_class, show_thread_default_cmd, + "Command prefix for showing default thread properties.", + &show_thread_default_cmd_list, "show thread default ", 0, + &show_thread_cmd_list); + + add_cmd ("pause", class_run, set_thread_pause_cmd, + "Set whether the current thread is suspended \ +while gdb has control.\n\ +A value of \"on\" takes effect immediately, otherwise nothing happens\n\ +until the next time the program is continued. This property normally\n\ +has no effect because the whole task is suspended, however, that may\n\ +be disabled with \"set task pause off\".\n\ +The default value is \"off\".", + &set_thread_cmd_list); + add_cmd ("pause", no_class, show_thread_pause_cmd, + "Show whether the current thread is suspended \ +while gdb has control.", + &show_thread_cmd_list); + + add_cmd ("run", class_run, set_thread_run_cmd, + "Set whether the current thread is allowed to run.", + &set_thread_cmd_list); + add_cmd ("run", no_class, show_thread_run_cmd, + "Show whether the current thread is allowed to run.", + &show_thread_cmd_list); + + add_cmd ("detach-suspend-count", class_run, set_thread_detach_sc_cmd, + "Set the suspend count will leave on the thread when detaching.\n\ +Note that this is relative to suspend count when gdb noticed the thread;\n\ +use the `thread takeover-suspend-count' to force it to an absolute value.", + &set_thread_cmd_list); + add_cmd ("detach-suspend-count", no_class, show_thread_detach_sc_cmd, + "Show the suspend count will leave on the thread when detaching.\n\ +Note that this is relative to suspend count when gdb noticed the thread;\n\ +use the `thread takeover-suspend-count' to force it to an absolute value.", + &show_thread_cmd_list); + + add_cmd ("exception-port", no_class, set_thread_exc_port_cmd, + "Set the thread exception port to which we forward exceptions.\n\ +This overrides the task exception port.\n\ +The argument should be the value of the send right in the task.", + &set_thread_cmd_list); + add_alias_cmd ("excp", "exception-port", no_class, 1, &set_thread_cmd_list); + add_alias_cmd ("exc-port", "exception-port", no_class, 1, + &set_thread_cmd_list); + + add_cmd ("takeover-suspend-count", no_class, thread_takeover_sc_cmd, + "Force the threads absolute suspend-count to be gdb's.\n\ +Prior to giving this command, gdb's thread suspend-counts are relative\n\ +to the thread's initial suspend-count when gdb notices the threads.", + &thread_cmd_list); +} + + +void +_initialize_gnu_nat (void) +{ + proc_server = getproc (); + + init_gnu_ops (); + add_target (&gnu_ops); + + add_task_commands (); + add_thread_commands (); + add_set_cmd ("gnu-debug", class_maintenance, + var_boolean, (char *) &gnu_debug_flag, + "Set debugging output for the gnu backend.", &maintenancelist); +} + +#ifdef FLUSH_INFERIOR_CACHE + +/* When over-writing code on some machines the I-Cache must be flushed + explicitly, because it is not kept coherent by the lazy hardware. + This definitely includes breakpoints, for instance, or else we + end up looping in mysterious Bpt traps */ + +void +flush_inferior_icache (CORE_ADDR pc, int amount) +{ + vm_machine_attribute_val_t flush = MATTR_VAL_ICACHE_FLUSH; + error_t ret; + + ret = vm_machine_attribute (current_inferior->task->port, + pc, + amount, + MATTR_CACHE, + &flush); + if (ret != KERN_SUCCESS) + warning ("Error flushing inferior's cache : %s", safe_strerror (ret)); +} +#endif /* FLUSH_INFERIOR_CACHE */ diff --git a/contrib/gdb/gdb/gnu-nat.h b/contrib/gdb/gdb/gnu-nat.h new file mode 100644 index 0000000..bcdfe6e --- /dev/null +++ b/contrib/gdb/gdb/gnu-nat.h @@ -0,0 +1,101 @@ +/* Common things used by the various *gnu-nat.c files + Copyright 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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. */ + +#ifndef __GNU_NAT_H__ +#define __GNU_NAT_H__ + +#include <unistd.h> +#include <mach.h> + +struct inf; + +extern struct inf *current_inferior; + +/* Converts a GDB pid to a struct proc. */ +struct proc *inf_tid_to_thread (struct inf *inf, int tid); + +/* Makes sure that INF's thread list is synced with the actual process. */ +int inf_update_procs (struct inf *inf); + +/* A proc is either a thread, or the task (there can only be one task proc + because it always has the same TID, PROC_TID_TASK). */ +struct proc + { + thread_t port; /* The task or thread port. */ + int tid; /* The GDB pid (actually a thread id). */ + int num; /* An id number for threads, to print. */ + + mach_port_t saved_exc_port; /* The task/thread's real exception port. */ + mach_port_t exc_port; /* Our replacement, which for. */ + + int sc; /* Desired suspend count. */ + int cur_sc; /* Implemented suspend count. */ + int run_sc; /* Default sc when the program is running. */ + int pause_sc; /* Default sc when gdb has control. */ + int resume_sc; /* Sc resulting from the last resume. */ + int detach_sc; /* SC to leave around when detaching + from program. */ + + thread_state_data_t state; /* Registers, &c. */ + int state_valid:1; /* True if STATE is up to date. */ + int state_changed:1; + + int aborted:1; /* True if thread_abort has been called. */ + int dead:1; /* We happen to know it's actually dead. */ + + /* Bit mask of registers fetched by gdb. This is used when we re-fetch + STATE after aborting the thread, to detect that gdb may have out-of-date + information. */ + unsigned long fetched_regs; + + struct inf *inf; /* Where we come from. */ + + struct proc *next; + }; + +/* The task has a thread entry with this TID. */ +#define PROC_TID_TASK (-1) + +#define proc_is_task(proc) ((proc)->tid == PROC_TID_TASK) +#define proc_is_thread(proc) ((proc)->tid != PROC_TID_TASK) + +extern int __proc_pid (struct proc *proc); + +/* Make sure that the state field in PROC is up to date, and return a + pointer to it, or 0 if something is wrong. If WILL_MODIFY is true, + makes sure that the thread is stopped and aborted first, and sets + the state_changed field in PROC to true. */ +extern thread_state_t proc_get_state (struct proc *proc, int will_modify); + +/* Return printable description of proc. */ +extern char *proc_string (struct proc *proc); + +#define proc_debug(_proc, msg, args...) \ + do { struct proc *__proc = (_proc); \ + debug ("{proc %d/%d %p}: " msg, \ + __proc_pid (__proc), __proc->tid, __proc , ##args); } while (0) + +extern int gnu_debug_flag; + +#define debug(msg, args...) \ + do { if (gnu_debug_flag) \ + fprintf_unfiltered (gdb_stdlog, "%s:%d: " msg "\r\n", __FILE__ , __LINE__ , ##args); } while (0) + +#endif /* __GNU_NAT_H__ */ diff --git a/contrib/gdb/gdb/i386-stub.c b/contrib/gdb/gdb/i386-stub.c new file mode 100644 index 0000000..1251567 --- /dev/null +++ b/contrib/gdb/gdb/i386-stub.c @@ -0,0 +1,952 @@ +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or it's performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for 386 by Jim Kingdon, Cygnus Support. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a trap #1. + * + * The external function exceptionHandler() is + * used to attach a specific handler to a specific 386 vector number. + * It should use the same privilege level it runs at. It should + * install it as an interrupt gate so that interrupts are masked + * while the handler runs. + * + * Because gdb will sometimes write to the stack area to execute function + * calls, this program cannot rely on using the supervisor stack so it + * uses it's own stack area reserved in the int array remcomStack. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include <stdio.h> +#include <string.h> + +/************************************************************************ + * + * external low-level support routines + */ + +extern void putDebugChar(); /* write a single character */ +extern int getDebugChar(); /* read and return a single char */ +extern void exceptionHandler(); /* assign an exception handler */ + +/************************************************************************/ +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ +/* at least NUMREGBYTES*2 are needed for register packets */ +#define BUFMAX 400 + +static char initialized; /* boolean flag. != 0 means we've been initialized */ + +int remote_debug; +/* debug > 0 prints ill-formed commands in valid packets & checksum errors */ + +static const char hexchars[]="0123456789abcdef"; + +/* Number of registers. */ +#define NUMREGS 16 + +/* Number of bytes of registers. */ +#define NUMREGBYTES (NUMREGS * 4) + +enum regnames {EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI, + PC /* also known as eip */, + PS /* also known as eflags */, + CS, SS, DS, ES, FS, GS}; + +/* + * these should not be static cuz they can be used outside this module + */ +int registers[NUMREGS]; + +#define STACKSIZE 10000 +int remcomStack[STACKSIZE/sizeof(int)]; +static int* stackPtr = &remcomStack[STACKSIZE/sizeof(int) - 1]; + +/*************************** ASSEMBLY CODE MACROS *************************/ +/* */ + +extern void +return_to_prog (); + +/* Restore the program's registers (including the stack pointer, which + means we get the right stack and don't have to worry about popping our + return address and any stack frames and so on) and return. */ +asm(".text"); +asm(".globl _return_to_prog"); +asm("_return_to_prog:"); +asm(" movw _registers+44, %ss"); +asm(" movl _registers+16, %esp"); +asm(" movl _registers+4, %ecx"); +asm(" movl _registers+8, %edx"); +asm(" movl _registers+12, %ebx"); +asm(" movl _registers+20, %ebp"); +asm(" movl _registers+24, %esi"); +asm(" movl _registers+28, %edi"); +asm(" movw _registers+48, %ds"); +asm(" movw _registers+52, %es"); +asm(" movw _registers+56, %fs"); +asm(" movw _registers+60, %gs"); +asm(" movl _registers+36, %eax"); +asm(" pushl %eax"); /* saved eflags */ +asm(" movl _registers+40, %eax"); +asm(" pushl %eax"); /* saved cs */ +asm(" movl _registers+32, %eax"); +asm(" pushl %eax"); /* saved eip */ +asm(" movl _registers, %eax"); +/* use iret to restore pc and flags together so + that trace flag works right. */ +asm(" iret"); + +#define BREAKPOINT() asm(" int $3"); + +/* Put the error code here just in case the user cares. */ +int gdb_i386errcode; +/* Likewise, the vector number here (since GDB only gets the signal + number through the usual means, and that's not very specific). */ +int gdb_i386vector = -1; + +/* GDB stores segment registers in 32-bit words (that's just the way + m-i386v.h is written). So zero the appropriate areas in registers. */ +#define SAVE_REGISTERS1() \ + asm ("movl %eax, _registers"); \ + asm ("movl %ecx, _registers+4"); \ + asm ("movl %edx, _registers+8"); \ + asm ("movl %ebx, _registers+12"); \ + asm ("movl %ebp, _registers+20"); \ + asm ("movl %esi, _registers+24"); \ + asm ("movl %edi, _registers+28"); \ + asm ("movw $0, %ax"); \ + asm ("movw %ds, _registers+48"); \ + asm ("movw %ax, _registers+50"); \ + asm ("movw %es, _registers+52"); \ + asm ("movw %ax, _registers+54"); \ + asm ("movw %fs, _registers+56"); \ + asm ("movw %ax, _registers+58"); \ + asm ("movw %gs, _registers+60"); \ + asm ("movw %ax, _registers+62"); +#define SAVE_ERRCODE() \ + asm ("popl %ebx"); \ + asm ("movl %ebx, _gdb_i386errcode"); +#define SAVE_REGISTERS2() \ + asm ("popl %ebx"); /* old eip */ \ + asm ("movl %ebx, _registers+32"); \ + asm ("popl %ebx"); /* old cs */ \ + asm ("movl %ebx, _registers+40"); \ + asm ("movw %ax, _registers+42"); \ + asm ("popl %ebx"); /* old eflags */ \ + asm ("movl %ebx, _registers+36"); \ + /* Now that we've done the pops, we can save the stack pointer."); */ \ + asm ("movw %ss, _registers+44"); \ + asm ("movw %ax, _registers+46"); \ + asm ("movl %esp, _registers+16"); + +/* See if mem_fault_routine is set, if so just IRET to that address. */ +#define CHECK_FAULT() \ + asm ("cmpl $0, _mem_fault_routine"); \ + asm ("jne mem_fault"); + +asm (".text"); +asm ("mem_fault:"); +/* OK to clobber temp registers; we're just going to end up in set_mem_err. */ +/* Pop error code from the stack and save it. */ +asm (" popl %eax"); +asm (" movl %eax, _gdb_i386errcode"); + +asm (" popl %eax"); /* eip */ +/* We don't want to return there, we want to return to the function + pointed to by mem_fault_routine instead. */ +asm (" movl _mem_fault_routine, %eax"); +asm (" popl %ecx"); /* cs (low 16 bits; junk in hi 16 bits). */ +asm (" popl %edx"); /* eflags */ + +/* Remove this stack frame; when we do the iret, we will be going to + the start of a function, so we want the stack to look just like it + would after a "call" instruction. */ +asm (" leave"); + +/* Push the stuff that iret wants. */ +asm (" pushl %edx"); /* eflags */ +asm (" pushl %ecx"); /* cs */ +asm (" pushl %eax"); /* eip */ + +/* Zero mem_fault_routine. */ +asm (" movl $0, %eax"); +asm (" movl %eax, _mem_fault_routine"); + +asm ("iret"); + +#define CALL_HOOK() asm("call _remcomHandler"); + +/* This function is called when a i386 exception occurs. It saves + * all the cpu regs in the _registers array, munges the stack a bit, + * and invokes an exception handler (remcom_handler). + * + * stack on entry: stack on exit: + * old eflags vector number + * old cs (zero-filled to 32 bits) + * old eip + * + */ +extern void _catchException3(); +asm(".text"); +asm(".globl __catchException3"); +asm("__catchException3:"); +SAVE_REGISTERS1(); +SAVE_REGISTERS2(); +asm ("pushl $3"); +CALL_HOOK(); + +/* Same thing for exception 1. */ +extern void _catchException1(); +asm(".text"); +asm(".globl __catchException1"); +asm("__catchException1:"); +SAVE_REGISTERS1(); +SAVE_REGISTERS2(); +asm ("pushl $1"); +CALL_HOOK(); + +/* Same thing for exception 0. */ +extern void _catchException0(); +asm(".text"); +asm(".globl __catchException0"); +asm("__catchException0:"); +SAVE_REGISTERS1(); +SAVE_REGISTERS2(); +asm ("pushl $0"); +CALL_HOOK(); + +/* Same thing for exception 4. */ +extern void _catchException4(); +asm(".text"); +asm(".globl __catchException4"); +asm("__catchException4:"); +SAVE_REGISTERS1(); +SAVE_REGISTERS2(); +asm ("pushl $4"); +CALL_HOOK(); + +/* Same thing for exception 5. */ +extern void _catchException5(); +asm(".text"); +asm(".globl __catchException5"); +asm("__catchException5:"); +SAVE_REGISTERS1(); +SAVE_REGISTERS2(); +asm ("pushl $5"); +CALL_HOOK(); + +/* Same thing for exception 6. */ +extern void _catchException6(); +asm(".text"); +asm(".globl __catchException6"); +asm("__catchException6:"); +SAVE_REGISTERS1(); +SAVE_REGISTERS2(); +asm ("pushl $6"); +CALL_HOOK(); + +/* Same thing for exception 7. */ +extern void _catchException7(); +asm(".text"); +asm(".globl __catchException7"); +asm("__catchException7:"); +SAVE_REGISTERS1(); +SAVE_REGISTERS2(); +asm ("pushl $7"); +CALL_HOOK(); + +/* Same thing for exception 8. */ +extern void _catchException8(); +asm(".text"); +asm(".globl __catchException8"); +asm("__catchException8:"); +SAVE_REGISTERS1(); +SAVE_ERRCODE(); +SAVE_REGISTERS2(); +asm ("pushl $8"); +CALL_HOOK(); + +/* Same thing for exception 9. */ +extern void _catchException9(); +asm(".text"); +asm(".globl __catchException9"); +asm("__catchException9:"); +SAVE_REGISTERS1(); +SAVE_REGISTERS2(); +asm ("pushl $9"); +CALL_HOOK(); + +/* Same thing for exception 10. */ +extern void _catchException10(); +asm(".text"); +asm(".globl __catchException10"); +asm("__catchException10:"); +SAVE_REGISTERS1(); +SAVE_ERRCODE(); +SAVE_REGISTERS2(); +asm ("pushl $10"); +CALL_HOOK(); + +/* Same thing for exception 12. */ +extern void _catchException12(); +asm(".text"); +asm(".globl __catchException12"); +asm("__catchException12:"); +SAVE_REGISTERS1(); +SAVE_ERRCODE(); +SAVE_REGISTERS2(); +asm ("pushl $12"); +CALL_HOOK(); + +/* Same thing for exception 16. */ +extern void _catchException16(); +asm(".text"); +asm(".globl __catchException16"); +asm("__catchException16:"); +SAVE_REGISTERS1(); +SAVE_REGISTERS2(); +asm ("pushl $16"); +CALL_HOOK(); + +/* For 13, 11, and 14 we have to deal with the CHECK_FAULT stuff. */ + +/* Same thing for exception 13. */ +extern void _catchException13 (); +asm (".text"); +asm (".globl __catchException13"); +asm ("__catchException13:"); +CHECK_FAULT(); +SAVE_REGISTERS1(); +SAVE_ERRCODE(); +SAVE_REGISTERS2(); +asm ("pushl $13"); +CALL_HOOK(); + +/* Same thing for exception 11. */ +extern void _catchException11 (); +asm (".text"); +asm (".globl __catchException11"); +asm ("__catchException11:"); +CHECK_FAULT(); +SAVE_REGISTERS1(); +SAVE_ERRCODE(); +SAVE_REGISTERS2(); +asm ("pushl $11"); +CALL_HOOK(); + +/* Same thing for exception 14. */ +extern void _catchException14 (); +asm (".text"); +asm (".globl __catchException14"); +asm ("__catchException14:"); +CHECK_FAULT(); +SAVE_REGISTERS1(); +SAVE_ERRCODE(); +SAVE_REGISTERS2(); +asm ("pushl $14"); +CALL_HOOK(); + +/* + * remcomHandler is a front end for handle_exception. It moves the + * stack pointer into an area reserved for debugger use. + */ +asm("_remcomHandler:"); +asm(" popl %eax"); /* pop off return address */ +asm(" popl %eax"); /* get the exception number */ +asm(" movl _stackPtr, %esp"); /* move to remcom stack area */ +asm(" pushl %eax"); /* push exception onto stack */ +asm(" call _handle_exception"); /* this never returns */ + +void +_returnFromException () +{ + return_to_prog (); +} + +int +hex (ch) + char ch; +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; + +/* scan for the sequence $<data>#<checksum> */ + +unsigned char * +getpacket (void) +{ + unsigned char *buffer = &remcomInBuffer[0]; + unsigned char checksum; + unsigned char xmitcsum; + int count; + char ch; + + while (1) + { + /* wait around for the start character, ignore all other characters */ + while ((ch = getDebugChar ()) != '$') + ; + + retry: + checksum = 0; + xmitcsum = -1; + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) + { + ch = getDebugChar (); + if (ch == '$') + goto retry; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') + { + ch = getDebugChar (); + xmitcsum = hex (ch) << 4; + ch = getDebugChar (); + xmitcsum += hex (ch); + + if (checksum != xmitcsum) + { + if (remote_debug) + { + fprintf (stderr, + "bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n", + checksum, xmitcsum, buffer); + } + putDebugChar ('-'); /* failed checksum */ + } + else + { + putDebugChar ('+'); /* successful transfer */ + + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') + { + putDebugChar (buffer[0]); + putDebugChar (buffer[1]); + + return &buffer[3]; + } + + return &buffer[0]; + } + } + } +} + +/* send the packet in buffer. */ + +void +putpacket (unsigned char *buffer) +{ + unsigned char checksum; + int count; + char ch; + + /* $<packet info>#<checksum>. */ + do + { + putDebugChar ('$'); + checksum = 0; + count = 0; + + while (ch = buffer[count]) + { + putDebugChar (ch); + checksum += ch; + count += 1; + } + + putDebugChar ('#'); + putDebugChar (hexchars[checksum >> 4]); + putDebugChar (hexchars[checksum % 16]); + + } + while (getDebugChar () != '+'); +} + +void +debug_error (format, parm) + char *format; + char *parm; +{ + if (remote_debug) + fprintf (stderr, format, parm); +} + +/* Address of a routine to RTE to if we get a memory fault. */ +static void (*volatile mem_fault_routine) () = NULL; + +/* Indicate to caller of mem2hex or hex2mem that there has been an + error. */ +static volatile int mem_err = 0; + +void +set_mem_err (void) +{ + mem_err = 1; +} + +/* These are separate functions so that they are so short and sweet + that the compiler won't save any registers (if there is a fault + to mem_fault, they won't get restored, so there better not be any + saved). */ +int +get_char (char *addr) +{ + return *addr; +} + +void +set_char (char *addr, int val) +{ + *addr = val; +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* return a pointer to the last char put in buf (null) */ +/* If MAY_FAULT is non-zero, then we should set mem_err in response to + a fault; if zero treat a fault like any other fault in the stub. */ +char * +mem2hex (mem, buf, count, may_fault) + char *mem; + char *buf; + int count; + int may_fault; +{ + int i; + unsigned char ch; + + if (may_fault) + mem_fault_routine = set_mem_err; + for (i = 0; i < count; i++) + { + ch = get_char (mem++); + if (may_fault && mem_err) + return (buf); + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + if (may_fault) + mem_fault_routine = NULL; + return (buf); +} + +/* convert the hex array pointed to by buf into binary to be placed in mem */ +/* return a pointer to the character AFTER the last byte written */ +char * +hex2mem (buf, mem, count, may_fault) + char *buf; + char *mem; + int count; + int may_fault; +{ + int i; + unsigned char ch; + + if (may_fault) + mem_fault_routine = set_mem_err; + for (i = 0; i < count; i++) + { + ch = hex (*buf++) << 4; + ch = ch + hex (*buf++); + set_char (mem++, ch); + if (may_fault && mem_err) + return (mem); + } + if (may_fault) + mem_fault_routine = NULL; + return (mem); +} + +/* this function takes the 386 exception vector and attempts to + translate this number into a unix compatible signal value */ +int +computeSignal (int exceptionVector) +{ + int sigval; + switch (exceptionVector) + { + case 0: + sigval = 8; + break; /* divide by zero */ + case 1: + sigval = 5; + break; /* debug exception */ + case 3: + sigval = 5; + break; /* breakpoint */ + case 4: + sigval = 16; + break; /* into instruction (overflow) */ + case 5: + sigval = 16; + break; /* bound instruction */ + case 6: + sigval = 4; + break; /* Invalid opcode */ + case 7: + sigval = 8; + break; /* coprocessor not available */ + case 8: + sigval = 7; + break; /* double fault */ + case 9: + sigval = 11; + break; /* coprocessor segment overrun */ + case 10: + sigval = 11; + break; /* Invalid TSS */ + case 11: + sigval = 11; + break; /* Segment not present */ + case 12: + sigval = 11; + break; /* stack exception */ + case 13: + sigval = 11; + break; /* general protection */ + case 14: + sigval = 11; + break; /* page fault */ + case 16: + sigval = 7; + break; /* coprocessor error */ + default: + sigval = 7; /* "software generated" */ + } + return (sigval); +} + +/**********************************************/ +/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */ +/* RETURN NUMBER OF CHARS PROCESSED */ +/**********************************************/ +int +hexToInt (char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) + { + hexValue = hex (**ptr); + if (hexValue >= 0) + { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } + else + break; + + (*ptr)++; + } + + return (numChars); +} + +/* + * This function does all command procesing for interfacing to gdb. + */ +void +handle_exception (int exceptionVector) +{ + int sigval, stepping; + int addr, length; + char *ptr; + int newPC; + + gdb_i386vector = exceptionVector; + + if (remote_debug) + { + printf ("vector=%d, sr=0x%x, pc=0x%x\n", + exceptionVector, registers[PS], registers[PC]); + } + + /* reply to host that an exception has occurred */ + sigval = computeSignal (exceptionVector); + + ptr = remcomOutBuffer; + + *ptr++ = 'T'; /* notify gdb with signo, PC, FP and SP */ + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + + *ptr++ = hexchars[ESP]; + *ptr++ = ':'; + ptr = mem2hex((char *)®isters[ESP], ptr, 4, 0); /* SP */ + *ptr++ = ';'; + + *ptr++ = hexchars[EBP]; + *ptr++ = ':'; + ptr = mem2hex((char *)®isters[EBP], ptr, 4, 0); /* FP */ + *ptr++ = ';'; + + *ptr++ = hexchars[PC]; + *ptr++ = ':'; + ptr = mem2hex((char *)®isters[PC], ptr, 4, 0); /* PC */ + *ptr++ = ';'; + + *ptr = '\0' + + putpacket (remcomOutBuffer); + + stepping = 0; + + while (1 == 1) + { + remcomOutBuffer[0] = 0; + ptr = getpacket (); + + switch (*ptr++) + { + case '?': + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[sigval >> 4]; + remcomOutBuffer[2] = hexchars[sigval % 16]; + remcomOutBuffer[3] = 0; + break; + case 'd': + remote_debug = !(remote_debug); /* toggle debug flag */ + break; + case 'g': /* return the value of the CPU registers */ + mem2hex ((char *) registers, remcomOutBuffer, NUMREGBYTES, 0); + break; + case 'G': /* set the value of the CPU registers - return OK */ + hex2mem (ptr, (char *) registers, NUMREGBYTES, 0); + strcpy (remcomOutBuffer, "OK"); + break; + case 'P': /* set the value of a single CPU register - return OK */ + { + int regno; + + if (hexToInt (&ptr, ®no) && *ptr++ == '=') + if (regno >= 0 && regno < NUMREGS) + { + hex2mem (ptr, (char *) ®isters[regno], 4, 0); + strcpy (remcomOutBuffer, "OK"); + break; + } + + strcpy (remcomOutBuffer, "E01"); + break; + } + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ + if (hexToInt (&ptr, &addr)) + if (*(ptr++) == ',') + if (hexToInt (&ptr, &length)) + { + ptr = 0; + mem_err = 0; + mem2hex ((char *) addr, remcomOutBuffer, length, 1); + if (mem_err) + { + strcpy (remcomOutBuffer, "E03"); + debug_error ("memory fault"); + } + } + + if (ptr) + { + strcpy (remcomOutBuffer, "E01"); + } + break; + + /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + case 'M': + /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ + if (hexToInt (&ptr, &addr)) + if (*(ptr++) == ',') + if (hexToInt (&ptr, &length)) + if (*(ptr++) == ':') + { + mem_err = 0; + hex2mem (ptr, (char *) addr, length, 1); + + if (mem_err) + { + strcpy (remcomOutBuffer, "E03"); + debug_error ("memory fault"); + } + else + { + strcpy (remcomOutBuffer, "OK"); + } + + ptr = 0; + } + if (ptr) + { + strcpy (remcomOutBuffer, "E02"); + } + break; + + /* cAA..AA Continue at address AA..AA(optional) */ + /* sAA..AA Step one instruction from AA..AA(optional) */ + case 's': + stepping = 1; + case 'c': + /* try to read optional parameter, pc unchanged if no parm */ + if (hexToInt (&ptr, &addr)) + registers[PC] = addr; + + newPC = registers[PC]; + + /* clear the trace bit */ + registers[PS] &= 0xfffffeff; + + /* set the trace bit if we're stepping */ + if (stepping) + registers[PS] |= 0x100; + + _returnFromException (); /* this is a jump */ + break; + + /* kill the program */ + case 'k': /* do nothing */ +#if 0 + /* Huh? This doesn't look like "nothing". + m68k-stub.c and sparc-stub.c don't have it. */ + BREAKPOINT (); +#endif + break; + } /* switch */ + + /* reply to the request */ + putpacket (remcomOutBuffer); + } +} + +/* this function is used to set up exception handlers for tracing and + breakpoints */ +void +set_debug_traps (void) +{ + stackPtr = &remcomStack[STACKSIZE / sizeof (int) - 1]; + + exceptionHandler (0, _catchException0); + exceptionHandler (1, _catchException1); + exceptionHandler (3, _catchException3); + exceptionHandler (4, _catchException4); + exceptionHandler (5, _catchException5); + exceptionHandler (6, _catchException6); + exceptionHandler (7, _catchException7); + exceptionHandler (8, _catchException8); + exceptionHandler (9, _catchException9); + exceptionHandler (10, _catchException10); + exceptionHandler (11, _catchException11); + exceptionHandler (12, _catchException12); + exceptionHandler (13, _catchException13); + exceptionHandler (14, _catchException14); + exceptionHandler (16, _catchException16); + + initialized = 1; +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ + +void +breakpoint (void) +{ + if (initialized) + BREAKPOINT (); +} diff --git a/contrib/gdb/gdb/i386gnu-nat.c b/contrib/gdb/gdb/i386gnu-nat.c new file mode 100644 index 0000000..7533f09 --- /dev/null +++ b/contrib/gdb/gdb/i386gnu-nat.c @@ -0,0 +1,293 @@ +/* Low level interface to i386 running the GNU Hurd. + Copyright 1992, 1995, 1996, 1998, 2000, 2001 + 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 "defs.h" +#include "inferior.h" +#include "floatformat.h" +#include "regcache.h" + +#include "gdb_assert.h" +#include <errno.h> +#include <stdio.h> + +#include <mach.h> +#include <mach_error.h> +#include <mach/message.h> +#include <mach/exception.h> + +#include "i386-tdep.h" + +#include "gnu-nat.h" +#include "i387-tdep.h" + +#ifdef HAVE_SYS_PROCFS_H +# include <sys/procfs.h> +# include "gregset.h" +#endif + +/* Offset to the thread_state_t location where REG is stored. */ +#define REG_OFFSET(reg) offsetof (struct i386_thread_state, reg) + +/* At REG_OFFSET[N] is the offset to the thread_state_t location where + the GDB register N is stored. */ +static int reg_offset[] = +{ + REG_OFFSET (eax), REG_OFFSET (ecx), REG_OFFSET (edx), REG_OFFSET (ebx), + REG_OFFSET (uesp), REG_OFFSET (ebp), REG_OFFSET (esi), REG_OFFSET (edi), + REG_OFFSET (eip), REG_OFFSET (efl), REG_OFFSET (cs), REG_OFFSET (ss), + REG_OFFSET (ds), REG_OFFSET (es), REG_OFFSET (fs), REG_OFFSET (gs) +}; + +#define REG_ADDR(state, regnum) ((char *)(state) + reg_offset[regnum]) + + +/* Get the whole floating-point state of THREAD and record the + values of the corresponding (pseudo) registers. */ +static void +fetch_fpregs (struct proc *thread) +{ + mach_msg_type_number_t count = i386_FLOAT_STATE_COUNT; + struct i386_float_state state; + error_t err; + + err = thread_get_state (thread->port, i386_FLOAT_STATE, + (thread_state_t) &state, &count); + if (err) + { + warning ("Couldn't fetch floating-point state from %s", + proc_string (thread)); + return; + } + + if (!state.initialized) + /* The floating-point state isn't initialized. */ + { + int i; + + for (i = FP0_REGNUM; i <= FOP_REGNUM; i++) + supply_register (i, NULL); + + return; + } + + /* Supply the floating-point registers. */ + i387_supply_fsave (current_regcache, -1, state.hw_state); +} + +#ifdef HAVE_SYS_PROCFS_H +/* These two calls are used by the core-regset.c code for + reading ELF core files. */ +void +supply_gregset (gdb_gregset_t *gregs) +{ + int i; + for (i = 0; i < I386_NUM_GREGS; i++) + supply_register (i, REG_ADDR (gregs, i)); +} + +void +supply_fpregset (gdb_fpregset_t *fpregs) +{ + i387_supply_fsave (current_regcache, -1, fpregs); +} +#endif + +/* Fetch register REGNO, or all regs if REGNO is -1. */ +void +gnu_fetch_registers (int regno) +{ + struct proc *thread; + + /* Make sure we know about new threads. */ + inf_update_procs (current_inferior); + + thread = inf_tid_to_thread (current_inferior, PIDGET (inferior_ptid)); + if (!thread) + error ("Can't fetch registers from thread %d: No such thread", + PIDGET (inferior_ptid)); + + if (regno < I386_NUM_GREGS || regno == -1) + { + thread_state_t state; + + /* This does the dirty work for us. */ + state = proc_get_state (thread, 0); + if (!state) + { + warning ("Couldn't fetch registers from %s", + proc_string (thread)); + return; + } + + if (regno == -1) + { + int i; + + proc_debug (thread, "fetching all register"); + + for (i = 0; i < I386_NUM_GREGS; i++) + supply_register (i, REG_ADDR (state, i)); + thread->fetched_regs = ~0; + } + else + { + proc_debug (thread, "fetching register %s", REGISTER_NAME (regno)); + + supply_register (regno, REG_ADDR (state, regno)); + thread->fetched_regs |= (1 << regno); + } + } + + if (regno >= I386_NUM_GREGS || regno == -1) + { + proc_debug (thread, "fetching floating-point registers"); + + fetch_fpregs (thread); + } +} + + +/* Store the whole floating-point state into THREAD using information + from the corresponding (pseudo) registers. */ +static void +store_fpregs (struct proc *thread, int regno) +{ + mach_msg_type_number_t count = i386_FLOAT_STATE_COUNT; + struct i386_float_state state; + error_t err; + + err = thread_get_state (thread->port, i386_FLOAT_STATE, + (thread_state_t) &state, &count); + if (err) + { + warning ("Couldn't fetch floating-point state from %s", + proc_string (thread)); + return; + } + + /* FIXME: kettenis/2001-07-15: Is this right? Should we somehow + take into account DEPRECATED_REGISTER_VALID like the old code did? */ + i387_fill_fsave (state.hw_state, regno); + + err = thread_set_state (thread->port, i386_FLOAT_STATE, + (thread_state_t) &state, i386_FLOAT_STATE_COUNT); + if (err) + { + warning ("Couldn't store floating-point state into %s", + proc_string (thread)); + return; + } +} + +/* Store at least register REGNO, or all regs if REGNO == -1. */ +void +gnu_store_registers (int regno) +{ + struct proc *thread; + + /* Make sure we know about new threads. */ + inf_update_procs (current_inferior); + + thread = inf_tid_to_thread (current_inferior, PIDGET (inferior_ptid)); + if (!thread) + error ("Couldn't store registers into thread %d: No such thread", + PIDGET (inferior_ptid)); + + if (regno < I386_NUM_GREGS || regno == -1) + { + thread_state_t state; + thread_state_data_t old_state; + int was_aborted = thread->aborted; + int was_valid = thread->state_valid; + int trace; + + if (!was_aborted && was_valid) + memcpy (&old_state, &thread->state, sizeof (old_state)); + + state = proc_get_state (thread, 1); + if (!state) + { + warning ("Couldn't store registers into %s", proc_string (thread)); + return; + } + + /* Save the T bit. We might try to restore the %eflags register + below, but changing the T bit would seriously confuse GDB. */ + trace = ((struct i386_thread_state *)state)->efl & 0x100; + + if (!was_aborted && was_valid) + /* See which registers have changed after aborting the thread. */ + { + int check_regno; + + for (check_regno = 0; check_regno < I386_NUM_GREGS; check_regno++) + if ((thread->fetched_regs & (1 << check_regno)) + && memcpy (REG_ADDR (&old_state, check_regno), + REG_ADDR (state, check_regno), + DEPRECATED_REGISTER_RAW_SIZE (check_regno))) + /* Register CHECK_REGNO has changed! Ack! */ + { + warning ("Register %s changed after the thread was aborted", + REGISTER_NAME (check_regno)); + if (regno >= 0 && regno != check_regno) + /* Update GDB's copy of the register. */ + supply_register (check_regno, REG_ADDR (state, check_regno)); + else + warning ("... also writing this register! Suspicious..."); + } + } + +#define fill(state, regno) \ + memcpy (REG_ADDR(state, regno), &deprecated_registers[DEPRECATED_REGISTER_BYTE (regno)], \ + DEPRECATED_REGISTER_RAW_SIZE (regno)) + + if (regno == -1) + { + int i; + + proc_debug (thread, "storing all registers"); + + for (i = 0; i < I386_NUM_GREGS; i++) + if (deprecated_register_valid[i]) + fill (state, i); + } + else + { + proc_debug (thread, "storing register %s", REGISTER_NAME (regno)); + + gdb_assert (deprecated_register_valid[regno]); + fill (state, regno); + } + + /* Restore the T bit. */ + ((struct i386_thread_state *)state)->efl &= ~0x100; + ((struct i386_thread_state *)state)->efl |= trace; + } + +#undef fill + + if (regno >= I386_NUM_GREGS || regno == -1) + { + proc_debug (thread, "storing floating-point registers"); + + store_fpregs (thread, regno); + } +} diff --git a/contrib/gdb/gdb/i386ly-tdep.c b/contrib/gdb/gdb/i386ly-tdep.c new file mode 100644 index 0000000..2374b71 --- /dev/null +++ b/contrib/gdb/gdb/i386ly-tdep.c @@ -0,0 +1,81 @@ +/* Target-dependent code for Intel 386 running LynxOS. + Copyright 1993, 1996, 2000, 2001, 2002, 2003 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 "defs.h" +#include "gdbcore.h" +#include "inferior.h" +#include "regcache.h" +#include "target.h" +#include "osabi.h" + +#include "i386-tdep.h" + +/* Return the PC of the caller from the call frame. Assumes the subr + prologue has already been executed, and the frame pointer setup. + If this is the outermost frame, we check to see if we are in a + system call by examining the previous instruction. If so, then the + return PC is actually at SP+4 because system calls use a different + calling sequence. */ + +static CORE_ADDR +i386lynx_saved_pc_after_call (struct frame_info *frame) +{ + char opcode[7]; + static const unsigned char call_inst[] = + { 0x9a, 0, 0, 0, 0, 8, 0 }; /* lcall 0x8,0x0 */ + + read_memory_nobpt (frame->pc - 7, opcode, 7); + if (memcmp (opcode, call_inst, 7) == 0) + return read_memory_unsigned_integer (read_register (SP_REGNUM) + 4, 4); + + return read_memory_unsigned_integer (read_register (SP_REGNUM), 4); +} + + +/* LynxOS. */ +static void +i386lynx_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + set_gdbarch_deprecated_saved_pc_after_call (gdbarch, i386lynx_saved_pc_after_call); +} + + +static enum gdb_osabi +i386lynx_coff_osabi_sniffer (bfd *abfd) +{ + if (strcmp (bfd_get_target (abfd), "coff-i386-lynx") == 0) + return GDB_OSABI_LYNXOS; + + return GDB_OSABI_UNKNOWN; +} + + +/* Provide a prototype to silence -Wmissing-prototypes. */ +void _initialize_i386lynx_tdep (void); + +void +_initialize_i386lynx_tdep (void) +{ + gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_coff_flavour, + i386lynx_coff_osabi_sniffer); + + gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_LYNXOS, + i386lynx_init_abi); +} diff --git a/contrib/gdb/gdb/i386v-nat.c b/contrib/gdb/gdb/i386v-nat.c new file mode 100644 index 0000000..678eabc --- /dev/null +++ b/contrib/gdb/gdb/i386v-nat.c @@ -0,0 +1,277 @@ +/* Intel 386 native support for System V systems (pre-SVR4). + + Copyright 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 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 "defs.h" + +#ifdef HAVE_PTRACE_H +#include <ptrace.h> +#else +#ifdef HAVE_SYS_PTRACE_H +#include <sys/ptrace.h> +#endif +#endif + +#include "frame.h" +#include "inferior.h" +#include "language.h" +#include "gdbcore.h" + +#ifdef USG +#include <sys/types.h> +#endif + +#include <sys/param.h> +#include <sys/dir.h> +#include <signal.h> +#include <sys/user.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#ifdef TARGET_HAS_HARDWARE_WATCHPOINTS +#include <sys/debugreg.h> +#endif + +#include <sys/file.h> +#include "gdb_stat.h" + +#ifdef HAVE_SYS_REG_H +#include <sys/reg.h> +#endif + +#include "floatformat.h" + +#include "target.h" + +#include "i386-tdep.h" + + +/* Mapping between the general-purpose registers in `struct user' + format and GDB's register array layout. */ +static int regmap[] = +{ + EAX, ECX, EDX, EBX, + UESP, EBP, ESI, EDI, + EIP, EFL, CS, SS, + DS, ES, FS, GS, +}; + +/* Support for the user struct. */ + +/* Return the address of register REGNUM. BLOCKEND is the value of + u.u_ar0, and points to the place where GS is stored. */ + +CORE_ADDR +register_u_addr (CORE_ADDR blockend, int regnum) +{ + struct user u; + CORE_ADDR fpstate; + + if (i386_fp_regnum_p (regnum)) + { +#ifdef KSTKSZ /* SCO, and others? */ + blockend += 4 * (SS + 1) - KSTKSZ; + fpstate = blockend + ((char *) &u.u_fps.u_fpstate - (char *) &u); + return (fpstate + 0x1c + 10 * (regnum - FP0_REGNUM)); +#else + fpstate = blockend + ((char *) &u.i387.st_space - (char *) &u); + return (fpstate + 10 * (regnum - FP0_REGNUM)); +#endif + } + + return (blockend + 4 * regmap[regnum]); +} + +/* Return the size of the user struct. */ + +int +kernel_u_size (void) +{ + return (sizeof (struct user)); +} + +#ifdef TARGET_HAS_HARDWARE_WATCHPOINTS + +#if !defined (offsetof) +#define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER) +#endif + +/* Record the value of the debug control register. */ +static int debug_control_mirror; + +/* Record which address associates with which register. */ +static CORE_ADDR address_lookup[DR_LASTADDR - DR_FIRSTADDR + 1]; + +static int i386_insert_aligned_watchpoint (int, CORE_ADDR, CORE_ADDR, int, + int); + +static int i386_insert_nonaligned_watchpoint (int, CORE_ADDR, CORE_ADDR, int, + int); + +/* Insert a watchpoint. */ + +int +i386_insert_watchpoint (int pid, CORE_ADDR addr, int len, int rw) +{ + return i386_insert_aligned_watchpoint (pid, addr, addr, len, rw); +} + +static int +i386_insert_aligned_watchpoint (int pid, CORE_ADDR waddr, CORE_ADDR addr, + int len, int rw) +{ + int i; + int read_write_bits, len_bits; + int free_debug_register; + int register_number; + + /* Look for a free debug register. */ + for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++) + { + if (address_lookup[i - DR_FIRSTADDR] == 0) + break; + } + + /* No more debug registers! */ + if (i > DR_LASTADDR) + return -1; + + read_write_bits = (rw & 1) ? DR_RW_READ : DR_RW_WRITE; + + if (len == 1) + len_bits = DR_LEN_1; + else if (len == 2) + { + if (addr % 2) + return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw); + len_bits = DR_LEN_2; + } + + else if (len == 4) + { + if (addr % 4) + return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw); + len_bits = DR_LEN_4; + } + else + return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw); + + free_debug_register = i; + register_number = free_debug_register - DR_FIRSTADDR; + debug_control_mirror |= + ((read_write_bits | len_bits) + << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * register_number)); + debug_control_mirror |= + (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * register_number)); + debug_control_mirror |= DR_LOCAL_SLOWDOWN; + debug_control_mirror &= ~DR_CONTROL_RESERVED; + + ptrace (6, pid, offsetof (struct user, u_debugreg[DR_CONTROL]), + debug_control_mirror); + ptrace (6, pid, offsetof (struct user, u_debugreg[free_debug_register]), + addr); + + /* Record where we came from. */ + address_lookup[register_number] = addr; + return 0; +} + +static int +i386_insert_nonaligned_watchpoint (int pid, CORE_ADDR waddr, CORE_ADDR addr, + int len, int rw) +{ + int align; + int size; + int rv; + + static int size_try_array[4][4] = + { + { 1, 1, 1, 1 }, /* trying size one */ + { 2, 1, 2, 1 }, /* trying size two */ + { 2, 1, 2, 1 }, /* trying size three */ + { 4, 1, 2, 1 } /* trying size four */ + }; + + rv = 0; + while (len > 0) + { + align = addr % 4; + /* Four is the maximum length for 386. */ + size = size_try_array[len > 4 ? 3 : len - 1][align]; + + rv = i386_insert_aligned_watchpoint (pid, waddr, addr, size, rw); + if (rv) + { + i386_remove_watchpoint (pid, waddr, size); + return rv; + } + addr += size; + len -= size; + } + return rv; +} + +/* Remove a watchpoint. */ + +int +i386_remove_watchpoint (int pid, CORE_ADDR addr, int len) +{ + int i; + int register_number; + + for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++) + { + register_number = i - DR_FIRSTADDR; + if (address_lookup[register_number] == addr) + { + debug_control_mirror &= + ~(1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * register_number)); + address_lookup[register_number] = 0; + } + } + ptrace (6, pid, offsetof (struct user, u_debugreg[DR_CONTROL]), + debug_control_mirror); + ptrace (6, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0); + + return 0; +} + +/* Check if stopped by a watchpoint. */ + +CORE_ADDR +i386_stopped_by_watchpoint (int pid) +{ + int i; + int status; + + status = ptrace (3, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0); + ptrace (6, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0); + + for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++) + { + if (status & (1 << (i - DR_FIRSTADDR))) + return address_lookup[i - DR_FIRSTADDR]; + } + + return 0; +} + +#endif /* TARGET_HAS_HARDWARE_WATCHPOINTS */ diff --git a/contrib/gdb/gdb/i386v4-nat.c b/contrib/gdb/gdb/i386v4-nat.c new file mode 100644 index 0000000..188f01b --- /dev/null +++ b/contrib/gdb/gdb/i386v4-nat.c @@ -0,0 +1,160 @@ +/* Native-dependent code for SVR4 Unix running on i386's. + Copyright 1988, 1989, 1991, 1992, 1996, 1997, 1998, 1999, 2000, + 2001, 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 "defs.h" +#include "value.h" +#include "inferior.h" +#include "regcache.h" + +#ifdef HAVE_SYS_REG_H +#include <sys/reg.h> +#endif + +#include "i386-tdep.h" +#include "i387-tdep.h" + +#ifdef HAVE_SYS_PROCFS_H + +#include <sys/procfs.h> + +/* Prototypes for supply_gregset etc. */ +#include "gregset.h" + +/* The `/proc' interface divides the target machine's register set up + into two different sets, the general purpose register set (gregset) + and the floating-point register set (fpregset). For each set, + there is an ioctl to get the current register set and another ioctl + to set the current values. + + The actual structure passed through the ioctl interface is, of + course, naturally machine dependent, and is different for each set + of registers. For the i386 for example, the general-purpose + register set is typically defined by: + + typedef int gregset_t[19]; (in <sys/regset.h>) + + #define GS 0 (in <sys/reg.h>) + #define FS 1 + ... + #define UESP 17 + #define SS 18 + + and the floating-point set by: + + typedef struct fpregset { + union { + struct fpchip_state // fp extension state // + { + int state[27]; // 287/387 saved state // + int status; // status word saved at // + // exception // + } fpchip_state; + struct fp_emul_space // for emulators // + { + char fp_emul[246]; + char fp_epad[2]; + } fp_emul_space; + int f_fpregs[62]; // union of the above // + } fp_reg_set; + long f_wregs[33]; // saved weitek state // + } fpregset_t; + + Incidentally fpchip_state contains the FPU state in the same format + as used by the "fsave" instruction, and that's the only thing we + support here. I don't know how the emulator stores it state. The + Weitek stuff definitely isn't supported. + + The routines defined here, provide the packing and unpacking of + gregset_t and fpregset_t formatted data. */ + +#ifdef HAVE_GREGSET_T + +/* Mapping between the general-purpose registers in `/proc' + format and GDB's register array layout. */ +static int regmap[] = +{ + EAX, ECX, EDX, EBX, + UESP, EBP, ESI, EDI, + EIP, EFL, CS, SS, + DS, ES, FS, GS, +}; + +/* Fill GDB's register array with the general-purpose register values + in *GREGSETP. */ + +void +supply_gregset (gregset_t *gregsetp) +{ + greg_t *regp = (greg_t *) gregsetp; + int i; + + for (i = 0; i < I386_NUM_GREGS; i++) + supply_register (i, (char *) (regp + regmap[i])); +} + +/* Fill register REGNO (if it is a general-purpose register) in + *GREGSETPS with the value in GDB's register array. If REGNO is -1, + do this for all registers. */ + +void +fill_gregset (gregset_t *gregsetp, int regno) +{ + greg_t *regp = (greg_t *) gregsetp; + int i; + + for (i = 0; i < I386_NUM_GREGS; i++) + if (regno == -1 || regno == i) + regcache_collect (i, regp + regmap[i]); +} + +#endif /* HAVE_GREGSET_T */ + +#ifdef HAVE_FPREGSET_T + +/* Fill GDB's register array with the floating-point register values in + *FPREGSETP. */ + +void +supply_fpregset (fpregset_t *fpregsetp) +{ + if (FP0_REGNUM == 0) + return; + + i387_supply_fsave (current_regcache, -1, fpregsetp); +} + +/* Fill register REGNO (if it is a floating-point register) in + *FPREGSETP with the value in GDB's register array. If REGNO is -1, + do this for all registers. */ + +void +fill_fpregset (fpregset_t *fpregsetp, int regno) +{ + if (FP0_REGNUM == 0) + return; + + i387_fill_fsave ((char *) fpregsetp, regno); +} + +#endif /* HAVE_FPREGSET_T */ + +#endif /* HAVE_SYS_PROCFS_H */ diff --git a/contrib/gdb/gdb/lynx-nat.c b/contrib/gdb/gdb/lynx-nat.c new file mode 100644 index 0000000..7bfd40e --- /dev/null +++ b/contrib/gdb/gdb/lynx-nat.c @@ -0,0 +1,624 @@ +/* Native-dependent code for LynxOS. + + Copyright 1993, 1994, 1995, 1996, 1999, 2000, 2001, 2003 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 "defs.h" +#include "frame.h" +#include "inferior.h" +#include "target.h" +#include "gdbcore.h" +#include "regcache.h" + +#include <sys/ptrace.h> +#Include "gdb_wait.h" +#include <sys/fpp.h> + +static unsigned long registers_addr (int pid); +static void fetch_core_registers (char *, unsigned, int, CORE_ADDR); + +#define X(ENTRY)(offsetof(struct econtext, ENTRY)) + +#ifdef I386 +/* Mappings from tm-i386v.h */ + +static int regmap[] = +{ + X (eax), + X (ecx), + X (edx), + X (ebx), + X (esp), /* sp */ + X (ebp), /* fp */ + X (esi), + X (edi), + X (eip), /* pc */ + X (flags), /* ps */ + X (cs), + X (ss), + X (ds), + X (es), + X (ecode), /* Lynx doesn't give us either fs or gs, so */ + X (fault), /* we just substitute these two in the hopes + that they are useful. */ +}; +#endif /* I386 */ + +#ifdef M68K +/* Mappings from tm-m68k.h */ + +static int regmap[] = +{ + X (regs[0]), /* d0 */ + X (regs[1]), /* d1 */ + X (regs[2]), /* d2 */ + X (regs[3]), /* d3 */ + X (regs[4]), /* d4 */ + X (regs[5]), /* d5 */ + X (regs[6]), /* d6 */ + X (regs[7]), /* d7 */ + X (regs[8]), /* a0 */ + X (regs[9]), /* a1 */ + X (regs[10]), /* a2 */ + X (regs[11]), /* a3 */ + X (regs[12]), /* a4 */ + X (regs[13]), /* a5 */ + X (regs[14]), /* fp */ + offsetof (st_t, usp) - offsetof (st_t, ec), /* sp */ + X (status), /* ps */ + X (pc), + + X (fregs[0 * 3]), /* fp0 */ + X (fregs[1 * 3]), /* fp1 */ + X (fregs[2 * 3]), /* fp2 */ + X (fregs[3 * 3]), /* fp3 */ + X (fregs[4 * 3]), /* fp4 */ + X (fregs[5 * 3]), /* fp5 */ + X (fregs[6 * 3]), /* fp6 */ + X (fregs[7 * 3]), /* fp7 */ + + X (fcregs[0]), /* fpcontrol */ + X (fcregs[1]), /* fpstatus */ + X (fcregs[2]), /* fpiaddr */ + X (ssw), /* fpcode */ + X (fault), /* fpflags */ +}; +#endif /* M68K */ + +#ifdef SPARC +/* Mappings from tm-sparc.h */ + +#define FX(ENTRY)(offsetof(struct fcontext, ENTRY)) + +static int regmap[] = +{ + -1, /* g0 */ + X (g1), + X (g2), + X (g3), + X (g4), + -1, /* g5->g7 aren't saved by Lynx */ + -1, + -1, + + X (o[0]), + X (o[1]), + X (o[2]), + X (o[3]), + X (o[4]), + X (o[5]), + X (o[6]), /* sp */ + X (o[7]), /* ra */ + + -1, -1, -1, -1, -1, -1, -1, -1, /* l0 -> l7 */ + + -1, -1, -1, -1, -1, -1, -1, -1, /* i0 -> i7 */ + + FX (f.fregs[0]), /* f0 */ + FX (f.fregs[1]), + FX (f.fregs[2]), + FX (f.fregs[3]), + FX (f.fregs[4]), + FX (f.fregs[5]), + FX (f.fregs[6]), + FX (f.fregs[7]), + FX (f.fregs[8]), + FX (f.fregs[9]), + FX (f.fregs[10]), + FX (f.fregs[11]), + FX (f.fregs[12]), + FX (f.fregs[13]), + FX (f.fregs[14]), + FX (f.fregs[15]), + FX (f.fregs[16]), + FX (f.fregs[17]), + FX (f.fregs[18]), + FX (f.fregs[19]), + FX (f.fregs[20]), + FX (f.fregs[21]), + FX (f.fregs[22]), + FX (f.fregs[23]), + FX (f.fregs[24]), + FX (f.fregs[25]), + FX (f.fregs[26]), + FX (f.fregs[27]), + FX (f.fregs[28]), + FX (f.fregs[29]), + FX (f.fregs[30]), + FX (f.fregs[31]), + + X (y), + X (psr), + X (wim), + X (tbr), + X (pc), + X (npc), + FX (fsr), /* fpsr */ + -1, /* cpsr */ +}; +#endif /* SPARC */ + +#ifdef rs6000 + +static int regmap[] = +{ + X (iregs[0]), /* r0 */ + X (iregs[1]), + X (iregs[2]), + X (iregs[3]), + X (iregs[4]), + X (iregs[5]), + X (iregs[6]), + X (iregs[7]), + X (iregs[8]), + X (iregs[9]), + X (iregs[10]), + X (iregs[11]), + X (iregs[12]), + X (iregs[13]), + X (iregs[14]), + X (iregs[15]), + X (iregs[16]), + X (iregs[17]), + X (iregs[18]), + X (iregs[19]), + X (iregs[20]), + X (iregs[21]), + X (iregs[22]), + X (iregs[23]), + X (iregs[24]), + X (iregs[25]), + X (iregs[26]), + X (iregs[27]), + X (iregs[28]), + X (iregs[29]), + X (iregs[30]), + X (iregs[31]), + + X (fregs[0]), /* f0 */ + X (fregs[1]), + X (fregs[2]), + X (fregs[3]), + X (fregs[4]), + X (fregs[5]), + X (fregs[6]), + X (fregs[7]), + X (fregs[8]), + X (fregs[9]), + X (fregs[10]), + X (fregs[11]), + X (fregs[12]), + X (fregs[13]), + X (fregs[14]), + X (fregs[15]), + X (fregs[16]), + X (fregs[17]), + X (fregs[18]), + X (fregs[19]), + X (fregs[20]), + X (fregs[21]), + X (fregs[22]), + X (fregs[23]), + X (fregs[24]), + X (fregs[25]), + X (fregs[26]), + X (fregs[27]), + X (fregs[28]), + X (fregs[29]), + X (fregs[30]), + X (fregs[31]), + + X (srr0), /* IAR (PC) */ + X (srr1), /* MSR (PS) */ + X (cr), /* CR */ + X (lr), /* LR */ + X (ctr), /* CTR */ + X (xer), /* XER */ + X (mq) /* MQ */ +}; + +#endif /* rs6000 */ + +#if defined (I386) || defined (M68K) || defined (rs6000) + +/* Return the offset relative to the start of the per-thread data to the + saved context block. */ + +static unsigned long +registers_addr (int pid) +{ + CORE_ADDR stblock; + int ecpoff = offsetof (st_t, ecp); + CORE_ADDR ecp; + + errno = 0; + stblock = (CORE_ADDR) ptrace (PTRACE_THREADUSER, pid, (PTRACE_ARG3_TYPE) 0, + 0); + if (errno) + perror_with_name ("ptrace(PTRACE_THREADUSER)"); + + ecp = (CORE_ADDR) ptrace (PTRACE_PEEKTHREAD, pid, (PTRACE_ARG3_TYPE) ecpoff, + 0); + if (errno) + perror_with_name ("ptrace(PTRACE_PEEKTHREAD)"); + + return ecp - stblock; +} + +/* Fetch one or more registers from the inferior. REGNO == -1 to get + them all. We actually fetch more than requested, when convenient, + marking them as valid so we won't fetch them again. */ + +void +fetch_inferior_registers (int regno) +{ + int reglo, reghi; + int i; + unsigned long ecp; + + if (regno == -1) + { + reglo = 0; + reghi = NUM_REGS - 1; + } + else + reglo = reghi = regno; + + ecp = registers_addr (PIDGET (inferior_ptid)); + + { + char buf[MAX_REGISTER_SIZE]; + for (regno = reglo; regno <= reghi; regno++) + { + int ptrace_fun = PTRACE_PEEKTHREAD; + +#ifdef M68K + ptrace_fun = regno == SP_REGNUM ? PTRACE_PEEKUSP : PTRACE_PEEKTHREAD; +#endif + + for (i = 0; i < DEPRECATED_REGISTER_RAW_SIZE (regno); i += sizeof (int)) + { + unsigned int reg; + + errno = 0; + reg = ptrace (ptrace_fun, PIDGET (inferior_ptid), + (PTRACE_ARG3_TYPE) (ecp + regmap[regno] + i), 0); + if (errno) + perror_with_name ("ptrace(PTRACE_PEEKUSP)"); + + *(int *) &buf[i] = reg; + } + supply_register (regno, buf); + } + } +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +void +store_inferior_registers (int regno) +{ + int reglo, reghi; + int i; + unsigned long ecp; + + if (regno == -1) + { + reglo = 0; + reghi = NUM_REGS - 1; + } + else + reglo = reghi = regno; + + ecp = registers_addr (PIDGET (inferior_ptid)); + + for (regno = reglo; regno <= reghi; regno++) + { + int ptrace_fun = PTRACE_POKEUSER; + + if (CANNOT_STORE_REGISTER (regno)) + continue; + +#ifdef M68K + ptrace_fun = regno == SP_REGNUM ? PTRACE_POKEUSP : PTRACE_POKEUSER; +#endif + + for (i = 0; i < DEPRECATED_REGISTER_RAW_SIZE (regno); i += sizeof (int)) + { + unsigned int reg; + + reg = *(unsigned int *) &deprecated_registers[DEPRECATED_REGISTER_BYTE (regno) + i]; + + errno = 0; + ptrace (ptrace_fun, PIDGET (inferior_ptid), + (PTRACE_ARG3_TYPE) (ecp + regmap[regno] + i), reg); + if (errno) + perror_with_name ("ptrace(PTRACE_POKEUSP)"); + } + } +} +#endif /* defined (I386) || defined (M68K) || defined (rs6000) */ + +/* Wait for child to do something. Return pid of child, or -1 in case + of error; store status through argument pointer OURSTATUS. */ + +ptid_t +child_wait (ptid_t ptid, struct target_waitstatus *ourstatus) +{ + int save_errno; + int thread; + union wait status; + int pid; + + while (1) + { + int sig; + + set_sigint_trap (); /* Causes SIGINT to be passed on to the + attached process. */ + pid = wait (&status); + + save_errno = errno; + + clear_sigint_trap (); + + if (pid == -1) + { + if (save_errno == EINTR) + continue; + fprintf_unfiltered (gdb_stderr, "Child process unexpectedly missing: %s.\n", + safe_strerror (save_errno)); + /* Claim it exited with unknown signal. */ + ourstatus->kind = TARGET_WAITKIND_SIGNALLED; + ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; + return -1; + } + + if (pid != PIDGET (inferior_ptid)) /* Some other process?!? */ + continue; + + thread = status.w_tid; /* Get thread id from status */ + + /* Initial thread value can only be acquired via wait, so we have to + resort to this hack. */ + + if (TIDGET (inferior_ptid) == 0 && thread != 0) + { + inferior_ptid = MERGEPID (PIDGET (inferior_ptid), thread); + add_thread (inferior_ptid); + } + + ptid = BUILDPID (pid, thread); + + /* We've become a single threaded process again. */ + if (thread == 0) + inferior_ptid = ptid; + + /* Check for thread creation. */ + if (WIFSTOPPED (status) + && WSTOPSIG (status) == SIGTRAP + && !in_thread_list (ptid)) + { + int realsig; + + realsig = ptrace (PTRACE_GETTRACESIG, PIDGET (ptid), + (PTRACE_ARG3_TYPE) 0, 0); + + if (realsig == SIGNEWTHREAD) + { + /* It's a new thread notification. We don't want to much with + realsig -- the code in wait_for_inferior expects SIGTRAP. */ + ourstatus->kind = TARGET_WAITKIND_SPURIOUS; + ourstatus->value.sig = TARGET_SIGNAL_0; + return ptid; + } + else + error ("Signal for unknown thread was not SIGNEWTHREAD"); + } + + /* Check for thread termination. */ + else if (WIFSTOPPED (status) + && WSTOPSIG (status) == SIGTRAP + && in_thread_list (ptid)) + { + int realsig; + + realsig = ptrace (PTRACE_GETTRACESIG, PIDGET (ptid), + (PTRACE_ARG3_TYPE) 0, 0); + + if (realsig == SIGTHREADEXIT) + { + ptrace (PTRACE_CONT, PIDGET (ptid), (PTRACE_ARG3_TYPE) 0, 0); + continue; + } + } + +#ifdef SPARC + /* SPARC Lynx uses an byte reversed wait status; we must use the + host macros to access it. These lines just a copy of + store_waitstatus. We can't use CHILD_SPECIAL_WAITSTATUS + because target.c can't include the Lynx <sys/wait.h>. */ + if (WIFEXITED (status)) + { + ourstatus->kind = TARGET_WAITKIND_EXITED; + ourstatus->value.integer = WEXITSTATUS (status); + } + else if (!WIFSTOPPED (status)) + { + ourstatus->kind = TARGET_WAITKIND_SIGNALLED; + ourstatus->value.sig = + target_signal_from_host (WTERMSIG (status)); + } + else + { + ourstatus->kind = TARGET_WAITKIND_STOPPED; + ourstatus->value.sig = + target_signal_from_host (WSTOPSIG (status)); + } +#else + store_waitstatus (ourstatus, status.w_status); +#endif + + return ptid; + } +} + +/* Return nonzero if the given thread is still alive. */ +int +child_thread_alive (ptid_t ptid) +{ + int pid = PIDGET (ptid); + + /* Arggh. Apparently pthread_kill only works for threads within + the process that calls pthread_kill. + + We want to avoid the lynx signal extensions as they simply don't + map well to the generic gdb interface we want to keep. + + All we want to do is determine if a particular thread is alive; + it appears as if we can just make a harmless thread specific + ptrace call to do that. */ + return (ptrace (PTRACE_THREADUSER, pid, 0, 0) != -1); +} + +/* Resume execution of the inferior process. + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. */ + +void +child_resume (ptid_t ptid, int step, enum target_signal signal) +{ + int func; + int pid = PIDGET (ptid); + + errno = 0; + + /* If pid == -1, then we want to step/continue all threads, else + we only want to step/continue a single thread. */ + if (pid == -1) + { + pid = PIDGET (inferior_ptid); + func = step ? PTRACE_SINGLESTEP : PTRACE_CONT; + } + else + func = step ? PTRACE_SINGLESTEP_ONE : PTRACE_CONT_ONE; + + + /* An address of (PTRACE_ARG3_TYPE)1 tells ptrace to continue from where + it was. (If GDB wanted it to start some other way, we have already + written a new PC value to the child.) + + If this system does not support PT_STEP, a higher level function will + have called single_step() to transmute the step request into a + continue request (by setting breakpoints on all possible successor + instructions), so we don't have to worry about that here. */ + + ptrace (func, pid, (PTRACE_ARG3_TYPE) 1, target_signal_to_host (signal)); + + if (errno) + perror_with_name ("ptrace"); +} + +/* Convert a Lynx process ID to a string. Returns the string in a static + buffer. */ + +char * +child_pid_to_str (ptid_t ptid) +{ + static char buf[40]; + + sprintf (buf, "process %d thread %d", PIDGET (ptid), TIDGET (ptid)); + + return buf; +} + +/* Extract the register values out of the core file and store + them where `read_register' will find them. + + CORE_REG_SECT points to the register values themselves, read into memory. + CORE_REG_SIZE is the size of that area. + WHICH says which set of registers we are handling (0 = int, 2 = float + on machines where they are discontiguous). + REG_ADDR is the offset from u.u_ar0 to the register values relative to + core_reg_sect. This is used with old-fashioned core files to + locate the registers in a large upage-plus-stack ".reg" section. + Original upage address X is at location core_reg_sect+x+reg_addr. + */ + +static void +fetch_core_registers (char *core_reg_sect, unsigned core_reg_size, int which, + CORE_ADDR reg_addr) +{ + struct st_entry s; + unsigned int regno; + + for (regno = 0; regno < NUM_REGS; regno++) + if (regmap[regno] != -1) + supply_register (regno, core_reg_sect + offsetof (st_t, ec) + + regmap[regno]); + +#ifdef SPARC +/* Fetching this register causes all of the I & L regs to be read from the + stack and validated. */ + + fetch_inferior_registers (I0_REGNUM); +#endif +} + + +/* Register that we are able to handle lynx core file formats. + FIXME: is this really bfd_target_unknown_flavour? */ + +static struct core_fns lynx_core_fns = +{ + bfd_target_unknown_flavour, /* core_flavour */ + default_check_format, /* check_format */ + default_core_sniffer, /* core_sniffer */ + fetch_core_registers, /* core_read_registers */ + NULL /* next */ +}; + +void +_initialize_core_lynx (void) +{ + add_core_fns (&lynx_core_fns); +} diff --git a/contrib/gdb/gdb/minimon.h b/contrib/gdb/gdb/minimon.h new file mode 100644 index 0000000..94fd774 --- /dev/null +++ b/contrib/gdb/gdb/minimon.h @@ -0,0 +1,601 @@ +/* Definitions and macros for support of AMD's remote debugger, MiniMON. + Copyright 1990, 1991 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. */ + +/* + * Some basic types. FIXME, this should be done by declaring bitfield + * sizes in the structs. We can't portably depend on a "long int" being + * 32 bits, etc. + */ +typedef long int INT32; /* 32 bit integer */ +typedef unsigned long int UINT32; /* 32 bit integer (unsigned) */ +typedef unsigned long int ADDR32; /* 32 bit address */ +typedef unsigned long int INST32; /* 32 bit instruction */ +typedef long int BOOLEAN; /* Boolean value (32 bit) */ +typedef unsigned char BYTE; /* byte (8 bit) */ +typedef short int INT16; /* 16 bit integer */ +typedef unsigned short int UINT16; /* 16 bit integer (unsigned) */ + +/****************************************************************************/ +/************************* Message Information ******************************/ +/****************************************************************************/ + +/* + * Error codes + */ + +/* General errors */ +#define EMUSAGE 1 /* Bad args / flags */ +#define EMFAIL 2 /* Unrecoverable error */ +#define EMBADADDR 3 /* Illegal address */ +#define EMBADREG 4 /* Illegal register */ +#define EMSYNTAX 5 /* Illegal command syntax */ +#define EMACCESS 6 /* Could not access memory */ +#define EMALLOC 7 /* Could not allocate memory */ +#define EMTARGET 8 /* Unknown target type */ +#define EMHINIT 9 /* Could not initialize host */ +#define EMCOMM 10 /* Could not open communication channel */ + +/* Message errors */ +#define EMBADMSG 11 /* Unknown message type */ +#define EMMSG2BIG 12 /* Message to large for buffer */ +#define EMNOSEND 13 /* Could not send message */ +#define EMNORECV 14 /* Could not receive message */ + +#define EMRESET 15 /* Could not RESET target */ +#define EMCONFIG 16 /* Could not get target CONFIG */ +#define EMSTATUS 17 /* Could not get target STATUS */ +#define EMREAD 18 /* Could not READ target memory */ +#define EMWRITE 19 /* Could not WRITE target memory */ +#define EMBKPTSET 20 /* Could not set breakpoint */ +#define EMBKPTRM 21 /* Could not remove breakpoint */ +#define EMBKPTSTAT 22 /* Could not get breakpoint status */ +#define EMBKPTNONE 23 /* All breakpoints in use */ +#define EMBKPTUSED 24 /* Breakpoints already in use */ +#define EMCOPY 25 /* Could not COPY target memory */ +#define EMFILL 26 /* Could not FILL target memory */ +#define EMINIT 27 /* Could not initialize target memory */ +#define EMGO 28 /* Could not start execution */ +#define EMSTEP 29 /* Could not single step */ +#define EMBREAK 30 /* Could not BREAK */ +#define EMHIF 31 /* Could not perform HIF service */ +#define EMCHANNEL0 32 /* Could not read CHANNEL0 */ +#define EMCHANNEL1 33 /* Could not write CHANNEL1 */ + +/* COFF file loader errors */ +#define EMOPEN 34 /* Could not open COFF file */ +#define EMHDR 35 /* Could not read COFF header */ +#define EMMAGIC 36 /* Bad magic number */ +#define EMAOUT 37 /* Could not read COFF a.out header */ +#define EMSCNHDR 38 /* Could not read COFF section header */ +#define EMSCN 39 /* Could not read COFF section */ +#define EMCLOSE 40 /* Could not close COFF file */ + +/* Log file errors */ +#define EMLOGOPEN 41 /* Could not open log file */ +#define EMLOGREAD 42 /* Could not read log file */ +#define EMLOGWRITE 43 /* Could not write to log file */ +#define EMLOGCLOSE 44 /* Could not close log file */ + +/* Command file errors */ +#define EMCMDOPEN 45 /* Could not open command file */ +#define EMCMDREAD 46 /* Could not read command file */ +#define EMCMDWRITE 47 /* Could not write to command file */ +#define EMCMDCLOSE 48 /* Could not close comand file */ + +#define EMTIMEOUT 49 /* Host timed out waiting for a message */ +#define EMCOMMTYPE 50 /* A '-t' flag must be specified */ +#define EMCOMMERR 51 /* Communication error */ +#define EMBAUD 52 /* Invalid baud rate specified */ +/* + * Memory Spaces + */ +#define LOCAL_REG 0 /* Local processor register */ +#define GLOBAL_REG 1 /* Global processor register */ +#define SPECIAL_REG 2 /* Special processor register */ +#define TLB_REG 3 /* Translation Lookaside Buffer */ +#define COPROC_REG 4 /* Coprocessor register */ +#define I_MEM 5 /* Instruction Memory */ +#define D_MEM 6 /* Data Memory */ +#define I_ROM 7 /* Instruction ROM */ +#define D_ROM 8 /* Data ROM */ +#define I_O 9 /* Input/Output */ +#define I_CACHE 10 /* Instruction Cache */ +#define D_CACHE 11 /* Data Cache */ + +/* To supress warnings for zero length array definitions */ +#define DUMMY 1 + +/* + ** Host to target definitions + */ + +#define RESET 0 +#define CONFIG_REQ 1 +#define STATUS_REQ 2 +#define READ_REQ 3 +#define WRITE_REQ 4 +#define BKPT_SET 5 +#define BKPT_RM 6 +#define BKPT_STAT 7 +#define COPY 8 +#define FILL 9 +#define INIT 10 +#define GO 11 +#define STEP 12 +#define BREAK 13 + +#define HIF_CALL_RTN 64 +#define CHANNEL0 65 +#define CHANNEL1_ACK 66 + + +/* + ** Target to host definitions + */ + +#define RESET_ACK 32 +#define CONFIG 33 +#define STATUS 34 +#define READ_ACK 35 +#define WRITE_ACK 36 +#define BKPT_SET_ACK 37 +#define BKPT_RM_ACK 38 +#define BKPT_STAT_ACK 39 +#define COPY_ACK 40 +#define FILL_ACK 41 +#define INIT_ACK 42 +#define HALT 43 + +#define ERROR 63 + +#define HIF_CALL 96 +#define CHANNEL0_ACK 97 +#define CHANNEL1 98 + + +/* A "generic" message */ +struct generic_msg_t + { + INT32 code; /* generic */ + INT32 length; + BYTE byte[DUMMY]; + }; + + +/* A "generic" message (with an INT32 array) */ +struct generic_int32_msg_t + { + INT32 code; /* generic */ + INT32 length; + INT32 int32[DUMMY]; + }; + + +/* + ** Host to target messages + */ + +struct reset_msg_t + { + INT32 code; /* 0 */ + INT32 length; + }; + + +struct config_req_msg_t + { + INT32 code; /* 1 */ + INT32 length; + }; + + +struct status_req_msg_t + { + INT32 code; /* 2 */ + INT32 length; + }; + + +struct read_req_msg_t + { + INT32 code; /* 3 */ + INT32 length; + INT32 memory_space; + ADDR32 address; + INT32 byte_count; + }; + + +struct write_req_msg_t + { + INT32 code; /* 4 */ + INT32 length; + INT32 memory_space; + ADDR32 address; + INT32 byte_count; + BYTE data[DUMMY]; + }; + + +struct write_r_msg_t + { + INT32 code; /* 4 */ + INT32 length; + INT32 memory_space; + ADDR32 address; + INT32 byte_count; + INT32 data[DUMMY]; + }; + + +struct bkpt_set_msg_t + { + INT32 code; /* 5 */ + INT32 length; + INT32 memory_space; + ADDR32 bkpt_addr; + INT32 pass_count; + INT32 bkpt_type; + }; + + +struct bkpt_rm_msg_t + { + INT32 code; /* 6 */ + INT32 length; + INT32 memory_space; + ADDR32 bkpt_addr; + }; + + +struct bkpt_stat_msg_t + { + INT32 code; /* 7 */ + INT32 length; + INT32 memory_space; + ADDR32 bkpt_addr; + }; + + +struct copy_msg_t + { + INT32 code; /* 8 */ + INT32 length; + INT32 source_space; + ADDR32 source_addr; + INT32 dest_space; + ADDR32 dest_addr; + INT32 byte_count; + }; + + +struct fill_msg_t + { + INT32 code; /* 9 */ + INT32 length; + INT32 memory_space; + ADDR32 start_addr; + INT32 fill_count; + INT32 byte_count; + BYTE fill_data[DUMMY]; + }; + + +struct init_msg_t + { + INT32 code; /* 10 */ + INT32 length; + ADDR32 text_start; + ADDR32 text_end; + ADDR32 data_start; + ADDR32 data_end; + ADDR32 entry_point; + INT32 mem_stack_size; + INT32 reg_stack_size; + ADDR32 arg_start; + INT32 os_control; + }; + + +struct go_msg_t + { + INT32 code; /* 11 */ + INT32 length; + }; + + +struct step_msg_t + { + INT32 code; /* 12 */ + INT32 length; + INT32 count; + }; + + +struct break_msg_t + { + INT32 code; /* 13 */ + INT32 length; + }; + + +struct hif_call_rtn_msg_t + { + INT32 code; /* 64 */ + INT32 length; + INT32 service_number; + INT32 gr121; + INT32 gr96; + INT32 gr97; + }; + + +struct channel0_msg_t + { + INT32 code; /* 65 */ + INT32 length; + BYTE data; + }; + + +struct channel1_ack_msg_t + { + INT32 code; /* 66 */ + INT32 length; + }; + + +/* + ** Target to host messages + */ + + +struct reset_ack_msg_t + { + INT32 code; /* 32 */ + INT32 length; + }; + + +struct config_msg_t + { + INT32 code; /* 33 */ + INT32 length; + INT32 processor_id; + INT32 version; + ADDR32 I_mem_start; + INT32 I_mem_size; + ADDR32 D_mem_start; + INT32 D_mem_size; + ADDR32 ROM_start; + INT32 ROM_size; + INT32 max_msg_size; + INT32 max_bkpts; + INT32 coprocessor; + INT32 reserved; + }; + + +struct status_msg_t + { + INT32 code; /* 34 */ + INT32 length; + INT32 msgs_sent; + INT32 msgs_received; + INT32 errors; + INT32 bkpts_hit; + INT32 bkpts_free; + INT32 traps; + INT32 fills; + INT32 spills; + INT32 cycles; + INT32 reserved; + }; + + +struct read_ack_msg_t + { + INT32 code; /* 35 */ + INT32 length; + INT32 memory_space; + ADDR32 address; + INT32 byte_count; + BYTE data[DUMMY]; + }; + +struct read_r_ack_msg_t + { + INT32 code; /* 35 */ + INT32 length; + INT32 memory_space; + ADDR32 address; + INT32 byte_count; + INT32 data[DUMMY]; + }; + + +struct write_ack_msg_t + { + INT32 code; /* 36 */ + INT32 length; + INT32 memory_space; + ADDR32 address; + INT32 byte_count; + }; + + +struct bkpt_set_ack_msg_t + { + INT32 code; /* 37 */ + INT32 length; + INT32 memory_space; + ADDR32 address; + INT32 pass_count; + INT32 bkpt_type; + }; + + +struct bkpt_rm_ack_msg_t + { + INT32 code; /* 38 */ + INT32 length; + INT32 memory_space; + ADDR32 address; + }; + + +struct bkpt_stat_ack_msg_t + { + INT32 code; /* 39 */ + INT32 length; + INT32 memory_space; + ADDR32 address; + INT32 pass_count; + INT32 bkpt_type; + }; + + +struct copy_ack_msg_t + { + INT32 code; /* 40 */ + INT32 length; + INT32 source_space; + ADDR32 source_addr; + INT32 dest_space; + ADDR32 dest_addr; + INT32 byte_count; + }; + + +struct fill_ack_msg_t + { + INT32 code; /* 41 */ + INT32 length; + INT32 memory_space; + ADDR32 start_addr; + INT32 fill_count; + INT32 byte_count; + }; + + +struct init_ack_msg_t + { + INT32 code; /* 42 */ + INT32 length; + }; + + +struct halt_msg_t + { + INT32 code; /* 43 */ + INT32 length; + INT32 memory_space; + ADDR32 pc0; + ADDR32 pc1; + INT32 trap_number; + }; + + +struct error_msg_t + { + INT32 code; /* 63 */ + INT32 length; + INT32 error_code; + INT32 memory_space; + ADDR32 address; + }; + + +struct hif_call_msg_t + { + INT32 code; /* 96 */ + INT32 length; + INT32 service_number; + INT32 lr2; + INT32 lr3; + INT32 lr4; + }; + + +struct channel0_ack_msg_t + { + INT32 code; /* 97 */ + INT32 length; + }; + + +struct channel1_msg_t + { + INT32 code; /* 98 */ + INT32 length; + BYTE data[DUMMY]; + }; + + + +/* + ** Union all of the message types together + */ + +union msg_t + { + struct generic_msg_t generic_msg; + struct generic_int32_msg_t generic_int32_msg; + + struct reset_msg_t reset_msg; + struct config_req_msg_t config_req_msg; + struct status_req_msg_t status_req_msg; + struct read_req_msg_t read_req_msg; + struct write_req_msg_t write_req_msg; + struct write_r_msg_t write_r_msg; + struct bkpt_set_msg_t bkpt_set_msg; + struct bkpt_rm_msg_t bkpt_rm_msg; + struct bkpt_stat_msg_t bkpt_stat_msg; + struct copy_msg_t copy_msg; + struct fill_msg_t fill_msg; + struct init_msg_t init_msg; + struct go_msg_t go_msg; + struct step_msg_t step_msg; + struct break_msg_t break_msg; + + struct hif_call_rtn_msg_t hif_call_rtn_msg; + struct channel0_msg_t channel0_msg; + struct channel1_ack_msg_t channel1_ack_msg; + + struct reset_ack_msg_t reset_ack_msg; + struct config_msg_t config_msg; + struct status_msg_t status_msg; + struct read_ack_msg_t read_ack_msg; + struct read_r_ack_msg_t read_r_ack_msg; + struct write_ack_msg_t write_ack_msg; + struct bkpt_set_ack_msg_t bkpt_set_ack_msg; + struct bkpt_rm_ack_msg_t bkpt_rm_ack_msg; + struct bkpt_stat_ack_msg_t bkpt_stat_ack_msg; + struct copy_ack_msg_t copy_ack_msg; + struct fill_ack_msg_t fill_ack_msg; + struct init_ack_msg_t init_ack_msg; + struct halt_msg_t halt_msg; + + struct error_msg_t error_msg; + + struct hif_call_msg_t hif_call_msg; + struct channel0_ack_msg_t channel0_ack_msg; + struct channel1_msg_t channel1_msg; + }; diff --git a/contrib/gdb/gdb/monitor.c b/contrib/gdb/gdb/monitor.c new file mode 100644 index 0000000..cd4f045 --- /dev/null +++ b/contrib/gdb/gdb/monitor.c @@ -0,0 +1,2310 @@ +/* Remote debugging interface for boot monitors, for GDB. + + Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + + Contributed by Cygnus Support. Written by Rob Savoye for Cygnus. + Resurrected from the ashes by Stu Grossman. + + 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. */ + +/* This file was derived from various remote-* modules. It is a collection + of generic support functions so GDB can talk directly to a ROM based + monitor. This saves use from having to hack an exception based handler + into existence, and makes for quick porting. + + This module talks to a debug monitor called 'MONITOR', which + We communicate with MONITOR via either a direct serial line, or a TCP + (or possibly TELNET) stream to a terminal multiplexor, + which in turn talks to the target board. */ + +/* FIXME 32x64: This code assumes that registers and addresses are at + most 32 bits long. If they can be larger, you will need to declare + values as LONGEST and use %llx or some such to print values when + building commands to send to the monitor. Since we don't know of + any actual 64-bit targets with ROM monitors that use this code, + it's not an issue right now. -sts 4/18/96 */ + +#include "defs.h" +#include "gdbcore.h" +#include "target.h" +#include <signal.h> +#include <ctype.h> +#include "gdb_string.h" +#include <sys/types.h> +#include "command.h" +#include "serial.h" +#include "monitor.h" +#include "gdbcmd.h" +#include "inferior.h" +#include "gdb_regex.h" +#include "srec.h" +#include "regcache.h" + +static char *dev_name; +static struct target_ops *targ_ops; + +static void monitor_vsprintf (char *sndbuf, char *pattern, va_list args); + +static int readchar (int timeout); + +static void monitor_fetch_register (int regno); +static void monitor_store_register (int regno); + +static void monitor_printable_string (char *newstr, char *oldstr, int len); +static void monitor_error (char *function, char *message, CORE_ADDR memaddr, int len, char *string, int final_char); +static void monitor_detach (char *args, int from_tty); +static void monitor_resume (ptid_t ptid, int step, enum target_signal sig); +static void monitor_interrupt (int signo); +static void monitor_interrupt_twice (int signo); +static void monitor_interrupt_query (void); +static void monitor_wait_cleanup (void *old_timeout); + +static ptid_t monitor_wait (ptid_t ptid, struct target_waitstatus *status); +static void monitor_fetch_registers (int regno); +static void monitor_store_registers (int regno); +static void monitor_prepare_to_store (void); +static int monitor_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, + int write, + struct mem_attrib *attrib, + struct target_ops *target); +static void monitor_files_info (struct target_ops *ops); +static int monitor_insert_breakpoint (CORE_ADDR addr, char *shadow); +static int monitor_remove_breakpoint (CORE_ADDR addr, char *shadow); +static void monitor_kill (void); +static void monitor_load (char *file, int from_tty); +static void monitor_mourn_inferior (void); +static void monitor_stop (void); + +static int monitor_read_memory (CORE_ADDR addr, char *myaddr, int len); +static int monitor_write_memory (CORE_ADDR addr, char *myaddr, int len); +static int monitor_write_memory_bytes (CORE_ADDR addr, char *myaddr, int len); +static int monitor_write_memory_block (CORE_ADDR memaddr, + char *myaddr, int len); +static int monitor_expect_regexp (struct re_pattern_buffer *pat, + char *buf, int buflen); +static void monitor_dump_regs (void); +#if 0 +static int from_hex (int a); +static unsigned long get_hex_word (void); +#endif +static void parse_register_dump (char *, int); + +static struct monitor_ops *current_monitor; + +static int hashmark; /* flag set by "set hash" */ + +static int timeout = 30; + +static int in_monitor_wait = 0; /* Non-zero means we are in monitor_wait() */ + +static void (*ofunc) (); /* Old SIGINT signal handler */ + +static CORE_ADDR *breakaddr; + +/* Descriptor for I/O to remote machine. Initialize it to NULL so + that monitor_open knows that we don't have a file open when the + program starts. */ + +static struct serial *monitor_desc = NULL; + +/* Pointer to regexp pattern matching data */ + +static struct re_pattern_buffer register_pattern; +static char register_fastmap[256]; + +static struct re_pattern_buffer getmem_resp_delim_pattern; +static char getmem_resp_delim_fastmap[256]; + +static struct re_pattern_buffer setmem_resp_delim_pattern; +static char setmem_resp_delim_fastmap[256]; + +static struct re_pattern_buffer setreg_resp_delim_pattern; +static char setreg_resp_delim_fastmap[256]; + +static int dump_reg_flag; /* Non-zero means do a dump_registers cmd when + monitor_wait wakes up. */ + +static int first_time = 0; /* is this the first time we're executing after + gaving created the child proccess? */ + +#define TARGET_BUF_SIZE 2048 + +/* Monitor specific debugging information. Typically only useful to + the developer of a new monitor interface. */ + +static void monitor_debug (const char *fmt, ...) ATTR_FORMAT(printf, 1, 2); + +static int monitor_debug_p = 0; + +/* NOTE: This file alternates between monitor_debug_p and remote_debug + when determining if debug information is printed. Perhaphs this + could be simplified. */ + +static void +monitor_debug (const char *fmt, ...) +{ + if (monitor_debug_p) + { + va_list args; + va_start (args, fmt); + vfprintf_filtered (gdb_stdlog, fmt, args); + va_end (args); + } +} + + +/* Convert a string into a printable representation, Return # byte in + the new string. When LEN is >0 it specifies the size of the + string. Otherwize strlen(oldstr) is used. */ + +static void +monitor_printable_string (char *newstr, char *oldstr, int len) +{ + int ch; + int i; + + if (len <= 0) + len = strlen (oldstr); + + for (i = 0; i < len; i++) + { + ch = oldstr[i]; + switch (ch) + { + default: + if (isprint (ch)) + *newstr++ = ch; + + else + { + sprintf (newstr, "\\x%02x", ch & 0xff); + newstr += 4; + } + break; + + case '\\': + *newstr++ = '\\'; + *newstr++ = '\\'; + break; + case '\b': + *newstr++ = '\\'; + *newstr++ = 'b'; + break; + case '\f': + *newstr++ = '\\'; + *newstr++ = 't'; + break; + case '\n': + *newstr++ = '\\'; + *newstr++ = 'n'; + break; + case '\r': + *newstr++ = '\\'; + *newstr++ = 'r'; + break; + case '\t': + *newstr++ = '\\'; + *newstr++ = 't'; + break; + case '\v': + *newstr++ = '\\'; + *newstr++ = 'v'; + break; + } + } + + *newstr++ = '\0'; +} + +/* Print monitor errors with a string, converting the string to printable + representation. */ + +static void +monitor_error (char *function, char *message, + CORE_ADDR memaddr, int len, char *string, int final_char) +{ + int real_len = (len == 0 && string != (char *) 0) ? strlen (string) : len; + char *safe_string = alloca ((real_len * 4) + 1); + monitor_printable_string (safe_string, string, real_len); + + if (final_char) + error ("%s (0x%s): %s: %s%c", function, paddr_nz (memaddr), message, safe_string, final_char); + else + error ("%s (0x%s): %s: %s", function, paddr_nz (memaddr), message, safe_string); +} + +/* Convert hex digit A to a number. */ + +static int +fromhex (int a) +{ + if (a >= '0' && a <= '9') + return a - '0'; + else if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + else if (a >= 'A' && a <= 'F') + return a - 'A' + 10; + else + error ("Invalid hex digit %d", a); +} + +/* monitor_vsprintf - similar to vsprintf but handles 64-bit addresses + + This function exists to get around the problem that many host platforms + don't have a printf that can print 64-bit addresses. The %A format + specification is recognized as a special case, and causes the argument + to be printed as a 64-bit hexadecimal address. + + Only format specifiers of the form "[0-9]*[a-z]" are recognized. + If it is a '%s' format, the argument is a string; otherwise the + argument is assumed to be a long integer. + + %% is also turned into a single %. + */ + +static void +monitor_vsprintf (char *sndbuf, char *pattern, va_list args) +{ + char format[10]; + char fmt; + char *p; + int i; + long arg_int; + CORE_ADDR arg_addr; + char *arg_string; + + for (p = pattern; *p; p++) + { + if (*p == '%') + { + /* Copy the format specifier to a separate buffer. */ + format[0] = *p++; + for (i = 1; *p >= '0' && *p <= '9' && i < (int) sizeof (format) - 2; + i++, p++) + format[i] = *p; + format[i] = fmt = *p; + format[i + 1] = '\0'; + + /* Fetch the next argument and print it. */ + switch (fmt) + { + case '%': + strcpy (sndbuf, "%"); + break; + case 'A': + arg_addr = va_arg (args, CORE_ADDR); + strcpy (sndbuf, paddr_nz (arg_addr)); + break; + case 's': + arg_string = va_arg (args, char *); + sprintf (sndbuf, format, arg_string); + break; + default: + arg_int = va_arg (args, long); + sprintf (sndbuf, format, arg_int); + break; + } + sndbuf += strlen (sndbuf); + } + else + *sndbuf++ = *p; + } + *sndbuf = '\0'; +} + + +/* monitor_printf_noecho -- Send data to monitor, but don't expect an echo. + Works just like printf. */ + +void +monitor_printf_noecho (char *pattern,...) +{ + va_list args; + char sndbuf[2000]; + int len; + + va_start (args, pattern); + + monitor_vsprintf (sndbuf, pattern, args); + + len = strlen (sndbuf); + if (len + 1 > sizeof sndbuf) + internal_error (__FILE__, __LINE__, "failed internal consistency check"); + + if (monitor_debug_p) + { + char *safe_string = (char *) alloca ((strlen (sndbuf) * 4) + 1); + monitor_printable_string (safe_string, sndbuf, 0); + fprintf_unfiltered (gdb_stdlog, "sent[%s]\n", safe_string); + } + + monitor_write (sndbuf, len); +} + +/* monitor_printf -- Send data to monitor and check the echo. Works just like + printf. */ + +void +monitor_printf (char *pattern,...) +{ + va_list args; + char sndbuf[2000]; + int len; + + va_start (args, pattern); + + monitor_vsprintf (sndbuf, pattern, args); + + len = strlen (sndbuf); + if (len + 1 > sizeof sndbuf) + internal_error (__FILE__, __LINE__, "failed internal consistency check"); + + if (monitor_debug_p) + { + char *safe_string = (char *) alloca ((len * 4) + 1); + monitor_printable_string (safe_string, sndbuf, 0); + fprintf_unfiltered (gdb_stdlog, "sent[%s]\n", safe_string); + } + + monitor_write (sndbuf, len); + + /* We used to expect that the next immediate output was the characters we + just output, but sometimes some extra junk appeared before the characters + we expected, like an extra prompt, or a portmaster sending telnet negotiations. + So, just start searching for what we sent, and skip anything unknown. */ + monitor_debug ("ExpectEcho\n"); + monitor_expect (sndbuf, (char *) 0, 0); +} + + +/* Write characters to the remote system. */ + +void +monitor_write (char *buf, int buflen) +{ + if (serial_write (monitor_desc, buf, buflen)) + fprintf_unfiltered (gdb_stderr, "serial_write failed: %s\n", + safe_strerror (errno)); +} + + +/* Read a binary character from the remote system, doing all the fancy + timeout stuff, but without interpreting the character in any way, + and without printing remote debug information. */ + +int +monitor_readchar (void) +{ + int c; + int looping; + + do + { + looping = 0; + c = serial_readchar (monitor_desc, timeout); + + if (c >= 0) + c &= 0xff; /* don't lose bit 7 */ + } + while (looping); + + if (c >= 0) + return c; + + if (c == SERIAL_TIMEOUT) + error ("Timeout reading from remote system."); + + perror_with_name ("remote-monitor"); +} + + +/* Read a character from the remote system, doing all the fancy + timeout stuff. */ + +static int +readchar (int timeout) +{ + int c; + static enum + { + last_random, last_nl, last_cr, last_crnl + } + state = last_random; + int looping; + + do + { + looping = 0; + c = serial_readchar (monitor_desc, timeout); + + if (c >= 0) + { + c &= 0x7f; + /* This seems to interfere with proper function of the + input stream */ + if (monitor_debug_p || remote_debug) + { + char buf[2]; + buf[0] = c; + buf[1] = '\0'; + puts_debug ("read -->", buf, "<--"); + } + + } + + /* Canonicialize \n\r combinations into one \r */ + if ((current_monitor->flags & MO_HANDLE_NL) != 0) + { + if ((c == '\r' && state == last_nl) + || (c == '\n' && state == last_cr)) + { + state = last_crnl; + looping = 1; + } + else if (c == '\r') + state = last_cr; + else if (c != '\n') + state = last_random; + else + { + state = last_nl; + c = '\r'; + } + } + } + while (looping); + + if (c >= 0) + return c; + + if (c == SERIAL_TIMEOUT) +#if 0 + /* I fail to see how detaching here can be useful */ + if (in_monitor_wait) /* Watchdog went off */ + { + target_mourn_inferior (); + error ("GDB serial timeout has expired. Target detached.\n"); + } + else +#endif + error ("Timeout reading from remote system."); + + perror_with_name ("remote-monitor"); +} + +/* Scan input from the remote system, until STRING is found. If BUF is non- + zero, then collect input until we have collected either STRING or BUFLEN-1 + chars. In either case we terminate BUF with a 0. If input overflows BUF + because STRING can't be found, return -1, else return number of chars in BUF + (minus the terminating NUL). Note that in the non-overflow case, STRING + will be at the end of BUF. */ + +int +monitor_expect (char *string, char *buf, int buflen) +{ + char *p = string; + int obuflen = buflen; + int c; + + if (monitor_debug_p) + { + char *safe_string = (char *) alloca ((strlen (string) * 4) + 1); + monitor_printable_string (safe_string, string, 0); + fprintf_unfiltered (gdb_stdlog, "MON Expecting '%s'\n", safe_string); + } + + immediate_quit++; + while (1) + { + if (buf) + { + if (buflen < 2) + { + *buf = '\000'; + immediate_quit--; + return -1; + } + + c = readchar (timeout); + if (c == '\000') + continue; + *buf++ = c; + buflen--; + } + else + c = readchar (timeout); + + /* Don't expect any ^C sent to be echoed */ + + if (*p == '\003' || c == *p) + { + p++; + if (*p == '\0') + { + immediate_quit--; + + if (buf) + { + *buf++ = '\000'; + return obuflen - buflen; + } + else + return 0; + } + } + else + { + /* We got a character that doesn't match the string. We need to + back up p, but how far? If we're looking for "..howdy" and the + monitor sends "...howdy"? There's certainly a match in there, + but when we receive the third ".", we won't find it if we just + restart the matching at the beginning of the string. + + This is a Boyer-Moore kind of situation. We want to reset P to + the end of the longest prefix of STRING that is a suffix of + what we've read so far. In the example above, that would be + ".." --- the longest prefix of "..howdy" that is a suffix of + "...". This longest prefix could be the empty string, if C + is nowhere to be found in STRING. + + If this longest prefix is not the empty string, it must contain + C, so let's search from the end of STRING for instances of C, + and see if the portion of STRING before that is a suffix of + what we read before C. Actually, we can search backwards from + p, since we know no prefix can be longer than that. + + Note that we can use STRING itself, along with C, as a record + of what we've received so far. :) */ + int i; + + for (i = (p - string) - 1; i >= 0; i--) + if (string[i] == c) + { + /* Is this prefix a suffix of what we've read so far? + In other words, does + string[0 .. i-1] == string[p - i, p - 1]? */ + if (! memcmp (string, p - i, i)) + { + p = string + i + 1; + break; + } + } + if (i < 0) + p = string; + } + } +} + +/* Search for a regexp. */ + +static int +monitor_expect_regexp (struct re_pattern_buffer *pat, char *buf, int buflen) +{ + char *mybuf; + char *p; + monitor_debug ("MON Expecting regexp\n"); + if (buf) + mybuf = buf; + else + { + mybuf = alloca (TARGET_BUF_SIZE); + buflen = TARGET_BUF_SIZE; + } + + p = mybuf; + while (1) + { + int retval; + + if (p - mybuf >= buflen) + { /* Buffer about to overflow */ + +/* On overflow, we copy the upper half of the buffer to the lower half. Not + great, but it usually works... */ + + memcpy (mybuf, mybuf + buflen / 2, buflen / 2); + p = mybuf + buflen / 2; + } + + *p++ = readchar (timeout); + + retval = re_search (pat, mybuf, p - mybuf, 0, p - mybuf, NULL); + if (retval >= 0) + return 1; + } +} + +/* Keep discarding input until we see the MONITOR prompt. + + The convention for dealing with the prompt is that you + o give your command + o *then* wait for the prompt. + + Thus the last thing that a procedure does with the serial line will + be an monitor_expect_prompt(). Exception: monitor_resume does not + wait for the prompt, because the terminal is being handed over to + the inferior. However, the next thing which happens after that is + a monitor_wait which does wait for the prompt. Note that this + includes abnormal exit, e.g. error(). This is necessary to prevent + getting into states from which we can't recover. */ + +int +monitor_expect_prompt (char *buf, int buflen) +{ + monitor_debug ("MON Expecting prompt\n"); + return monitor_expect (current_monitor->prompt, buf, buflen); +} + +/* Get N 32-bit words from remote, each preceded by a space, and put + them in registers starting at REGNO. */ + +#if 0 +static unsigned long +get_hex_word (void) +{ + unsigned long val; + int i; + int ch; + + do + ch = readchar (timeout); + while (isspace (ch)); + + val = from_hex (ch); + + for (i = 7; i >= 1; i--) + { + ch = readchar (timeout); + if (!isxdigit (ch)) + break; + val = (val << 4) | from_hex (ch); + } + + return val; +} +#endif + +static void +compile_pattern (char *pattern, struct re_pattern_buffer *compiled_pattern, + char *fastmap) +{ + int tmp; + const char *val; + + compiled_pattern->fastmap = fastmap; + + tmp = re_set_syntax (RE_SYNTAX_EMACS); + val = re_compile_pattern (pattern, + strlen (pattern), + compiled_pattern); + re_set_syntax (tmp); + + if (val) + error ("compile_pattern: Can't compile pattern string `%s': %s!", pattern, val); + + if (fastmap) + re_compile_fastmap (compiled_pattern); +} + +/* Open a connection to a remote debugger. NAME is the filename used + for communication. */ + +void +monitor_open (char *args, struct monitor_ops *mon_ops, int from_tty) +{ + char *name; + char **p; + + if (mon_ops->magic != MONITOR_OPS_MAGIC) + error ("Magic number of monitor_ops struct wrong."); + + targ_ops = mon_ops->target; + name = targ_ops->to_shortname; + + if (!args) + error ("Use `target %s DEVICE-NAME' to use a serial port, or \n\ +`target %s HOST-NAME:PORT-NUMBER' to use a network connection.", name, name); + + target_preopen (from_tty); + + /* Setup pattern for register dump */ + + if (mon_ops->register_pattern) + compile_pattern (mon_ops->register_pattern, ®ister_pattern, + register_fastmap); + + if (mon_ops->getmem.resp_delim) + compile_pattern (mon_ops->getmem.resp_delim, &getmem_resp_delim_pattern, + getmem_resp_delim_fastmap); + + if (mon_ops->setmem.resp_delim) + compile_pattern (mon_ops->setmem.resp_delim, &setmem_resp_delim_pattern, + setmem_resp_delim_fastmap); + + if (mon_ops->setreg.resp_delim) + compile_pattern (mon_ops->setreg.resp_delim, &setreg_resp_delim_pattern, + setreg_resp_delim_fastmap); + + unpush_target (targ_ops); + + if (dev_name) + xfree (dev_name); + dev_name = xstrdup (args); + + monitor_desc = serial_open (dev_name); + + if (!monitor_desc) + perror_with_name (dev_name); + + if (baud_rate != -1) + { + if (serial_setbaudrate (monitor_desc, baud_rate)) + { + serial_close (monitor_desc); + perror_with_name (dev_name); + } + } + + serial_raw (monitor_desc); + + serial_flush_input (monitor_desc); + + /* some systems only work with 2 stop bits */ + + serial_setstopbits (monitor_desc, mon_ops->stopbits); + + current_monitor = mon_ops; + + /* See if we can wake up the monitor. First, try sending a stop sequence, + then send the init strings. Last, remove all breakpoints. */ + + if (current_monitor->stop) + { + monitor_stop (); + if ((current_monitor->flags & MO_NO_ECHO_ON_OPEN) == 0) + { + monitor_debug ("EXP Open echo\n"); + monitor_expect_prompt (NULL, 0); + } + } + + /* wake up the monitor and see if it's alive */ + for (p = mon_ops->init; *p != NULL; p++) + { + /* Some of the characters we send may not be echoed, + but we hope to get a prompt at the end of it all. */ + + if ((current_monitor->flags & MO_NO_ECHO_ON_OPEN) == 0) + monitor_printf (*p); + else + monitor_printf_noecho (*p); + monitor_expect_prompt (NULL, 0); + } + + serial_flush_input (monitor_desc); + + /* Alloc breakpoints */ + if (mon_ops->set_break != NULL) + { + if (mon_ops->num_breakpoints == 0) + mon_ops->num_breakpoints = 8; + + breakaddr = (CORE_ADDR *) xmalloc (mon_ops->num_breakpoints * sizeof (CORE_ADDR)); + memset (breakaddr, 0, mon_ops->num_breakpoints * sizeof (CORE_ADDR)); + } + + /* Remove all breakpoints */ + + if (mon_ops->clr_all_break) + { + monitor_printf (mon_ops->clr_all_break); + monitor_expect_prompt (NULL, 0); + } + + if (from_tty) + printf_unfiltered ("Remote target %s connected to %s\n", name, dev_name); + + push_target (targ_ops); + + inferior_ptid = pid_to_ptid (42000); /* Make run command think we are busy... */ + + /* Give monitor_wait something to read */ + + monitor_printf (current_monitor->line_term); + + start_remote (); +} + +/* Close out all files and local state before this target loses + control. */ + +void +monitor_close (int quitting) +{ + if (monitor_desc) + serial_close (monitor_desc); + + /* Free breakpoint memory */ + if (breakaddr != NULL) + { + xfree (breakaddr); + breakaddr = NULL; + } + + monitor_desc = NULL; +} + +/* Terminate the open connection to the remote debugger. Use this + when you want to detach and do something else with your gdb. */ + +static void +monitor_detach (char *args, int from_tty) +{ + pop_target (); /* calls monitor_close to do the real work */ + if (from_tty) + printf_unfiltered ("Ending remote %s debugging\n", target_shortname); +} + +/* Convert VALSTR into the target byte-ordered value of REGNO and store it. */ + +char * +monitor_supply_register (int regno, char *valstr) +{ + ULONGEST val; + unsigned char regbuf[MAX_REGISTER_SIZE]; + char *p; + + val = 0; + p = valstr; + while (p && *p != '\0') + { + if (*p == '\r' || *p == '\n') + { + while (*p != '\0') + p++; + break; + } + if (isspace (*p)) + { + p++; + continue; + } + if (!isxdigit (*p) && *p != 'x') + { + break; + } + + val <<= 4; + val += fromhex (*p++); + } + monitor_debug ("Supplying Register %d %s\n", regno, valstr); + + if (val == 0 && valstr == p) + error ("monitor_supply_register (%d): bad value from monitor: %s.", + regno, valstr); + + /* supply register stores in target byte order, so swap here */ + + store_unsigned_integer (regbuf, DEPRECATED_REGISTER_RAW_SIZE (regno), val); + + supply_register (regno, regbuf); + + return p; +} + +/* Tell the remote machine to resume. */ + +static void +monitor_resume (ptid_t ptid, int step, enum target_signal sig) +{ + /* Some monitors require a different command when starting a program */ + monitor_debug ("MON resume\n"); + if (current_monitor->flags & MO_RUN_FIRST_TIME && first_time == 1) + { + first_time = 0; + monitor_printf ("run\r"); + if (current_monitor->flags & MO_NEED_REGDUMP_AFTER_CONT) + dump_reg_flag = 1; + return; + } + if (step) + monitor_printf (current_monitor->step); + else + { + if (current_monitor->continue_hook) + (*current_monitor->continue_hook) (); + else + monitor_printf (current_monitor->cont); + if (current_monitor->flags & MO_NEED_REGDUMP_AFTER_CONT) + dump_reg_flag = 1; + } +} + +/* Parse the output of a register dump command. A monitor specific + regexp is used to extract individual register descriptions of the + form REG=VAL. Each description is split up into a name and a value + string which are passed down to monitor specific code. */ + +static void +parse_register_dump (char *buf, int len) +{ + monitor_debug ("MON Parsing register dump\n"); + while (1) + { + int regnamelen, vallen; + char *regname, *val; + /* Element 0 points to start of register name, and element 1 + points to the start of the register value. */ + struct re_registers register_strings; + + memset (®ister_strings, 0, sizeof (struct re_registers)); + + if (re_search (®ister_pattern, buf, len, 0, len, + ®ister_strings) == -1) + break; + + regnamelen = register_strings.end[1] - register_strings.start[1]; + regname = buf + register_strings.start[1]; + vallen = register_strings.end[2] - register_strings.start[2]; + val = buf + register_strings.start[2]; + + current_monitor->supply_register (regname, regnamelen, val, vallen); + + buf += register_strings.end[0]; + len -= register_strings.end[0]; + } +} + +/* Send ^C to target to halt it. Target will respond, and send us a + packet. */ + +static void +monitor_interrupt (int signo) +{ + /* If this doesn't work, try more severe steps. */ + signal (signo, monitor_interrupt_twice); + + if (monitor_debug_p || remote_debug) + fprintf_unfiltered (gdb_stdlog, "monitor_interrupt called\n"); + + target_stop (); +} + +/* The user typed ^C twice. */ + +static void +monitor_interrupt_twice (int signo) +{ + signal (signo, ofunc); + + monitor_interrupt_query (); + + signal (signo, monitor_interrupt); +} + +/* Ask the user what to do when an interrupt is received. */ + +static void +monitor_interrupt_query (void) +{ + target_terminal_ours (); + + if (query ("Interrupted while waiting for the program.\n\ +Give up (and stop debugging it)? ")) + { + target_mourn_inferior (); + throw_exception (RETURN_QUIT); + } + + target_terminal_inferior (); +} + +static void +monitor_wait_cleanup (void *old_timeout) +{ + timeout = *(int *) old_timeout; + signal (SIGINT, ofunc); + in_monitor_wait = 0; +} + + + +static void +monitor_wait_filter (char *buf, + int bufmax, + int *ext_resp_len, + struct target_waitstatus *status) +{ + int resp_len; + do + { + resp_len = monitor_expect_prompt (buf, bufmax); + *ext_resp_len = resp_len; + + if (resp_len <= 0) + fprintf_unfiltered (gdb_stderr, "monitor_wait: excessive response from monitor: %s.", buf); + } + while (resp_len < 0); + + /* Print any output characters that were preceded by ^O. */ + /* FIXME - This would be great as a user settabgle flag */ + if (monitor_debug_p || remote_debug + || current_monitor->flags & MO_PRINT_PROGRAM_OUTPUT) + { + int i; + + for (i = 0; i < resp_len - 1; i++) + if (buf[i] == 0x0f) + putchar_unfiltered (buf[++i]); + } +} + + + +/* Wait until the remote machine stops, then return, storing status in + status just as `wait' would. */ + +static ptid_t +monitor_wait (ptid_t ptid, struct target_waitstatus *status) +{ + int old_timeout = timeout; + char buf[TARGET_BUF_SIZE]; + int resp_len; + struct cleanup *old_chain; + + status->kind = TARGET_WAITKIND_EXITED; + status->value.integer = 0; + + old_chain = make_cleanup (monitor_wait_cleanup, &old_timeout); + monitor_debug ("MON wait\n"); + +#if 0 + /* This is somthing other than a maintenance command */ + in_monitor_wait = 1; + timeout = watchdog > 0 ? watchdog : -1; +#else + timeout = -1; /* Don't time out -- user program is running. */ +#endif + + ofunc = (void (*)()) signal (SIGINT, monitor_interrupt); + + if (current_monitor->wait_filter) + (*current_monitor->wait_filter) (buf, sizeof (buf), &resp_len, status); + else + monitor_wait_filter (buf, sizeof (buf), &resp_len, status); + +#if 0 /* Transferred to monitor wait filter */ + do + { + resp_len = monitor_expect_prompt (buf, sizeof (buf)); + + if (resp_len <= 0) + fprintf_unfiltered (gdb_stderr, "monitor_wait: excessive response from monitor: %s.", buf); + } + while (resp_len < 0); + + /* Print any output characters that were preceded by ^O. */ + /* FIXME - This would be great as a user settabgle flag */ + if (monitor_debug_p || remote_debug + || current_monitor->flags & MO_PRINT_PROGRAM_OUTPUT) + { + int i; + + for (i = 0; i < resp_len - 1; i++) + if (buf[i] == 0x0f) + putchar_unfiltered (buf[++i]); + } +#endif + + signal (SIGINT, ofunc); + + timeout = old_timeout; +#if 0 + if (dump_reg_flag && current_monitor->dump_registers) + { + dump_reg_flag = 0; + monitor_printf (current_monitor->dump_registers); + resp_len = monitor_expect_prompt (buf, sizeof (buf)); + } + + if (current_monitor->register_pattern) + parse_register_dump (buf, resp_len); +#else + monitor_debug ("Wait fetching registers after stop\n"); + monitor_dump_regs (); +#endif + + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_TRAP; + + discard_cleanups (old_chain); + + in_monitor_wait = 0; + + return inferior_ptid; +} + +/* Fetch register REGNO, or all registers if REGNO is -1. Returns + errno value. */ + +static void +monitor_fetch_register (int regno) +{ + const char *name; + char *zerobuf; + char *regbuf; + int i; + + regbuf = alloca (MAX_REGISTER_SIZE * 2 + 1); + zerobuf = alloca (MAX_REGISTER_SIZE); + memset (zerobuf, 0, MAX_REGISTER_SIZE); + + if (current_monitor->regname != NULL) + name = current_monitor->regname (regno); + else + name = current_monitor->regnames[regno]; + monitor_debug ("MON fetchreg %d '%s'\n", regno, name ? name : "(null name)"); + + if (!name || (*name == '\0')) + { + monitor_debug ("No register known for %d\n", regno); + supply_register (regno, zerobuf); + return; + } + + /* send the register examine command */ + + monitor_printf (current_monitor->getreg.cmd, name); + + /* If RESP_DELIM is specified, we search for that as a leading + delimiter for the register value. Otherwise, we just start + searching from the start of the buf. */ + + if (current_monitor->getreg.resp_delim) + { + monitor_debug ("EXP getreg.resp_delim\n"); + monitor_expect (current_monitor->getreg.resp_delim, NULL, 0); + /* Handle case of first 32 registers listed in pairs. */ + if (current_monitor->flags & MO_32_REGS_PAIRED + && (regno & 1) != 0 && regno < 32) + { + monitor_debug ("EXP getreg.resp_delim\n"); + monitor_expect (current_monitor->getreg.resp_delim, NULL, 0); + } + } + + /* Skip leading spaces and "0x" if MO_HEX_PREFIX flag is set */ + if (current_monitor->flags & MO_HEX_PREFIX) + { + int c; + c = readchar (timeout); + while (c == ' ') + c = readchar (timeout); + if ((c == '0') && ((c = readchar (timeout)) == 'x')) + ; + else + error ("Bad value returned from monitor while fetching register %x.", + regno); + } + + /* Read upto the maximum number of hex digits for this register, skipping + spaces, but stop reading if something else is seen. Some monitors + like to drop leading zeros. */ + + for (i = 0; i < DEPRECATED_REGISTER_RAW_SIZE (regno) * 2; i++) + { + int c; + c = readchar (timeout); + while (c == ' ') + c = readchar (timeout); + + if (!isxdigit (c)) + break; + + regbuf[i] = c; + } + + regbuf[i] = '\000'; /* terminate the number */ + monitor_debug ("REGVAL '%s'\n", regbuf); + + /* If TERM is present, we wait for that to show up. Also, (if TERM + is present), we will send TERM_CMD if that is present. In any + case, we collect all of the output into buf, and then wait for + the normal prompt. */ + + if (current_monitor->getreg.term) + { + monitor_debug ("EXP getreg.term\n"); + monitor_expect (current_monitor->getreg.term, NULL, 0); /* get response */ + } + + if (current_monitor->getreg.term_cmd) + { + monitor_debug ("EMIT getreg.term.cmd\n"); + monitor_printf (current_monitor->getreg.term_cmd); + } + if (!current_monitor->getreg.term || /* Already expected or */ + current_monitor->getreg.term_cmd) /* ack expected */ + monitor_expect_prompt (NULL, 0); /* get response */ + + monitor_supply_register (regno, regbuf); +} + +/* Sometimes, it takes several commands to dump the registers */ +/* This is a primitive for use by variations of monitor interfaces in + case they need to compose the operation. + */ +int +monitor_dump_reg_block (char *block_cmd) +{ + char buf[TARGET_BUF_SIZE]; + int resp_len; + monitor_printf (block_cmd); + resp_len = monitor_expect_prompt (buf, sizeof (buf)); + parse_register_dump (buf, resp_len); + return 1; +} + + +/* Read the remote registers into the block regs. */ +/* Call the specific function if it has been provided */ + +static void +monitor_dump_regs (void) +{ + char buf[TARGET_BUF_SIZE]; + int resp_len; + if (current_monitor->dumpregs) + (*(current_monitor->dumpregs)) (); /* call supplied function */ + else if (current_monitor->dump_registers) /* default version */ + { + monitor_printf (current_monitor->dump_registers); + resp_len = monitor_expect_prompt (buf, sizeof (buf)); + parse_register_dump (buf, resp_len); + } + else + internal_error (__FILE__, __LINE__, "failed internal consistency check"); /* Need some way to read registers */ +} + +static void +monitor_fetch_registers (int regno) +{ + monitor_debug ("MON fetchregs\n"); + if (current_monitor->getreg.cmd) + { + if (regno >= 0) + { + monitor_fetch_register (regno); + return; + } + + for (regno = 0; regno < NUM_REGS; regno++) + monitor_fetch_register (regno); + } + else + { + monitor_dump_regs (); + } +} + +/* Store register REGNO, or all if REGNO == 0. Return errno value. */ + +static void +monitor_store_register (int regno) +{ + const char *name; + ULONGEST val; + + if (current_monitor->regname != NULL) + name = current_monitor->regname (regno); + else + name = current_monitor->regnames[regno]; + + if (!name || (*name == '\0')) + { + monitor_debug ("MON Cannot store unknown register\n"); + return; + } + + val = read_register (regno); + monitor_debug ("MON storeg %d %s\n", regno, + phex (val, DEPRECATED_REGISTER_RAW_SIZE (regno))); + + /* send the register deposit command */ + + if (current_monitor->flags & MO_REGISTER_VALUE_FIRST) + monitor_printf (current_monitor->setreg.cmd, val, name); + else if (current_monitor->flags & MO_SETREG_INTERACTIVE) + monitor_printf (current_monitor->setreg.cmd, name); + else + monitor_printf (current_monitor->setreg.cmd, name, val); + + if (current_monitor->setreg.resp_delim) + { + monitor_debug ("EXP setreg.resp_delim\n"); + monitor_expect_regexp (&setreg_resp_delim_pattern, NULL, 0); + if (current_monitor->flags & MO_SETREG_INTERACTIVE) + monitor_printf ("%s\r", paddr_nz (val)); + } + if (current_monitor->setreg.term) + { + monitor_debug ("EXP setreg.term\n"); + monitor_expect (current_monitor->setreg.term, NULL, 0); + if (current_monitor->flags & MO_SETREG_INTERACTIVE) + monitor_printf ("%s\r", paddr_nz (val)); + monitor_expect_prompt (NULL, 0); + } + else + monitor_expect_prompt (NULL, 0); + if (current_monitor->setreg.term_cmd) /* Mode exit required */ + { + monitor_debug ("EXP setreg_termcmd\n"); + monitor_printf ("%s", current_monitor->setreg.term_cmd); + monitor_expect_prompt (NULL, 0); + } +} /* monitor_store_register */ + +/* Store the remote registers. */ + +static void +monitor_store_registers (int regno) +{ + if (regno >= 0) + { + monitor_store_register (regno); + return; + } + + for (regno = 0; regno < NUM_REGS; regno++) + monitor_store_register (regno); +} + +/* Get ready to modify the registers array. On machines which store + individual registers, this doesn't need to do anything. On machines + which store all the registers in one fell swoop, this makes sure + that registers contains all the registers from the program being + debugged. */ + +static void +monitor_prepare_to_store (void) +{ + /* Do nothing, since we can store individual regs */ +} + +static void +monitor_files_info (struct target_ops *ops) +{ + printf_unfiltered ("\tAttached to %s at %d baud.\n", dev_name, baud_rate); +} + +static int +monitor_write_memory (CORE_ADDR memaddr, char *myaddr, int len) +{ + unsigned int val, hostval; + char *cmd; + int i; + + monitor_debug ("MON write %d %s\n", len, paddr (memaddr)); + + if (current_monitor->flags & MO_ADDR_BITS_REMOVE) + memaddr = ADDR_BITS_REMOVE (memaddr); + + /* Use memory fill command for leading 0 bytes. */ + + if (current_monitor->fill) + { + for (i = 0; i < len; i++) + if (myaddr[i] != 0) + break; + + if (i > 4) /* More than 4 zeros is worth doing */ + { + monitor_debug ("MON FILL %d\n", i); + if (current_monitor->flags & MO_FILL_USES_ADDR) + monitor_printf (current_monitor->fill, memaddr, (memaddr + i) - 1, 0); + else + monitor_printf (current_monitor->fill, memaddr, i, 0); + + monitor_expect_prompt (NULL, 0); + + return i; + } + } + +#if 0 + /* Can't actually use long longs if VAL is an int (nice idea, though). */ + if ((memaddr & 0x7) == 0 && len >= 8 && current_monitor->setmem.cmdll) + { + len = 8; + cmd = current_monitor->setmem.cmdll; + } + else +#endif + if ((memaddr & 0x3) == 0 && len >= 4 && current_monitor->setmem.cmdl) + { + len = 4; + cmd = current_monitor->setmem.cmdl; + } + else if ((memaddr & 0x1) == 0 && len >= 2 && current_monitor->setmem.cmdw) + { + len = 2; + cmd = current_monitor->setmem.cmdw; + } + else + { + len = 1; + cmd = current_monitor->setmem.cmdb; + } + + val = extract_unsigned_integer (myaddr, len); + + if (len == 4) + { + hostval = *(unsigned int *) myaddr; + monitor_debug ("Hostval(%08x) val(%08x)\n", hostval, val); + } + + + if (current_monitor->flags & MO_NO_ECHO_ON_SETMEM) + monitor_printf_noecho (cmd, memaddr, val); + else if (current_monitor->flags & MO_SETMEM_INTERACTIVE) + { + + monitor_printf_noecho (cmd, memaddr); + + if (current_monitor->setmem.resp_delim) + { + monitor_debug ("EXP setmem.resp_delim"); + monitor_expect_regexp (&setmem_resp_delim_pattern, NULL, 0); + monitor_printf ("%x\r", val); + } + if (current_monitor->setmem.term) + { + monitor_debug ("EXP setmem.term"); + monitor_expect (current_monitor->setmem.term, NULL, 0); + monitor_printf ("%x\r", val); + } + if (current_monitor->setmem.term_cmd) + { /* Emit this to get out of the memory editing state */ + monitor_printf ("%s", current_monitor->setmem.term_cmd); + /* Drop through to expecting a prompt */ + } + } + else + monitor_printf (cmd, memaddr, val); + + monitor_expect_prompt (NULL, 0); + + return len; +} + + +static int +monitor_write_memory_bytes (CORE_ADDR memaddr, char *myaddr, int len) +{ + unsigned char val; + int written = 0; + if (len == 0) + return 0; + /* Enter the sub mode */ + monitor_printf (current_monitor->setmem.cmdb, memaddr); + monitor_expect_prompt (NULL, 0); + while (len) + { + val = *myaddr; + monitor_printf ("%x\r", val); + myaddr++; + memaddr++; + written++; + /* If we wanted to, here we could validate the address */ + monitor_expect_prompt (NULL, 0); + len--; + } + /* Now exit the sub mode */ + monitor_printf (current_monitor->getreg.term_cmd); + monitor_expect_prompt (NULL, 0); + return written; +} + + +static void +longlongendswap (unsigned char *a) +{ + int i, j; + unsigned char x; + i = 0; + j = 7; + while (i < 4) + { + x = *(a + i); + *(a + i) = *(a + j); + *(a + j) = x; + i++, j--; + } +} +/* Format 32 chars of long long value, advance the pointer */ +static char *hexlate = "0123456789abcdef"; +static char * +longlong_hexchars (unsigned long long value, + char *outbuff) +{ + if (value == 0) + { + *outbuff++ = '0'; + return outbuff; + } + else + { + static unsigned char disbuf[8]; /* disassembly buffer */ + unsigned char *scan, *limit; /* loop controls */ + unsigned char c, nib; + int leadzero = 1; + scan = disbuf; + limit = scan + 8; + { + unsigned long long *dp; + dp = (unsigned long long *) scan; + *dp = value; + } + longlongendswap (disbuf); /* FIXME: ONly on big endian hosts */ + while (scan < limit) + { + c = *scan++; /* a byte of our long long value */ + if (leadzero) + { + if (c == 0) + continue; + else + leadzero = 0; /* henceforth we print even zeroes */ + } + nib = c >> 4; /* high nibble bits */ + *outbuff++ = hexlate[nib]; + nib = c & 0x0f; /* low nibble bits */ + *outbuff++ = hexlate[nib]; + } + return outbuff; + } +} /* longlong_hexchars */ + + + +/* I am only going to call this when writing virtual byte streams. + Which possably entails endian conversions + */ +static int +monitor_write_memory_longlongs (CORE_ADDR memaddr, char *myaddr, int len) +{ + static char hexstage[20]; /* At least 16 digits required, plus null */ + char *endstring; + long long *llptr; + long long value; + int written = 0; + llptr = (unsigned long long *) myaddr; + if (len == 0) + return 0; + monitor_printf (current_monitor->setmem.cmdll, memaddr); + monitor_expect_prompt (NULL, 0); + while (len >= 8) + { + value = *llptr; + endstring = longlong_hexchars (*llptr, hexstage); + *endstring = '\0'; /* NUll terminate for printf */ + monitor_printf ("%s\r", hexstage); + llptr++; + memaddr += 8; + written += 8; + /* If we wanted to, here we could validate the address */ + monitor_expect_prompt (NULL, 0); + len -= 8; + } + /* Now exit the sub mode */ + monitor_printf (current_monitor->getreg.term_cmd); + monitor_expect_prompt (NULL, 0); + return written; +} /* */ + + + +/* ----- MONITOR_WRITE_MEMORY_BLOCK ---------------------------- */ +/* This is for the large blocks of memory which may occur in downloading. + And for monitors which use interactive entry, + And for monitors which do not have other downloading methods. + Without this, we will end up calling monitor_write_memory many times + and do the entry and exit of the sub mode many times + This currently assumes... + MO_SETMEM_INTERACTIVE + ! MO_NO_ECHO_ON_SETMEM + To use this, the you have to patch the monitor_cmds block with + this function. Otherwise, its not tuned up for use by all + monitor variations. + */ + +static int +monitor_write_memory_block (CORE_ADDR memaddr, char *myaddr, int len) +{ + int written; + written = 0; + /* FIXME: This would be a good place to put the zero test */ +#if 1 + if ((len > 8) && (((len & 0x07)) == 0) && current_monitor->setmem.cmdll) + { + return monitor_write_memory_longlongs (memaddr, myaddr, len); + } +#endif + written = monitor_write_memory_bytes (memaddr, myaddr, len); + return written; +} + +/* This is an alternate form of monitor_read_memory which is used for monitors + which can only read a single byte/word/etc. at a time. */ + +static int +monitor_read_memory_single (CORE_ADDR memaddr, char *myaddr, int len) +{ + unsigned int val; + char membuf[sizeof (int) * 2 + 1]; + char *p; + char *cmd; + + monitor_debug ("MON read single\n"); +#if 0 + /* Can't actually use long longs (nice idea, though). In fact, the + call to strtoul below will fail if it tries to convert a value + that's too big to fit in a long. */ + if ((memaddr & 0x7) == 0 && len >= 8 && current_monitor->getmem.cmdll) + { + len = 8; + cmd = current_monitor->getmem.cmdll; + } + else +#endif + if ((memaddr & 0x3) == 0 && len >= 4 && current_monitor->getmem.cmdl) + { + len = 4; + cmd = current_monitor->getmem.cmdl; + } + else if ((memaddr & 0x1) == 0 && len >= 2 && current_monitor->getmem.cmdw) + { + len = 2; + cmd = current_monitor->getmem.cmdw; + } + else + { + len = 1; + cmd = current_monitor->getmem.cmdb; + } + + /* Send the examine command. */ + + monitor_printf (cmd, memaddr); + + /* If RESP_DELIM is specified, we search for that as a leading + delimiter for the memory value. Otherwise, we just start + searching from the start of the buf. */ + + if (current_monitor->getmem.resp_delim) + { + monitor_debug ("EXP getmem.resp_delim\n"); + monitor_expect_regexp (&getmem_resp_delim_pattern, NULL, 0); + } + + /* Now, read the appropriate number of hex digits for this loc, + skipping spaces. */ + + /* Skip leading spaces and "0x" if MO_HEX_PREFIX flag is set. */ + if (current_monitor->flags & MO_HEX_PREFIX) + { + int c; + + c = readchar (timeout); + while (c == ' ') + c = readchar (timeout); + if ((c == '0') && ((c = readchar (timeout)) == 'x')) + ; + else + monitor_error ("monitor_read_memory_single", + "bad response from monitor", + memaddr, 0, NULL, 0); + } + + { + int i; + for (i = 0; i < len * 2; i++) + { + int c; + + while (1) + { + c = readchar (timeout); + if (isxdigit (c)) + break; + if (c == ' ') + continue; + + monitor_error ("monitor_read_memory_single", + "bad response from monitor", + memaddr, i, membuf, 0); + } + membuf[i] = c; + } + membuf[i] = '\000'; /* terminate the number */ + } + +/* If TERM is present, we wait for that to show up. Also, (if TERM is + present), we will send TERM_CMD if that is present. In any case, we collect + all of the output into buf, and then wait for the normal prompt. */ + + if (current_monitor->getmem.term) + { + monitor_expect (current_monitor->getmem.term, NULL, 0); /* get response */ + + if (current_monitor->getmem.term_cmd) + { + monitor_printf (current_monitor->getmem.term_cmd); + monitor_expect_prompt (NULL, 0); + } + } + else + monitor_expect_prompt (NULL, 0); /* get response */ + + p = membuf; + val = strtoul (membuf, &p, 16); + + if (val == 0 && membuf == p) + monitor_error ("monitor_read_memory_single", + "bad value from monitor", + memaddr, 0, membuf, 0); + + /* supply register stores in target byte order, so swap here */ + + store_unsigned_integer (myaddr, len, val); + + return len; +} + +/* Copy LEN bytes of data from debugger memory at MYADDR to inferior's + memory at MEMADDR. Returns length moved. Currently, we do no more + than 16 bytes at a time. */ + +static int +monitor_read_memory (CORE_ADDR memaddr, char *myaddr, int len) +{ + unsigned int val; + char buf[512]; + char *p, *p1; + int resp_len; + int i; + CORE_ADDR dumpaddr; + + if (len <= 0) + { + monitor_debug ("Zero length call to monitor_read_memory\n"); + return 0; + } + + monitor_debug ("MON read block ta(%s) ha(%lx) %d\n", + paddr_nz (memaddr), (long) myaddr, len); + + if (current_monitor->flags & MO_ADDR_BITS_REMOVE) + memaddr = ADDR_BITS_REMOVE (memaddr); + + if (current_monitor->flags & MO_GETMEM_READ_SINGLE) + return monitor_read_memory_single (memaddr, myaddr, len); + + len = min (len, 16); + + /* Some dumpers align the first data with the preceeding 16 + byte boundary. Some print blanks and start at the + requested boundary. EXACT_DUMPADDR + */ + + dumpaddr = (current_monitor->flags & MO_EXACT_DUMPADDR) + ? memaddr : memaddr & ~0x0f; + + /* See if xfer would cross a 16 byte boundary. If so, clip it. */ + if (((memaddr ^ (memaddr + len - 1)) & ~0xf) != 0) + len = ((memaddr + len) & ~0xf) - memaddr; + + /* send the memory examine command */ + + if (current_monitor->flags & MO_GETMEM_NEEDS_RANGE) + monitor_printf (current_monitor->getmem.cmdb, memaddr, memaddr + len); + else if (current_monitor->flags & MO_GETMEM_16_BOUNDARY) + monitor_printf (current_monitor->getmem.cmdb, dumpaddr); + else + monitor_printf (current_monitor->getmem.cmdb, memaddr, len); + + /* If TERM is present, we wait for that to show up. Also, (if TERM + is present), we will send TERM_CMD if that is present. In any + case, we collect all of the output into buf, and then wait for + the normal prompt. */ + + if (current_monitor->getmem.term) + { + resp_len = monitor_expect (current_monitor->getmem.term, buf, sizeof buf); /* get response */ + + if (resp_len <= 0) + monitor_error ("monitor_read_memory", + "excessive response from monitor", + memaddr, resp_len, buf, 0); + + if (current_monitor->getmem.term_cmd) + { + serial_write (monitor_desc, current_monitor->getmem.term_cmd, + strlen (current_monitor->getmem.term_cmd)); + monitor_expect_prompt (NULL, 0); + } + } + else + resp_len = monitor_expect_prompt (buf, sizeof buf); /* get response */ + + p = buf; + + /* If RESP_DELIM is specified, we search for that as a leading + delimiter for the values. Otherwise, we just start searching + from the start of the buf. */ + + if (current_monitor->getmem.resp_delim) + { + int retval, tmp; + struct re_registers resp_strings; + monitor_debug ("MON getmem.resp_delim %s\n", current_monitor->getmem.resp_delim); + + memset (&resp_strings, 0, sizeof (struct re_registers)); + tmp = strlen (p); + retval = re_search (&getmem_resp_delim_pattern, p, tmp, 0, tmp, + &resp_strings); + + if (retval < 0) + monitor_error ("monitor_read_memory", + "bad response from monitor", + memaddr, resp_len, buf, 0); + + p += resp_strings.end[0]; +#if 0 + p = strstr (p, current_monitor->getmem.resp_delim); + if (!p) + monitor_error ("monitor_read_memory", + "bad response from monitor", + memaddr, resp_len, buf, 0); + p += strlen (current_monitor->getmem.resp_delim); +#endif + } + monitor_debug ("MON scanning %d ,%lx '%s'\n", len, (long) p, p); + if (current_monitor->flags & MO_GETMEM_16_BOUNDARY) + { + char c; + int fetched = 0; + i = len; + c = *p; + + + while (!(c == '\000' || c == '\n' || c == '\r') && i > 0) + { + if (isxdigit (c)) + { + if ((dumpaddr >= memaddr) && (i > 0)) + { + val = fromhex (c) * 16 + fromhex (*(p + 1)); + *myaddr++ = val; + if (monitor_debug_p || remote_debug) + fprintf_unfiltered (gdb_stdlog, "[%02x]", val); + --i; + fetched++; + } + ++dumpaddr; + ++p; + } + ++p; /* skip a blank or other non hex char */ + c = *p; + } + if (fetched == 0) + error ("Failed to read via monitor"); + if (monitor_debug_p || remote_debug) + fprintf_unfiltered (gdb_stdlog, "\n"); + return fetched; /* Return the number of bytes actually read */ + } + monitor_debug ("MON scanning bytes\n"); + + for (i = len; i > 0; i--) + { + /* Skip non-hex chars, but bomb on end of string and newlines */ + + while (1) + { + if (isxdigit (*p)) + break; + + if (*p == '\000' || *p == '\n' || *p == '\r') + monitor_error ("monitor_read_memory", + "badly terminated response from monitor", + memaddr, resp_len, buf, 0); + p++; + } + + val = strtoul (p, &p1, 16); + + if (val == 0 && p == p1) + monitor_error ("monitor_read_memory", + "bad value from monitor", + memaddr, resp_len, buf, 0); + + *myaddr++ = val; + + if (i == 1) + break; + + p = p1; + } + + return len; +} + +/* Transfer LEN bytes between target address MEMADDR and GDB address + MYADDR. Returns 0 for success, errno code for failure. TARGET is + unused. */ + +static int +monitor_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write, + struct mem_attrib *attrib, struct target_ops *target) +{ + int res; + + if (write) + { + if (current_monitor->flags & MO_HAS_BLOCKWRITES) + res = monitor_write_memory_block(memaddr, myaddr, len); + else + res = monitor_write_memory(memaddr, myaddr, len); + } + else + { + res = monitor_read_memory(memaddr, myaddr, len); + } + + return res; +} + +static void +monitor_kill (void) +{ + return; /* ignore attempts to kill target system */ +} + +/* All we actually do is set the PC to the start address of exec_bfd, and start + the program at that point. */ + +static void +monitor_create_inferior (char *exec_file, char *args, char **env) +{ + if (args && (*args != '\000')) + error ("Args are not supported by the monitor."); + + first_time = 1; + clear_proceed_status (); + proceed (bfd_get_start_address (exec_bfd), TARGET_SIGNAL_0, 0); +} + +/* Clean up when a program exits. + The program actually lives on in the remote processor's RAM, and may be + run again without a download. Don't leave it full of breakpoint + instructions. */ + +static void +monitor_mourn_inferior (void) +{ + unpush_target (targ_ops); + generic_mourn_inferior (); /* Do all the proper things now */ +} + +/* Tell the monitor to add a breakpoint. */ + +static int +monitor_insert_breakpoint (CORE_ADDR addr, char *shadow) +{ + int i; + const unsigned char *bp; + int bplen; + + monitor_debug ("MON inst bkpt %s\n", paddr (addr)); + if (current_monitor->set_break == NULL) + error ("No set_break defined for this monitor"); + + if (current_monitor->flags & MO_ADDR_BITS_REMOVE) + addr = ADDR_BITS_REMOVE (addr); + + /* Determine appropriate breakpoint size for this address. */ + bp = gdbarch_breakpoint_from_pc (current_gdbarch, &addr, &bplen); + + for (i = 0; i < current_monitor->num_breakpoints; i++) + { + if (breakaddr[i] == 0) + { + breakaddr[i] = addr; + monitor_read_memory (addr, shadow, bplen); + monitor_printf (current_monitor->set_break, addr); + monitor_expect_prompt (NULL, 0); + return 0; + } + } + + error ("Too many breakpoints (> %d) for monitor.", current_monitor->num_breakpoints); +} + +/* Tell the monitor to remove a breakpoint. */ + +static int +monitor_remove_breakpoint (CORE_ADDR addr, char *shadow) +{ + int i; + + monitor_debug ("MON rmbkpt %s\n", paddr (addr)); + if (current_monitor->clr_break == NULL) + error ("No clr_break defined for this monitor"); + + if (current_monitor->flags & MO_ADDR_BITS_REMOVE) + addr = ADDR_BITS_REMOVE (addr); + + for (i = 0; i < current_monitor->num_breakpoints; i++) + { + if (breakaddr[i] == addr) + { + breakaddr[i] = 0; + /* some monitors remove breakpoints based on the address */ + if (current_monitor->flags & MO_CLR_BREAK_USES_ADDR) + monitor_printf (current_monitor->clr_break, addr); + else if (current_monitor->flags & MO_CLR_BREAK_1_BASED) + monitor_printf (current_monitor->clr_break, i + 1); + else + monitor_printf (current_monitor->clr_break, i); + monitor_expect_prompt (NULL, 0); + return 0; + } + } + fprintf_unfiltered (gdb_stderr, + "Can't find breakpoint associated with 0x%s\n", + paddr_nz (addr)); + return 1; +} + +/* monitor_wait_srec_ack -- wait for the target to send an acknowledgement for + an S-record. Return non-zero if the ACK is received properly. */ + +static int +monitor_wait_srec_ack (void) +{ + int ch; + + if (current_monitor->flags & MO_SREC_ACK_PLUS) + { + return (readchar (timeout) == '+'); + } + else if (current_monitor->flags & MO_SREC_ACK_ROTATE) + { + /* Eat two backspaces, a "rotating" char (|/-\), and a space. */ + if ((ch = readchar (1)) < 0) + return 0; + if ((ch = readchar (1)) < 0) + return 0; + if ((ch = readchar (1)) < 0) + return 0; + if ((ch = readchar (1)) < 0) + return 0; + } + return 1; +} + +/* monitor_load -- download a file. */ + +static void +monitor_load (char *file, int from_tty) +{ + monitor_debug ("MON load\n"); + + if (current_monitor->load_routine) + current_monitor->load_routine (monitor_desc, file, hashmark); + else + { /* The default is ascii S-records */ + int n; + unsigned long load_offset; + char buf[128]; + + /* enable user to specify address for downloading as 2nd arg to load */ + n = sscanf (file, "%s 0x%lx", buf, &load_offset); + if (n > 1) + file = buf; + else + load_offset = 0; + + monitor_printf (current_monitor->load); + if (current_monitor->loadresp) + monitor_expect (current_monitor->loadresp, NULL, 0); + + load_srec (monitor_desc, file, (bfd_vma) load_offset, + 32, SREC_ALL, hashmark, + current_monitor->flags & MO_SREC_ACK ? + monitor_wait_srec_ack : NULL); + + monitor_expect_prompt (NULL, 0); + } + + /* Finally, make the PC point at the start address */ + if (exec_bfd) + write_pc (bfd_get_start_address (exec_bfd)); + + /* There used to be code here which would clear inferior_ptid and + call clear_symtab_users. None of that should be necessary: + monitor targets should behave like remote protocol targets, and + since generic_load does none of those things, this function + shouldn't either. + + Furthermore, clearing inferior_ptid is *incorrect*. After doing + a load, we still have a valid connection to the monitor, with a + live processor state to fiddle with. The user can type + `continue' or `jump *start' and make the program run. If they do + these things, however, GDB will be talking to a running program + while inferior_ptid is null_ptid; this makes things like + reinit_frame_cache very confused. */ +} + +static void +monitor_stop (void) +{ + monitor_debug ("MON stop\n"); + if ((current_monitor->flags & MO_SEND_BREAK_ON_STOP) != 0) + serial_send_break (monitor_desc); + if (current_monitor->stop) + monitor_printf_noecho (current_monitor->stop); +} + +/* Put a COMMAND string out to MONITOR. Output from MONITOR is placed + in OUTPUT until the prompt is seen. FIXME: We read the characters + ourseleves here cause of a nasty echo. */ + +static void +monitor_rcmd (char *command, + struct ui_file *outbuf) +{ + char *p; + int resp_len; + char buf[1000]; + + if (monitor_desc == NULL) + error ("monitor target not open."); + + p = current_monitor->prompt; + + /* Send the command. Note that if no args were supplied, then we're + just sending the monitor a newline, which is sometimes useful. */ + + monitor_printf ("%s\r", (command ? command : "")); + + resp_len = monitor_expect_prompt (buf, sizeof buf); + + fputs_unfiltered (buf, outbuf); /* Output the response */ +} + +/* Convert hex digit A to a number. */ + +#if 0 +static int +from_hex (int a) +{ + if (a >= '0' && a <= '9') + return a - '0'; + if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + if (a >= 'A' && a <= 'F') + return a - 'A' + 10; + + error ("Reply contains invalid hex digit 0x%x", a); +} +#endif + +char * +monitor_get_dev_name (void) +{ + return dev_name; +} + +static struct target_ops monitor_ops; + +static void +init_base_monitor_ops (void) +{ + monitor_ops.to_close = monitor_close; + monitor_ops.to_detach = monitor_detach; + monitor_ops.to_resume = monitor_resume; + monitor_ops.to_wait = monitor_wait; + monitor_ops.to_fetch_registers = monitor_fetch_registers; + monitor_ops.to_store_registers = monitor_store_registers; + monitor_ops.to_prepare_to_store = monitor_prepare_to_store; + monitor_ops.to_xfer_memory = monitor_xfer_memory; + monitor_ops.to_files_info = monitor_files_info; + monitor_ops.to_insert_breakpoint = monitor_insert_breakpoint; + monitor_ops.to_remove_breakpoint = monitor_remove_breakpoint; + monitor_ops.to_kill = monitor_kill; + monitor_ops.to_load = monitor_load; + monitor_ops.to_create_inferior = monitor_create_inferior; + monitor_ops.to_mourn_inferior = monitor_mourn_inferior; + monitor_ops.to_stop = monitor_stop; + monitor_ops.to_rcmd = monitor_rcmd; + monitor_ops.to_stratum = process_stratum; + monitor_ops.to_has_all_memory = 1; + monitor_ops.to_has_memory = 1; + monitor_ops.to_has_stack = 1; + monitor_ops.to_has_registers = 1; + monitor_ops.to_has_execution = 1; + monitor_ops.to_magic = OPS_MAGIC; +} /* init_base_monitor_ops */ + +/* Init the target_ops structure pointed at by OPS */ + +void +init_monitor_ops (struct target_ops *ops) +{ + if (monitor_ops.to_magic != OPS_MAGIC) + init_base_monitor_ops (); + + memcpy (ops, &monitor_ops, sizeof monitor_ops); +} + +/* Define additional commands that are usually only used by monitors. */ + +extern initialize_file_ftype _initialize_remote_monitors; /* -Wmissing-prototypes */ + +void +_initialize_remote_monitors (void) +{ + init_base_monitor_ops (); + add_show_from_set (add_set_cmd ("hash", no_class, var_boolean, + (char *) &hashmark, + "Set display of activity while downloading a file.\n\ +When enabled, a hashmark \'#\' is displayed.", + &setlist), + &showlist); + + add_show_from_set + (add_set_cmd ("monitor", no_class, var_zinteger, + (char *) &monitor_debug_p, + "Set debugging of remote monitor communication.\n\ +When enabled, communication between GDB and the remote monitor\n\ +is displayed.", &setdebuglist), + &showdebuglist); +} diff --git a/contrib/gdb/gdb/monitor.h b/contrib/gdb/gdb/monitor.h new file mode 100644 index 0000000..2f8ca22 --- /dev/null +++ b/contrib/gdb/gdb/monitor.h @@ -0,0 +1,260 @@ +/* Definitions for remote debugging interface for ROM monitors. + Copyright 1990, 1991, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2000 + Free Software Foundation, Inc. + Contributed by Cygnus Support. Written by Rob Savoye for Cygnus. + + 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. + */ + +#ifndef MONITOR_H +#define MONITOR_H + +struct target_waitstatus; +struct serial; + +/* This structure describes the strings necessary to give small command + sequences to the monitor, and parse the response. + + CMD is the actual command typed at the monitor. Usually this has + embedded sequences ala printf, which are substituted with the + arguments appropriate to that type of command. Ie: to examine a + register, we substitute the register name for the first arg. To + modify memory, we substitute the memory location and the new + contents for the first and second args, etc... + + RESP_DELIM used to home in on the response string, and is used to + disambiguate the answer within the pile of text returned by the + monitor. This should be a unique string that immediately precedes + the answer. Ie: if your monitor prints out `PC: 00000001= ' in + response to asking for the PC, you should use `: ' as the + RESP_DELIM. RESP_DELIM may be NULL if the res- ponse is going to + be ignored, or has no particular leading text. + + TERM is the string that the monitor outputs to indicate that it is + idle, and waiting for input. This is usually a prompt of some + sort. In the previous example, it would be `= '. It is important + that TERM really means that the monitor is idle, otherwise GDB may + try to type at it when it isn't ready for input. This is a problem + because many monitors cannot deal with type-ahead. TERM may be + NULL if the normal prompt is output. + + TERM_CMD is used to quit out of the subcommand mode and get back to + the main prompt. TERM_CMD may be NULL if it isn't necessary. It + will also be ignored if TERM is NULL. */ + +struct memrw_cmd + { + char *cmdb; /* Command to send for byte read/write */ + char *cmdw; /* Command for word (16 bit) read/write */ + char *cmdl; /* Command for long (32 bit) read/write */ + char *cmdll; /* Command for long long (64 bit) read/write */ + char *resp_delim; /* String just prior to the desired value */ + char *term; /* Terminating string to search for */ + char *term_cmd; /* String to get out of sub-mode (if necessary) */ + }; + +struct regrw_cmd + { + char *cmd; /* Command to send for reg read/write */ + char *resp_delim; /* String (actually a regexp if getmem) just + prior to the desired value */ + char *term; /* Terminating string to search for */ + char *term_cmd; /* String to get out of sub-mode (if necessary) */ + }; + +struct monitor_ops + { + int flags; /* See below */ + char **init; /* List of init commands. NULL terminated. */ + char *cont; /* continue command */ + char *step; /* single step */ + char *stop; /* Interrupt program string */ + char *set_break; /* set a breakpoint. If NULL, monitor implementation + sets its own to_insert_breakpoint method. */ + char *clr_break; /* clear a breakpoint */ + char *clr_all_break; /* Clear all breakpoints */ + char *fill; /* Memory fill cmd (addr len val) */ + struct memrw_cmd setmem; /* set memory to a value */ + struct memrw_cmd getmem; /* display memory */ + struct regrw_cmd setreg; /* set a register */ + struct regrw_cmd getreg; /* get a register */ + /* Some commands can dump a bunch of registers + at once. This comes as a set of REG=VAL + pairs. This should be called for each pair + of registers that we can parse to supply + GDB with the value of a register. */ + char *dump_registers; /* Command to dump all regs at once */ + char *register_pattern; /* Pattern that picks out register from reg dump */ + void (*supply_register) (char *name, int namelen, char *val, int vallen); + void (*load_routine) (struct serial *desc, char *file, + int hashmark); /* Download routine */ + int (*dumpregs) (void); /* routine to dump all registers */ + int (*continue_hook) (void); /* Emit the continue command */ + int (*wait_filter) (char *buf, /* Maybe contains registers */ + int bufmax, + int *response_length, + struct target_waitstatus * status); + char *load; /* load command */ + char *loadresp; /* Response to load command */ + char *prompt; /* monitor command prompt */ + char *line_term; /* end-of-command delimitor */ + char *cmd_end; /* optional command terminator */ + struct target_ops *target; /* target operations */ + int stopbits; /* number of stop bits */ + char **regnames; /* array of register names in ascii */ + /* deprecated: use regname instead */ + const char *(*regname) (int index); + /* function for dynamic regname array */ + int num_breakpoints; /* If set_break != NULL, number of supported + breakpoints */ + int magic; /* Check value */ + }; + +/* The monitor ops magic number, used to detect if an ops structure doesn't + have the right number of entries filled in. */ + +#define MONITOR_OPS_MAGIC 600925 + +/* Flag definitions. */ + +/* If set, then clear breakpoint command uses address, otherwise it + uses an index returned by the monitor. */ + +#define MO_CLR_BREAK_USES_ADDR 0x1 + +/* If set, then memory fill command uses STARTADDR, ENDADDR+1, VALUE + as args, else it uses STARTADDR, LENGTH, VALUE as args. */ + +#define MO_FILL_USES_ADDR 0x2 + +/* If set, then monitor doesn't automatically supply register dump + when coming back after a continue. */ + +#define MO_NEED_REGDUMP_AFTER_CONT 0x4 + +/* getmem needs start addr and end addr */ + +#define MO_GETMEM_NEEDS_RANGE 0x8 + +/* getmem can only read one loc at a time */ + +#define MO_GETMEM_READ_SINGLE 0x10 + +/* handle \r\n combinations */ + +#define MO_HANDLE_NL 0x20 + +/* don't expect echos in monitor_open */ + +#define MO_NO_ECHO_ON_OPEN 0x40 + +/* If set, send break to stop monitor */ + +#define MO_SEND_BREAK_ON_STOP 0x80 + +/* If set, target sends an ACK after each S-record */ + +#define MO_SREC_ACK 0x100 + +/* Allow 0x prefix on addresses retured from monitor */ + +#define MO_HEX_PREFIX 0x200 + +/* Some monitors require a different command when starting a program */ + +#define MO_RUN_FIRST_TIME 0x400 + +/* Don't expect echos when getting memory */ + +#define MO_NO_ECHO_ON_SETMEM 0x800 + +/* If set, then register store command expects value BEFORE regname */ + +#define MO_REGISTER_VALUE_FIRST 0x1000 + +/* If set, then the monitor displays registers as pairs. */ + +#define MO_32_REGS_PAIRED 0x2000 + +/* If set, then register setting happens interactively. */ + +#define MO_SETREG_INTERACTIVE 0x4000 + +/* If set, then memory setting happens interactively. */ + +#define MO_SETMEM_INTERACTIVE 0x8000 + +/* If set, then memory dumps are always on 16-byte boundaries, even + when less is desired. */ + +#define MO_GETMEM_16_BOUNDARY 0x10000 + +/* If set, then the monitor numbers its breakpoints starting from 1. */ + +#define MO_CLR_BREAK_1_BASED 0x20000 + +/* If set, then the monitor acks srecords with a plus sign. */ + +#define MO_SREC_ACK_PLUS 0x40000 + +/* If set, then the monitor "acks" srecords with rotating lines. */ + +#define MO_SREC_ACK_ROTATE 0x80000 + +/* If set, then remove useless address bits from memory addresses. */ + +#define MO_ADDR_BITS_REMOVE 0x100000 + +/* If set, then display target program output if prefixed by ^O. */ + +#define MO_PRINT_PROGRAM_OUTPUT 0x200000 + +/* Some dump bytes commands align the first data with the preceeding + 16 byte boundary. Some print blanks and start at the exactly the + requested boundary. */ + +#define MO_EXACT_DUMPADDR 0x400000 + +/* Rather entering and exiting the write memory dialog for each word byte, + we can save time by transferring the whole block without exiting + the memory editing mode. You only need to worry about this + if you are doing memory downloading. + This engages a new write function registered with dcache. + */ +#define MO_HAS_BLOCKWRITES 0x800000 + +#define SREC_SIZE 160 + +extern void monitor_open (char *args, struct monitor_ops *ops, int from_tty); +extern void monitor_close (int quitting); +extern char *monitor_supply_register (int regno, char *valstr); +extern int monitor_expect (char *prompt, char *buf, int buflen); +extern int monitor_expect_prompt (char *buf, int buflen); +/* Note: The variable argument functions monitor_printf and + monitor_printf_noecho vararg do not take take standard format style + arguments. Instead they take custom formats interpretered directly + by monitor_vsprintf. */ +extern void monitor_printf (char *, ...); +extern void monitor_printf_noecho (char *, ...); +extern void monitor_write (char *buf, int buflen); +extern int monitor_readchar (void); +extern char *monitor_get_dev_name (void); +extern void init_monitor_ops (struct target_ops *); +extern int monitor_dump_reg_block (char *dump_cmd); + +#endif diff --git a/contrib/gdb/gdb/ppcbug-rom.c b/contrib/gdb/gdb/ppcbug-rom.c new file mode 100644 index 0000000..0619964 --- /dev/null +++ b/contrib/gdb/gdb/ppcbug-rom.c @@ -0,0 +1,225 @@ +/* Remote debugging interface for PPCbug (PowerPC) Rom monitor + for GDB, the GNU debugger. + Copyright 1995, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + + Written by Stu Grossman of Cygnus Support + + 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 "defs.h" +#include "gdbcore.h" +#include "target.h" +#include "monitor.h" +#include "serial.h" +#include "regcache.h" + +static void +ppcbug_supply_register (char *regname, int regnamelen, char *val, int vallen) +{ + int regno = 0; + + if (regnamelen < 2 || regnamelen > 4) + return; + + switch (regname[0]) + { + case 'R': + if (regname[1] < '0' || regname[1] > '9') + return; + if (regnamelen == 2) + regno = regname[1] - '0'; + else if (regnamelen == 3 && regname[2] >= '0' && regname[2] <= '9') + regno = (regname[1] - '0') * 10 + (regname[2] - '0'); + else + return; + break; + case 'F': + if (regname[1] != 'R' || regname[2] < '0' || regname[2] > '9') + return; + if (regnamelen == 3) + regno = 32 + regname[2] - '0'; + else if (regnamelen == 4 && regname[3] >= '0' && regname[3] <= '9') + regno = 32 + (regname[2] - '0') * 10 + (regname[3] - '0'); + else + return; + break; + case 'I': + if (regnamelen != 2 || regname[1] != 'P') + return; + regno = 64; + break; + case 'M': + if (regnamelen != 3 || regname[1] != 'S' || regname[2] != 'R') + return; + regno = 65; + break; + case 'C': + if (regnamelen != 2 || regname[1] != 'R') + return; + regno = 66; + break; + case 'S': + if (regnamelen != 4 || regname[1] != 'P' || regname[2] != 'R') + return; + else if (regname[3] == '8') + regno = 67; + else if (regname[3] == '9') + regno = 68; + else if (regname[3] == '1') + regno = 69; + else if (regname[3] == '0') + regno = 70; + else + return; + break; + default: + return; + } + + monitor_supply_register (regno, val); +} + +/* + * This array of registers needs to match the indexes used by GDB. The + * whole reason this exists is because the various ROM monitors use + * different names than GDB does, and don't support all the + * registers either. So, typing "info reg sp" becomes an "A7". + */ + +static char *ppcbug_regnames[] = +{ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + + "fr0", "fr1", "fr2", "fr3", "fr4", "fr5", "fr6", "fr7", + "fr8", "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15", + "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", "fr22", "fr23", + "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", "fr30", "fr31", + +/* pc ps cnd lr cnt xer mq */ + "ip", "msr", "cr", "spr8", "spr9", "spr1", "spr0" +}; + +/* + * Define the monitor command strings. Since these are passed directly + * through to a printf style function, we need can include formatting + * strings. We also need a CR or LF on the end. + */ + +static struct target_ops ppcbug_ops0; +static struct target_ops ppcbug_ops1; + +static char *ppcbug_inits[] = +{"\r", NULL}; + +static void +init_ppc_cmds (char *LOAD_CMD, + struct monitor_ops *OPS, + struct target_ops *targops) +{ + OPS->flags = MO_CLR_BREAK_USES_ADDR | MO_HANDLE_NL; + OPS->init = ppcbug_inits; /* Init strings */ + OPS->cont = "g\r"; /* continue command */ + OPS->step = "t\r"; /* single step */ + OPS->stop = NULL; /* interrupt command */ + OPS->set_break = "br %x\r"; /* set a breakpoint */ + OPS->clr_break = "nobr %x\r"; /* clear a breakpoint */ + OPS->clr_all_break = "nobr\r"; /* clear all breakpoints */ + OPS->fill = "bf %x:%x %x;b\r"; /* fill (start count val) */ + OPS->setmem.cmdb = "ms %x %02x\r"; /* setmem.cmdb (addr, value) */ + OPS->setmem.cmdw = "ms %x %04x\r"; /* setmem.cmdw (addr, value) */ + OPS->setmem.cmdl = "ms %x %08x\r"; /* setmem.cmdl (addr, value) */ + OPS->setmem.cmdll = NULL; /* setmem.cmdll (addr, value) */ + OPS->setmem.resp_delim = NULL; /* setreg.resp_delim */ + OPS->setmem.term = NULL; /* setreg.term */ + OPS->setmem.term_cmd = NULL; /* setreg.term_cmd */ + OPS->getmem.cmdb = "md %x:%x;b\r"; /* getmem.cmdb (addr, len) */ + OPS->getmem.cmdw = "md %x:%x;b\r"; /* getmem.cmdw (addr, len) */ + OPS->getmem.cmdl = "md %x:%x;b\r"; /* getmem.cmdl (addr, len) */ + OPS->getmem.cmdll = NULL; /* getmem.cmdll (addr, len) */ + OPS->getmem.resp_delim = " "; /* getmem.resp_delim */ + OPS->getmem.term = NULL; /* getmem.term */ + OPS->getmem.term_cmd = NULL; /* getmem.term_cmd */ + OPS->setreg.cmd = "rs %s %x\r"; /* setreg.cmd (name, value) */ + OPS->setreg.resp_delim = NULL; /* setreg.resp_delim */ + OPS->setreg.term = NULL; /* setreg.term */ + OPS->setreg.term_cmd = NULL; /* setreg.term_cmd */ + OPS->getreg.cmd = "rs %s\r"; /* getreg.cmd (name) */ + OPS->getreg.resp_delim = "="; /* getreg.resp_delim */ + OPS->getreg.term = NULL; /* getreg.term */ + OPS->getreg.term_cmd = NULL; /* getreg.term_cmd */ + OPS->register_pattern = "\\(\\w+\\) +=\\([0-9a-fA-F]+\\b\\)"; /* register_pattern */ + OPS->supply_register = ppcbug_supply_register; /* supply_register */ + OPS->dump_registers = "rd\r"; /* dump all registers */ + OPS->load_routine = NULL; /* load_routine (defaults to SRECs) */ + OPS->load = LOAD_CMD; /* download command */ + OPS->loadresp = NULL; /* load response */ + OPS->prompt = "PPC1-Bug>"; /* monitor command prompt */ + OPS->line_term = "\r"; /* end-of-line terminator */ + OPS->cmd_end = NULL; /* optional command terminator */ + OPS->target = targops; /* target operations */ + OPS->stopbits = SERIAL_1_STOPBITS; /* number of stop bits */ + OPS->regnames = ppcbug_regnames; /* registers names */ + OPS->magic = MONITOR_OPS_MAGIC; /* magic */ +} + + +static struct monitor_ops ppcbug_cmds0; +static struct monitor_ops ppcbug_cmds1; + +static void +ppcbug_open0 (char *args, int from_tty) +{ + monitor_open (args, &ppcbug_cmds0, from_tty); +} + +static void +ppcbug_open1 (char *args, int from_tty) +{ + monitor_open (args, &ppcbug_cmds1, from_tty); +} + +extern initialize_file_ftype _initialize_ppcbug_rom; /* -Wmissing-prototypes */ + +void +_initialize_ppcbug_rom (void) +{ + init_ppc_cmds ("lo 0\r", &ppcbug_cmds0, &ppcbug_ops0); + init_ppc_cmds ("lo 1\r", &ppcbug_cmds1, &ppcbug_ops1); + init_monitor_ops (&ppcbug_ops0); + + ppcbug_ops0.to_shortname = "ppcbug"; + ppcbug_ops0.to_longname = "PowerPC PPCBug monitor on port 0"; + ppcbug_ops0.to_doc = "Debug via the PowerPC PPCBug monitor using port 0.\n\ +Specify the serial device it is connected to (e.g. /dev/ttya)."; + ppcbug_ops0.to_open = ppcbug_open0; + + add_target (&ppcbug_ops0); + + init_monitor_ops (&ppcbug_ops1); + + ppcbug_ops1.to_shortname = "ppcbug1"; + ppcbug_ops1.to_longname = "PowerPC PPCBug monitor on port 1"; + ppcbug_ops1.to_doc = "Debug via the PowerPC PPCBug monitor using port 1.\n\ +Specify the serial device it is connected to (e.g. /dev/ttya)."; + ppcbug_ops1.to_open = ppcbug_open1; + + add_target (&ppcbug_ops1); +} diff --git a/contrib/gdb/gdb/procfs.c b/contrib/gdb/gdb/procfs.c new file mode 100644 index 0000000..352b735 --- /dev/null +++ b/contrib/gdb/gdb/procfs.c @@ -0,0 +1,5960 @@ +/* Machine independent support for SVR4 /proc (process file system) for GDB. + + Copyright 1999, 2000, 2001, 2002, 2003 Free Software Foundation, + Inc. + + Written by Michael Snyder at Cygnus Solutions. + Based on work by Fred Fish, Stu Grossman, Geoff Noer, and others. + +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 "defs.h" +#include "inferior.h" +#include "target.h" +#include "gdbcore.h" +#include "elf-bfd.h" /* for elfcore_write_* */ +#include "gdbcmd.h" +#include "gdbthread.h" + +#if defined (NEW_PROC_API) +#define _STRUCTURED_PROC 1 /* Should be done by configure script. */ +#endif + +#include <sys/procfs.h> +#ifdef HAVE_SYS_FAULT_H +#include <sys/fault.h> +#endif +#ifdef HAVE_SYS_SYSCALL_H +#include <sys/syscall.h> +#endif +#include <sys/errno.h> +#include "gdb_wait.h" +#include <signal.h> +#include <ctype.h> +#include "gdb_string.h" +#include "gdb_assert.h" +#include "inflow.h" +#include "auxv.h" + +/* + * PROCFS.C + * + * This module provides the interface between GDB and the + * /proc file system, which is used on many versions of Unix + * as a means for debuggers to control other processes. + * Examples of the systems that use this interface are: + * Irix + * Solaris + * OSF + * Unixware + * AIX5 + * + * /proc works by imitating a file system: you open a simulated file + * that represents the process you wish to interact with, and + * perform operations on that "file" in order to examine or change + * the state of the other process. + * + * The most important thing to know about /proc and this module + * is that there are two very different interfaces to /proc: + * One that uses the ioctl system call, and + * another that uses read and write system calls. + * This module has to support both /proc interfaces. This means + * that there are two different ways of doing every basic operation. + * + * In order to keep most of the code simple and clean, I have + * defined an interface "layer" which hides all these system calls. + * An ifdef (NEW_PROC_API) determines which interface we are using, + * and most or all occurrances of this ifdef should be confined to + * this interface layer. + */ + + +/* Determine which /proc API we are using: + The ioctl API defines PIOCSTATUS, while + the read/write (multiple fd) API never does. */ + +#ifdef NEW_PROC_API +#include <sys/types.h> +#include "gdb_dirent.h" /* opendir/readdir, for listing the LWP's */ +#endif + +#include <fcntl.h> /* for O_RDONLY */ +#include <unistd.h> /* for "X_OK" */ +#include "gdb_stat.h" /* for struct stat */ + +/* Note: procfs-utils.h must be included after the above system header + files, because it redefines various system calls using macros. + This may be incompatible with the prototype declarations. */ + +#include "proc-utils.h" + +/* Prototypes for supply_gregset etc. */ +#include "gregset.h" + +/* =================== TARGET_OPS "MODULE" =================== */ + +/* + * This module defines the GDB target vector and its methods. + */ + +static void procfs_open (char *, int); +static void procfs_attach (char *, int); +static void procfs_detach (char *, int); +static void procfs_resume (ptid_t, int, enum target_signal); +static int procfs_can_run (void); +static void procfs_stop (void); +static void procfs_files_info (struct target_ops *); +static void procfs_fetch_registers (int); +static void procfs_store_registers (int); +static void procfs_notice_signals (ptid_t); +static void procfs_prepare_to_store (void); +static void procfs_kill_inferior (void); +static void procfs_mourn_inferior (void); +static void procfs_create_inferior (char *, char *, char **); +static ptid_t procfs_wait (ptid_t, struct target_waitstatus *); +static int procfs_xfer_memory (CORE_ADDR, char *, int, int, + struct mem_attrib *attrib, + struct target_ops *); +static LONGEST procfs_xfer_partial (struct target_ops *ops, + enum target_object object, + const char *annex, + void *readbuf, const void *writebuf, + ULONGEST offset, LONGEST len); + +static int procfs_thread_alive (ptid_t); + +void procfs_find_new_threads (void); +char *procfs_pid_to_str (ptid_t); + +static int proc_find_memory_regions (int (*) (CORE_ADDR, + unsigned long, + int, int, int, + void *), + void *); + +static char * procfs_make_note_section (bfd *, int *); + +static int procfs_can_use_hw_breakpoint (int, int, int); + +struct target_ops procfs_ops; /* the target vector */ + +static void +init_procfs_ops (void) +{ + procfs_ops.to_shortname = "procfs"; + procfs_ops.to_longname = "Unix /proc child process"; + procfs_ops.to_doc = + "Unix /proc child process (started by the \"run\" command)."; + procfs_ops.to_open = procfs_open; + procfs_ops.to_can_run = procfs_can_run; + procfs_ops.to_create_inferior = procfs_create_inferior; + procfs_ops.to_kill = procfs_kill_inferior; + procfs_ops.to_mourn_inferior = procfs_mourn_inferior; + procfs_ops.to_attach = procfs_attach; + procfs_ops.to_detach = procfs_detach; + procfs_ops.to_wait = procfs_wait; + procfs_ops.to_resume = procfs_resume; + procfs_ops.to_prepare_to_store = procfs_prepare_to_store; + procfs_ops.to_fetch_registers = procfs_fetch_registers; + procfs_ops.to_store_registers = procfs_store_registers; + procfs_ops.to_xfer_partial = procfs_xfer_partial; + procfs_ops.to_xfer_memory = procfs_xfer_memory; + procfs_ops.to_insert_breakpoint = memory_insert_breakpoint; + procfs_ops.to_remove_breakpoint = memory_remove_breakpoint; + procfs_ops.to_notice_signals = procfs_notice_signals; + procfs_ops.to_files_info = procfs_files_info; + procfs_ops.to_stop = procfs_stop; + + procfs_ops.to_terminal_init = terminal_init_inferior; + procfs_ops.to_terminal_inferior = terminal_inferior; + procfs_ops.to_terminal_ours_for_output = terminal_ours_for_output; + procfs_ops.to_terminal_ours = terminal_ours; + procfs_ops.to_terminal_save_ours = terminal_save_ours; + procfs_ops.to_terminal_info = child_terminal_info; + + procfs_ops.to_find_new_threads = procfs_find_new_threads; + procfs_ops.to_thread_alive = procfs_thread_alive; + procfs_ops.to_pid_to_str = procfs_pid_to_str; + + procfs_ops.to_has_all_memory = 1; + procfs_ops.to_has_memory = 1; + procfs_ops.to_has_execution = 1; + procfs_ops.to_has_stack = 1; + procfs_ops.to_has_registers = 1; + procfs_ops.to_stratum = process_stratum; + procfs_ops.to_has_thread_control = tc_schedlock; + procfs_ops.to_find_memory_regions = proc_find_memory_regions; + procfs_ops.to_make_corefile_notes = procfs_make_note_section; + procfs_ops.to_can_use_hw_breakpoint = procfs_can_use_hw_breakpoint; + procfs_ops.to_magic = OPS_MAGIC; +} + +/* =================== END, TARGET_OPS "MODULE" =================== */ + +/* + * World Unification: + * + * Put any typedefs, defines etc. here that are required for + * the unification of code that handles different versions of /proc. + */ + +#ifdef NEW_PROC_API /* Solaris 7 && 8 method for watchpoints */ +#ifdef WA_READ + enum { READ_WATCHFLAG = WA_READ, + WRITE_WATCHFLAG = WA_WRITE, + EXEC_WATCHFLAG = WA_EXEC, + AFTER_WATCHFLAG = WA_TRAPAFTER + }; +#endif +#else /* Irix method for watchpoints */ + enum { READ_WATCHFLAG = MA_READ, + WRITE_WATCHFLAG = MA_WRITE, + EXEC_WATCHFLAG = MA_EXEC, + AFTER_WATCHFLAG = 0 /* trapafter not implemented */ + }; +#endif + +/* gdb_sigset_t */ +#ifdef HAVE_PR_SIGSET_T +typedef pr_sigset_t gdb_sigset_t; +#else +typedef sigset_t gdb_sigset_t; +#endif + +/* sigaction */ +#ifdef HAVE_PR_SIGACTION64_T +typedef pr_sigaction64_t gdb_sigaction_t; +#else +typedef struct sigaction gdb_sigaction_t; +#endif + +/* siginfo */ +#ifdef HAVE_PR_SIGINFO64_T +typedef pr_siginfo64_t gdb_siginfo_t; +#else +typedef struct siginfo gdb_siginfo_t; +#endif + +/* gdb_premptysysset */ +#ifdef premptysysset +#define gdb_premptysysset premptysysset +#else +#define gdb_premptysysset premptyset +#endif + +/* praddsysset */ +#ifdef praddsysset +#define gdb_praddsysset praddsysset +#else +#define gdb_praddsysset praddset +#endif + +/* prdelsysset */ +#ifdef prdelsysset +#define gdb_prdelsysset prdelsysset +#else +#define gdb_prdelsysset prdelset +#endif + +/* prissyssetmember */ +#ifdef prissyssetmember +#define gdb_pr_issyssetmember prissyssetmember +#else +#define gdb_pr_issyssetmember prismember +#endif + +/* As a feature test, saying ``#if HAVE_PRSYSENT_T'' everywhere isn't + as intuitively descriptive as it could be, so we'll define + DYNAMIC_SYSCALLS to mean the same thing. Anyway, at the time of + this writing, this feature is only found on AIX5 systems and + basically means that the set of syscalls is not fixed. I.e, + there's no nice table that one can #include to get all of the + syscall numbers. Instead, they're stored in /proc/PID/sysent + for each process. We are at least guaranteed that they won't + change over the lifetime of the process. But each process could + (in theory) have different syscall numbers. +*/ +#ifdef HAVE_PRSYSENT_T +#define DYNAMIC_SYSCALLS +#endif + + + +/* =================== STRUCT PROCINFO "MODULE" =================== */ + + /* FIXME: this comment will soon be out of date W.R.T. threads. */ + +/* The procinfo struct is a wrapper to hold all the state information + concerning a /proc process. There should be exactly one procinfo + for each process, and since GDB currently can debug only one + process at a time, that means there should be only one procinfo. + All of the LWP's of a process can be accessed indirectly thru the + single process procinfo. + + However, against the day when GDB may debug more than one process, + this data structure is kept in a list (which for now will hold no + more than one member), and many functions will have a pointer to a + procinfo as an argument. + + There will be a separate procinfo structure for use by the (not yet + implemented) "info proc" command, so that we can print useful + information about any random process without interfering with the + inferior's procinfo information. */ + +#ifdef NEW_PROC_API +/* format strings for /proc paths */ +# ifndef CTL_PROC_NAME_FMT +# define MAIN_PROC_NAME_FMT "/proc/%d" +# define CTL_PROC_NAME_FMT "/proc/%d/ctl" +# define AS_PROC_NAME_FMT "/proc/%d/as" +# define MAP_PROC_NAME_FMT "/proc/%d/map" +# define STATUS_PROC_NAME_FMT "/proc/%d/status" +# define MAX_PROC_NAME_SIZE sizeof("/proc/99999/lwp/8096/lstatus") +# endif +/* the name of the proc status struct depends on the implementation */ +typedef pstatus_t gdb_prstatus_t; +typedef lwpstatus_t gdb_lwpstatus_t; +#else /* ! NEW_PROC_API */ +/* format strings for /proc paths */ +# ifndef CTL_PROC_NAME_FMT +# define MAIN_PROC_NAME_FMT "/proc/%05d" +# define CTL_PROC_NAME_FMT "/proc/%05d" +# define AS_PROC_NAME_FMT "/proc/%05d" +# define MAP_PROC_NAME_FMT "/proc/%05d" +# define STATUS_PROC_NAME_FMT "/proc/%05d" +# define MAX_PROC_NAME_SIZE sizeof("/proc/ttttppppp") +# endif +/* the name of the proc status struct depends on the implementation */ +typedef prstatus_t gdb_prstatus_t; +typedef prstatus_t gdb_lwpstatus_t; +#endif /* NEW_PROC_API */ + +typedef struct procinfo { + struct procinfo *next; + int pid; /* Process ID */ + int tid; /* Thread/LWP id */ + + /* process state */ + int was_stopped; + int ignore_next_sigstop; + + /* The following four fd fields may be identical, or may contain + several different fd's, depending on the version of /proc + (old ioctl or new read/write). */ + + int ctl_fd; /* File descriptor for /proc control file */ + /* + * The next three file descriptors are actually only needed in the + * read/write, multiple-file-descriptor implemenation (NEW_PROC_API). + * However, to avoid a bunch of #ifdefs in the code, we will use + * them uniformly by (in the case of the ioctl single-file-descriptor + * implementation) filling them with copies of the control fd. + */ + int status_fd; /* File descriptor for /proc status file */ + int as_fd; /* File descriptor for /proc as file */ + + char pathname[MAX_PROC_NAME_SIZE]; /* Pathname to /proc entry */ + + fltset_t saved_fltset; /* Saved traced hardware fault set */ + gdb_sigset_t saved_sigset; /* Saved traced signal set */ + gdb_sigset_t saved_sighold; /* Saved held signal set */ + sysset_t *saved_exitset; /* Saved traced system call exit set */ + sysset_t *saved_entryset; /* Saved traced system call entry set */ + + gdb_prstatus_t prstatus; /* Current process status info */ + +#ifndef NEW_PROC_API + gdb_fpregset_t fpregset; /* Current floating point registers */ +#endif + +#ifdef DYNAMIC_SYSCALLS + int num_syscalls; /* Total number of syscalls */ + char **syscall_names; /* Syscall number to name map */ +#endif + + struct procinfo *thread_list; + + int status_valid : 1; + int gregs_valid : 1; + int fpregs_valid : 1; + int threads_valid: 1; +} procinfo; + +static char errmsg[128]; /* shared error msg buffer */ + +/* Function prototypes for procinfo module: */ + +static procinfo *find_procinfo_or_die (int pid, int tid); +static procinfo *find_procinfo (int pid, int tid); +static procinfo *create_procinfo (int pid, int tid); +static void destroy_procinfo (procinfo * p); +static void do_destroy_procinfo_cleanup (void *); +static void dead_procinfo (procinfo * p, char *msg, int killp); +static int open_procinfo_files (procinfo * p, int which); +static void close_procinfo_files (procinfo * p); +static int sysset_t_size (procinfo *p); +static sysset_t *sysset_t_alloc (procinfo * pi); +#ifdef DYNAMIC_SYSCALLS +static void load_syscalls (procinfo *pi); +static void free_syscalls (procinfo *pi); +static int find_syscall (procinfo *pi, char *name); +#endif /* DYNAMIC_SYSCALLS */ + +/* The head of the procinfo list: */ +static procinfo * procinfo_list; + +/* + * Function: find_procinfo + * + * Search the procinfo list. + * + * Returns: pointer to procinfo, or NULL if not found. + */ + +static procinfo * +find_procinfo (int pid, int tid) +{ + procinfo *pi; + + for (pi = procinfo_list; pi; pi = pi->next) + if (pi->pid == pid) + break; + + if (pi) + if (tid) + { + /* Don't check threads_valid. If we're updating the + thread_list, we want to find whatever threads are already + here. This means that in general it is the caller's + responsibility to check threads_valid and update before + calling find_procinfo, if the caller wants to find a new + thread. */ + + for (pi = pi->thread_list; pi; pi = pi->next) + if (pi->tid == tid) + break; + } + + return pi; +} + +/* + * Function: find_procinfo_or_die + * + * Calls find_procinfo, but errors on failure. + */ + +static procinfo * +find_procinfo_or_die (int pid, int tid) +{ + procinfo *pi = find_procinfo (pid, tid); + + if (pi == NULL) + { + if (tid) + error ("procfs: couldn't find pid %d (kernel thread %d) in procinfo list.", + pid, tid); + else + error ("procfs: couldn't find pid %d in procinfo list.", pid); + } + return pi; +} + +/* open_with_retry() is a wrapper for open(). The appropriate + open() call is attempted; if unsuccessful, it will be retried as + many times as needed for the EAGAIN and EINTR conditions. + + For other conditions, open_with_retry() will retry the open() a + limited number of times. In addition, a short sleep is imposed + prior to retrying the open(). The reason for this sleep is to give + the kernel a chance to catch up and create the file in question in + the event that GDB "wins" the race to open a file before the kernel + has created it. */ + +static int +open_with_retry (const char *pathname, int flags) +{ + int retries_remaining, status; + + retries_remaining = 2; + + while (1) + { + status = open (pathname, flags); + + if (status >= 0 || retries_remaining == 0) + break; + else if (errno != EINTR && errno != EAGAIN) + { + retries_remaining--; + sleep (1); + } + } + + return status; +} + +/* + * Function: open_procinfo_files + * + * Open the file descriptor for the process or LWP. + * ifdef NEW_PROC_API, we only open the control file descriptor; + * the others are opened lazily as needed. + * else (if not NEW_PROC_API), there is only one real + * file descriptor, but we keep multiple copies of it so that + * the code that uses them does not have to be #ifdef'd. + * + * Return: file descriptor, or zero for failure. + */ + +enum { FD_CTL, FD_STATUS, FD_AS }; + +static int +open_procinfo_files (procinfo *pi, int which) +{ +#ifdef NEW_PROC_API + char tmp[MAX_PROC_NAME_SIZE]; +#endif + int fd; + + /* + * This function is getting ALMOST long enough to break up into several. + * Here is some rationale: + * + * NEW_PROC_API (Solaris 2.6, Solaris 2.7, Unixware): + * There are several file descriptors that may need to be open + * for any given process or LWP. The ones we're intereted in are: + * - control (ctl) write-only change the state + * - status (status) read-only query the state + * - address space (as) read/write access memory + * - map (map) read-only virtual addr map + * Most of these are opened lazily as they are needed. + * The pathnames for the 'files' for an LWP look slightly + * different from those of a first-class process: + * Pathnames for a process (<proc-id>): + * /proc/<proc-id>/ctl + * /proc/<proc-id>/status + * /proc/<proc-id>/as + * /proc/<proc-id>/map + * Pathnames for an LWP (lwp-id): + * /proc/<proc-id>/lwp/<lwp-id>/lwpctl + * /proc/<proc-id>/lwp/<lwp-id>/lwpstatus + * An LWP has no map or address space file descriptor, since + * the memory map and address space are shared by all LWPs. + * + * Everyone else (Solaris 2.5, Irix, OSF) + * There is only one file descriptor for each process or LWP. + * For convenience, we copy the same file descriptor into all + * three fields of the procinfo struct (ctl_fd, status_fd, and + * as_fd, see NEW_PROC_API above) so that code that uses them + * doesn't need any #ifdef's. + * Pathname for all: + * /proc/<proc-id> + * + * Solaris 2.5 LWP's: + * Each LWP has an independent file descriptor, but these + * are not obtained via the 'open' system call like the rest: + * instead, they're obtained thru an ioctl call (PIOCOPENLWP) + * to the file descriptor of the parent process. + * + * OSF threads: + * These do not even have their own independent file descriptor. + * All operations are carried out on the file descriptor of the + * parent process. Therefore we just call open again for each + * thread, getting a new handle for the same 'file'. + */ + +#ifdef NEW_PROC_API + /* + * In this case, there are several different file descriptors that + * we might be asked to open. The control file descriptor will be + * opened early, but the others will be opened lazily as they are + * needed. + */ + + strcpy (tmp, pi->pathname); + switch (which) { /* which file descriptor to open? */ + case FD_CTL: + if (pi->tid) + strcat (tmp, "/lwpctl"); + else + strcat (tmp, "/ctl"); + fd = open_with_retry (tmp, O_WRONLY); + if (fd <= 0) + return 0; /* fail */ + pi->ctl_fd = fd; + break; + case FD_AS: + if (pi->tid) + return 0; /* there is no 'as' file descriptor for an lwp */ + strcat (tmp, "/as"); + fd = open_with_retry (tmp, O_RDWR); + if (fd <= 0) + return 0; /* fail */ + pi->as_fd = fd; + break; + case FD_STATUS: + if (pi->tid) + strcat (tmp, "/lwpstatus"); + else + strcat (tmp, "/status"); + fd = open_with_retry (tmp, O_RDONLY); + if (fd <= 0) + return 0; /* fail */ + pi->status_fd = fd; + break; + default: + return 0; /* unknown file descriptor */ + } +#else /* not NEW_PROC_API */ + /* + * In this case, there is only one file descriptor for each procinfo + * (ie. each process or LWP). In fact, only the file descriptor for + * the process can actually be opened by an 'open' system call. + * The ones for the LWPs have to be obtained thru an IOCTL call + * on the process's file descriptor. + * + * For convenience, we copy each procinfo's single file descriptor + * into all of the fields occupied by the several file descriptors + * of the NEW_PROC_API implementation. That way, the code that uses + * them can be written without ifdefs. + */ + + +#ifdef PIOCTSTATUS /* OSF */ + /* Only one FD; just open it. */ + if ((fd = open_with_retry (pi->pathname, O_RDWR)) == 0) + return 0; +#else /* Sol 2.5, Irix, other? */ + if (pi->tid == 0) /* Master procinfo for the process */ + { + fd = open_with_retry (pi->pathname, O_RDWR); + if (fd <= 0) + return 0; /* fail */ + } + else /* LWP thread procinfo */ + { +#ifdef PIOCOPENLWP /* Sol 2.5, thread/LWP */ + procinfo *process; + int lwpid = pi->tid; + + /* Find the procinfo for the entire process. */ + if ((process = find_procinfo (pi->pid, 0)) == NULL) + return 0; /* fail */ + + /* Now obtain the file descriptor for the LWP. */ + if ((fd = ioctl (process->ctl_fd, PIOCOPENLWP, &lwpid)) <= 0) + return 0; /* fail */ +#else /* Irix, other? */ + return 0; /* Don't know how to open threads */ +#endif /* Sol 2.5 PIOCOPENLWP */ + } +#endif /* OSF PIOCTSTATUS */ + pi->ctl_fd = pi->as_fd = pi->status_fd = fd; +#endif /* NEW_PROC_API */ + + return 1; /* success */ +} + +/* + * Function: create_procinfo + * + * Allocate a data structure and link it into the procinfo list. + * (First tries to find a pre-existing one (FIXME: why?) + * + * Return: pointer to new procinfo struct. + */ + +static procinfo * +create_procinfo (int pid, int tid) +{ + procinfo *pi, *parent; + + if ((pi = find_procinfo (pid, tid))) + return pi; /* Already exists, nothing to do. */ + + /* find parent before doing malloc, to save having to cleanup */ + if (tid != 0) + parent = find_procinfo_or_die (pid, 0); /* FIXME: should I + create it if it + doesn't exist yet? */ + + pi = (procinfo *) xmalloc (sizeof (procinfo)); + memset (pi, 0, sizeof (procinfo)); + pi->pid = pid; + pi->tid = tid; + +#ifdef DYNAMIC_SYSCALLS + load_syscalls (pi); +#endif + + pi->saved_entryset = sysset_t_alloc (pi); + pi->saved_exitset = sysset_t_alloc (pi); + + /* Chain into list. */ + if (tid == 0) + { + sprintf (pi->pathname, MAIN_PROC_NAME_FMT, pid); + pi->next = procinfo_list; + procinfo_list = pi; + } + else + { +#ifdef NEW_PROC_API + sprintf (pi->pathname, "/proc/%05d/lwp/%d", pid, tid); +#else + sprintf (pi->pathname, MAIN_PROC_NAME_FMT, pid); +#endif + pi->next = parent->thread_list; + parent->thread_list = pi; + } + return pi; +} + +/* + * Function: close_procinfo_files + * + * Close all file descriptors associated with the procinfo + */ + +static void +close_procinfo_files (procinfo *pi) +{ + if (pi->ctl_fd > 0) + close (pi->ctl_fd); +#ifdef NEW_PROC_API + if (pi->as_fd > 0) + close (pi->as_fd); + if (pi->status_fd > 0) + close (pi->status_fd); +#endif + pi->ctl_fd = pi->as_fd = pi->status_fd = 0; +} + +/* + * Function: destroy_procinfo + * + * Destructor function. Close, unlink and deallocate the object. + */ + +static void +destroy_one_procinfo (procinfo **list, procinfo *pi) +{ + procinfo *ptr; + + /* Step one: unlink the procinfo from its list */ + if (pi == *list) + *list = pi->next; + else + for (ptr = *list; ptr; ptr = ptr->next) + if (ptr->next == pi) + { + ptr->next = pi->next; + break; + } + + /* Step two: close any open file descriptors */ + close_procinfo_files (pi); + + /* Step three: free the memory. */ +#ifdef DYNAMIC_SYSCALLS + free_syscalls (pi); +#endif + xfree (pi->saved_entryset); + xfree (pi->saved_exitset); + xfree (pi); +} + +static void +destroy_procinfo (procinfo *pi) +{ + procinfo *tmp; + + if (pi->tid != 0) /* destroy a thread procinfo */ + { + tmp = find_procinfo (pi->pid, 0); /* find the parent process */ + destroy_one_procinfo (&tmp->thread_list, pi); + } + else /* destroy a process procinfo and all its threads */ + { + /* First destroy the children, if any; */ + while (pi->thread_list != NULL) + destroy_one_procinfo (&pi->thread_list, pi->thread_list); + /* Then destroy the parent. Genocide!!! */ + destroy_one_procinfo (&procinfo_list, pi); + } +} + +static void +do_destroy_procinfo_cleanup (void *pi) +{ + destroy_procinfo (pi); +} + +enum { NOKILL, KILL }; + +/* + * Function: dead_procinfo + * + * To be called on a non_recoverable error for a procinfo. + * Prints error messages, optionally sends a SIGKILL to the process, + * then destroys the data structure. + */ + +static void +dead_procinfo (procinfo *pi, char *msg, int kill_p) +{ + char procfile[80]; + + if (pi->pathname) + { + print_sys_errmsg (pi->pathname, errno); + } + else + { + sprintf (procfile, "process %d", pi->pid); + print_sys_errmsg (procfile, errno); + } + if (kill_p == KILL) + kill (pi->pid, SIGKILL); + + destroy_procinfo (pi); + error (msg); +} + +/* + * Function: sysset_t_size + * + * Returns the (complete) size of a sysset_t struct. Normally, this + * is just sizeof (syset_t), but in the case of Monterey/64, the actual + * size of sysset_t isn't known until runtime. + */ + +static int +sysset_t_size (procinfo * pi) +{ +#ifndef DYNAMIC_SYSCALLS + return sizeof (sysset_t); +#else + return sizeof (sysset_t) - sizeof (uint64_t) + + sizeof (uint64_t) * ((pi->num_syscalls + (8 * sizeof (uint64_t) - 1)) + / (8 * sizeof (uint64_t))); +#endif +} + +/* Function: sysset_t_alloc + + Allocate and (partially) initialize a sysset_t struct. */ + +static sysset_t * +sysset_t_alloc (procinfo * pi) +{ + sysset_t *ret; + int size = sysset_t_size (pi); + ret = xmalloc (size); +#ifdef DYNAMIC_SYSCALLS + ret->pr_size = (pi->num_syscalls + (8 * sizeof (uint64_t) - 1)) + / (8 * sizeof (uint64_t)); +#endif + return ret; +} + +#ifdef DYNAMIC_SYSCALLS + +/* Function: load_syscalls + + Extract syscall numbers and names from /proc/<pid>/sysent. Initialize + pi->num_syscalls with the number of syscalls and pi->syscall_names + with the names. (Certain numbers may be skipped in which case the + names for these numbers will be left as NULL.) */ + +#define MAX_SYSCALL_NAME_LENGTH 256 +#define MAX_SYSCALLS 65536 + +static void +load_syscalls (procinfo *pi) +{ + char pathname[MAX_PROC_NAME_SIZE]; + int sysent_fd; + prsysent_t header; + prsyscall_t *syscalls; + int i, size, maxcall; + + pi->num_syscalls = 0; + pi->syscall_names = 0; + + /* Open the file descriptor for the sysent file */ + sprintf (pathname, "/proc/%d/sysent", pi->pid); + sysent_fd = open_with_retry (pathname, O_RDONLY); + if (sysent_fd < 0) + { + error ("load_syscalls: Can't open /proc/%d/sysent", pi->pid); + } + + size = sizeof header - sizeof (prsyscall_t); + if (read (sysent_fd, &header, size) != size) + { + error ("load_syscalls: Error reading /proc/%d/sysent", pi->pid); + } + + if (header.pr_nsyscalls == 0) + { + error ("load_syscalls: /proc/%d/sysent contains no syscalls!", pi->pid); + } + + size = header.pr_nsyscalls * sizeof (prsyscall_t); + syscalls = xmalloc (size); + + if (read (sysent_fd, syscalls, size) != size) + { + xfree (syscalls); + error ("load_syscalls: Error reading /proc/%d/sysent", pi->pid); + } + + /* Find maximum syscall number. This may not be the same as + pr_nsyscalls since that value refers to the number of entries + in the table. (Also, the docs indicate that some system + call numbers may be skipped.) */ + + maxcall = syscalls[0].pr_number; + + for (i = 1; i < header.pr_nsyscalls; i++) + if (syscalls[i].pr_number > maxcall + && syscalls[i].pr_nameoff > 0 + && syscalls[i].pr_number < MAX_SYSCALLS) + maxcall = syscalls[i].pr_number; + + pi->num_syscalls = maxcall+1; + pi->syscall_names = xmalloc (pi->num_syscalls * sizeof (char *)); + + for (i = 0; i < pi->num_syscalls; i++) + pi->syscall_names[i] = NULL; + + /* Read the syscall names in */ + for (i = 0; i < header.pr_nsyscalls; i++) + { + char namebuf[MAX_SYSCALL_NAME_LENGTH]; + int nread; + int callnum; + + if (syscalls[i].pr_number >= MAX_SYSCALLS + || syscalls[i].pr_number < 0 + || syscalls[i].pr_nameoff <= 0 + || (lseek (sysent_fd, (off_t) syscalls[i].pr_nameoff, SEEK_SET) + != (off_t) syscalls[i].pr_nameoff)) + continue; + + nread = read (sysent_fd, namebuf, sizeof namebuf); + if (nread <= 0) + continue; + + callnum = syscalls[i].pr_number; + + if (pi->syscall_names[callnum] != NULL) + { + /* FIXME: Generate warning */ + continue; + } + + namebuf[nread-1] = '\0'; + size = strlen (namebuf) + 1; + pi->syscall_names[callnum] = xmalloc (size); + strncpy (pi->syscall_names[callnum], namebuf, size-1); + pi->syscall_names[callnum][size-1] = '\0'; + } + + close (sysent_fd); + xfree (syscalls); +} + +/* Function: free_syscalls + + Free the space allocated for the syscall names from the procinfo + structure. */ + +static void +free_syscalls (procinfo *pi) +{ + if (pi->syscall_names) + { + int i; + + for (i = 0; i < pi->num_syscalls; i++) + if (pi->syscall_names[i] != NULL) + xfree (pi->syscall_names[i]); + + xfree (pi->syscall_names); + pi->syscall_names = 0; + } +} + +/* Function: find_syscall + + Given a name, look up (and return) the corresponding syscall number. + If no match is found, return -1. */ + +static int +find_syscall (procinfo *pi, char *name) +{ + int i; + for (i = 0; i < pi->num_syscalls; i++) + { + if (pi->syscall_names[i] && strcmp (name, pi->syscall_names[i]) == 0) + return i; + } + return -1; +} +#endif + +/* =================== END, STRUCT PROCINFO "MODULE" =================== */ + +/* =================== /proc "MODULE" =================== */ + +/* + * This "module" is the interface layer between the /proc system API + * and the gdb target vector functions. This layer consists of + * access functions that encapsulate each of the basic operations + * that we need to use from the /proc API. + * + * The main motivation for this layer is to hide the fact that + * there are two very different implementations of the /proc API. + * Rather than have a bunch of #ifdefs all thru the gdb target vector + * functions, we do our best to hide them all in here. + */ + +int proc_get_status (procinfo * pi); +long proc_flags (procinfo * pi); +int proc_why (procinfo * pi); +int proc_what (procinfo * pi); +int proc_set_run_on_last_close (procinfo * pi); +int proc_unset_run_on_last_close (procinfo * pi); +int proc_set_inherit_on_fork (procinfo * pi); +int proc_unset_inherit_on_fork (procinfo * pi); +int proc_set_async (procinfo * pi); +int proc_unset_async (procinfo * pi); +int proc_stop_process (procinfo * pi); +int proc_trace_signal (procinfo * pi, int signo); +int proc_ignore_signal (procinfo * pi, int signo); +int proc_clear_current_fault (procinfo * pi); +int proc_set_current_signal (procinfo * pi, int signo); +int proc_clear_current_signal (procinfo * pi); +int proc_set_gregs (procinfo * pi); +int proc_set_fpregs (procinfo * pi); +int proc_wait_for_stop (procinfo * pi); +int proc_run_process (procinfo * pi, int step, int signo); +int proc_kill (procinfo * pi, int signo); +int proc_parent_pid (procinfo * pi); +int proc_get_nthreads (procinfo * pi); +int proc_get_current_thread (procinfo * pi); +int proc_set_held_signals (procinfo * pi, gdb_sigset_t * sighold); +int proc_set_traced_sysexit (procinfo * pi, sysset_t * sysset); +int proc_set_traced_sysentry (procinfo * pi, sysset_t * sysset); +int proc_set_traced_faults (procinfo * pi, fltset_t * fltset); +int proc_set_traced_signals (procinfo * pi, gdb_sigset_t * sigset); + +int proc_update_threads (procinfo * pi); +int proc_iterate_over_threads (procinfo * pi, + int (*func) (procinfo *, procinfo *, void *), + void *ptr); + +gdb_gregset_t *proc_get_gregs (procinfo * pi); +gdb_fpregset_t *proc_get_fpregs (procinfo * pi); +sysset_t *proc_get_traced_sysexit (procinfo * pi, sysset_t * save); +sysset_t *proc_get_traced_sysentry (procinfo * pi, sysset_t * save); +fltset_t *proc_get_traced_faults (procinfo * pi, fltset_t * save); +gdb_sigset_t *proc_get_traced_signals (procinfo * pi, gdb_sigset_t * save); +gdb_sigset_t *proc_get_held_signals (procinfo * pi, gdb_sigset_t * save); +gdb_sigset_t *proc_get_pending_signals (procinfo * pi, gdb_sigset_t * save); +gdb_sigaction_t *proc_get_signal_actions (procinfo * pi, gdb_sigaction_t *save); + +void proc_warn (procinfo * pi, char *func, int line); +void proc_error (procinfo * pi, char *func, int line); + +void +proc_warn (procinfo *pi, char *func, int line) +{ + sprintf (errmsg, "procfs: %s line %d, %s", func, line, pi->pathname); + print_sys_errmsg (errmsg, errno); +} + +void +proc_error (procinfo *pi, char *func, int line) +{ + sprintf (errmsg, "procfs: %s line %d, %s", func, line, pi->pathname); + perror_with_name (errmsg); +} + +/* + * Function: proc_get_status + * + * Updates the status struct in the procinfo. + * There is a 'valid' flag, to let other functions know when + * this function needs to be called (so the status is only + * read when it is needed). The status file descriptor is + * also only opened when it is needed. + * + * Return: non-zero for success, zero for failure. + */ + +int +proc_get_status (procinfo *pi) +{ + /* Status file descriptor is opened "lazily" */ + if (pi->status_fd == 0 && + open_procinfo_files (pi, FD_STATUS) == 0) + { + pi->status_valid = 0; + return 0; + } + +#ifdef NEW_PROC_API + if (lseek (pi->status_fd, 0, SEEK_SET) < 0) + pi->status_valid = 0; /* fail */ + else + { + /* Sigh... I have to read a different data structure, + depending on whether this is a main process or an LWP. */ + if (pi->tid) + pi->status_valid = (read (pi->status_fd, + (char *) &pi->prstatus.pr_lwp, + sizeof (lwpstatus_t)) + == sizeof (lwpstatus_t)); + else + { + pi->status_valid = (read (pi->status_fd, + (char *) &pi->prstatus, + sizeof (gdb_prstatus_t)) + == sizeof (gdb_prstatus_t)); +#if 0 /*def UNIXWARE*/ + if (pi->status_valid && + (pi->prstatus.pr_lwp.pr_flags & PR_ISTOP) && + pi->prstatus.pr_lwp.pr_why == PR_REQUESTED) + /* Unixware peculiarity -- read the damn thing again! */ + pi->status_valid = (read (pi->status_fd, + (char *) &pi->prstatus, + sizeof (gdb_prstatus_t)) + == sizeof (gdb_prstatus_t)); +#endif /* UNIXWARE */ + } + } +#else /* ioctl method */ +#ifdef PIOCTSTATUS /* osf */ + if (pi->tid == 0) /* main process */ + { + /* Just read the danged status. Now isn't that simple? */ + pi->status_valid = + (ioctl (pi->status_fd, PIOCSTATUS, &pi->prstatus) >= 0); + } + else + { + int win; + struct { + long pr_count; + tid_t pr_error_thread; + struct prstatus status; + } thread_status; + + thread_status.pr_count = 1; + thread_status.status.pr_tid = pi->tid; + win = (ioctl (pi->status_fd, PIOCTSTATUS, &thread_status) >= 0); + if (win) + { + memcpy (&pi->prstatus, &thread_status.status, + sizeof (pi->prstatus)); + pi->status_valid = 1; + } + } +#else + /* Just read the danged status. Now isn't that simple? */ + pi->status_valid = (ioctl (pi->status_fd, PIOCSTATUS, &pi->prstatus) >= 0); +#endif +#endif + + if (pi->status_valid) + { + PROC_PRETTYFPRINT_STATUS (proc_flags (pi), + proc_why (pi), + proc_what (pi), + proc_get_current_thread (pi)); + } + + /* The status struct includes general regs, so mark them valid too */ + pi->gregs_valid = pi->status_valid; +#ifdef NEW_PROC_API + /* In the read/write multiple-fd model, + the status struct includes the fp regs too, so mark them valid too */ + pi->fpregs_valid = pi->status_valid; +#endif + return pi->status_valid; /* True if success, false if failure. */ +} + +/* + * Function: proc_flags + * + * returns the process flags (pr_flags field). + */ + +long +proc_flags (procinfo *pi) +{ + if (!pi->status_valid) + if (!proc_get_status (pi)) + return 0; /* FIXME: not a good failure value (but what is?) */ + +#ifdef NEW_PROC_API +# ifdef UNIXWARE + /* UnixWare 7.1 puts process status flags, e.g. PR_ASYNC, in + pstatus_t and LWP status flags, e.g. PR_STOPPED, in lwpstatus_t. + The two sets of flags don't overlap. */ + return pi->prstatus.pr_flags | pi->prstatus.pr_lwp.pr_flags; +# else + return pi->prstatus.pr_lwp.pr_flags; +# endif +#else + return pi->prstatus.pr_flags; +#endif +} + +/* + * Function: proc_why + * + * returns the pr_why field (why the process stopped). + */ + +int +proc_why (procinfo *pi) +{ + if (!pi->status_valid) + if (!proc_get_status (pi)) + return 0; /* FIXME: not a good failure value (but what is?) */ + +#ifdef NEW_PROC_API + return pi->prstatus.pr_lwp.pr_why; +#else + return pi->prstatus.pr_why; +#endif +} + +/* + * Function: proc_what + * + * returns the pr_what field (details of why the process stopped). + */ + +int +proc_what (procinfo *pi) +{ + if (!pi->status_valid) + if (!proc_get_status (pi)) + return 0; /* FIXME: not a good failure value (but what is?) */ + +#ifdef NEW_PROC_API + return pi->prstatus.pr_lwp.pr_what; +#else + return pi->prstatus.pr_what; +#endif +} + +#ifndef PIOCSSPCACT /* The following is not supported on OSF. */ +/* + * Function: proc_nsysarg + * + * returns the pr_nsysarg field (number of args to the current syscall). + */ + +int +proc_nsysarg (procinfo *pi) +{ + if (!pi->status_valid) + if (!proc_get_status (pi)) + return 0; + +#ifdef NEW_PROC_API + return pi->prstatus.pr_lwp.pr_nsysarg; +#else + return pi->prstatus.pr_nsysarg; +#endif +} + +/* + * Function: proc_sysargs + * + * returns the pr_sysarg field (pointer to the arguments of current syscall). + */ + +long * +proc_sysargs (procinfo *pi) +{ + if (!pi->status_valid) + if (!proc_get_status (pi)) + return NULL; + +#ifdef NEW_PROC_API + return (long *) &pi->prstatus.pr_lwp.pr_sysarg; +#else + return (long *) &pi->prstatus.pr_sysarg; +#endif +} + +/* + * Function: proc_syscall + * + * returns the pr_syscall field (id of current syscall if we are in one). + */ + +int +proc_syscall (procinfo *pi) +{ + if (!pi->status_valid) + if (!proc_get_status (pi)) + return 0; + +#ifdef NEW_PROC_API + return pi->prstatus.pr_lwp.pr_syscall; +#else + return pi->prstatus.pr_syscall; +#endif +} +#endif /* PIOCSSPCACT */ + +/* + * Function: proc_cursig: + * + * returns the pr_cursig field (current signal). + */ + +long +proc_cursig (struct procinfo *pi) +{ + if (!pi->status_valid) + if (!proc_get_status (pi)) + return 0; /* FIXME: not a good failure value (but what is?) */ + +#ifdef NEW_PROC_API + return pi->prstatus.pr_lwp.pr_cursig; +#else + return pi->prstatus.pr_cursig; +#endif +} + +/* + * Function: proc_modify_flag + * + * === I appologize for the messiness of this function. + * === This is an area where the different versions of + * === /proc are more inconsistent than usual. MVS + * + * Set or reset any of the following process flags: + * PR_FORK -- forked child will inherit trace flags + * PR_RLC -- traced process runs when last /proc file closed. + * PR_KLC -- traced process is killed when last /proc file closed. + * PR_ASYNC -- LWP's get to run/stop independently. + * + * There are three methods for doing this function: + * 1) Newest: read/write [PCSET/PCRESET/PCUNSET] + * [Sol6, Sol7, UW] + * 2) Middle: PIOCSET/PIOCRESET + * [Irix, Sol5] + * 3) Oldest: PIOCSFORK/PIOCRFORK/PIOCSRLC/PIOCRRLC + * [OSF, Sol5] + * + * Note: Irix does not define PR_ASYNC. + * Note: OSF does not define PR_KLC. + * Note: OSF is the only one that can ONLY use the oldest method. + * + * Arguments: + * pi -- the procinfo + * flag -- one of PR_FORK, PR_RLC, or PR_ASYNC + * mode -- 1 for set, 0 for reset. + * + * Returns non-zero for success, zero for failure. + */ + +enum { FLAG_RESET, FLAG_SET }; + +static int +proc_modify_flag (procinfo *pi, long flag, long mode) +{ + long win = 0; /* default to fail */ + + /* + * These operations affect the process as a whole, and applying + * them to an individual LWP has the same meaning as applying them + * to the main process. Therefore, if we're ever called with a + * pointer to an LWP's procinfo, let's substitute the process's + * procinfo and avoid opening the LWP's file descriptor + * unnecessarily. + */ + + if (pi->pid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API /* Newest method: UnixWare and newer Solarii */ + /* First normalize the PCUNSET/PCRESET command opcode + (which for no obvious reason has a different definition + from one operating system to the next...) */ +#ifdef PCUNSET +#define GDBRESET PCUNSET +#else +#ifdef PCRESET +#define GDBRESET PCRESET +#endif +#endif + { + procfs_ctl_t arg[2]; + + if (mode == FLAG_SET) /* Set the flag (RLC, FORK, or ASYNC) */ + arg[0] = PCSET; + else /* Reset the flag */ + arg[0] = GDBRESET; + + arg[1] = flag; + win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg)); + } +#else +#ifdef PIOCSET /* Irix/Sol5 method */ + if (mode == FLAG_SET) /* Set the flag (hopefully RLC, FORK, or ASYNC) */ + { + win = (ioctl (pi->ctl_fd, PIOCSET, &flag) >= 0); + } + else /* Reset the flag */ + { + win = (ioctl (pi->ctl_fd, PIOCRESET, &flag) >= 0); + } + +#else +#ifdef PIOCSRLC /* Oldest method: OSF */ + switch (flag) { + case PR_RLC: + if (mode == FLAG_SET) /* Set run-on-last-close */ + { + win = (ioctl (pi->ctl_fd, PIOCSRLC, NULL) >= 0); + } + else /* Clear run-on-last-close */ + { + win = (ioctl (pi->ctl_fd, PIOCRRLC, NULL) >= 0); + } + break; + case PR_FORK: + if (mode == FLAG_SET) /* Set inherit-on-fork */ + { + win = (ioctl (pi->ctl_fd, PIOCSFORK, NULL) >= 0); + } + else /* Clear inherit-on-fork */ + { + win = (ioctl (pi->ctl_fd, PIOCRFORK, NULL) >= 0); + } + break; + default: + win = 0; /* fail -- unknown flag (can't do PR_ASYNC) */ + break; + } +#endif +#endif +#endif +#undef GDBRESET + /* The above operation renders the procinfo's cached pstatus obsolete. */ + pi->status_valid = 0; + + if (!win) + warning ("procfs: modify_flag failed to turn %s %s", + flag == PR_FORK ? "PR_FORK" : + flag == PR_RLC ? "PR_RLC" : +#ifdef PR_ASYNC + flag == PR_ASYNC ? "PR_ASYNC" : +#endif +#ifdef PR_KLC + flag == PR_KLC ? "PR_KLC" : +#endif + "<unknown flag>", + mode == FLAG_RESET ? "off" : "on"); + + return win; +} + +/* + * Function: proc_set_run_on_last_close + * + * Set the run_on_last_close flag. + * Process with all threads will become runnable + * when debugger closes all /proc fds. + * + * Returns non-zero for success, zero for failure. + */ + +int +proc_set_run_on_last_close (procinfo *pi) +{ + return proc_modify_flag (pi, PR_RLC, FLAG_SET); +} + +/* + * Function: proc_unset_run_on_last_close + * + * Reset the run_on_last_close flag. + * Process will NOT become runnable + * when debugger closes its file handles. + * + * Returns non-zero for success, zero for failure. + */ + +int +proc_unset_run_on_last_close (procinfo *pi) +{ + return proc_modify_flag (pi, PR_RLC, FLAG_RESET); +} + +#ifdef PR_KLC +/* + * Function: proc_set_kill_on_last_close + * + * Set the kill_on_last_close flag. + * Process with all threads will be killed when debugger + * closes all /proc fds (or debugger exits or dies). + * + * Returns non-zero for success, zero for failure. + */ + +int +proc_set_kill_on_last_close (procinfo *pi) +{ + return proc_modify_flag (pi, PR_KLC, FLAG_SET); +} + +/* + * Function: proc_unset_kill_on_last_close + * + * Reset the kill_on_last_close flag. + * Process will NOT be killed when debugger + * closes its file handles (or exits or dies). + * + * Returns non-zero for success, zero for failure. + */ + +int +proc_unset_kill_on_last_close (procinfo *pi) +{ + return proc_modify_flag (pi, PR_KLC, FLAG_RESET); +} +#endif /* PR_KLC */ + +/* + * Function: proc_set_inherit_on_fork + * + * Set inherit_on_fork flag. + * If the process forks a child while we are registered for events + * in the parent, then we will also recieve events from the child. + * + * Returns non-zero for success, zero for failure. + */ + +int +proc_set_inherit_on_fork (procinfo *pi) +{ + return proc_modify_flag (pi, PR_FORK, FLAG_SET); +} + +/* + * Function: proc_unset_inherit_on_fork + * + * Reset inherit_on_fork flag. + * If the process forks a child while we are registered for events + * in the parent, then we will NOT recieve events from the child. + * + * Returns non-zero for success, zero for failure. + */ + +int +proc_unset_inherit_on_fork (procinfo *pi) +{ + return proc_modify_flag (pi, PR_FORK, FLAG_RESET); +} + +#ifdef PR_ASYNC +/* + * Function: proc_set_async + * + * Set PR_ASYNC flag. + * If one LWP stops because of a debug event (signal etc.), + * the remaining LWPs will continue to run. + * + * Returns non-zero for success, zero for failure. + */ + +int +proc_set_async (procinfo *pi) +{ + return proc_modify_flag (pi, PR_ASYNC, FLAG_SET); +} + +/* + * Function: proc_unset_async + * + * Reset PR_ASYNC flag. + * If one LWP stops because of a debug event (signal etc.), + * then all other LWPs will stop as well. + * + * Returns non-zero for success, zero for failure. + */ + +int +proc_unset_async (procinfo *pi) +{ + return proc_modify_flag (pi, PR_ASYNC, FLAG_RESET); +} +#endif /* PR_ASYNC */ + +/* + * Function: proc_stop_process + * + * Request the process/LWP to stop. Does not wait. + * Returns non-zero for success, zero for failure. + */ + +int +proc_stop_process (procinfo *pi) +{ + int win; + + /* + * We might conceivably apply this operation to an LWP, and + * the LWP's ctl file descriptor might not be open. + */ + + if (pi->ctl_fd == 0 && + open_procinfo_files (pi, FD_CTL) == 0) + return 0; + else + { +#ifdef NEW_PROC_API + procfs_ctl_t cmd = PCSTOP; + win = (write (pi->ctl_fd, (char *) &cmd, sizeof (cmd)) == sizeof (cmd)); +#else /* ioctl method */ + win = (ioctl (pi->ctl_fd, PIOCSTOP, &pi->prstatus) >= 0); + /* Note: the call also reads the prstatus. */ + if (win) + { + pi->status_valid = 1; + PROC_PRETTYFPRINT_STATUS (proc_flags (pi), + proc_why (pi), + proc_what (pi), + proc_get_current_thread (pi)); + } +#endif + } + + return win; +} + +/* + * Function: proc_wait_for_stop + * + * Wait for the process or LWP to stop (block until it does). + * Returns non-zero for success, zero for failure. + */ + +int +proc_wait_for_stop (procinfo *pi) +{ + int win; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + { + procfs_ctl_t cmd = PCWSTOP; + win = (write (pi->ctl_fd, (char *) &cmd, sizeof (cmd)) == sizeof (cmd)); + /* We been runnin' and we stopped -- need to update status. */ + pi->status_valid = 0; + } +#else /* ioctl method */ + win = (ioctl (pi->ctl_fd, PIOCWSTOP, &pi->prstatus) >= 0); + /* Above call also refreshes the prstatus. */ + if (win) + { + pi->status_valid = 1; + PROC_PRETTYFPRINT_STATUS (proc_flags (pi), + proc_why (pi), + proc_what (pi), + proc_get_current_thread (pi)); + } +#endif + + return win; +} + +/* + * Function: proc_run_process + * + * Make the process or LWP runnable. + * Options (not all are implemented): + * - single-step + * - clear current fault + * - clear current signal + * - abort the current system call + * - stop as soon as finished with system call + * - (ioctl): set traced signal set + * - (ioctl): set held signal set + * - (ioctl): set traced fault set + * - (ioctl): set start pc (vaddr) + * Always clear the current fault. + * Clear the current signal if 'signo' is zero. + * + * Arguments: + * pi the process or LWP to operate on. + * step if true, set the process or LWP to trap after one instr. + * signo if zero, clear the current signal if any. + * if non-zero, set the current signal to this one. + * + * Returns non-zero for success, zero for failure. + */ + +int +proc_run_process (procinfo *pi, int step, int signo) +{ + int win; + int runflags; + + /* + * We will probably have to apply this operation to individual threads, + * so make sure the control file descriptor is open. + */ + + if (pi->ctl_fd == 0 && + open_procinfo_files (pi, FD_CTL) == 0) + { + return 0; + } + + runflags = PRCFAULT; /* always clear current fault */ + if (step) + runflags |= PRSTEP; + if (signo == 0) + runflags |= PRCSIG; + else if (signo != -1) /* -1 means do nothing W.R.T. signals */ + proc_set_current_signal (pi, signo); + +#ifdef NEW_PROC_API + { + procfs_ctl_t cmd[2]; + + cmd[0] = PCRUN; + cmd[1] = runflags; + win = (write (pi->ctl_fd, (char *) &cmd, sizeof (cmd)) == sizeof (cmd)); + } +#else /* ioctl method */ + { + prrun_t prrun; + + memset (&prrun, 0, sizeof (prrun)); + prrun.pr_flags = runflags; + win = (ioctl (pi->ctl_fd, PIOCRUN, &prrun) >= 0); + } +#endif + + return win; +} + +/* + * Function: proc_set_traced_signals + * + * Register to trace signals in the process or LWP. + * Returns non-zero for success, zero for failure. + */ + +int +proc_set_traced_signals (procinfo *pi, gdb_sigset_t *sigset) +{ + int win; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + { + struct { + procfs_ctl_t cmd; + /* Use char array to avoid alignment issues. */ + char sigset[sizeof (gdb_sigset_t)]; + } arg; + + arg.cmd = PCSTRACE; + memcpy (&arg.sigset, sigset, sizeof (gdb_sigset_t)); + + win = (write (pi->ctl_fd, (char *) &arg, sizeof (arg)) == sizeof (arg)); + } +#else /* ioctl method */ + win = (ioctl (pi->ctl_fd, PIOCSTRACE, sigset) >= 0); +#endif + /* The above operation renders the procinfo's cached pstatus obsolete. */ + pi->status_valid = 0; + + if (!win) + warning ("procfs: set_traced_signals failed"); + return win; +} + +/* + * Function: proc_set_traced_faults + * + * Register to trace hardware faults in the process or LWP. + * Returns non-zero for success, zero for failure. + */ + +int +proc_set_traced_faults (procinfo *pi, fltset_t *fltset) +{ + int win; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + { + struct { + procfs_ctl_t cmd; + /* Use char array to avoid alignment issues. */ + char fltset[sizeof (fltset_t)]; + } arg; + + arg.cmd = PCSFAULT; + memcpy (&arg.fltset, fltset, sizeof (fltset_t)); + + win = (write (pi->ctl_fd, (char *) &arg, sizeof (arg)) == sizeof (arg)); + } +#else /* ioctl method */ + win = (ioctl (pi->ctl_fd, PIOCSFAULT, fltset) >= 0); +#endif + /* The above operation renders the procinfo's cached pstatus obsolete. */ + pi->status_valid = 0; + + return win; +} + +/* + * Function: proc_set_traced_sysentry + * + * Register to trace entry to system calls in the process or LWP. + * Returns non-zero for success, zero for failure. + */ + +int +proc_set_traced_sysentry (procinfo *pi, sysset_t *sysset) +{ + int win; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + { + struct gdb_proc_ctl_pcsentry { + procfs_ctl_t cmd; + /* Use char array to avoid alignment issues. */ + char sysset[sizeof (sysset_t)]; + } *argp; + int argp_size = sizeof (struct gdb_proc_ctl_pcsentry) + - sizeof (sysset_t) + + sysset_t_size (pi); + + argp = xmalloc (argp_size); + + argp->cmd = PCSENTRY; + memcpy (&argp->sysset, sysset, sysset_t_size (pi)); + + win = (write (pi->ctl_fd, (char *) argp, argp_size) == argp_size); + xfree (argp); + } +#else /* ioctl method */ + win = (ioctl (pi->ctl_fd, PIOCSENTRY, sysset) >= 0); +#endif + /* The above operation renders the procinfo's cached pstatus obsolete. */ + pi->status_valid = 0; + + return win; +} + +/* + * Function: proc_set_traced_sysexit + * + * Register to trace exit from system calls in the process or LWP. + * Returns non-zero for success, zero for failure. + */ + +int +proc_set_traced_sysexit (procinfo *pi, sysset_t *sysset) +{ + int win; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + { + struct gdb_proc_ctl_pcsexit { + procfs_ctl_t cmd; + /* Use char array to avoid alignment issues. */ + char sysset[sizeof (sysset_t)]; + } *argp; + int argp_size = sizeof (struct gdb_proc_ctl_pcsexit) + - sizeof (sysset_t) + + sysset_t_size (pi); + + argp = xmalloc (argp_size); + + argp->cmd = PCSEXIT; + memcpy (&argp->sysset, sysset, sysset_t_size (pi)); + + win = (write (pi->ctl_fd, (char *) argp, argp_size) == argp_size); + xfree (argp); + } +#else /* ioctl method */ + win = (ioctl (pi->ctl_fd, PIOCSEXIT, sysset) >= 0); +#endif + /* The above operation renders the procinfo's cached pstatus obsolete. */ + pi->status_valid = 0; + + return win; +} + +/* + * Function: proc_set_held_signals + * + * Specify the set of blocked / held signals in the process or LWP. + * Returns non-zero for success, zero for failure. + */ + +int +proc_set_held_signals (procinfo *pi, gdb_sigset_t *sighold) +{ + int win; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + { + struct { + procfs_ctl_t cmd; + /* Use char array to avoid alignment issues. */ + char hold[sizeof (gdb_sigset_t)]; + } arg; + + arg.cmd = PCSHOLD; + memcpy (&arg.hold, sighold, sizeof (gdb_sigset_t)); + win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg)); + } +#else + win = (ioctl (pi->ctl_fd, PIOCSHOLD, sighold) >= 0); +#endif + /* The above operation renders the procinfo's cached pstatus obsolete. */ + pi->status_valid = 0; + + return win; +} + +/* + * Function: proc_get_pending_signals + * + * returns the set of signals that are pending in the process or LWP. + * Will also copy the sigset if 'save' is non-zero. + */ + +gdb_sigset_t * +proc_get_pending_signals (procinfo *pi, gdb_sigset_t *save) +{ + gdb_sigset_t *ret = NULL; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + + if (!pi->status_valid) + if (!proc_get_status (pi)) + return NULL; + +#ifdef NEW_PROC_API + ret = &pi->prstatus.pr_lwp.pr_lwppend; +#else + ret = &pi->prstatus.pr_sigpend; +#endif + if (save && ret) + memcpy (save, ret, sizeof (gdb_sigset_t)); + + return ret; +} + +/* + * Function: proc_get_signal_actions + * + * returns the set of signal actions. + * Will also copy the sigactionset if 'save' is non-zero. + */ + +gdb_sigaction_t * +proc_get_signal_actions (procinfo *pi, gdb_sigaction_t *save) +{ + gdb_sigaction_t *ret = NULL; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + + if (!pi->status_valid) + if (!proc_get_status (pi)) + return NULL; + +#ifdef NEW_PROC_API + ret = &pi->prstatus.pr_lwp.pr_action; +#else + ret = &pi->prstatus.pr_action; +#endif + if (save && ret) + memcpy (save, ret, sizeof (gdb_sigaction_t)); + + return ret; +} + +/* + * Function: proc_get_held_signals + * + * returns the set of signals that are held / blocked. + * Will also copy the sigset if 'save' is non-zero. + */ + +gdb_sigset_t * +proc_get_held_signals (procinfo *pi, gdb_sigset_t *save) +{ + gdb_sigset_t *ret = NULL; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + if (!pi->status_valid) + if (!proc_get_status (pi)) + return NULL; + +#ifdef UNIXWARE + ret = &pi->prstatus.pr_lwp.pr_context.uc_sigmask; +#else + ret = &pi->prstatus.pr_lwp.pr_lwphold; +#endif /* UNIXWARE */ +#else /* not NEW_PROC_API */ + { + static gdb_sigset_t sigheld; + + if (ioctl (pi->ctl_fd, PIOCGHOLD, &sigheld) >= 0) + ret = &sigheld; + } +#endif /* NEW_PROC_API */ + if (save && ret) + memcpy (save, ret, sizeof (gdb_sigset_t)); + + return ret; +} + +/* + * Function: proc_get_traced_signals + * + * returns the set of signals that are traced / debugged. + * Will also copy the sigset if 'save' is non-zero. + */ + +gdb_sigset_t * +proc_get_traced_signals (procinfo *pi, gdb_sigset_t *save) +{ + gdb_sigset_t *ret = NULL; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + if (!pi->status_valid) + if (!proc_get_status (pi)) + return NULL; + + ret = &pi->prstatus.pr_sigtrace; +#else + { + static gdb_sigset_t sigtrace; + + if (ioctl (pi->ctl_fd, PIOCGTRACE, &sigtrace) >= 0) + ret = &sigtrace; + } +#endif + if (save && ret) + memcpy (save, ret, sizeof (gdb_sigset_t)); + + return ret; +} + +/* + * Function: proc_trace_signal + * + * Add 'signo' to the set of signals that are traced. + * Returns non-zero for success, zero for failure. + */ + +int +proc_trace_signal (procinfo *pi, int signo) +{ + gdb_sigset_t temp; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + + if (pi) + { + if (proc_get_traced_signals (pi, &temp)) + { + praddset (&temp, signo); + return proc_set_traced_signals (pi, &temp); + } + } + + return 0; /* failure */ +} + +/* + * Function: proc_ignore_signal + * + * Remove 'signo' from the set of signals that are traced. + * Returns non-zero for success, zero for failure. + */ + +int +proc_ignore_signal (procinfo *pi, int signo) +{ + gdb_sigset_t temp; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + + if (pi) + { + if (proc_get_traced_signals (pi, &temp)) + { + prdelset (&temp, signo); + return proc_set_traced_signals (pi, &temp); + } + } + + return 0; /* failure */ +} + +/* + * Function: proc_get_traced_faults + * + * returns the set of hardware faults that are traced /debugged. + * Will also copy the faultset if 'save' is non-zero. + */ + +fltset_t * +proc_get_traced_faults (procinfo *pi, fltset_t *save) +{ + fltset_t *ret = NULL; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + if (!pi->status_valid) + if (!proc_get_status (pi)) + return NULL; + + ret = &pi->prstatus.pr_flttrace; +#else + { + static fltset_t flttrace; + + if (ioctl (pi->ctl_fd, PIOCGFAULT, &flttrace) >= 0) + ret = &flttrace; + } +#endif + if (save && ret) + memcpy (save, ret, sizeof (fltset_t)); + + return ret; +} + +/* + * Function: proc_get_traced_sysentry + * + * returns the set of syscalls that are traced /debugged on entry. + * Will also copy the syscall set if 'save' is non-zero. + */ + +sysset_t * +proc_get_traced_sysentry (procinfo *pi, sysset_t *save) +{ + sysset_t *ret = NULL; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + if (!pi->status_valid) + if (!proc_get_status (pi)) + return NULL; + +#ifndef DYNAMIC_SYSCALLS + ret = &pi->prstatus.pr_sysentry; +#else /* DYNAMIC_SYSCALLS */ + { + static sysset_t *sysentry; + size_t size; + + if (!sysentry) + sysentry = sysset_t_alloc (pi); + ret = sysentry; + if (pi->status_fd == 0 && open_procinfo_files (pi, FD_STATUS) == 0) + return NULL; + if (pi->prstatus.pr_sysentry_offset == 0) + { + gdb_premptysysset (sysentry); + } + else + { + int rsize; + + if (lseek (pi->status_fd, (off_t) pi->prstatus.pr_sysentry_offset, + SEEK_SET) + != (off_t) pi->prstatus.pr_sysentry_offset) + return NULL; + size = sysset_t_size (pi); + gdb_premptysysset (sysentry); + rsize = read (pi->status_fd, sysentry, size); + if (rsize < 0) + return NULL; + } + } +#endif /* DYNAMIC_SYSCALLS */ +#else /* !NEW_PROC_API */ + { + static sysset_t sysentry; + + if (ioctl (pi->ctl_fd, PIOCGENTRY, &sysentry) >= 0) + ret = &sysentry; + } +#endif /* NEW_PROC_API */ + if (save && ret) + memcpy (save, ret, sysset_t_size (pi)); + + return ret; +} + +/* + * Function: proc_get_traced_sysexit + * + * returns the set of syscalls that are traced /debugged on exit. + * Will also copy the syscall set if 'save' is non-zero. + */ + +sysset_t * +proc_get_traced_sysexit (procinfo *pi, sysset_t *save) +{ + sysset_t * ret = NULL; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + if (!pi->status_valid) + if (!proc_get_status (pi)) + return NULL; + +#ifndef DYNAMIC_SYSCALLS + ret = &pi->prstatus.pr_sysexit; +#else /* DYNAMIC_SYSCALLS */ + { + static sysset_t *sysexit; + size_t size; + + if (!sysexit) + sysexit = sysset_t_alloc (pi); + ret = sysexit; + if (pi->status_fd == 0 && open_procinfo_files (pi, FD_STATUS) == 0) + return NULL; + if (pi->prstatus.pr_sysexit_offset == 0) + { + gdb_premptysysset (sysexit); + } + else + { + int rsize; + + if (lseek (pi->status_fd, (off_t) pi->prstatus.pr_sysexit_offset, SEEK_SET) + != (off_t) pi->prstatus.pr_sysexit_offset) + return NULL; + size = sysset_t_size (pi); + gdb_premptysysset (sysexit); + rsize = read (pi->status_fd, sysexit, size); + if (rsize < 0) + return NULL; + } + } +#endif /* DYNAMIC_SYSCALLS */ +#else + { + static sysset_t sysexit; + + if (ioctl (pi->ctl_fd, PIOCGEXIT, &sysexit) >= 0) + ret = &sysexit; + } +#endif + if (save && ret) + memcpy (save, ret, sysset_t_size (pi)); + + return ret; +} + +/* + * Function: proc_clear_current_fault + * + * The current fault (if any) is cleared; the associated signal + * will not be sent to the process or LWP when it resumes. + * Returns non-zero for success, zero for failure. + */ + +int +proc_clear_current_fault (procinfo *pi) +{ + int win; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + { + procfs_ctl_t cmd = PCCFAULT; + win = (write (pi->ctl_fd, (void *) &cmd, sizeof (cmd)) == sizeof (cmd)); + } +#else + win = (ioctl (pi->ctl_fd, PIOCCFAULT, 0) >= 0); +#endif + + return win; +} + +/* + * Function: proc_set_current_signal + * + * Set the "current signal" that will be delivered next to the process. + * NOTE: semantics are different from those of KILL. + * This signal will be delivered to the process or LWP + * immediately when it is resumed (even if the signal is held/blocked); + * it will NOT immediately cause another event of interest, and will NOT + * first trap back to the debugger. + * + * Returns non-zero for success, zero for failure. + */ + +int +proc_set_current_signal (procinfo *pi, int signo) +{ + int win; + struct { + procfs_ctl_t cmd; + /* Use char array to avoid alignment issues. */ + char sinfo[sizeof (gdb_siginfo_t)]; + } arg; + gdb_siginfo_t *mysinfo; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef PROCFS_DONT_PIOCSSIG_CURSIG + /* With Alpha OSF/1 procfs, the kernel gets really confused if it + * receives a PIOCSSIG with a signal identical to the current signal, + * it messes up the current signal. Work around the kernel bug. + */ + if (signo > 0 && + signo == proc_cursig (pi)) + return 1; /* I assume this is a success? */ +#endif + + /* The pointer is just a type alias. */ + mysinfo = (gdb_siginfo_t *) &arg.sinfo; + mysinfo->si_signo = signo; + mysinfo->si_code = 0; + mysinfo->si_pid = getpid (); /* ?why? */ + mysinfo->si_uid = getuid (); /* ?why? */ + +#ifdef NEW_PROC_API + arg.cmd = PCSSIG; + win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg)); +#else + win = (ioctl (pi->ctl_fd, PIOCSSIG, (void *) &arg.sinfo) >= 0); +#endif + + return win; +} + +/* + * Function: proc_clear_current_signal + * + * The current signal (if any) is cleared, and + * is not sent to the process or LWP when it resumes. + * Returns non-zero for success, zero for failure. + */ + +int +proc_clear_current_signal (procinfo *pi) +{ + int win; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + +#ifdef NEW_PROC_API + { + struct { + procfs_ctl_t cmd; + /* Use char array to avoid alignment issues. */ + char sinfo[sizeof (gdb_siginfo_t)]; + } arg; + gdb_siginfo_t *mysinfo; + + arg.cmd = PCSSIG; + /* The pointer is just a type alias. */ + mysinfo = (gdb_siginfo_t *) &arg.sinfo; + mysinfo->si_signo = 0; + mysinfo->si_code = 0; + mysinfo->si_errno = 0; + mysinfo->si_pid = getpid (); /* ?why? */ + mysinfo->si_uid = getuid (); /* ?why? */ + + win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg)); + } +#else + win = (ioctl (pi->ctl_fd, PIOCSSIG, 0) >= 0); +#endif + + return win; +} + +/* + * Function: proc_get_gregs + * + * Get the general registers for the process or LWP. + * Returns non-zero for success, zero for failure. + */ + +gdb_gregset_t * +proc_get_gregs (procinfo *pi) +{ + if (!pi->status_valid || !pi->gregs_valid) + if (!proc_get_status (pi)) + return NULL; + + /* + * OK, sorry about the ifdef's. + * There's three cases instead of two, because + * in this instance Unixware and Solaris/RW differ. + */ + +#ifdef NEW_PROC_API +#ifdef UNIXWARE /* ugh, a true architecture dependency */ + return &pi->prstatus.pr_lwp.pr_context.uc_mcontext.gregs; +#else /* not Unixware */ + return &pi->prstatus.pr_lwp.pr_reg; +#endif /* Unixware */ +#else /* not NEW_PROC_API */ + return &pi->prstatus.pr_reg; +#endif /* NEW_PROC_API */ +} + +/* + * Function: proc_get_fpregs + * + * Get the floating point registers for the process or LWP. + * Returns non-zero for success, zero for failure. + */ + +gdb_fpregset_t * +proc_get_fpregs (procinfo *pi) +{ +#ifdef NEW_PROC_API + if (!pi->status_valid || !pi->fpregs_valid) + if (!proc_get_status (pi)) + return NULL; + +#ifdef UNIXWARE /* a true architecture dependency */ + return &pi->prstatus.pr_lwp.pr_context.uc_mcontext.fpregs; +#else + return &pi->prstatus.pr_lwp.pr_fpreg; +#endif /* Unixware */ + +#else /* not NEW_PROC_API */ + if (pi->fpregs_valid) + return &pi->fpregset; /* already got 'em */ + else + { + if (pi->ctl_fd == 0 && + open_procinfo_files (pi, FD_CTL) == 0) + { + return NULL; + } + else + { +#ifdef PIOCTGFPREG + struct { + long pr_count; + tid_t pr_error_thread; + tfpregset_t thread_1; + } thread_fpregs; + + thread_fpregs.pr_count = 1; + thread_fpregs.thread_1.tid = pi->tid; + + if (pi->tid == 0 && + ioctl (pi->ctl_fd, PIOCGFPREG, &pi->fpregset) >= 0) + { + pi->fpregs_valid = 1; + return &pi->fpregset; /* got 'em now! */ + } + else if (pi->tid != 0 && + ioctl (pi->ctl_fd, PIOCTGFPREG, &thread_fpregs) >= 0) + { + memcpy (&pi->fpregset, &thread_fpregs.thread_1.pr_fpregs, + sizeof (pi->fpregset)); + pi->fpregs_valid = 1; + return &pi->fpregset; /* got 'em now! */ + } + else + { + return NULL; + } +#else + if (ioctl (pi->ctl_fd, PIOCGFPREG, &pi->fpregset) >= 0) + { + pi->fpregs_valid = 1; + return &pi->fpregset; /* got 'em now! */ + } + else + { + return NULL; + } +#endif + } + } +#endif +} + +/* + * Function: proc_set_gregs + * + * Write the general registers back to the process or LWP. + * Returns non-zero for success, zero for failure. + */ + +int +proc_set_gregs (procinfo *pi) +{ + gdb_gregset_t *gregs; + int win; + + if ((gregs = proc_get_gregs (pi)) == NULL) + return 0; /* get_regs has already warned */ + + if (pi->ctl_fd == 0 && + open_procinfo_files (pi, FD_CTL) == 0) + { + return 0; + } + else + { +#ifdef NEW_PROC_API + struct { + procfs_ctl_t cmd; + /* Use char array to avoid alignment issues. */ + char gregs[sizeof (gdb_gregset_t)]; + } arg; + + arg.cmd = PCSREG; + memcpy (&arg.gregs, gregs, sizeof (arg.gregs)); + win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg)); +#else + win = (ioctl (pi->ctl_fd, PIOCSREG, gregs) >= 0); +#endif + } + + /* Policy: writing the regs invalidates our cache. */ + pi->gregs_valid = 0; + return win; +} + +/* + * Function: proc_set_fpregs + * + * Modify the floating point register set of the process or LWP. + * Returns non-zero for success, zero for failure. + */ + +int +proc_set_fpregs (procinfo *pi) +{ + gdb_fpregset_t *fpregs; + int win; + + if ((fpregs = proc_get_fpregs (pi)) == NULL) + return 0; /* get_fpregs has already warned */ + + if (pi->ctl_fd == 0 && + open_procinfo_files (pi, FD_CTL) == 0) + { + return 0; + } + else + { +#ifdef NEW_PROC_API + struct { + procfs_ctl_t cmd; + /* Use char array to avoid alignment issues. */ + char fpregs[sizeof (gdb_fpregset_t)]; + } arg; + + arg.cmd = PCSFPREG; + memcpy (&arg.fpregs, fpregs, sizeof (arg.fpregs)); + win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg)); +#else +#ifdef PIOCTSFPREG + if (pi->tid == 0) + win = (ioctl (pi->ctl_fd, PIOCSFPREG, fpregs) >= 0); + else + { + struct { + long pr_count; + tid_t pr_error_thread; + tfpregset_t thread_1; + } thread_fpregs; + + thread_fpregs.pr_count = 1; + thread_fpregs.thread_1.tid = pi->tid; + memcpy (&thread_fpregs.thread_1.pr_fpregs, fpregs, + sizeof (*fpregs)); + win = (ioctl (pi->ctl_fd, PIOCTSFPREG, &thread_fpregs) >= 0); + } +#else + win = (ioctl (pi->ctl_fd, PIOCSFPREG, fpregs) >= 0); +#endif /* osf PIOCTSFPREG */ +#endif /* NEW_PROC_API */ + } + + /* Policy: writing the regs invalidates our cache. */ + pi->fpregs_valid = 0; + return win; +} + +/* + * Function: proc_kill + * + * Send a signal to the proc or lwp with the semantics of "kill()". + * Returns non-zero for success, zero for failure. + */ + +int +proc_kill (procinfo *pi, int signo) +{ + int win; + + /* + * We might conceivably apply this operation to an LWP, and + * the LWP's ctl file descriptor might not be open. + */ + + if (pi->ctl_fd == 0 && + open_procinfo_files (pi, FD_CTL) == 0) + { + return 0; + } + else + { +#ifdef NEW_PROC_API + procfs_ctl_t cmd[2]; + + cmd[0] = PCKILL; + cmd[1] = signo; + win = (write (pi->ctl_fd, (char *) &cmd, sizeof (cmd)) == sizeof (cmd)); +#else /* ioctl method */ + /* FIXME: do I need the Alpha OSF fixups present in + procfs.c/unconditionally_kill_inferior? Perhaps only for SIGKILL? */ + win = (ioctl (pi->ctl_fd, PIOCKILL, &signo) >= 0); +#endif + } + + return win; +} + +/* + * Function: proc_parent_pid + * + * Find the pid of the process that started this one. + * Returns the parent process pid, or zero. + */ + +int +proc_parent_pid (procinfo *pi) +{ + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + + if (!pi->status_valid) + if (!proc_get_status (pi)) + return 0; + + return pi->prstatus.pr_ppid; +} + + +/* Convert a target address (a.k.a. CORE_ADDR) into a host address + (a.k.a void pointer)! */ + +static void * +procfs_address_to_host_pointer (CORE_ADDR addr) +{ + void *ptr; + + gdb_assert (sizeof (ptr) == TYPE_LENGTH (builtin_type_void_data_ptr)); + ADDRESS_TO_POINTER (builtin_type_void_data_ptr, &ptr, addr); + return ptr; +} + +/* + * Function: proc_set_watchpoint + * + */ + +int +proc_set_watchpoint (procinfo *pi, CORE_ADDR addr, int len, int wflags) +{ +#if !defined (TARGET_HAS_HARDWARE_WATCHPOINTS) + return 0; +#else +/* Horrible hack! Detect Solaris 2.5, because this doesn't work on 2.5 */ +#if defined (PIOCOPENLWP) || defined (UNIXWARE) /* Solaris 2.5: bail out */ + return 0; +#else + struct { + procfs_ctl_t cmd; + char watch[sizeof (prwatch_t)]; + } arg; + prwatch_t *pwatch; + + pwatch = (prwatch_t *) &arg.watch; + /* NOTE: cagney/2003-02-01: Even more horrible hack. Need to + convert a target address into something that can be stored in a + native data structure. */ +#ifdef PCAGENT /* Horrible hack: only defined on Solaris 2.6+ */ + pwatch->pr_vaddr = (uintptr_t) procfs_address_to_host_pointer (addr); +#else + pwatch->pr_vaddr = (caddr_t) procfs_address_to_host_pointer (addr); +#endif + pwatch->pr_size = len; + pwatch->pr_wflags = wflags; +#if defined(NEW_PROC_API) && defined (PCWATCH) + arg.cmd = PCWATCH; + return (write (pi->ctl_fd, &arg, sizeof (arg)) == sizeof (arg)); +#else +#if defined (PIOCSWATCH) + return (ioctl (pi->ctl_fd, PIOCSWATCH, pwatch) >= 0); +#else + return 0; /* Fail */ +#endif +#endif +#endif +#endif +} + +#ifdef TM_I386SOL2_H /* Is it hokey to use this? */ + +#include <sys/sysi86.h> + +/* + * Function: proc_get_LDT_entry + * + * Inputs: + * procinfo *pi; + * int key; + * + * The 'key' is actually the value of the lower 16 bits of + * the GS register for the LWP that we're interested in. + * + * Return: matching ssh struct (LDT entry). + */ + +struct ssd * +proc_get_LDT_entry (procinfo *pi, int key) +{ + static struct ssd *ldt_entry = NULL; +#ifdef NEW_PROC_API + char pathname[MAX_PROC_NAME_SIZE]; + struct cleanup *old_chain = NULL; + int fd; + + /* Allocate space for one LDT entry. + This alloc must persist, because we return a pointer to it. */ + if (ldt_entry == NULL) + ldt_entry = (struct ssd *) xmalloc (sizeof (struct ssd)); + + /* Open the file descriptor for the LDT table. */ + sprintf (pathname, "/proc/%d/ldt", pi->pid); + if ((fd = open_with_retry (pathname, O_RDONLY)) < 0) + { + proc_warn (pi, "proc_get_LDT_entry (open)", __LINE__); + return NULL; + } + /* Make sure it gets closed again! */ + old_chain = make_cleanup_close (fd); + + /* Now 'read' thru the table, find a match and return it. */ + while (read (fd, ldt_entry, sizeof (struct ssd)) == sizeof (struct ssd)) + { + if (ldt_entry->sel == 0 && + ldt_entry->bo == 0 && + ldt_entry->acc1 == 0 && + ldt_entry->acc2 == 0) + break; /* end of table */ + /* If key matches, return this entry. */ + if (ldt_entry->sel == key) + return ldt_entry; + } + /* Loop ended, match not found. */ + return NULL; +#else + int nldt, i; + static int nalloc = 0; + + /* Get the number of LDT entries. */ + if (ioctl (pi->ctl_fd, PIOCNLDT, &nldt) < 0) + { + proc_warn (pi, "proc_get_LDT_entry (PIOCNLDT)", __LINE__); + return NULL; + } + + /* Allocate space for the number of LDT entries. */ + /* This alloc has to persist, 'cause we return a pointer to it. */ + if (nldt > nalloc) + { + ldt_entry = (struct ssd *) + xrealloc (ldt_entry, (nldt + 1) * sizeof (struct ssd)); + nalloc = nldt; + } + + /* Read the whole table in one gulp. */ + if (ioctl (pi->ctl_fd, PIOCLDT, ldt_entry) < 0) + { + proc_warn (pi, "proc_get_LDT_entry (PIOCLDT)", __LINE__); + return NULL; + } + + /* Search the table and return the (first) entry matching 'key'. */ + for (i = 0; i < nldt; i++) + if (ldt_entry[i].sel == key) + return &ldt_entry[i]; + + /* Loop ended, match not found. */ + return NULL; +#endif +} + +#endif /* TM_I386SOL2_H */ + +/* =============== END, non-thread part of /proc "MODULE" =============== */ + +/* =================== Thread "MODULE" =================== */ + +/* NOTE: you'll see more ifdefs and duplication of functions here, + since there is a different way to do threads on every OS. */ + +/* + * Function: proc_get_nthreads + * + * Return the number of threads for the process + */ + +#if defined (PIOCNTHR) && defined (PIOCTLIST) +/* + * OSF version + */ +int +proc_get_nthreads (procinfo *pi) +{ + int nthreads = 0; + + if (ioctl (pi->ctl_fd, PIOCNTHR, &nthreads) < 0) + proc_warn (pi, "procfs: PIOCNTHR failed", __LINE__); + + return nthreads; +} + +#else +#if defined (SYS_lwpcreate) || defined (SYS_lwp_create) /* FIXME: multiple */ +/* + * Solaris and Unixware version + */ +int +proc_get_nthreads (procinfo *pi) +{ + if (!pi->status_valid) + if (!proc_get_status (pi)) + return 0; + + /* + * NEW_PROC_API: only works for the process procinfo, + * because the LWP procinfos do not get prstatus filled in. + */ +#ifdef NEW_PROC_API + if (pi->tid != 0) /* find the parent process procinfo */ + pi = find_procinfo_or_die (pi->pid, 0); +#endif + return pi->prstatus.pr_nlwp; +} + +#else +/* + * Default version + */ +int +proc_get_nthreads (procinfo *pi) +{ + return 0; +} +#endif +#endif + +/* + * Function: proc_get_current_thread (LWP version) + * + * Return the ID of the thread that had an event of interest. + * (ie. the one that hit a breakpoint or other traced event). + * All other things being equal, this should be the ID of a + * thread that is currently executing. + */ + +#if defined (SYS_lwpcreate) || defined (SYS_lwp_create) /* FIXME: multiple */ +/* + * Solaris and Unixware version + */ +int +proc_get_current_thread (procinfo *pi) +{ + /* + * Note: this should be applied to the root procinfo for the process, + * not to the procinfo for an LWP. If applied to the procinfo for + * an LWP, it will simply return that LWP's ID. In that case, + * find the parent process procinfo. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + + if (!pi->status_valid) + if (!proc_get_status (pi)) + return 0; + +#ifdef NEW_PROC_API + return pi->prstatus.pr_lwp.pr_lwpid; +#else + return pi->prstatus.pr_who; +#endif +} + +#else +#if defined (PIOCNTHR) && defined (PIOCTLIST) +/* + * OSF version + */ +int +proc_get_current_thread (procinfo *pi) +{ +#if 0 /* FIXME: not ready for prime time? */ + return pi->prstatus.pr_tid; +#else + return 0; +#endif +} + +#else +/* + * Default version + */ +int +proc_get_current_thread (procinfo *pi) +{ + return 0; +} + +#endif +#endif + +/* + * Function: proc_update_threads + * + * Discover the IDs of all the threads within the process, and + * create a procinfo for each of them (chained to the parent). + * + * This unfortunately requires a different method on every OS. + * + * Return: non-zero for success, zero for failure. + */ + +int +proc_delete_dead_threads (procinfo *parent, procinfo *thread, void *ignore) +{ + if (thread && parent) /* sanity */ + { + thread->status_valid = 0; + if (!proc_get_status (thread)) + destroy_one_procinfo (&parent->thread_list, thread); + } + return 0; /* keep iterating */ +} + +#if defined (PIOCLSTATUS) +/* + * Solaris 2.5 (ioctl) version + */ +int +proc_update_threads (procinfo *pi) +{ + gdb_prstatus_t *prstatus; + struct cleanup *old_chain = NULL; + procinfo *thread; + int nlwp, i; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + + proc_iterate_over_threads (pi, proc_delete_dead_threads, NULL); + + if ((nlwp = proc_get_nthreads (pi)) <= 1) + return 1; /* Process is not multi-threaded; nothing to do. */ + + prstatus = xmalloc (sizeof (gdb_prstatus_t) * (nlwp + 1)); + + old_chain = make_cleanup (xfree, prstatus); + if (ioctl (pi->ctl_fd, PIOCLSTATUS, prstatus) < 0) + proc_error (pi, "update_threads (PIOCLSTATUS)", __LINE__); + + /* Skip element zero, which represents the process as a whole. */ + for (i = 1; i < nlwp + 1; i++) + { + if ((thread = create_procinfo (pi->pid, prstatus[i].pr_who)) == NULL) + proc_error (pi, "update_threads, create_procinfo", __LINE__); + + memcpy (&thread->prstatus, &prstatus[i], sizeof (*prstatus)); + thread->status_valid = 1; + } + pi->threads_valid = 1; + do_cleanups (old_chain); + return 1; +} +#else +#ifdef NEW_PROC_API +/* + * Unixware and Solaris 6 (and later) version + */ +static void +do_closedir_cleanup (void *dir) +{ + closedir (dir); +} + +int +proc_update_threads (procinfo *pi) +{ + char pathname[MAX_PROC_NAME_SIZE + 16]; + struct dirent *direntry; + struct cleanup *old_chain = NULL; + procinfo *thread; + DIR *dirp; + int lwpid; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + + proc_iterate_over_threads (pi, proc_delete_dead_threads, NULL); + + /* + * Unixware + * + * Note: this brute-force method is the only way I know of + * to accomplish this task on Unixware. This method will + * also work on Solaris 2.6 and 2.7. There is a much simpler + * and more elegant way to do this on Solaris, but the margins + * of this manuscript are too small to write it here... ;-) + */ + + strcpy (pathname, pi->pathname); + strcat (pathname, "/lwp"); + if ((dirp = opendir (pathname)) == NULL) + proc_error (pi, "update_threads, opendir", __LINE__); + + old_chain = make_cleanup (do_closedir_cleanup, dirp); + while ((direntry = readdir (dirp)) != NULL) + if (direntry->d_name[0] != '.') /* skip '.' and '..' */ + { + lwpid = atoi (&direntry->d_name[0]); + if ((thread = create_procinfo (pi->pid, lwpid)) == NULL) + proc_error (pi, "update_threads, create_procinfo", __LINE__); + } + pi->threads_valid = 1; + do_cleanups (old_chain); + return 1; +} +#else +#ifdef PIOCTLIST +/* + * OSF version + */ +int +proc_update_threads (procinfo *pi) +{ + int nthreads, i; + tid_t *threads; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + + proc_iterate_over_threads (pi, proc_delete_dead_threads, NULL); + + nthreads = proc_get_nthreads (pi); + if (nthreads < 2) + return 0; /* nothing to do for 1 or fewer threads */ + + threads = xmalloc (nthreads * sizeof (tid_t)); + + if (ioctl (pi->ctl_fd, PIOCTLIST, threads) < 0) + proc_error (pi, "procfs: update_threads (PIOCTLIST)", __LINE__); + + for (i = 0; i < nthreads; i++) + { + if (!find_procinfo (pi->pid, threads[i])) + if (!create_procinfo (pi->pid, threads[i])) + proc_error (pi, "update_threads, create_procinfo", __LINE__); + } + pi->threads_valid = 1; + return 1; +} +#else +/* + * Default version + */ +int +proc_update_threads (procinfo *pi) +{ + return 0; +} +#endif /* OSF PIOCTLIST */ +#endif /* NEW_PROC_API */ +#endif /* SOL 2.5 PIOCLSTATUS */ + +/* + * Function: proc_iterate_over_threads + * + * Description: + * Given a pointer to a function, call that function once + * for each lwp in the procinfo list, until the function + * returns non-zero, in which event return the value + * returned by the function. + * + * Note: this function does NOT call update_threads. + * If you want to discover new threads first, you must + * call that function explicitly. This function just makes + * a quick pass over the currently-known procinfos. + * + * Arguments: + * pi - parent process procinfo + * func - per-thread function + * ptr - opaque parameter for function. + * + * Return: + * First non-zero return value from the callee, or zero. + */ + +int +proc_iterate_over_threads (procinfo *pi, + int (*func) (procinfo *, procinfo *, void *), + void *ptr) +{ + procinfo *thread, *next; + int retval = 0; + + /* + * We should never have to apply this operation to any procinfo + * except the one for the main process. If that ever changes + * for any reason, then take out the following clause and + * replace it with one that makes sure the ctl_fd is open. + */ + + if (pi->tid != 0) + pi = find_procinfo_or_die (pi->pid, 0); + + for (thread = pi->thread_list; thread != NULL; thread = next) + { + next = thread->next; /* in case thread is destroyed */ + if ((retval = (*func) (pi, thread, ptr)) != 0) + break; + } + + return retval; +} + +/* =================== END, Thread "MODULE" =================== */ + +/* =================== END, /proc "MODULE" =================== */ + +/* =================== GDB "MODULE" =================== */ + +/* + * Here are all of the gdb target vector functions and their friends. + */ + +static ptid_t do_attach (ptid_t ptid); +static void do_detach (int signo); +static int register_gdb_signals (procinfo *, gdb_sigset_t *); + +/* + * Function: procfs_debug_inferior + * + * Sets up the inferior to be debugged. + * Registers to trace signals, hardware faults, and syscalls. + * Note: does not set RLC flag: caller may want to customize that. + * + * Returns: zero for success (note! unlike most functions in this module) + * On failure, returns the LINE NUMBER where it failed! + */ + +static int +procfs_debug_inferior (procinfo *pi) +{ + fltset_t traced_faults; + gdb_sigset_t traced_signals; + sysset_t *traced_syscall_entries; + sysset_t *traced_syscall_exits; + int status; + +#ifdef PROCFS_DONT_TRACE_FAULTS + /* On some systems (OSF), we don't trace hardware faults. + Apparently it's enough that we catch them as signals. + Wonder why we don't just do that in general? */ + premptyset (&traced_faults); /* don't trace faults. */ +#else + /* Register to trace hardware faults in the child. */ + prfillset (&traced_faults); /* trace all faults... */ + prdelset (&traced_faults, FLTPAGE); /* except page fault. */ +#endif + if (!proc_set_traced_faults (pi, &traced_faults)) + return __LINE__; + + /* Register to trace selected signals in the child. */ + premptyset (&traced_signals); + if (!register_gdb_signals (pi, &traced_signals)) + return __LINE__; + + + /* Register to trace the 'exit' system call (on entry). */ + traced_syscall_entries = sysset_t_alloc (pi); + gdb_premptysysset (traced_syscall_entries); +#ifdef SYS_exit + gdb_praddsysset (traced_syscall_entries, SYS_exit); +#endif +#ifdef SYS_lwpexit + gdb_praddsysset (traced_syscall_entries, SYS_lwpexit); /* And _lwp_exit... */ +#endif +#ifdef SYS_lwp_exit + gdb_praddsysset (traced_syscall_entries, SYS_lwp_exit); +#endif +#ifdef DYNAMIC_SYSCALLS + { + int callnum = find_syscall (pi, "_exit"); + if (callnum >= 0) + gdb_praddsysset (traced_syscall_entries, callnum); + } +#endif + + status = proc_set_traced_sysentry (pi, traced_syscall_entries); + xfree (traced_syscall_entries); + if (!status) + return __LINE__; + +#ifdef PRFS_STOPEXEC /* defined on OSF */ + /* OSF method for tracing exec syscalls. Quoting: + Under Alpha OSF/1 we have to use a PIOCSSPCACT ioctl to trace + exits from exec system calls because of the user level loader. */ + /* FIXME: make nice and maybe move into an access function. */ + { + int prfs_flags; + + if (ioctl (pi->ctl_fd, PIOCGSPCACT, &prfs_flags) < 0) + return __LINE__; + + prfs_flags |= PRFS_STOPEXEC; + + if (ioctl (pi->ctl_fd, PIOCSSPCACT, &prfs_flags) < 0) + return __LINE__; + } +#else /* not PRFS_STOPEXEC */ + /* Everyone else's (except OSF) method for tracing exec syscalls */ + /* GW: Rationale... + Not all systems with /proc have all the exec* syscalls with the same + names. On the SGI, for example, there is no SYS_exec, but there + *is* a SYS_execv. So, we try to account for that. */ + + traced_syscall_exits = sysset_t_alloc (pi); + gdb_premptysysset (traced_syscall_exits); +#ifdef SYS_exec + gdb_praddsysset (traced_syscall_exits, SYS_exec); +#endif +#ifdef SYS_execve + gdb_praddsysset (traced_syscall_exits, SYS_execve); +#endif +#ifdef SYS_execv + gdb_praddsysset (traced_syscall_exits, SYS_execv); +#endif + +#ifdef SYS_lwpcreate + gdb_praddsysset (traced_syscall_exits, SYS_lwpcreate); + gdb_praddsysset (traced_syscall_exits, SYS_lwpexit); +#endif + +#ifdef SYS_lwp_create /* FIXME: once only, please */ + gdb_praddsysset (traced_syscall_exits, SYS_lwp_create); + gdb_praddsysset (traced_syscall_exits, SYS_lwp_exit); +#endif + +#ifdef DYNAMIC_SYSCALLS + { + int callnum = find_syscall (pi, "execve"); + if (callnum >= 0) + gdb_praddsysset (traced_syscall_exits, callnum); + callnum = find_syscall (pi, "ra_execve"); + if (callnum >= 0) + gdb_praddsysset (traced_syscall_exits, callnum); + } +#endif + + status = proc_set_traced_sysexit (pi, traced_syscall_exits); + xfree (traced_syscall_exits); + if (!status) + return __LINE__; + +#endif /* PRFS_STOPEXEC */ + return 0; +} + +static void +procfs_attach (char *args, int from_tty) +{ + char *exec_file; + int pid; + + if (!args) + error_no_arg ("process-id to attach"); + + pid = atoi (args); + if (pid == getpid ()) + error ("Attaching GDB to itself is not a good idea..."); + + if (from_tty) + { + exec_file = get_exec_file (0); + + if (exec_file) + printf_filtered ("Attaching to program `%s', %s\n", + exec_file, target_pid_to_str (pid_to_ptid (pid))); + else + printf_filtered ("Attaching to %s\n", + target_pid_to_str (pid_to_ptid (pid))); + + fflush (stdout); + } + inferior_ptid = do_attach (pid_to_ptid (pid)); + push_target (&procfs_ops); +} + +static void +procfs_detach (char *args, int from_tty) +{ + char *exec_file; + int signo = 0; + + if (from_tty) + { + exec_file = get_exec_file (0); + if (exec_file == 0) + exec_file = ""; + printf_filtered ("Detaching from program: %s %s\n", + exec_file, target_pid_to_str (inferior_ptid)); + fflush (stdout); + } + if (args) + signo = atoi (args); + + do_detach (signo); + inferior_ptid = null_ptid; + unpush_target (&procfs_ops); /* Pop out of handling an inferior */ +} + +static ptid_t +do_attach (ptid_t ptid) +{ + procinfo *pi; + int fail; + + if ((pi = create_procinfo (PIDGET (ptid), 0)) == NULL) + perror ("procfs: out of memory in 'attach'"); + + if (!open_procinfo_files (pi, FD_CTL)) + { + fprintf_filtered (gdb_stderr, "procfs:%d -- ", __LINE__); + sprintf (errmsg, "do_attach: couldn't open /proc file for process %d", + PIDGET (ptid)); + dead_procinfo (pi, errmsg, NOKILL); + } + + /* Stop the process (if it isn't already stopped). */ + if (proc_flags (pi) & (PR_STOPPED | PR_ISTOP)) + { + pi->was_stopped = 1; + proc_prettyprint_why (proc_why (pi), proc_what (pi), 1); + } + else + { + pi->was_stopped = 0; + /* Set the process to run again when we close it. */ + if (!proc_set_run_on_last_close (pi)) + dead_procinfo (pi, "do_attach: couldn't set RLC.", NOKILL); + + /* Now stop the process. */ + if (!proc_stop_process (pi)) + dead_procinfo (pi, "do_attach: couldn't stop the process.", NOKILL); + pi->ignore_next_sigstop = 1; + } + /* Save some of the /proc state to be restored if we detach. */ + if (!proc_get_traced_faults (pi, &pi->saved_fltset)) + dead_procinfo (pi, "do_attach: couldn't save traced faults.", NOKILL); + if (!proc_get_traced_signals (pi, &pi->saved_sigset)) + dead_procinfo (pi, "do_attach: couldn't save traced signals.", NOKILL); + if (!proc_get_traced_sysentry (pi, pi->saved_entryset)) + dead_procinfo (pi, "do_attach: couldn't save traced syscall entries.", + NOKILL); + if (!proc_get_traced_sysexit (pi, pi->saved_exitset)) + dead_procinfo (pi, "do_attach: couldn't save traced syscall exits.", + NOKILL); + if (!proc_get_held_signals (pi, &pi->saved_sighold)) + dead_procinfo (pi, "do_attach: couldn't save held signals.", NOKILL); + + if ((fail = procfs_debug_inferior (pi)) != 0) + dead_procinfo (pi, "do_attach: failed in procfs_debug_inferior", NOKILL); + + /* Let GDB know that the inferior was attached. */ + attach_flag = 1; + return MERGEPID (pi->pid, proc_get_current_thread (pi)); +} + +static void +do_detach (int signo) +{ + procinfo *pi; + + /* Find procinfo for the main process */ + pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0); /* FIXME: threads */ + if (signo) + if (!proc_set_current_signal (pi, signo)) + proc_warn (pi, "do_detach, set_current_signal", __LINE__); + + if (!proc_set_traced_signals (pi, &pi->saved_sigset)) + proc_warn (pi, "do_detach, set_traced_signal", __LINE__); + + if (!proc_set_traced_faults (pi, &pi->saved_fltset)) + proc_warn (pi, "do_detach, set_traced_faults", __LINE__); + + if (!proc_set_traced_sysentry (pi, pi->saved_entryset)) + proc_warn (pi, "do_detach, set_traced_sysentry", __LINE__); + + if (!proc_set_traced_sysexit (pi, pi->saved_exitset)) + proc_warn (pi, "do_detach, set_traced_sysexit", __LINE__); + + if (!proc_set_held_signals (pi, &pi->saved_sighold)) + proc_warn (pi, "do_detach, set_held_signals", __LINE__); + + if (signo || (proc_flags (pi) & (PR_STOPPED | PR_ISTOP))) + if (signo || !(pi->was_stopped) || + query ("Was stopped when attached, make it runnable again? ")) + { + /* Clear any pending signal. */ + if (!proc_clear_current_fault (pi)) + proc_warn (pi, "do_detach, clear_current_fault", __LINE__); + + if (signo == 0 && !proc_clear_current_signal (pi)) + proc_warn (pi, "do_detach, clear_current_signal", __LINE__); + + if (!proc_set_run_on_last_close (pi)) + proc_warn (pi, "do_detach, set_rlc", __LINE__); + } + + attach_flag = 0; + destroy_procinfo (pi); +} + +/* + * fetch_registers + * + * Since the /proc interface cannot give us individual registers, + * we pay no attention to the (regno) argument, and just fetch them all. + * This results in the possibility that we will do unnecessarily many + * fetches, since we may be called repeatedly for individual registers. + * So we cache the results, and mark the cache invalid when the process + * is resumed. + */ + +static void +procfs_fetch_registers (int regno) +{ + gdb_fpregset_t *fpregs; + gdb_gregset_t *gregs; + procinfo *pi; + int pid; + int tid; + + pid = PIDGET (inferior_ptid); + tid = TIDGET (inferior_ptid); + + /* First look up procinfo for the main process. */ + pi = find_procinfo_or_die (pid, 0); + + /* If the event thread is not the same as GDB's requested thread + (ie. inferior_ptid), then look up procinfo for the requested + thread. */ + if ((tid != 0) && + (tid != proc_get_current_thread (pi))) + pi = find_procinfo_or_die (pid, tid); + + if (pi == NULL) + error ("procfs: fetch_registers failed to find procinfo for %s", + target_pid_to_str (inferior_ptid)); + + if ((gregs = proc_get_gregs (pi)) == NULL) + proc_error (pi, "fetch_registers, get_gregs", __LINE__); + + supply_gregset (gregs); + + if (FP0_REGNUM >= 0) /* need floating point? */ + { + if ((regno >= 0 && regno < FP0_REGNUM) + || regno == PC_REGNUM + || regno == DEPRECATED_FP_REGNUM + || regno == SP_REGNUM) + return; /* not a floating point register */ + + if ((fpregs = proc_get_fpregs (pi)) == NULL) + proc_error (pi, "fetch_registers, get_fpregs", __LINE__); + + supply_fpregset (fpregs); + } +} + +/* Get ready to modify the registers array. On machines which store + individual registers, this doesn't need to do anything. On + machines which store all the registers in one fell swoop, such as + /proc, this makes sure that registers contains all the registers + from the program being debugged. */ + +static void +procfs_prepare_to_store (void) +{ +#ifdef CHILD_PREPARE_TO_STORE + CHILD_PREPARE_TO_STORE (); +#endif +} + +/* + * store_registers + * + * Since the /proc interface will not read individual registers, + * we will cache these requests until the process is resumed, and + * only then write them back to the inferior process. + * + * FIXME: is that a really bad idea? Have to think about cases + * where writing one register might affect the value of others, etc. + */ + +static void +procfs_store_registers (int regno) +{ + gdb_fpregset_t *fpregs; + gdb_gregset_t *gregs; + procinfo *pi; + int pid; + int tid; + + pid = PIDGET (inferior_ptid); + tid = TIDGET (inferior_ptid); + + /* First find procinfo for main process */ + pi = find_procinfo_or_die (pid, 0); + + /* If current lwp for process is not the same as requested thread + (ie. inferior_ptid), then find procinfo for the requested thread. */ + + if ((tid != 0) && + (tid != proc_get_current_thread (pi))) + pi = find_procinfo_or_die (pid, tid); + + if (pi == NULL) + error ("procfs: store_registers: failed to find procinfo for %s", + target_pid_to_str (inferior_ptid)); + + if ((gregs = proc_get_gregs (pi)) == NULL) + proc_error (pi, "store_registers, get_gregs", __LINE__); + + fill_gregset (gregs, regno); + if (!proc_set_gregs (pi)) + proc_error (pi, "store_registers, set_gregs", __LINE__); + + if (FP0_REGNUM >= 0) /* need floating point? */ + { + if ((regno >= 0 && regno < FP0_REGNUM) + || regno == PC_REGNUM + || regno == DEPRECATED_FP_REGNUM + || regno == SP_REGNUM) + return; /* not a floating point register */ + + if ((fpregs = proc_get_fpregs (pi)) == NULL) + proc_error (pi, "store_registers, get_fpregs", __LINE__); + + fill_fpregset (fpregs, regno); + if (!proc_set_fpregs (pi)) + proc_error (pi, "store_registers, set_fpregs", __LINE__); + } +} + +static int +syscall_is_lwp_exit (procinfo *pi, int scall) +{ + +#ifdef SYS_lwp_exit + if (scall == SYS_lwp_exit) + return 1; +#endif +#ifdef SYS_lwpexit + if (scall == SYS_lwpexit) + return 1; +#endif + return 0; +} + +static int +syscall_is_exit (procinfo *pi, int scall) +{ +#ifdef SYS_exit + if (scall == SYS_exit) + return 1; +#endif +#ifdef DYNAMIC_SYSCALLS + if (find_syscall (pi, "_exit") == scall) + return 1; +#endif + return 0; +} + +static int +syscall_is_exec (procinfo *pi, int scall) +{ +#ifdef SYS_exec + if (scall == SYS_exec) + return 1; +#endif +#ifdef SYS_execv + if (scall == SYS_execv) + return 1; +#endif +#ifdef SYS_execve + if (scall == SYS_execve) + return 1; +#endif +#ifdef DYNAMIC_SYSCALLS + if (find_syscall (pi, "_execve")) + return 1; + if (find_syscall (pi, "ra_execve")) + return 1; +#endif + return 0; +} + +static int +syscall_is_lwp_create (procinfo *pi, int scall) +{ +#ifdef SYS_lwp_create + if (scall == SYS_lwp_create) + return 1; +#endif +#ifdef SYS_lwpcreate + if (scall == SYS_lwpcreate) + return 1; +#endif + return 0; +} + +/* + * Function: target_wait + * + * Retrieve the next stop event from the child process. + * If child has not stopped yet, wait for it to stop. + * Translate /proc eventcodes (or possibly wait eventcodes) + * into gdb internal event codes. + * + * Return: id of process (and possibly thread) that incurred the event. + * event codes are returned thru a pointer parameter. + */ + +static ptid_t +procfs_wait (ptid_t ptid, struct target_waitstatus *status) +{ + /* First cut: loosely based on original version 2.1 */ + procinfo *pi; + int wstat; + int temp_tid; + ptid_t retval, temp_ptid; + int why, what, flags; + int retry = 0; + +wait_again: + + retry++; + wstat = 0; + retval = pid_to_ptid (-1); + + /* Find procinfo for main process */ + pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0); + if (pi) + { + /* We must assume that the status is stale now... */ + pi->status_valid = 0; + pi->gregs_valid = 0; + pi->fpregs_valid = 0; + +#if 0 /* just try this out... */ + flags = proc_flags (pi); + why = proc_why (pi); + if ((flags & PR_STOPPED) && (why == PR_REQUESTED)) + pi->status_valid = 0; /* re-read again, IMMEDIATELY... */ +#endif + /* If child is not stopped, wait for it to stop. */ + if (!(proc_flags (pi) & (PR_STOPPED | PR_ISTOP)) && + !proc_wait_for_stop (pi)) + { + /* wait_for_stop failed: has the child terminated? */ + if (errno == ENOENT) + { + int wait_retval; + + /* /proc file not found; presumably child has terminated. */ + wait_retval = wait (&wstat); /* "wait" for the child's exit */ + + if (wait_retval != PIDGET (inferior_ptid)) /* wrong child? */ + error ("procfs: couldn't stop process %d: wait returned %d\n", + PIDGET (inferior_ptid), wait_retval); + /* FIXME: might I not just use waitpid? + Or try find_procinfo to see if I know about this child? */ + retval = pid_to_ptid (wait_retval); + } + else if (errno == EINTR) + goto wait_again; + else + { + /* Unknown error from wait_for_stop. */ + proc_error (pi, "target_wait (wait_for_stop)", __LINE__); + } + } + else + { + /* This long block is reached if either: + a) the child was already stopped, or + b) we successfully waited for the child with wait_for_stop. + This block will analyze the /proc status, and translate it + into a waitstatus for GDB. + + If we actually had to call wait because the /proc file + is gone (child terminated), then we skip this block, + because we already have a waitstatus. */ + + flags = proc_flags (pi); + why = proc_why (pi); + what = proc_what (pi); + + if (flags & (PR_STOPPED | PR_ISTOP)) + { +#ifdef PR_ASYNC + /* If it's running async (for single_thread control), + set it back to normal again. */ + if (flags & PR_ASYNC) + if (!proc_unset_async (pi)) + proc_error (pi, "target_wait, unset_async", __LINE__); +#endif + + if (info_verbose) + proc_prettyprint_why (why, what, 1); + + /* The 'pid' we will return to GDB is composed of + the process ID plus the lwp ID. */ + retval = MERGEPID (pi->pid, proc_get_current_thread (pi)); + + switch (why) { + case PR_SIGNALLED: + wstat = (what << 8) | 0177; + break; + case PR_SYSENTRY: + if (syscall_is_lwp_exit (pi, what)) + { + printf_filtered ("[%s exited]\n", + target_pid_to_str (retval)); + delete_thread (retval); + status->kind = TARGET_WAITKIND_SPURIOUS; + return retval; + } + else if (syscall_is_exit (pi, what)) + { + /* Handle SYS_exit call only */ + /* Stopped at entry to SYS_exit. + Make it runnable, resume it, then use + the wait system call to get its exit code. + Proc_run_process always clears the current + fault and signal. + Then return its exit status. */ + pi->status_valid = 0; + wstat = 0; + /* FIXME: what we should do is return + TARGET_WAITKIND_SPURIOUS. */ + if (!proc_run_process (pi, 0, 0)) + proc_error (pi, "target_wait, run_process", __LINE__); + if (attach_flag) + { + /* Don't call wait: simulate waiting for exit, + return a "success" exit code. Bogus: what if + it returns something else? */ + wstat = 0; + retval = inferior_ptid; /* ? ? ? */ + } + else + { + int temp = wait (&wstat); + + /* FIXME: shouldn't I make sure I get the right + event from the right process? If (for + instance) I have killed an earlier inferior + process but failed to clean up after it + somehow, I could get its termination event + here. */ + + /* If wait returns -1, that's what we return to GDB. */ + if (temp < 0) + retval = pid_to_ptid (temp); + } + } + else + { + printf_filtered ("procfs: trapped on entry to "); + proc_prettyprint_syscall (proc_what (pi), 0); + printf_filtered ("\n"); +#ifndef PIOCSSPCACT + { + long i, nsysargs, *sysargs; + + if ((nsysargs = proc_nsysarg (pi)) > 0 && + (sysargs = proc_sysargs (pi)) != NULL) + { + printf_filtered ("%ld syscall arguments:\n", nsysargs); + for (i = 0; i < nsysargs; i++) + printf_filtered ("#%ld: 0x%08lx\n", + i, sysargs[i]); + } + + } +#endif + if (status) + { + /* How to exit gracefully, returning "unknown event" */ + status->kind = TARGET_WAITKIND_SPURIOUS; + return inferior_ptid; + } + else + { + /* How to keep going without returning to wfi: */ + target_resume (ptid, 0, TARGET_SIGNAL_0); + goto wait_again; + } + } + break; + case PR_SYSEXIT: + if (syscall_is_exec (pi, what)) + { + /* Hopefully this is our own "fork-child" execing + the real child. Hoax this event into a trap, and + GDB will see the child about to execute its start + address. */ + wstat = (SIGTRAP << 8) | 0177; + } + else if (syscall_is_lwp_create (pi, what)) + { + /* + * This syscall is somewhat like fork/exec. + * We will get the event twice: once for the parent LWP, + * and once for the child. We should already know about + * the parent LWP, but the child will be new to us. So, + * whenever we get this event, if it represents a new + * thread, simply add the thread to the list. + */ + + /* If not in procinfo list, add it. */ + temp_tid = proc_get_current_thread (pi); + if (!find_procinfo (pi->pid, temp_tid)) + create_procinfo (pi->pid, temp_tid); + + temp_ptid = MERGEPID (pi->pid, temp_tid); + /* If not in GDB's thread list, add it. */ + if (!in_thread_list (temp_ptid)) + { + printf_filtered ("[New %s]\n", + target_pid_to_str (temp_ptid)); + add_thread (temp_ptid); + } + /* Return to WFI, but tell it to immediately resume. */ + status->kind = TARGET_WAITKIND_SPURIOUS; + return inferior_ptid; + } + else if (syscall_is_lwp_exit (pi, what)) + { + printf_filtered ("[%s exited]\n", + target_pid_to_str (retval)); + delete_thread (retval); + status->kind = TARGET_WAITKIND_SPURIOUS; + return retval; + } + else if (0) + { + /* FIXME: Do we need to handle SYS_sproc, + SYS_fork, or SYS_vfork here? The old procfs + seemed to use this event to handle threads on + older (non-LWP) systems, where I'm assuming + that threads were actually separate processes. + Irix, maybe? Anyway, low priority for now. */ + } + else + { + printf_filtered ("procfs: trapped on exit from "); + proc_prettyprint_syscall (proc_what (pi), 0); + printf_filtered ("\n"); +#ifndef PIOCSSPCACT + { + long i, nsysargs, *sysargs; + + if ((nsysargs = proc_nsysarg (pi)) > 0 && + (sysargs = proc_sysargs (pi)) != NULL) + { + printf_filtered ("%ld syscall arguments:\n", nsysargs); + for (i = 0; i < nsysargs; i++) + printf_filtered ("#%ld: 0x%08lx\n", + i, sysargs[i]); + } + } +#endif + status->kind = TARGET_WAITKIND_SPURIOUS; + return inferior_ptid; + } + break; + case PR_REQUESTED: +#if 0 /* FIXME */ + wstat = (SIGSTOP << 8) | 0177; + break; +#else + if (retry < 5) + { + printf_filtered ("Retry #%d:\n", retry); + pi->status_valid = 0; + goto wait_again; + } + else + { + /* If not in procinfo list, add it. */ + temp_tid = proc_get_current_thread (pi); + if (!find_procinfo (pi->pid, temp_tid)) + create_procinfo (pi->pid, temp_tid); + + /* If not in GDB's thread list, add it. */ + temp_ptid = MERGEPID (pi->pid, temp_tid); + if (!in_thread_list (temp_ptid)) + { + printf_filtered ("[New %s]\n", + target_pid_to_str (temp_ptid)); + add_thread (temp_ptid); + } + + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = 0; + return retval; + } +#endif + case PR_JOBCONTROL: + wstat = (what << 8) | 0177; + break; + case PR_FAULTED: + switch (what) { +#ifdef FLTWATCH + case FLTWATCH: + wstat = (SIGTRAP << 8) | 0177; + break; +#endif +#ifdef FLTKWATCH + case FLTKWATCH: + wstat = (SIGTRAP << 8) | 0177; + break; +#endif + /* FIXME: use si_signo where possible. */ + case FLTPRIV: +#if (FLTILL != FLTPRIV) /* avoid "duplicate case" error */ + case FLTILL: +#endif + wstat = (SIGILL << 8) | 0177; + break; + case FLTBPT: +#if (FLTTRACE != FLTBPT) /* avoid "duplicate case" error */ + case FLTTRACE: +#endif + wstat = (SIGTRAP << 8) | 0177; + break; + case FLTSTACK: + case FLTACCESS: +#if (FLTBOUNDS != FLTSTACK) /* avoid "duplicate case" error */ + case FLTBOUNDS: +#endif + wstat = (SIGSEGV << 8) | 0177; + break; + case FLTIOVF: + case FLTIZDIV: +#if (FLTFPE != FLTIOVF) /* avoid "duplicate case" error */ + case FLTFPE: +#endif + wstat = (SIGFPE << 8) | 0177; + break; + case FLTPAGE: /* Recoverable page fault */ + default: /* FIXME: use si_signo if possible for fault */ + retval = pid_to_ptid (-1); + printf_filtered ("procfs:%d -- ", __LINE__); + printf_filtered ("child stopped for unknown reason:\n"); + proc_prettyprint_why (why, what, 1); + error ("... giving up..."); + break; + } + break; /* case PR_FAULTED: */ + default: /* switch (why) unmatched */ + printf_filtered ("procfs:%d -- ", __LINE__); + printf_filtered ("child stopped for unknown reason:\n"); + proc_prettyprint_why (why, what, 1); + error ("... giving up..."); + break; + } + /* + * Got this far without error: + * If retval isn't in the threads database, add it. + */ + if (PIDGET (retval) > 0 && + !ptid_equal (retval, inferior_ptid) && + !in_thread_list (retval)) + { + /* + * We have a new thread. + * We need to add it both to GDB's list and to our own. + * If we don't create a procinfo, resume may be unhappy + * later. + */ + printf_filtered ("[New %s]\n", target_pid_to_str (retval)); + add_thread (retval); + if (find_procinfo (PIDGET (retval), TIDGET (retval)) == NULL) + create_procinfo (PIDGET (retval), TIDGET (retval)); + + /* In addition, it's possible that this is the first + * new thread we've seen, in which case we may not + * have created entries for inferior_ptid yet. + */ + if (TIDGET (inferior_ptid) != 0) + { + if (!in_thread_list (inferior_ptid)) + add_thread (inferior_ptid); + if (find_procinfo (PIDGET (inferior_ptid), + TIDGET (inferior_ptid)) == NULL) + create_procinfo (PIDGET (inferior_ptid), + TIDGET (inferior_ptid)); + } + } + } + else /* flags do not indicate STOPPED */ + { + /* surely this can't happen... */ + printf_filtered ("procfs:%d -- process not stopped.\n", + __LINE__); + proc_prettyprint_flags (flags, 1); + error ("procfs: ...giving up..."); + } + } + + if (status) + store_waitstatus (status, wstat); + } + + return retval; +} + +/* Perform a partial transfer to/from the specified object. For + memory transfers, fall back to the old memory xfer functions. */ + +static LONGEST +procfs_xfer_partial (struct target_ops *ops, enum target_object object, + const char *annex, void *readbuf, + const void *writebuf, ULONGEST offset, LONGEST len) +{ + switch (object) + { + case TARGET_OBJECT_MEMORY: + if (readbuf) + return (*ops->to_xfer_memory) (offset, readbuf, len, 0/*write*/, + NULL, ops); + if (writebuf) + return (*ops->to_xfer_memory) (offset, readbuf, len, 1/*write*/, + NULL, ops); + return -1; + +#ifdef NEW_PROC_API + case TARGET_OBJECT_AUXV: + return procfs_xfer_auxv (ops, object, annex, readbuf, writebuf, + offset, len); +#endif + + default: + if (ops->beneath != NULL) + return ops->beneath->to_xfer_partial (ops->beneath, object, annex, + readbuf, writebuf, offset, len); + return -1; + } +} + + +/* Transfer LEN bytes between GDB address MYADDR and target address + MEMADDR. If DOWRITE is non-zero, transfer them to the target, + otherwise transfer them from the target. TARGET is unused. + + The return value is 0 if an error occurred or no bytes were + transferred. Otherwise, it will be a positive value which + indicates the number of bytes transferred between gdb and the + target. (Note that the interface also makes provisions for + negative values, but this capability isn't implemented here.) */ + +static int +procfs_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int dowrite, + struct mem_attrib *attrib, struct target_ops *target) +{ + procinfo *pi; + int nbytes = 0; + + /* Find procinfo for main process */ + pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0); + if (pi->as_fd == 0 && + open_procinfo_files (pi, FD_AS) == 0) + { + proc_warn (pi, "xfer_memory, open_proc_files", __LINE__); + return 0; + } + + if (lseek (pi->as_fd, (off_t) memaddr, SEEK_SET) == (off_t) memaddr) + { + if (dowrite) + { +#ifdef NEW_PROC_API + PROCFS_NOTE ("write memory: "); +#else + PROCFS_NOTE ("write memory: \n"); +#endif + nbytes = write (pi->as_fd, myaddr, len); + } + else + { + PROCFS_NOTE ("read memory: \n"); + nbytes = read (pi->as_fd, myaddr, len); + } + if (nbytes < 0) + { + nbytes = 0; + } + } + return nbytes; +} + +/* + * Function: invalidate_cache + * + * Called by target_resume before making child runnable. + * Mark cached registers and status's invalid. + * If there are "dirty" caches that need to be written back + * to the child process, do that. + * + * File descriptors are also cached. + * As they are a limited resource, we cannot hold onto them indefinitely. + * However, as they are expensive to open, we don't want to throw them + * away indescriminately either. As a compromise, we will keep the + * file descriptors for the parent process, but discard any file + * descriptors we may have accumulated for the threads. + * + * Return value: + * As this function is called by iterate_over_threads, it always + * returns zero (so that iterate_over_threads will keep iterating). + */ + + +static int +invalidate_cache (procinfo *parent, procinfo *pi, void *ptr) +{ + /* + * About to run the child; invalidate caches and do any other cleanup. + */ + +#if 0 + if (pi->gregs_dirty) + if (parent == NULL || + proc_get_current_thread (parent) != pi->tid) + if (!proc_set_gregs (pi)) /* flush gregs cache */ + proc_warn (pi, "target_resume, set_gregs", + __LINE__); + if (FP0_REGNUM >= 0) + if (pi->fpregs_dirty) + if (parent == NULL || + proc_get_current_thread (parent) != pi->tid) + if (!proc_set_fpregs (pi)) /* flush fpregs cache */ + proc_warn (pi, "target_resume, set_fpregs", + __LINE__); +#endif + + if (parent != NULL) + { + /* The presence of a parent indicates that this is an LWP. + Close any file descriptors that it might have open. + We don't do this to the master (parent) procinfo. */ + + close_procinfo_files (pi); + } + pi->gregs_valid = 0; + pi->fpregs_valid = 0; +#if 0 + pi->gregs_dirty = 0; + pi->fpregs_dirty = 0; +#endif + pi->status_valid = 0; + pi->threads_valid = 0; + + return 0; +} + +#if 0 +/* + * Function: make_signal_thread_runnable + * + * A callback function for iterate_over_threads. + * Find the asynchronous signal thread, and make it runnable. + * See if that helps matters any. + */ + +static int +make_signal_thread_runnable (procinfo *process, procinfo *pi, void *ptr) +{ +#ifdef PR_ASLWP + if (proc_flags (pi) & PR_ASLWP) + { + if (!proc_run_process (pi, 0, -1)) + proc_error (pi, "make_signal_thread_runnable", __LINE__); + return 1; + } +#endif + return 0; +} +#endif + +/* + * Function: target_resume + * + * Make the child process runnable. Normally we will then call + * procfs_wait and wait for it to stop again (unles gdb is async). + * + * Arguments: + * step: if true, then arrange for the child to stop again + * after executing a single instruction. + * signo: if zero, then cancel any pending signal. + * If non-zero, then arrange for the indicated signal + * to be delivered to the child when it runs. + * pid: if -1, then allow any child thread to run. + * if non-zero, then allow only the indicated thread to run. + ******* (not implemented yet) + */ + +static void +procfs_resume (ptid_t ptid, int step, enum target_signal signo) +{ + procinfo *pi, *thread; + int native_signo; + + /* 2.1: + prrun.prflags |= PRSVADDR; + prrun.pr_vaddr = $PC; set resume address + prrun.prflags |= PRSTRACE; trace signals in pr_trace (all) + prrun.prflags |= PRSFAULT; trace faults in pr_fault (all but PAGE) + prrun.prflags |= PRCFAULT; clear current fault. + + PRSTRACE and PRSFAULT can be done by other means + (proc_trace_signals, proc_trace_faults) + PRSVADDR is unnecessary. + PRCFAULT may be replaced by a PIOCCFAULT call (proc_clear_current_fault) + This basically leaves PRSTEP and PRCSIG. + PRCSIG is like PIOCSSIG (proc_clear_current_signal). + So basically PR_STEP is the sole argument that must be passed + to proc_run_process (for use in the prrun struct by ioctl). */ + + /* Find procinfo for main process */ + pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0); + + /* First cut: ignore pid argument */ + errno = 0; + + /* Convert signal to host numbering. */ + if (signo == 0 || + (signo == TARGET_SIGNAL_STOP && pi->ignore_next_sigstop)) + native_signo = 0; + else + native_signo = target_signal_to_host (signo); + + pi->ignore_next_sigstop = 0; + + /* Running the process voids all cached registers and status. */ + /* Void the threads' caches first */ + proc_iterate_over_threads (pi, invalidate_cache, NULL); + /* Void the process procinfo's caches. */ + invalidate_cache (NULL, pi, NULL); + + if (PIDGET (ptid) != -1) + { + /* Resume a specific thread, presumably suppressing the others. */ + thread = find_procinfo (PIDGET (ptid), TIDGET (ptid)); + if (thread != NULL) + { + if (thread->tid != 0) + { + /* We're to resume a specific thread, and not the others. + * Set the child process's PR_ASYNC flag. + */ +#ifdef PR_ASYNC + if (!proc_set_async (pi)) + proc_error (pi, "target_resume, set_async", __LINE__); +#endif +#if 0 + proc_iterate_over_threads (pi, + make_signal_thread_runnable, + NULL); +#endif + pi = thread; /* substitute the thread's procinfo for run */ + } + } + } + + if (!proc_run_process (pi, step, native_signo)) + { + if (errno == EBUSY) + warning ("resume: target already running. Pretend to resume, and hope for the best!\n"); + else + proc_error (pi, "target_resume", __LINE__); + } +} + +/* + * Function: register_gdb_signals + * + * Traverse the list of signals that GDB knows about + * (see "handle" command), and arrange for the target + * to be stopped or not, according to these settings. + * + * Returns non-zero for success, zero for failure. + */ + +static int +register_gdb_signals (procinfo *pi, gdb_sigset_t *signals) +{ + int signo; + + for (signo = 0; signo < NSIG; signo ++) + if (signal_stop_state (target_signal_from_host (signo)) == 0 && + signal_print_state (target_signal_from_host (signo)) == 0 && + signal_pass_state (target_signal_from_host (signo)) == 1) + prdelset (signals, signo); + else + praddset (signals, signo); + + return proc_set_traced_signals (pi, signals); +} + +/* + * Function: target_notice_signals + * + * Set up to trace signals in the child process. + */ + +static void +procfs_notice_signals (ptid_t ptid) +{ + gdb_sigset_t signals; + procinfo *pi = find_procinfo_or_die (PIDGET (ptid), 0); + + if (proc_get_traced_signals (pi, &signals) && + register_gdb_signals (pi, &signals)) + return; + else + proc_error (pi, "notice_signals", __LINE__); +} + +/* + * Function: target_files_info + * + * Print status information about the child process. + */ + +static void +procfs_files_info (struct target_ops *ignore) +{ + printf_filtered ("\tUsing the running image of %s %s via /proc.\n", + attach_flag? "attached": "child", + target_pid_to_str (inferior_ptid)); +} + +/* + * Function: target_open + * + * A dummy: you don't open procfs. + */ + +static void +procfs_open (char *args, int from_tty) +{ + error ("Use the \"run\" command to start a Unix child process."); +} + +/* + * Function: target_can_run + * + * This tells GDB that this target vector can be invoked + * for "run" or "attach". + */ + +int procfs_suppress_run = 0; /* Non-zero if procfs should pretend not to + be a runnable target. Used by targets + that can sit atop procfs, such as solaris + thread support. */ + + +static int +procfs_can_run (void) +{ + /* This variable is controlled by modules that sit atop procfs that + may layer their own process structure atop that provided here. + sol-thread.c does this because of the Solaris two-level thread + model. */ + + /* NOTE: possibly obsolete -- use the thread_stratum approach instead. */ + + return !procfs_suppress_run; +} + +/* + * Function: target_stop + * + * Stop the child process asynchronously, as when the + * gdb user types control-c or presses a "stop" button. + * + * Works by sending kill(SIGINT) to the child's process group. + */ + +static void +procfs_stop (void) +{ + kill (-inferior_process_group, SIGINT); +} + +/* + * Function: unconditionally_kill_inferior + * + * Make it die. Wait for it to die. Clean up after it. + * Note: this should only be applied to the real process, + * not to an LWP, because of the check for parent-process. + * If we need this to work for an LWP, it needs some more logic. + */ + +static void +unconditionally_kill_inferior (procinfo *pi) +{ + int parent_pid; + + parent_pid = proc_parent_pid (pi); +#ifdef PROCFS_NEED_CLEAR_CURSIG_FOR_KILL + /* FIXME: use access functions */ + /* Alpha OSF/1-3.x procfs needs a clear of the current signal + before the PIOCKILL, otherwise it might generate a corrupted core + file for the inferior. */ + if (ioctl (pi->ctl_fd, PIOCSSIG, NULL) < 0) + { + printf_filtered ("unconditionally_kill: SSIG failed!\n"); + } +#endif +#ifdef PROCFS_NEED_PIOCSSIG_FOR_KILL + /* Alpha OSF/1-2.x procfs needs a PIOCSSIG call with a SIGKILL signal + to kill the inferior, otherwise it might remain stopped with a + pending SIGKILL. + We do not check the result of the PIOCSSIG, the inferior might have + died already. */ + { + gdb_siginfo_t newsiginfo; + + memset ((char *) &newsiginfo, 0, sizeof (newsiginfo)); + newsiginfo.si_signo = SIGKILL; + newsiginfo.si_code = 0; + newsiginfo.si_errno = 0; + newsiginfo.si_pid = getpid (); + newsiginfo.si_uid = getuid (); + /* FIXME: use proc_set_current_signal */ + ioctl (pi->ctl_fd, PIOCSSIG, &newsiginfo); + } +#else /* PROCFS_NEED_PIOCSSIG_FOR_KILL */ + if (!proc_kill (pi, SIGKILL)) + proc_error (pi, "unconditionally_kill, proc_kill", __LINE__); +#endif /* PROCFS_NEED_PIOCSSIG_FOR_KILL */ + destroy_procinfo (pi); + + /* If pi is GDB's child, wait for it to die. */ + if (parent_pid == getpid ()) + /* FIXME: should we use waitpid to make sure we get the right event? + Should we check the returned event? */ + { +#if 0 + int status, ret; + + ret = waitpid (pi->pid, &status, 0); +#else + wait (NULL); +#endif + } +} + +/* + * Function: target_kill_inferior + * + * We're done debugging it, and we want it to go away. + * Then we want GDB to forget all about it. + */ + +static void +procfs_kill_inferior (void) +{ + if (!ptid_equal (inferior_ptid, null_ptid)) /* ? */ + { + /* Find procinfo for main process */ + procinfo *pi = find_procinfo (PIDGET (inferior_ptid), 0); + + if (pi) + unconditionally_kill_inferior (pi); + target_mourn_inferior (); + } +} + +/* + * Function: target_mourn_inferior + * + * Forget we ever debugged this thing! + */ + +static void +procfs_mourn_inferior (void) +{ + procinfo *pi; + + if (!ptid_equal (inferior_ptid, null_ptid)) + { + /* Find procinfo for main process */ + pi = find_procinfo (PIDGET (inferior_ptid), 0); + if (pi) + destroy_procinfo (pi); + } + unpush_target (&procfs_ops); + generic_mourn_inferior (); +} + +/* + * Function: init_inferior + * + * When GDB forks to create a runnable inferior process, + * this function is called on the parent side of the fork. + * It's job is to do whatever is necessary to make the child + * ready to be debugged, and then wait for the child to synchronize. + */ + +static void +procfs_init_inferior (int pid) +{ + procinfo *pi; + gdb_sigset_t signals; + int fail; + + /* This routine called on the parent side (GDB side) + after GDB forks the inferior. */ + + push_target (&procfs_ops); + + if ((pi = create_procinfo (pid, 0)) == NULL) + perror ("procfs: out of memory in 'init_inferior'"); + + if (!open_procinfo_files (pi, FD_CTL)) + proc_error (pi, "init_inferior, open_proc_files", __LINE__); + + /* + xmalloc // done + open_procinfo_files // done + link list // done + prfillset (trace) + procfs_notice_signals + prfillset (fault) + prdelset (FLTPAGE) + PIOCWSTOP + PIOCSFAULT + */ + + /* If not stopped yet, wait for it to stop. */ + if (!(proc_flags (pi) & PR_STOPPED) && + !(proc_wait_for_stop (pi))) + dead_procinfo (pi, "init_inferior: wait_for_stop failed", KILL); + + /* Save some of the /proc state to be restored if we detach. */ + /* FIXME: Why? In case another debugger was debugging it? + We're it's parent, for Ghu's sake! */ + if (!proc_get_traced_signals (pi, &pi->saved_sigset)) + proc_error (pi, "init_inferior, get_traced_signals", __LINE__); + if (!proc_get_held_signals (pi, &pi->saved_sighold)) + proc_error (pi, "init_inferior, get_held_signals", __LINE__); + if (!proc_get_traced_faults (pi, &pi->saved_fltset)) + proc_error (pi, "init_inferior, get_traced_faults", __LINE__); + if (!proc_get_traced_sysentry (pi, pi->saved_entryset)) + proc_error (pi, "init_inferior, get_traced_sysentry", __LINE__); + if (!proc_get_traced_sysexit (pi, pi->saved_exitset)) + proc_error (pi, "init_inferior, get_traced_sysexit", __LINE__); + + /* Register to trace selected signals in the child. */ + prfillset (&signals); + if (!register_gdb_signals (pi, &signals)) + proc_error (pi, "init_inferior, register_signals", __LINE__); + + if ((fail = procfs_debug_inferior (pi)) != 0) + proc_error (pi, "init_inferior (procfs_debug_inferior)", fail); + + /* FIXME: logically, we should really be turning OFF run-on-last-close, + and possibly even turning ON kill-on-last-close at this point. But + I can't make that change without careful testing which I don't have + time to do right now... */ + /* Turn on run-on-last-close flag so that the child + will die if GDB goes away for some reason. */ + if (!proc_set_run_on_last_close (pi)) + proc_error (pi, "init_inferior, set_RLC", __LINE__); + + /* The 'process ID' we return to GDB is composed of + the actual process ID plus the lwp ID. */ + inferior_ptid = MERGEPID (pi->pid, proc_get_current_thread (pi)); + + /* Typically two, one trap to exec the shell, one to exec the + program being debugged. Defined by "inferior.h". */ + startup_inferior (START_INFERIOR_TRAPS_EXPECTED); +} + +/* + * Function: set_exec_trap + * + * When GDB forks to create a new process, this function is called + * on the child side of the fork before GDB exec's the user program. + * Its job is to make the child minimally debuggable, so that the + * parent GDB process can connect to the child and take over. + * This function should do only the minimum to make that possible, + * and to synchronize with the parent process. The parent process + * should take care of the details. + */ + +static void +procfs_set_exec_trap (void) +{ + /* This routine called on the child side (inferior side) + after GDB forks the inferior. It must use only local variables, + because it may be sharing data space with its parent. */ + + procinfo *pi; + sysset_t *exitset; + + if ((pi = create_procinfo (getpid (), 0)) == NULL) + perror_with_name ("procfs: create_procinfo failed in child."); + + if (open_procinfo_files (pi, FD_CTL) == 0) + { + proc_warn (pi, "set_exec_trap, open_proc_files", __LINE__); + gdb_flush (gdb_stderr); + /* no need to call "dead_procinfo", because we're going to exit. */ + _exit (127); + } + +#ifdef PRFS_STOPEXEC /* defined on OSF */ + /* OSF method for tracing exec syscalls. Quoting: + Under Alpha OSF/1 we have to use a PIOCSSPCACT ioctl to trace + exits from exec system calls because of the user level loader. */ + /* FIXME: make nice and maybe move into an access function. */ + { + int prfs_flags; + + if (ioctl (pi->ctl_fd, PIOCGSPCACT, &prfs_flags) < 0) + { + proc_warn (pi, "set_exec_trap (PIOCGSPCACT)", __LINE__); + gdb_flush (gdb_stderr); + _exit (127); + } + prfs_flags |= PRFS_STOPEXEC; + + if (ioctl (pi->ctl_fd, PIOCSSPCACT, &prfs_flags) < 0) + { + proc_warn (pi, "set_exec_trap (PIOCSSPCACT)", __LINE__); + gdb_flush (gdb_stderr); + _exit (127); + } + } +#else /* not PRFS_STOPEXEC */ + /* Everyone else's (except OSF) method for tracing exec syscalls */ + /* GW: Rationale... + Not all systems with /proc have all the exec* syscalls with the same + names. On the SGI, for example, there is no SYS_exec, but there + *is* a SYS_execv. So, we try to account for that. */ + + exitset = sysset_t_alloc (pi); + gdb_premptysysset (exitset); +#ifdef SYS_exec + gdb_praddsysset (exitset, SYS_exec); +#endif +#ifdef SYS_execve + gdb_praddsysset (exitset, SYS_execve); +#endif +#ifdef SYS_execv + gdb_praddsysset (exitset, SYS_execv); +#endif +#ifdef DYNAMIC_SYSCALLS + { + int callnum = find_syscall (pi, "execve"); + + if (callnum >= 0) + gdb_praddsysset (exitset, callnum); + + callnum = find_syscall (pi, "ra_execve"); + if (callnum >= 0) + gdb_praddsysset (exitset, callnum); + } +#endif /* DYNAMIC_SYSCALLS */ + + if (!proc_set_traced_sysexit (pi, exitset)) + { + proc_warn (pi, "set_exec_trap, set_traced_sysexit", __LINE__); + gdb_flush (gdb_stderr); + _exit (127); + } +#endif /* PRFS_STOPEXEC */ + + /* FIXME: should this be done in the parent instead? */ + /* Turn off inherit on fork flag so that all grand-children + of gdb start with tracing flags cleared. */ + if (!proc_unset_inherit_on_fork (pi)) + proc_warn (pi, "set_exec_trap, unset_inherit", __LINE__); + + /* Turn off run on last close flag, so that the child process + cannot run away just because we close our handle on it. + We want it to wait for the parent to attach. */ + if (!proc_unset_run_on_last_close (pi)) + proc_warn (pi, "set_exec_trap, unset_RLC", __LINE__); + + /* FIXME: No need to destroy the procinfo -- + we have our own address space, and we're about to do an exec! */ + /*destroy_procinfo (pi);*/ +} + +/* + * Function: create_inferior + * + * This function is called BEFORE gdb forks the inferior process. + * Its only real responsibility is to set things up for the fork, + * and tell GDB which two functions to call after the fork (one + * for the parent, and one for the child). + * + * This function does a complicated search for a unix shell program, + * which it then uses to parse arguments and environment variables + * to be sent to the child. I wonder whether this code could not + * be abstracted out and shared with other unix targets such as + * infptrace? + */ + +static void +procfs_create_inferior (char *exec_file, char *allargs, char **env) +{ + char *shell_file = getenv ("SHELL"); + char *tryname; + if (shell_file != NULL && strchr (shell_file, '/') == NULL) + { + + /* We will be looking down the PATH to find shell_file. If we + just do this the normal way (via execlp, which operates by + attempting an exec for each element of the PATH until it + finds one which succeeds), then there will be an exec for + each failed attempt, each of which will cause a PR_SYSEXIT + stop, and we won't know how to distinguish the PR_SYSEXIT's + for these failed execs with the ones for successful execs + (whether the exec has succeeded is stored at that time in the + carry bit or some such architecture-specific and + non-ABI-specified place). + + So I can't think of anything better than to search the PATH + now. This has several disadvantages: (1) There is a race + condition; if we find a file now and it is deleted before we + exec it, we lose, even if the deletion leaves a valid file + further down in the PATH, (2) there is no way to know exactly + what an executable (in the sense of "capable of being + exec'd") file is. Using access() loses because it may lose + if the caller is the superuser; failing to use it loses if + there are ACLs or some such. */ + + char *p; + char *p1; + /* FIXME-maybe: might want "set path" command so user can change what + path is used from within GDB. */ + char *path = getenv ("PATH"); + int len; + struct stat statbuf; + + if (path == NULL) + path = "/bin:/usr/bin"; + + tryname = alloca (strlen (path) + strlen (shell_file) + 2); + for (p = path; p != NULL; p = p1 ? p1 + 1: NULL) + { + p1 = strchr (p, ':'); + if (p1 != NULL) + len = p1 - p; + else + len = strlen (p); + strncpy (tryname, p, len); + tryname[len] = '\0'; + strcat (tryname, "/"); + strcat (tryname, shell_file); + if (access (tryname, X_OK) < 0) + continue; + if (stat (tryname, &statbuf) < 0) + continue; + if (!S_ISREG (statbuf.st_mode)) + /* We certainly need to reject directories. I'm not quite + as sure about FIFOs, sockets, etc., but I kind of doubt + that people want to exec() these things. */ + continue; + break; + } + if (p == NULL) + /* Not found. This must be an error rather than merely passing + the file to execlp(), because execlp() would try all the + exec()s, causing GDB to get confused. */ + error ("procfs:%d -- Can't find shell %s in PATH", + __LINE__, shell_file); + + shell_file = tryname; + } + + fork_inferior (exec_file, allargs, env, procfs_set_exec_trap, + procfs_init_inferior, NULL, shell_file); + + /* We are at the first instruction we care about. */ + /* Pedal to the metal... */ + + proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0); +} + +/* + * Function: notice_thread + * + * Callback for find_new_threads. + * Calls "add_thread". + */ + +static int +procfs_notice_thread (procinfo *pi, procinfo *thread, void *ptr) +{ + ptid_t gdb_threadid = MERGEPID (pi->pid, thread->tid); + + if (!in_thread_list (gdb_threadid)) + add_thread (gdb_threadid); + + return 0; +} + +/* + * Function: target_find_new_threads + * + * Query all the threads that the target knows about, + * and give them back to GDB to add to its list. + */ + +void +procfs_find_new_threads (void) +{ + procinfo *pi; + + /* Find procinfo for main process */ + pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0); + proc_update_threads (pi); + proc_iterate_over_threads (pi, procfs_notice_thread, NULL); +} + +/* + * Function: target_thread_alive + * + * Return true if the thread is still 'alive'. + * + * This guy doesn't really seem to be doing his job. + * Got to investigate how to tell when a thread is really gone. + */ + +static int +procfs_thread_alive (ptid_t ptid) +{ + int proc, thread; + procinfo *pi; + + proc = PIDGET (ptid); + thread = TIDGET (ptid); + /* If I don't know it, it ain't alive! */ + if ((pi = find_procinfo (proc, thread)) == NULL) + return 0; + + /* If I can't get its status, it ain't alive! + What's more, I need to forget about it! */ + if (!proc_get_status (pi)) + { + destroy_procinfo (pi); + return 0; + } + /* I couldn't have got its status if it weren't alive, so it's alive. */ + return 1; +} + +/* + * Function: target_pid_to_str + * + * Return a string to be used to identify the thread in + * the "info threads" display. + */ + +char * +procfs_pid_to_str (ptid_t ptid) +{ + static char buf[80]; + int proc, thread; + procinfo *pi; + + proc = PIDGET (ptid); + thread = TIDGET (ptid); + pi = find_procinfo (proc, thread); + + if (thread == 0) + sprintf (buf, "Process %d", proc); + else + sprintf (buf, "LWP %d", thread); + return &buf[0]; +} + +/* + * Function: procfs_set_watchpoint + * Insert a watchpoint + */ + +int +procfs_set_watchpoint (ptid_t ptid, CORE_ADDR addr, int len, int rwflag, + int after) +{ +#ifndef UNIXWARE +#ifndef AIX5 + int pflags = 0; + procinfo *pi; + + pi = find_procinfo_or_die (PIDGET (ptid) == -1 ? + PIDGET (inferior_ptid) : PIDGET (ptid), 0); + + /* Translate from GDB's flags to /proc's */ + if (len > 0) /* len == 0 means delete watchpoint */ + { + switch (rwflag) { /* FIXME: need an enum! */ + case hw_write: /* default watchpoint (write) */ + pflags = WRITE_WATCHFLAG; + break; + case hw_read: /* read watchpoint */ + pflags = READ_WATCHFLAG; + break; + case hw_access: /* access watchpoint */ + pflags = READ_WATCHFLAG | WRITE_WATCHFLAG; + break; + case hw_execute: /* execution HW breakpoint */ + pflags = EXEC_WATCHFLAG; + break; + default: /* Something weird. Return error. */ + return -1; + } + if (after) /* Stop after r/w access is completed. */ + pflags |= AFTER_WATCHFLAG; + } + + if (!proc_set_watchpoint (pi, addr, len, pflags)) + { + if (errno == E2BIG) /* Typical error for no resources */ + return -1; /* fail */ + /* GDB may try to remove the same watchpoint twice. + If a remove request returns no match, don't error. */ + if (errno == ESRCH && len == 0) + return 0; /* ignore */ + proc_error (pi, "set_watchpoint", __LINE__); + } +#endif /* AIX5 */ +#endif /* UNIXWARE */ + return 0; +} + +/* Return non-zero if we can set a hardware watchpoint of type TYPE. TYPE + is one of bp_hardware_watchpoint, bp_read_watchpoint, bp_write_watchpoint, + or bp_hardware_watchpoint. CNT is the number of watchpoints used so + far. + + Note: procfs_can_use_hw_breakpoint() is not yet used by all + procfs.c targets due to the fact that some of them still define + TARGET_CAN_USE_HARDWARE_WATCHPOINT. */ + +static int +procfs_can_use_hw_breakpoint (int type, int cnt, int othertype) +{ +#ifndef TARGET_HAS_HARDWARE_WATCHPOINTS + return 0; +#else + /* Due to the way that proc_set_watchpoint() is implemented, host + and target pointers must be of the same size. If they are not, + we can't use hardware watchpoints. This limitation is due to the + fact that proc_set_watchpoint() calls + procfs_address_to_host_pointer(); a close inspection of + procfs_address_to_host_pointer will reveal that an internal error + will be generated when the host and target pointer sizes are + different. */ + if (sizeof (void *) != TYPE_LENGTH (builtin_type_void_data_ptr)) + return 0; + + /* Other tests here??? */ + + return 1; +#endif +} + +/* + * Function: stopped_by_watchpoint + * + * Returns non-zero if process is stopped on a hardware watchpoint fault, + * else returns zero. + */ + +int +procfs_stopped_by_watchpoint (ptid_t ptid) +{ + procinfo *pi; + + pi = find_procinfo_or_die (PIDGET (ptid) == -1 ? + PIDGET (inferior_ptid) : PIDGET (ptid), 0); + + if (!pi) /* If no process, then not stopped by watchpoint! */ + return 0; + + if (proc_flags (pi) & (PR_STOPPED | PR_ISTOP)) + { + if (proc_why (pi) == PR_FAULTED) + { +#ifdef FLTWATCH + if (proc_what (pi) == FLTWATCH) + return 1; +#endif +#ifdef FLTKWATCH + if (proc_what (pi) == FLTKWATCH) + return 1; +#endif + } + } + return 0; +} + +#ifdef TM_I386SOL2_H +/* + * Function: procfs_find_LDT_entry + * + * Input: + * ptid_t ptid; // The GDB-style pid-plus-LWP. + * + * Return: + * pointer to the corresponding LDT entry. + */ + +struct ssd * +procfs_find_LDT_entry (ptid_t ptid) +{ + gdb_gregset_t *gregs; + int key; + procinfo *pi; + + /* Find procinfo for the lwp. */ + if ((pi = find_procinfo (PIDGET (ptid), TIDGET (ptid))) == NULL) + { + warning ("procfs_find_LDT_entry: could not find procinfo for %d:%d.", + PIDGET (ptid), TIDGET (ptid)); + return NULL; + } + /* get its general registers. */ + if ((gregs = proc_get_gregs (pi)) == NULL) + { + warning ("procfs_find_LDT_entry: could not read gregs for %d:%d.", + PIDGET (ptid), TIDGET (ptid)); + return NULL; + } + /* Now extract the GS register's lower 16 bits. */ + key = (*gregs)[GS] & 0xffff; + + /* Find the matching entry and return it. */ + return proc_get_LDT_entry (pi, key); +} +#endif /* TM_I386SOL2_H */ + +/* + * Memory Mappings Functions: + */ + +/* + * Function: iterate_over_mappings + * + * Call a callback function once for each mapping, passing it the mapping, + * an optional secondary callback function, and some optional opaque data. + * Quit and return the first non-zero value returned from the callback. + * + * Arguments: + * pi -- procinfo struct for the process to be mapped. + * func -- callback function to be called by this iterator. + * data -- optional opaque data to be passed to the callback function. + * child_func -- optional secondary function pointer to be passed + * to the child function. + * + * Return: First non-zero return value from the callback function, + * or zero. + */ + +static int +iterate_over_mappings (procinfo *pi, int (*child_func) (), void *data, + int (*func) (struct prmap *map, + int (*child_func) (), + void *data)) +{ + char pathname[MAX_PROC_NAME_SIZE]; + struct prmap *prmaps; + struct prmap *prmap; + int funcstat; + int map_fd; + int nmap; +#ifdef NEW_PROC_API + struct stat sbuf; +#endif + + /* Get the number of mappings, allocate space, + and read the mappings into prmaps. */ +#ifdef NEW_PROC_API + /* Open map fd. */ + sprintf (pathname, "/proc/%d/map", pi->pid); + if ((map_fd = open (pathname, O_RDONLY)) < 0) + proc_error (pi, "iterate_over_mappings (open)", __LINE__); + + /* Make sure it gets closed again. */ + make_cleanup_close (map_fd); + + /* Use stat to determine the file size, and compute + the number of prmap_t objects it contains. */ + if (fstat (map_fd, &sbuf) != 0) + proc_error (pi, "iterate_over_mappings (fstat)", __LINE__); + + nmap = sbuf.st_size / sizeof (prmap_t); + prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); + if (read (map_fd, (char *) prmaps, nmap * sizeof (*prmaps)) + != (nmap * sizeof (*prmaps))) + proc_error (pi, "iterate_over_mappings (read)", __LINE__); +#else + /* Use ioctl command PIOCNMAP to get number of mappings. */ + if (ioctl (pi->ctl_fd, PIOCNMAP, &nmap) != 0) + proc_error (pi, "iterate_over_mappings (PIOCNMAP)", __LINE__); + + prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); + if (ioctl (pi->ctl_fd, PIOCMAP, prmaps) != 0) + proc_error (pi, "iterate_over_mappings (PIOCMAP)", __LINE__); +#endif + + for (prmap = prmaps; nmap > 0; prmap++, nmap--) + if ((funcstat = (*func) (prmap, child_func, data)) != 0) + return funcstat; + + return 0; +} + +/* + * Function: solib_mappings_callback + * + * Calls the supplied callback function once for each mapped address + * space in the process. The callback function receives an open + * file descriptor for the file corresponding to that mapped + * address space (if there is one), and the base address of the + * mapped space. Quit when the callback function returns a + * nonzero value, or at teh end of the mappings. + * + * Returns: the first non-zero return value of the callback function, + * or zero. + */ + +int solib_mappings_callback (struct prmap *map, + int (*func) (int, CORE_ADDR), + void *data) +{ + procinfo *pi = data; + int fd; + +#ifdef NEW_PROC_API + char name[MAX_PROC_NAME_SIZE + sizeof (map->pr_mapname)]; + + if (map->pr_vaddr == 0 && map->pr_size == 0) + return -1; /* sanity */ + + if (map->pr_mapname[0] == 0) + { + fd = -1; /* no map file */ + } + else + { + sprintf (name, "/proc/%d/object/%s", pi->pid, map->pr_mapname); + /* Note: caller's responsibility to close this fd! */ + fd = open_with_retry (name, O_RDONLY); + /* Note: we don't test the above call for failure; + we just pass the FD on as given. Sometimes there is + no file, so the open may return failure, but that's + not a problem. */ + } +#else + fd = ioctl (pi->ctl_fd, PIOCOPENM, &map->pr_vaddr); + /* Note: we don't test the above call for failure; + we just pass the FD on as given. Sometimes there is + no file, so the ioctl may return failure, but that's + not a problem. */ +#endif + return (*func) (fd, (CORE_ADDR) map->pr_vaddr); +} + +/* + * Function: proc_iterate_over_mappings + * + * Uses the unified "iterate_over_mappings" function + * to implement the exported interface to solib-svr4.c. + * + * Given a pointer to a function, call that function once for every + * mapped address space in the process. The callback function + * receives an open file descriptor for the file corresponding to + * that mapped address space (if there is one), and the base address + * of the mapped space. Quit when the callback function returns a + * nonzero value, or at teh end of the mappings. + * + * Returns: the first non-zero return value of the callback function, + * or zero. + */ + +int +proc_iterate_over_mappings (int (*func) (int, CORE_ADDR)) +{ + procinfo *pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0); + + return iterate_over_mappings (pi, func, pi, solib_mappings_callback); +} + +/* + * Function: find_memory_regions_callback + * + * Implements the to_find_memory_regions method. + * Calls an external function for each memory region. + * External function will have the signiture: + * + * int callback (CORE_ADDR vaddr, + * unsigned long size, + * int read, int write, int execute, + * void *data); + * + * Returns the integer value returned by the callback. + */ + +static int +find_memory_regions_callback (struct prmap *map, + int (*func) (CORE_ADDR, + unsigned long, + int, int, int, + void *), + void *data) +{ + return (*func) ((CORE_ADDR) map->pr_vaddr, + map->pr_size, + (map->pr_mflags & MA_READ) != 0, + (map->pr_mflags & MA_WRITE) != 0, + (map->pr_mflags & MA_EXEC) != 0, + data); +} + +/* + * Function: proc_find_memory_regions + * + * External interface. Calls a callback function once for each + * mapped memory region in the child process, passing as arguments + * CORE_ADDR virtual_address, + * unsigned long size, + * int read, TRUE if region is readable by the child + * int write, TRUE if region is writable by the child + * int execute TRUE if region is executable by the child. + * + * Stops iterating and returns the first non-zero value + * returned by the callback. + */ + +static int +proc_find_memory_regions (int (*func) (CORE_ADDR, + unsigned long, + int, int, int, + void *), + void *data) +{ + procinfo *pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0); + + return iterate_over_mappings (pi, func, data, + find_memory_regions_callback); +} + +/* + * Function: mappingflags + * + * Returns an ascii representation of a memory mapping's flags. + */ + +static char * +mappingflags (long flags) +{ + static char asciiflags[8]; + + strcpy (asciiflags, "-------"); +#if defined (MA_PHYS) + if (flags & MA_PHYS) + asciiflags[0] = 'd'; +#endif + if (flags & MA_STACK) + asciiflags[1] = 's'; + if (flags & MA_BREAK) + asciiflags[2] = 'b'; + if (flags & MA_SHARED) + asciiflags[3] = 's'; + if (flags & MA_READ) + asciiflags[4] = 'r'; + if (flags & MA_WRITE) + asciiflags[5] = 'w'; + if (flags & MA_EXEC) + asciiflags[6] = 'x'; + return (asciiflags); +} + +/* + * Function: info_mappings_callback + * + * Callback function, does the actual work for 'info proc mappings'. + */ + +static int +info_mappings_callback (struct prmap *map, int (*ignore) (), void *unused) +{ + char *data_fmt_string; + + if (TARGET_ADDR_BIT == 32) + data_fmt_string = "\t%#10lx %#10lx %#10x %#10x %7s\n"; + else + data_fmt_string = " %#18lx %#18lx %#10x %#10x %7s\n"; + + printf_filtered (data_fmt_string, + (unsigned long) map->pr_vaddr, + (unsigned long) map->pr_vaddr + map->pr_size - 1, + map->pr_size, +#ifdef PCAGENT /* Horrible hack: only defined on Solaris 2.6+ */ + (unsigned int) map->pr_offset, +#else + map->pr_off, +#endif + mappingflags (map->pr_mflags)); + + return 0; +} + +/* + * Function: info_proc_mappings + * + * Implement the "info proc mappings" subcommand. + */ + +static void +info_proc_mappings (procinfo *pi, int summary) +{ + char *header_fmt_string; + + if (TARGET_PTR_BIT == 32) + header_fmt_string = "\t%10s %10s %10s %10s %7s\n"; + else + header_fmt_string = " %18s %18s %10s %10s %7s\n"; + + if (summary) + return; /* No output for summary mode. */ + + printf_filtered ("Mapped address spaces:\n\n"); + printf_filtered (header_fmt_string, + "Start Addr", + " End Addr", + " Size", + " Offset", + "Flags"); + + iterate_over_mappings (pi, NULL, NULL, info_mappings_callback); + printf_filtered ("\n"); +} + +/* + * Function: info_proc_cmd + * + * Implement the "info proc" command. + */ + +static void +info_proc_cmd (char *args, int from_tty) +{ + struct cleanup *old_chain; + procinfo *process = NULL; + procinfo *thread = NULL; + char **argv = NULL; + char *tmp = NULL; + int pid = 0; + int tid = 0; + int mappings = 0; + + old_chain = make_cleanup (null_cleanup, 0); + if (args) + { + if ((argv = buildargv (args)) == NULL) + nomem (0); + else + make_cleanup_freeargv (argv); + } + while (argv != NULL && *argv != NULL) + { + if (isdigit (argv[0][0])) + { + pid = strtoul (argv[0], &tmp, 10); + if (*tmp == '/') + tid = strtoul (++tmp, NULL, 10); + } + else if (argv[0][0] == '/') + { + tid = strtoul (argv[0] + 1, NULL, 10); + } + else if (strncmp (argv[0], "mappings", strlen (argv[0])) == 0) + { + mappings = 1; + } + else + { + /* [...] */ + } + argv++; + } + if (pid == 0) + pid = PIDGET (inferior_ptid); + if (pid == 0) + error ("No current process: you must name one."); + else + { + /* Have pid, will travel. + First see if it's a process we're already debugging. */ + process = find_procinfo (pid, 0); + if (process == NULL) + { + /* No. So open a procinfo for it, but + remember to close it again when finished. */ + process = create_procinfo (pid, 0); + make_cleanup (do_destroy_procinfo_cleanup, process); + if (!open_procinfo_files (process, FD_CTL)) + proc_error (process, "info proc, open_procinfo_files", __LINE__); + } + } + if (tid != 0) + thread = create_procinfo (pid, tid); + + if (process) + { + printf_filtered ("process %d flags:\n", process->pid); + proc_prettyprint_flags (proc_flags (process), 1); + if (proc_flags (process) & (PR_STOPPED | PR_ISTOP)) + proc_prettyprint_why (proc_why (process), proc_what (process), 1); + if (proc_get_nthreads (process) > 1) + printf_filtered ("Process has %d threads.\n", + proc_get_nthreads (process)); + } + if (thread) + { + printf_filtered ("thread %d flags:\n", thread->tid); + proc_prettyprint_flags (proc_flags (thread), 1); + if (proc_flags (thread) & (PR_STOPPED | PR_ISTOP)) + proc_prettyprint_why (proc_why (thread), proc_what (thread), 1); + } + + if (mappings) + { + info_proc_mappings (process, 0); + } + + do_cleanups (old_chain); +} + +static void +proc_trace_syscalls (char *args, int from_tty, int entry_or_exit, int mode) +{ + procinfo *pi; + sysset_t *sysset; + int syscallnum = 0; + + if (PIDGET (inferior_ptid) <= 0) + error ("you must be debugging a process to use this command."); + + if (args == NULL || args[0] == 0) + error_no_arg ("system call to trace"); + + pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0); + if (isdigit (args[0])) + { + syscallnum = atoi (args); + if (entry_or_exit == PR_SYSENTRY) + sysset = proc_get_traced_sysentry (pi, NULL); + else + sysset = proc_get_traced_sysexit (pi, NULL); + + if (sysset == NULL) + proc_error (pi, "proc-trace, get_traced_sysset", __LINE__); + + if (mode == FLAG_SET) + gdb_praddsysset (sysset, syscallnum); + else + gdb_prdelsysset (sysset, syscallnum); + + if (entry_or_exit == PR_SYSENTRY) + { + if (!proc_set_traced_sysentry (pi, sysset)) + proc_error (pi, "proc-trace, set_traced_sysentry", __LINE__); + } + else + { + if (!proc_set_traced_sysexit (pi, sysset)) + proc_error (pi, "proc-trace, set_traced_sysexit", __LINE__); + } + } +} + +static void +proc_trace_sysentry_cmd (char *args, int from_tty) +{ + proc_trace_syscalls (args, from_tty, PR_SYSENTRY, FLAG_SET); +} + +static void +proc_trace_sysexit_cmd (char *args, int from_tty) +{ + proc_trace_syscalls (args, from_tty, PR_SYSEXIT, FLAG_SET); +} + +static void +proc_untrace_sysentry_cmd (char *args, int from_tty) +{ + proc_trace_syscalls (args, from_tty, PR_SYSENTRY, FLAG_RESET); +} + +static void +proc_untrace_sysexit_cmd (char *args, int from_tty) +{ + proc_trace_syscalls (args, from_tty, PR_SYSEXIT, FLAG_RESET); +} + + +void +_initialize_procfs (void) +{ + init_procfs_ops (); + add_target (&procfs_ops); + add_info ("proc", info_proc_cmd, + "Show /proc process information about any running process.\n\ +Specify process id, or use the program being debugged by default.\n\ +Specify keyword 'mappings' for detailed info on memory mappings."); + add_com ("proc-trace-entry", no_class, proc_trace_sysentry_cmd, + "Give a trace of entries into the syscall."); + add_com ("proc-trace-exit", no_class, proc_trace_sysexit_cmd, + "Give a trace of exits from the syscall."); + add_com ("proc-untrace-entry", no_class, proc_untrace_sysentry_cmd, + "Cancel a trace of entries into the syscall."); + add_com ("proc-untrace-exit", no_class, proc_untrace_sysexit_cmd, + "Cancel a trace of exits from the syscall."); +} + +/* =================== END, GDB "MODULE" =================== */ + + + +/* miscellaneous stubs: */ +/* The following satisfy a few random symbols mostly created by */ +/* the solaris threads implementation, which I will chase down */ +/* later. */ + +/* + * Return a pid for which we guarantee + * we will be able to find a 'live' procinfo. + */ + +ptid_t +procfs_first_available (void) +{ + return pid_to_ptid (procinfo_list ? procinfo_list->pid : -1); +} + +/* =================== GCORE .NOTE "MODULE" =================== */ +#if defined (UNIXWARE) || defined (PIOCOPENLWP) || defined (PCAGENT) +/* gcore only implemented on solaris and unixware (so far) */ + +static char * +procfs_do_thread_registers (bfd *obfd, ptid_t ptid, + char *note_data, int *note_size) +{ + gdb_gregset_t gregs; + gdb_fpregset_t fpregs; + unsigned long merged_pid; + + merged_pid = TIDGET (ptid) << 16 | PIDGET (ptid); + + fill_gregset (&gregs, -1); +#if defined (UNIXWARE) + note_data = (char *) elfcore_write_lwpstatus (obfd, + note_data, + note_size, + merged_pid, + stop_signal, + &gregs); +#else + note_data = (char *) elfcore_write_prstatus (obfd, + note_data, + note_size, + merged_pid, + stop_signal, + &gregs); +#endif + fill_fpregset (&fpregs, -1); + note_data = (char *) elfcore_write_prfpreg (obfd, + note_data, + note_size, + &fpregs, + sizeof (fpregs)); + return note_data; +} + +struct procfs_corefile_thread_data { + bfd *obfd; + char *note_data; + int *note_size; +}; + +static int +procfs_corefile_thread_callback (procinfo *pi, procinfo *thread, void *data) +{ + struct procfs_corefile_thread_data *args = data; + + if (pi != NULL && thread->tid != 0) + { + ptid_t saved_ptid = inferior_ptid; + inferior_ptid = MERGEPID (pi->pid, thread->tid); + args->note_data = procfs_do_thread_registers (args->obfd, inferior_ptid, + args->note_data, + args->note_size); + inferior_ptid = saved_ptid; + } + return 0; +} + +static char * +procfs_make_note_section (bfd *obfd, int *note_size) +{ + struct cleanup *old_chain; + gdb_gregset_t gregs; + gdb_fpregset_t fpregs; + char fname[16] = {'\0'}; + char psargs[80] = {'\0'}; + procinfo *pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0); + char *note_data = NULL; + char *inf_args; + struct procfs_corefile_thread_data thread_args; + char *auxv; + int auxv_len; + + if (get_exec_file (0)) + { + strncpy (fname, strrchr (get_exec_file (0), '/') + 1, sizeof (fname)); + strncpy (psargs, get_exec_file (0), + sizeof (psargs)); + + inf_args = get_inferior_args (); + if (inf_args && *inf_args && + strlen (inf_args) < ((int) sizeof (psargs) - (int) strlen (psargs))) + { + strncat (psargs, " ", + sizeof (psargs) - strlen (psargs)); + strncat (psargs, inf_args, + sizeof (psargs) - strlen (psargs)); + } + } + + note_data = (char *) elfcore_write_prpsinfo (obfd, + note_data, + note_size, + fname, + psargs); + +#ifdef UNIXWARE + fill_gregset (&gregs, -1); + note_data = elfcore_write_pstatus (obfd, note_data, note_size, + PIDGET (inferior_ptid), + stop_signal, &gregs); +#endif + + thread_args.obfd = obfd; + thread_args.note_data = note_data; + thread_args.note_size = note_size; + proc_iterate_over_threads (pi, procfs_corefile_thread_callback, &thread_args); + + if (thread_args.note_data == note_data) + { + /* iterate_over_threads didn't come up with any threads; + just use inferior_ptid. */ + note_data = procfs_do_thread_registers (obfd, inferior_ptid, + note_data, note_size); + } + else + { + note_data = thread_args.note_data; + } + + auxv_len = target_auxv_read (¤t_target, &auxv); + if (auxv_len > 0) + { + note_data = elfcore_write_note (obfd, note_data, note_size, + "CORE", NT_AUXV, auxv, auxv_len); + xfree (auxv); + } + + make_cleanup (xfree, note_data); + return note_data; +} +#else /* !(Solaris or Unixware) */ +static char * +procfs_make_note_section (bfd *obfd, int *note_size) +{ + error ("gcore not implemented for this host."); + return NULL; /* lint */ +} +#endif /* Solaris or Unixware */ +/* =================== END GCORE .NOTE "MODULE" =================== */ diff --git a/contrib/gdb/gdb/remote-e7000.c b/contrib/gdb/gdb/remote-e7000.c new file mode 100644 index 0000000..c422c94 --- /dev/null +++ b/contrib/gdb/gdb/remote-e7000.c @@ -0,0 +1,2194 @@ +/* Remote debugging interface for Renesas E7000 ICE, for GDB + + Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + 2002, 2003 Free Software Foundation, Inc. + + Contributed by Cygnus Support. + + Written by Steve Chamberlain for Cygnus Support. + + 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. */ + +/* The E7000 is an in-circuit emulator for the Renesas H8/300-H and + Renesas-SH processor. It has serial port and a lan port. + + The monitor command set makes it difficult to load large ammounts of + data over the lan without using ftp - so try not to issue load + commands when communicating over ethernet; use the ftpload command. + + The monitor pauses for a second when dumping srecords to the serial + line too, so we use a slower per byte mechanism but without the + startup overhead. Even so, it's pretty slow... */ + +#include "defs.h" +#include "gdbcore.h" +#include "gdbarch.h" +#include "inferior.h" +#include "target.h" +#include "value.h" +#include "command.h" +#include "gdb_string.h" +#include "gdbcmd.h" +#include <sys/types.h> +#include "serial.h" +#include "remote-utils.h" +#include "symfile.h" +#include "regcache.h" +#include <time.h> +#include <ctype.h> + + +#if 1 +#define HARD_BREAKPOINTS /* Now handled by set option. */ +#define BC_BREAKPOINTS use_hard_breakpoints +#endif + +#define CTRLC 0x03 +#define ENQ 0x05 +#define ACK 0x06 +#define CTRLZ 0x1a + +/* This file is used by 2 different targets, sh-elf and h8300. The + h8300 is not multiarched and doesn't use the registers defined in + tm-sh.h. To avoid using a macro GDB_TARGET_IS_SH, we do runtime check + of the target, which requires that these namse below are always + defined also in the h8300 case. */ + +#if !defined (PR_REGNUM) +#define PR_REGNUM -1 +#endif +#if !defined (GBR_REGNUM) +#define GBR_REGNUM -1 +#endif +#if !defined (VBR_REGNUM) +#define VBR_REGNUM -1 +#endif +#if !defined (MACH_REGNUM) +#define MACH_REGNUM -1 +#endif +#if !defined (MACL_REGNUM) +#define MACL_REGNUM -1 +#endif +#if !defined (SR_REGNUM) +#define SR_REGNUM -1 +#endif + +extern void report_transfer_performance (unsigned long, time_t, time_t); + +extern char *sh_processor_type; + +/* Local function declarations. */ + +static void e7000_close (int); + +static void e7000_fetch_register (int); + +static void e7000_store_register (int); + +static void e7000_command (char *, int); + +static void e7000_login_command (char *, int); + +static void e7000_ftp_command (char *, int); + +static void e7000_drain_command (char *, int); + +static void expect (char *); + +static void expect_full_prompt (void); + +static void expect_prompt (void); + +static int e7000_parse_device (char *args, char *dev_name, int baudrate); +/* Variables. */ + +static struct serial *e7000_desc; + +/* Allow user to chose between using hardware breakpoints or memory. */ +static int use_hard_breakpoints = 0; /* use sw breakpoints by default */ + +/* Nonzero if using the tcp serial driver. */ + +static int using_tcp; /* direct tcp connection to target */ +static int using_tcp_remote; /* indirect connection to target + via tcp to controller */ + +/* Nonzero if using the pc isa card. */ + +static int using_pc; + +extern struct target_ops e7000_ops; /* Forward declaration */ + +char *ENQSTRING = "\005"; + +/* Nonzero if some routine (as opposed to the user) wants echoing. + FIXME: Do this reentrantly with an extra parameter. */ + +static int echo; + +static int ctrl_c; + +static int timeout = 20; + +/* Send data to e7000debug. */ + +static void +puts_e7000debug (char *buf) +{ + if (!e7000_desc) + error ("Use \"target e7000 ...\" first."); + + if (remote_debug) + printf_unfiltered ("Sending %s\n", buf); + + if (serial_write (e7000_desc, buf, strlen (buf))) + fprintf_unfiltered (gdb_stderr, "serial_write failed: %s\n", safe_strerror (errno)); + + /* And expect to see it echoed, unless using the pc interface */ +#if 0 + if (!using_pc) +#endif + expect (buf); +} + +static void +putchar_e7000 (int x) +{ + char b[1]; + + b[0] = x; + serial_write (e7000_desc, b, 1); +} + +static void +write_e7000 (char *s) +{ + serial_write (e7000_desc, s, strlen (s)); +} + +static int +normal (int x) +{ + if (x == '\n') + return '\r'; + return x; +} + +/* Read a character from the remote system, doing all the fancy timeout + stuff. Handles serial errors and EOF. If TIMEOUT == 0, and no chars, + returns -1, else returns next char. Discards chars > 127. */ + +static int +readchar (int timeout) +{ + int c; + + do + { + c = serial_readchar (e7000_desc, timeout); + } + while (c > 127); + + if (c == SERIAL_TIMEOUT) + { + if (timeout == 0) + return -1; + echo = 0; + error ("Timeout reading from remote system."); + } + else if (c < 0) + error ("Serial communication error"); + + if (remote_debug) + { + putchar_unfiltered (c); + gdb_flush (gdb_stdout); + } + + return normal (c); +} + +#if 0 +char * +tl (int x) +{ + static char b[8][10]; + static int p; + + p++; + p &= 7; + if (x >= ' ') + { + b[p][0] = x; + b[p][1] = 0; + } + else + { + sprintf (b[p], "<%d>", x); + } + + return b[p]; +} +#endif + +/* Scan input from the remote system, until STRING is found. If + DISCARD is non-zero, then discard non-matching input, else print it + out. Let the user break out immediately. */ + +static void +expect (char *string) +{ + char *p = string; + int c; + int nl = 0; + + while (1) + { + c = readchar (timeout); + + if (echo) + { + if (c == '\r' || c == '\n') + { + if (!nl) + putchar_unfiltered ('\n'); + nl = 1; + } + else + { + nl = 0; + putchar_unfiltered (c); + } + gdb_flush (gdb_stdout); + } + if (normal (c) == normal (*p++)) + { + if (*p == '\0') + return; + } + else + { + p = string; + + if (normal (c) == normal (string[0])) + p++; + } + } +} + +/* Keep discarding input until we see the e7000 prompt. + + The convention for dealing with the prompt is that you + o give your command + o *then* wait for the prompt. + + Thus the last thing that a procedure does with the serial line will + be an expect_prompt(). Exception: e7000_resume does not wait for + the prompt, because the terminal is being handed over to the + inferior. However, the next thing which happens after that is a + e7000_wait which does wait for the prompt. Note that this includes + abnormal exit, e.g. error(). This is necessary to prevent getting + into states from which we can't recover. */ + +static void +expect_prompt (void) +{ + expect (":"); +} + +static void +expect_full_prompt (void) +{ + expect ("\r:"); +} + +static int +convert_hex_digit (int ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + return -1; +} + +static int +get_hex (int *start) +{ + int value = convert_hex_digit (*start); + int try; + + *start = readchar (timeout); + while ((try = convert_hex_digit (*start)) >= 0) + { + value <<= 4; + value += try; + *start = readchar (timeout); + } + return value; +} + +#if 0 +/* Get N 32-bit words from remote, each preceded by a space, and put + them in registers starting at REGNO. */ + +static void +get_hex_regs (int n, int regno) +{ + long val; + int i; + + for (i = 0; i < n; i++) + { + int j; + + val = 0; + for (j = 0; j < 8; j++) + val = (val << 4) + get_hex_digit (j == 0); + supply_register (regno++, (char *) &val); + } +} +#endif + +/* This is called not only when we first attach, but also when the + user types "run" after having attached. */ + +static void +e7000_create_inferior (char *execfile, char *args, char **env) +{ + int entry_pt; + + if (args && *args) + error ("Can't pass arguments to remote E7000DEBUG process"); + + if (execfile == 0 || exec_bfd == 0) + error ("No executable file specified"); + + entry_pt = (int) bfd_get_start_address (exec_bfd); + +#ifdef CREATE_INFERIOR_HOOK + CREATE_INFERIOR_HOOK (0); /* No process-ID */ +#endif + + /* The "process" (board) is already stopped awaiting our commands, and + the program is already downloaded. We just set its PC and go. */ + + clear_proceed_status (); + + /* Tell wait_for_inferior that we've started a new process. */ + init_wait_for_inferior (); + + /* Set up the "saved terminal modes" of the inferior + based on what modes we are starting it with. */ + target_terminal_init (); + + /* Install inferior's terminal modes. */ + target_terminal_inferior (); + + /* insert_step_breakpoint (); FIXME, do we need this? */ + proceed ((CORE_ADDR) entry_pt, -1, 0); /* Let 'er rip... */ +} + +/* Open a connection to a remote debugger. NAME is the filename used + for communication. */ + +static int baudrate = 9600; +static char dev_name[100]; + +static char *machine = ""; +static char *user = ""; +static char *passwd = ""; +static char *dir = ""; + +/* Grab the next token and buy some space for it */ + +static char * +next (char **ptr) +{ + char *p = *ptr; + char *s; + char *r; + int l = 0; + + while (*p && *p == ' ') + p++; + s = p; + while (*p && (*p != ' ' && *p != '\t')) + { + l++; + p++; + } + r = xmalloc (l + 1); + memcpy (r, s, l); + r[l] = 0; + *ptr = p; + return r; +} + +static void +e7000_login_command (char *args, int from_tty) +{ + if (args) + { + machine = next (&args); + user = next (&args); + passwd = next (&args); + dir = next (&args); + if (from_tty) + { + printf_unfiltered ("Set info to %s %s %s %s\n", machine, user, passwd, dir); + } + } + else + { + error ("Syntax is ftplogin <machine> <user> <passwd> <directory>"); + } +} + +/* Start an ftp transfer from the E7000 to a host */ + +static void +e7000_ftp_command (char *args, int from_tty) +{ + /* FIXME: arbitrary limit on machine names and such. */ + char buf[200]; + + int oldtimeout = timeout; + timeout = remote_timeout; + + sprintf (buf, "ftp %s\r", machine); + puts_e7000debug (buf); + expect (" Username : "); + sprintf (buf, "%s\r", user); + puts_e7000debug (buf); + expect (" Password : "); + write_e7000 (passwd); + write_e7000 ("\r"); + expect ("success\r"); + expect ("FTP>"); + sprintf (buf, "cd %s\r", dir); + puts_e7000debug (buf); + expect ("FTP>"); + sprintf (buf, "ll 0;s:%s\r", args); + puts_e7000debug (buf); + expect ("FTP>"); + puts_e7000debug ("bye\r"); + expect (":"); + timeout = oldtimeout; +} + +static int +e7000_parse_device (char *args, char *dev_name, int baudrate) +{ + char junk[128]; + int n = 0; + if (args && strcasecmp (args, "pc") == 0) + { + strcpy (dev_name, args); + using_pc = 1; + } + else + { + /* FIXME! temp hack to allow use with port master - + target tcp_remote <device> */ + if (args && strncmp (args, "tcp", 10) == 0) + { + char com_type[128]; + n = sscanf (args, " %s %s %d %s", com_type, dev_name, &baudrate, junk); + using_tcp_remote = 1; + n--; + } + else if (args) + { + n = sscanf (args, " %s %d %s", dev_name, &baudrate, junk); + } + + if (n != 1 && n != 2) + { + error ("Bad arguments. Usage:\ttarget e7000 <device> <speed>\n\ +or \t\ttarget e7000 <host>[:<port>]\n\ +or \t\ttarget e7000 tcp_remote <host>[:<port>]\n\ +or \t\ttarget e7000 pc\n"); + } + +#if !defined(__GO32__) && !defined(_WIN32) && !defined(__CYGWIN__) + /* FIXME! test for ':' is ambiguous */ + if (n == 1 && strchr (dev_name, ':') == 0) + { + /* Default to normal telnet port */ + /* serial_open will use this to determine tcp communication */ + strcat (dev_name, ":23"); + } +#endif + if (!using_tcp_remote && strchr (dev_name, ':')) + using_tcp = 1; + } + + return n; +} + +/* Stub for catch_errors. */ + +static int +e7000_start_remote (void *dummy) +{ + int loop; + int sync; + int try; + int quit_trying; + + immediate_quit++; /* Allow user to interrupt it */ + + /* Hello? Are you there? */ + sync = 0; + loop = 0; + try = 0; + quit_trying = 20; + putchar_e7000 (CTRLC); + while (!sync && ++try <= quit_trying) + { + int c; + + printf_unfiltered ("[waiting for e7000...]\n"); + + write_e7000 ("\r"); + c = readchar (1); + + /* FIXME! this didn't seem right-> while (c != SERIAL_TIMEOUT) + * we get stuck in this loop ... + * We may never timeout, and never sync up :-( + */ + while (!sync && c != -1) + { + /* Dont echo cr's */ + if (c != '\r') + { + putchar_unfiltered (c); + gdb_flush (gdb_stdout); + } + /* Shouldn't we either break here, or check for sync in inner loop? */ + if (c == ':') + sync = 1; + + if (loop++ == 20) + { + putchar_e7000 (CTRLC); + loop = 0; + } + + QUIT; + + if (quit_flag) + { + putchar_e7000 (CTRLC); + /* Was-> quit_flag = 0; */ + c = -1; + quit_trying = try + 1; /* we don't want to try anymore */ + } + else + { + c = readchar (1); + } + } + } + + if (!sync) + { + fprintf_unfiltered (gdb_stderr, "Giving up after %d tries...\n", try); + error ("Unable to synchronize with target.\n"); + } + + puts_e7000debug ("\r"); + expect_prompt (); + puts_e7000debug ("b -\r"); /* Clear breakpoints */ + expect_prompt (); + + immediate_quit--; + +/* This is really the job of start_remote however, that makes an assumption + that the target is about to print out a status message of some sort. That + doesn't happen here. */ + + flush_cached_frames (); + registers_changed (); + stop_pc = read_pc (); + print_stack_frame (get_selected_frame (), -1, 1); + + return 1; +} + +static void +e7000_open (char *args, int from_tty) +{ + int n; + + target_preopen (from_tty); + + n = e7000_parse_device (args, dev_name, baudrate); + + push_target (&e7000_ops); + + e7000_desc = serial_open (dev_name); + + if (!e7000_desc) + perror_with_name (dev_name); + + if (serial_setbaudrate (e7000_desc, baudrate)) + { + serial_close (e7000_desc); + perror_with_name (dev_name); + } + serial_raw (e7000_desc); + + /* Start the remote connection; if error (0), discard this target. + In particular, if the user quits, be sure to discard it + (we'd be in an inconsistent state otherwise). */ + if (!catch_errors (e7000_start_remote, (char *) 0, + "Couldn't establish connection to remote target\n", RETURN_MASK_ALL)) + if (from_tty) + printf_filtered ("Remote target %s connected to %s\n", target_shortname, + dev_name); +} + +/* Close out all files and local state before this target loses control. */ + +static void +e7000_close (int quitting) +{ + if (e7000_desc) + { + serial_close (e7000_desc); + e7000_desc = 0; + } +} + +/* Terminate the open connection to the remote debugger. Use this + when you want to detach and do something else with your gdb. */ + +static void +e7000_detach (char *arg, int from_tty) +{ + pop_target (); /* calls e7000_close to do the real work */ + if (from_tty) + printf_unfiltered ("Ending remote %s debugging\n", target_shortname); +} + +/* Tell the remote machine to resume. */ + +static void +e7000_resume (ptid_t ptid, int step, enum target_signal sigal) +{ + if (step) + puts_e7000debug ("S\r"); + else + puts_e7000debug ("G\r"); +} + +/* Read the remote registers into the block REGS. + + For the H8/300 a register dump looks like: + + PC=00021A CCR=80:I******* + ER0 - ER3 0000000A 0000002E 0000002E 00000000 + ER4 - ER7 00000000 00000000 00000000 00FFEFF6 + 000218 MOV.B R1L,R2L + STEP NORMAL END or + BREAK POINT + */ + +char *want_h8300h = "PC=%p CCR=%c\n\ + ER0 - ER3 %0 %1 %2 %3\n\ + ER4 - ER7 %4 %5 %6 %7\n"; + +char *want_nopc_h8300h = "%p CCR=%c\n\ + ER0 - ER3 %0 %1 %2 %3\n\ + ER4 - ER7 %4 %5 %6 %7"; + +char *want_h8300s = "PC=%p CCR=%c\n\ + MACH=\n\ + ER0 - ER3 %0 %1 %2 %3\n\ + ER4 - ER7 %4 %5 %6 %7\n"; + +char *want_nopc_h8300s = "%p CCR=%c EXR=%9\n\ + ER0 - ER3 %0 %1 %2 %3\n\ + ER4 - ER7 %4 %5 %6 %7"; + +char *want_sh = "PC=%16 SR=%22\n\ +PR=%17 GBR=%18 VBR=%19\n\ +MACH=%20 MACL=%21\n\ +R0-7 %0 %1 %2 %3 %4 %5 %6 %7\n\ +R8-15 %8 %9 %10 %11 %12 %13 %14 %15\n"; + +char *want_nopc_sh = "%16 SR=%22\n\ + PR=%17 GBR=%18 VBR=%19\n\ + MACH=%20 MACL=%21\n\ + R0-7 %0 %1 %2 %3 %4 %5 %6 %7\n\ + R8-15 %8 %9 %10 %11 %12 %13 %14 %15"; + +char *want_sh3 = "PC=%16 SR=%22\n\ +PR=%17 GBR=%18 VBR=%19\n\ +MACH=%20 MACL=%21 SSR=%23 SPC=%24\n\ +R0-7 %0 %1 %2 %3 %4 %5 %6 %7\n\ +R8-15 %8 %9 %10 %11 %12 %13 %14 %15\n\ +R0_BANK0-R3_BANK0 %25 %26 %27 %28\n\ +R4_BANK0-R7_BANK0 %29 %30 %31 %32\n\ +R0_BANK1-R3_BANK1 %33 %34 %35 %36\n\ +R4_BANK1-R7_BANK1 %37 %38 %39 %40"; + +char *want_nopc_sh3 = "%16 SR=%22\n\ + PR=%17 GBR=%18 VBR=%19\n\ + MACH=%20 MACL=%21 SSR=%22 SPC=%23\n\ + R0-7 %0 %1 %2 %3 %4 %5 %6 %7\n\ + R8-15 %8 %9 %10 %11 %12 %13 %14 %15\n\ + R0_BANK0-R3_BANK0 %25 %26 %27 %28\n\ + R4_BANK0-R7_BANK0 %29 %30 %31 %32\n\ + R0_BANK1-R3_BANK1 %33 %34 %35 %36\n\ + R4_BANK1-R7_BANK1 %37 %38 %39 %40"; + +static int +gch (void) +{ + return readchar (timeout); +} + +static unsigned int +gbyte (void) +{ + int high = convert_hex_digit (gch ()); + int low = convert_hex_digit (gch ()); + + return (high << 4) + low; +} + +static void +fetch_regs_from_dump (int (*nextchar) (), char *want) +{ + int regno; + char buf[MAX_REGISTER_SIZE]; + + int thischar = nextchar (); + + if (want == NULL) + internal_error (__FILE__, __LINE__, "Register set not selected."); + + while (*want) + { + switch (*want) + { + case '\n': + /* Skip to end of line and then eat all new line type stuff */ + while (thischar != '\n' && thischar != '\r') + thischar = nextchar (); + while (thischar == '\n' || thischar == '\r') + thischar = nextchar (); + want++; + break; + + case ' ': + while (thischar == ' ' + || thischar == '\t' + || thischar == '\r' + || thischar == '\n') + thischar = nextchar (); + want++; + break; + + default: + if (*want == thischar) + { + want++; + if (*want) + thischar = nextchar (); + + } + else if (thischar == ' ' || thischar == '\n' || thischar == '\r') + { + thischar = nextchar (); + } + else + { + error ("out of sync in fetch registers wanted <%s>, got <%c 0x%x>", + want, thischar, thischar); + } + + break; + case '%': + /* Got a register command */ + want++; + switch (*want) + { +#ifdef PC_REGNUM + case 'p': + regno = PC_REGNUM; + want++; + break; +#endif +#ifdef CCR_REGNUM + case 'c': + regno = CCR_REGNUM; + want++; + break; +#endif +#ifdef SP_REGNUM + case 's': + regno = SP_REGNUM; + want++; + break; +#endif +#ifdef DEPRECATED_FP_REGNUM + case 'f': + regno = DEPRECATED_FP_REGNUM; + want++; + break; +#endif + + default: + if (isdigit (want[0])) + { + if (isdigit (want[1])) + { + regno = (want[0] - '0') * 10 + want[1] - '0'; + want += 2; + } + else + { + regno = want[0] - '0'; + want++; + } + } + + else + internal_error (__FILE__, __LINE__, "failed internal consistency check"); + } + store_signed_integer (buf, + DEPRECATED_REGISTER_RAW_SIZE (regno), + (LONGEST) get_hex (&thischar)); + supply_register (regno, buf); + break; + } + } +} + +static void +e7000_fetch_registers (void) +{ + int regno; + char *wanted = NULL; + + puts_e7000debug ("R\r"); + + if (TARGET_ARCHITECTURE->arch == bfd_arch_sh) + { + wanted = want_sh; + switch (TARGET_ARCHITECTURE->mach) + { + case bfd_mach_sh3: + case bfd_mach_sh3e: + case bfd_mach_sh4: + wanted = want_sh3; + } + } + if (TARGET_ARCHITECTURE->arch == bfd_arch_h8300) + { + wanted = want_h8300h; + switch (TARGET_ARCHITECTURE->mach) + { + case bfd_mach_h8300s: + case bfd_mach_h8300sn: + case bfd_mach_h8300sx: + case bfd_mach_h8300sxn: + wanted = want_h8300s; + } + } + + fetch_regs_from_dump (gch, wanted); + + /* And supply the extra ones the simulator uses */ + for (regno = NUM_REALREGS; regno < NUM_REGS; regno++) + { + int buf = 0; + + supply_register (regno, (char *) (&buf)); + } +} + +/* Fetch register REGNO, or all registers if REGNO is -1. Returns + errno value. */ + +static void +e7000_fetch_register (int regno) +{ + e7000_fetch_registers (); +} + +/* Store the remote registers from the contents of the block REGS. */ + +static void +e7000_store_registers (void) +{ + int regno; + + for (regno = 0; regno < NUM_REALREGS; regno++) + e7000_store_register (regno); + + registers_changed (); +} + +/* Store register REGNO, or all if REGNO == 0. Return errno value. */ + +static void +e7000_store_register (int regno) +{ + char buf[200]; + + if (regno == -1) + { + e7000_store_registers (); + return; + } + + if (TARGET_ARCHITECTURE->arch == bfd_arch_h8300) + { + if (regno <= 7) + { + sprintf (buf, ".ER%d %s\r", regno, phex_nz (read_register (regno), 0)); + puts_e7000debug (buf); + } + else if (regno == PC_REGNUM) + { + sprintf (buf, ".PC %s\r", phex_nz (read_register (regno), 0)); + puts_e7000debug (buf); + } +#ifdef CCR_REGNUM + else if (regno == CCR_REGNUM) + { + sprintf (buf, ".CCR %s\r", phex_nz (read_register (regno), 0)); + puts_e7000debug (buf); + } +#endif + } + + else if (TARGET_ARCHITECTURE->arch == bfd_arch_sh) + { + if (regno == PC_REGNUM) + { + sprintf (buf, ".PC %s\r", phex_nz (read_register (regno), 0)); + puts_e7000debug (buf); + } + + else if (regno == SR_REGNUM) + { + sprintf (buf, ".SR %s\r", phex_nz (read_register (regno), 0)); + puts_e7000debug (buf); + } + + else if (regno == PR_REGNUM) + { + sprintf (buf, ".PR %s\r", phex_nz (read_register (regno), 0)); + puts_e7000debug (buf); + } + + else if (regno == GBR_REGNUM) + { + sprintf (buf, ".GBR %s\r", phex_nz (read_register (regno), 0)); + puts_e7000debug (buf); + } + + else if (regno == VBR_REGNUM) + { + sprintf (buf, ".VBR %s\r", phex_nz (read_register (regno), 0)); + puts_e7000debug (buf); + } + + else if (regno == MACH_REGNUM) + { + sprintf (buf, ".MACH %s\r", phex_nz (read_register (regno), 0)); + puts_e7000debug (buf); + } + + else if (regno == MACL_REGNUM) + { + sprintf (buf, ".MACL %s\r", phex_nz (read_register (regno), 0)); + puts_e7000debug (buf); + } + else + { + sprintf (buf, ".R%d %s\r", regno, phex_nz (read_register (regno), 0)); + puts_e7000debug (buf); + } + } + + expect_prompt (); +} + +/* Get ready to modify the registers array. On machines which store + individual registers, this doesn't need to do anything. On machines + which store all the registers in one fell swoop, this makes sure + that registers contains all the registers from the program being + debugged. */ + +static void +e7000_prepare_to_store (void) +{ + /* Do nothing, since we can store individual regs */ +} + +static void +e7000_files_info (struct target_ops *ops) +{ + printf_unfiltered ("\tAttached to %s at %d baud.\n", dev_name, baudrate); +} + +static int +stickbyte (char *where, unsigned int what) +{ + static CONST char digs[] = "0123456789ABCDEF"; + + where[0] = digs[(what >> 4) & 0xf]; + where[1] = digs[(what & 0xf) & 0xf]; + + return what; +} + +/* Write a small ammount of memory. */ + +static int +write_small (CORE_ADDR memaddr, unsigned char *myaddr, int len) +{ + int i; + char buf[200]; + + for (i = 0; i < len; i++) + { + if (((memaddr + i) & 3) == 0 && (i + 3 < len)) + { + /* Can be done with a long word */ + sprintf (buf, "m %s %x%02x%02x%02x;l\r", + paddr_nz (memaddr + i), + myaddr[i], myaddr[i + 1], myaddr[i + 2], myaddr[i + 3]); + puts_e7000debug (buf); + i += 3; + } + else + { + sprintf (buf, "m %s %x\r", paddr_nz (memaddr + i), myaddr[i]); + puts_e7000debug (buf); + } + } + + expect_prompt (); + + return len; +} + +/* Write a large ammount of memory, this only works with the serial + mode enabled. Command is sent as + + il ;s:s\r -> + <- il ;s:s\r + <- ENQ + ACK -> + <- LO s\r + Srecords... + ^Z -> + <- ENQ + ACK -> + <- : + */ + +static int +write_large (CORE_ADDR memaddr, unsigned char *myaddr, int len) +{ + int i; +#define maxstride 128 + int stride; + + puts_e7000debug ("IL ;S:FK\r"); + expect (ENQSTRING); + putchar_e7000 (ACK); + expect ("LO FK\r"); + + for (i = 0; i < len; i += stride) + { + char compose[maxstride * 2 + 50]; + int address = i + memaddr; + int j; + int check_sum; + int where = 0; + int alen; + + stride = len - i; + if (stride > maxstride) + stride = maxstride; + + compose[where++] = 'S'; + check_sum = 0; + if (address >= 0xffffff) + alen = 4; + else if (address >= 0xffff) + alen = 3; + else + alen = 2; + /* Insert type. */ + compose[where++] = alen - 1 + '0'; + /* Insert length. */ + check_sum += stickbyte (compose + where, alen + stride + 1); + where += 2; + while (alen > 0) + { + alen--; + check_sum += stickbyte (compose + where, address >> (8 * (alen))); + where += 2; + } + + for (j = 0; j < stride; j++) + { + check_sum += stickbyte (compose + where, myaddr[i + j]); + where += 2; + } + stickbyte (compose + where, ~check_sum); + where += 2; + compose[where++] = '\r'; + compose[where++] = '\n'; + compose[where++] = 0; + + serial_write (e7000_desc, compose, where); + j = readchar (0); + if (j == -1) + { + /* This is ok - nothing there */ + } + else if (j == ENQ) + { + /* Hmm, it's trying to tell us something */ + expect (":"); + error ("Error writing memory"); + } + else + { + printf_unfiltered ("@%d}@", j); + while ((j = readchar (0)) > 0) + { + printf_unfiltered ("@{%d}@", j); + } + } + } + + /* Send the trailer record */ + write_e7000 ("S70500000000FA\r"); + putchar_e7000 (CTRLZ); + expect (ENQSTRING); + putchar_e7000 (ACK); + expect (":"); + + return len; +} + +/* Copy LEN bytes of data from debugger memory at MYADDR to inferior's + memory at MEMADDR. Returns length moved. + + Can't use the Srecord load over ethernet, so don't use fast method + then. */ + +static int +e7000_write_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) +{ + if (len < 16 || using_tcp || using_pc) + return write_small (memaddr, myaddr, len); + else + return write_large (memaddr, myaddr, len); +} + +/* Read LEN bytes from inferior memory at MEMADDR. Put the result + at debugger address MYADDR. Returns length moved. + + Small transactions we send + m <addr>;l + and receive + 00000000 12345678 ? + */ + +static int +e7000_read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) +{ + int count; + int c; + int i; + char buf[200]; + /* Starting address of this pass. */ + +/* printf("READ INF %x %x %d\n", memaddr, myaddr, len); */ + if (((memaddr - 1) + len) < memaddr) + { + errno = EIO; + return 0; + } + + sprintf (buf, "m %s;l\r", paddr_nz (memaddr)); + puts_e7000debug (buf); + + for (count = 0; count < len; count += 4) + { + /* Suck away the address */ + c = gch (); + while (c != ' ') + c = gch (); + c = gch (); + if (c == '*') + { /* Some kind of error */ + puts_e7000debug (".\r"); /* Some errors leave us in memory input mode */ + expect_full_prompt (); + return -1; + } + while (c != ' ') + c = gch (); + + /* Now read in the data */ + for (i = 0; i < 4; i++) + { + int b = gbyte (); + if (count + i < len) + { + myaddr[count + i] = b; + } + } + + /* Skip the trailing ? and send a . to end and a cr for more */ + gch (); + gch (); + if (count + 4 >= len) + puts_e7000debug (".\r"); + else + puts_e7000debug ("\r"); + + } + expect_prompt (); + return len; +} + + + +/* + For large transfers we used to send + + + d <addr> <endaddr>\r + + and receive + <ADDRESS> < D A T A > < ASCII CODE > + 00000000 5F FD FD FF DF 7F DF FF 01 00 01 00 02 00 08 04 "_..............." + 00000010 FF D7 FF 7F D7 F1 7F FF 00 05 00 00 08 00 40 00 "..............@." + 00000020 7F FD FF F7 7F FF FF F7 00 00 00 00 00 00 00 00 "................" + + A cost in chars for each transaction of 80 + 5*n-bytes. + + Large transactions could be done with the srecord load code, but + there is a pause for a second before dumping starts, which slows the + average rate down! + */ + +static int +e7000_read_inferior_memory_large (CORE_ADDR memaddr, unsigned char *myaddr, + int len) +{ + int count; + int c; + char buf[200]; + + /* Starting address of this pass. */ + + if (((memaddr - 1) + len) < memaddr) + { + errno = EIO; + return 0; + } + + sprintf (buf, "d %s %s\r", paddr_nz (memaddr), paddr_nz (memaddr + len - 1)); + puts_e7000debug (buf); + + count = 0; + c = gch (); + + /* skip down to the first ">" */ + while (c != '>') + c = gch (); + /* now skip to the end of that line */ + while (c != '\r') + c = gch (); + c = gch (); + + while (count < len) + { + /* get rid of any white space before the address */ + while (c <= ' ') + c = gch (); + + /* Skip the address */ + get_hex (&c); + + /* read in the bytes on the line */ + while (c != '"' && count < len) + { + if (c == ' ') + c = gch (); + else + { + myaddr[count++] = get_hex (&c); + } + } + /* throw out the rest of the line */ + while (c != '\r') + c = gch (); + } + + /* wait for the ":" prompt */ + while (c != ':') + c = gch (); + + return len; +} + +#if 0 + +static int +fast_but_for_the_pause_e7000_read_inferior_memory (CORE_ADDR memaddr, + char *myaddr, int len) +{ + int loop; + int c; + char buf[200]; + + if (((memaddr - 1) + len) < memaddr) + { + errno = EIO; + return 0; + } + + sprintf (buf, "is %x@%x:s\r", memaddr, len); + puts_e7000debug (buf); + gch (); + c = gch (); + if (c != ENQ) + { + /* Got an error */ + error ("Memory read error"); + } + putchar_e7000 (ACK); + expect ("SV s"); + loop = 1; + while (loop) + { + int type; + int length; + int addr; + int i; + + c = gch (); + switch (c) + { + case ENQ: /* ENQ, at the end */ + loop = 0; + break; + case 'S': + /* Start of an Srecord */ + type = gch (); + length = gbyte (); + switch (type) + { + case '7': /* Termination record, ignore */ + case '0': + case '8': + case '9': + /* Header record - ignore it */ + while (length--) + { + gbyte (); + } + break; + case '1': + case '2': + case '3': + { + int alen; + + alen = type - '0' + 1; + addr = 0; + while (alen--) + { + addr = (addr << 8) + gbyte (); + length--; + } + + for (i = 0; i < length - 1; i++) + myaddr[i + addr - memaddr] = gbyte (); + + gbyte (); /* Ignore checksum */ + } + } + } + } + + putchar_e7000 (ACK); + expect ("TOP ADDRESS ="); + expect ("END ADDRESS ="); + expect (":"); + + return len; +} + +#endif + +/* Transfer LEN bytes between GDB address MYADDR and target address + MEMADDR. If WRITE is non-zero, transfer them to the target, + otherwise transfer them from the target. TARGET is unused. + + Returns the number of bytes transferred. */ + +static int +e7000_xfer_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len, + int write, struct mem_attrib *attrib, + struct target_ops *target) +{ + if (write) + return e7000_write_inferior_memory (memaddr, myaddr, len); + else if (len < 16) + return e7000_read_inferior_memory (memaddr, myaddr, len); + else + return e7000_read_inferior_memory_large (memaddr, myaddr, len); +} + +static void +e7000_kill (void) +{ +} + +static void +e7000_load (char *args, int from_tty) +{ + struct cleanup *old_chain; + asection *section; + bfd *pbfd; + bfd_vma entry; +#define WRITESIZE 0x1000 + char buf[2 + 4 + 4 + WRITESIZE]; /* `DT' + <addr> + <len> + <data> */ + char *filename; + int quiet; + int nostart; + time_t start_time, end_time; /* Start and end times of download */ + unsigned long data_count; /* Number of bytes transferred to memory */ + int oldtimeout = timeout; + + timeout = remote_timeout; + + + /* FIXME! change test to test for type of download */ + if (!using_tcp) + { + generic_load (args, from_tty); + return; + } + + /* for direct tcp connections, we can do a fast binary download */ + buf[0] = 'D'; + buf[1] = 'T'; + quiet = 0; + nostart = 0; + filename = NULL; + + while (*args != '\000') + { + char *arg; + + while (isspace (*args)) + args++; + + arg = args; + + while ((*args != '\000') && !isspace (*args)) + args++; + + if (*args != '\000') + *args++ = '\000'; + + if (*arg != '-') + filename = arg; + else if (strncmp (arg, "-quiet", strlen (arg)) == 0) + quiet = 1; + else if (strncmp (arg, "-nostart", strlen (arg)) == 0) + nostart = 1; + else + error ("unknown option `%s'", arg); + } + + if (!filename) + filename = get_exec_file (1); + + pbfd = bfd_openr (filename, gnutarget); + if (pbfd == NULL) + { + perror_with_name (filename); + return; + } + old_chain = make_cleanup_bfd_close (pbfd); + + if (!bfd_check_format (pbfd, bfd_object)) + error ("\"%s\" is not an object file: %s", filename, + bfd_errmsg (bfd_get_error ())); + + start_time = time (NULL); + data_count = 0; + + puts_e7000debug ("mw\r"); + + expect ("\nOK"); + + for (section = pbfd->sections; section; section = section->next) + { + if (bfd_get_section_flags (pbfd, section) & SEC_LOAD) + { + bfd_vma section_address; + bfd_size_type section_size; + file_ptr fptr; + + section_address = bfd_get_section_vma (pbfd, section); + section_size = bfd_get_section_size_before_reloc (section); + + if (!quiet) + printf_filtered ("[Loading section %s at 0x%s (%s bytes)]\n", + bfd_get_section_name (pbfd, section), + paddr_nz (section_address), + paddr_u (section_size)); + + fptr = 0; + + data_count += section_size; + + while (section_size > 0) + { + int count; + static char inds[] = "|/-\\"; + static int k = 0; + + QUIT; + + count = min (section_size, WRITESIZE); + + buf[2] = section_address >> 24; + buf[3] = section_address >> 16; + buf[4] = section_address >> 8; + buf[5] = section_address; + + buf[6] = count >> 24; + buf[7] = count >> 16; + buf[8] = count >> 8; + buf[9] = count; + + bfd_get_section_contents (pbfd, section, buf + 10, fptr, count); + + if (serial_write (e7000_desc, buf, count + 10)) + fprintf_unfiltered (gdb_stderr, + "e7000_load: serial_write failed: %s\n", + safe_strerror (errno)); + + expect ("OK"); + + if (!quiet) + { + printf_unfiltered ("\r%c", inds[k++ % 4]); + gdb_flush (gdb_stdout); + } + + section_address += count; + fptr += count; + section_size -= count; + } + } + } + + write_e7000 ("ED"); + + expect_prompt (); + + end_time = time (NULL); + +/* Finally, make the PC point at the start address */ + + if (exec_bfd) + write_pc (bfd_get_start_address (exec_bfd)); + + inferior_ptid = null_ptid; /* No process now */ + +/* This is necessary because many things were based on the PC at the time that + we attached to the monitor, which is no longer valid now that we have loaded + new code (and just changed the PC). Another way to do this might be to call + normal_stop, except that the stack may not be valid, and things would get + horribly confused... */ + + clear_symtab_users (); + + if (!nostart) + { + entry = bfd_get_start_address (pbfd); + + if (!quiet) + printf_unfiltered ("[Starting %s at 0x%s]\n", filename, paddr_nz (entry)); + +/* start_routine (entry); */ + } + + report_transfer_performance (data_count, start_time, end_time); + + do_cleanups (old_chain); + timeout = oldtimeout; +} + +/* Clean up when a program exits. + + The program actually lives on in the remote processor's RAM, and may be + run again without a download. Don't leave it full of breakpoint + instructions. */ + +static void +e7000_mourn_inferior (void) +{ + remove_breakpoints (); + unpush_target (&e7000_ops); + generic_mourn_inferior (); /* Do all the proper things now */ +} + +#define MAX_BREAKPOINTS 200 +#ifdef HARD_BREAKPOINTS +#define MAX_E7000DEBUG_BREAKPOINTS (BC_BREAKPOINTS ? 5 : MAX_BREAKPOINTS) +#else +#define MAX_E7000DEBUG_BREAKPOINTS MAX_BREAKPOINTS +#endif + +/* Since we can change to soft breakpoints dynamically, we must define + more than enough. Was breakaddr[MAX_E7000DEBUG_BREAKPOINTS]. */ +static CORE_ADDR breakaddr[MAX_BREAKPOINTS] = +{0}; + +static int +e7000_insert_breakpoint (CORE_ADDR addr, char *shadow) +{ + int i; + char buf[200]; +#if 0 + static char nop[2] = NOP; +#endif + + for (i = 0; i <= MAX_E7000DEBUG_BREAKPOINTS; i++) + if (breakaddr[i] == 0) + { + breakaddr[i] = addr; + /* Save old contents, and insert a nop in the space */ +#ifdef HARD_BREAKPOINTS + if (BC_BREAKPOINTS) + { + sprintf (buf, "BC%d A=%s\r", i + 1, paddr_nz (addr)); + puts_e7000debug (buf); + } + else + { + sprintf (buf, "B %s\r", paddr_nz (addr)); + puts_e7000debug (buf); + } +#else +#if 0 + e7000_read_inferior_memory (addr, shadow, 2); + e7000_write_inferior_memory (addr, nop, 2); +#endif + + sprintf (buf, "B %x\r", addr); + puts_e7000debug (buf); +#endif + expect_prompt (); + return 0; + } + + error ("Too many breakpoints ( > %d) for the E7000\n", + MAX_E7000DEBUG_BREAKPOINTS); + return 1; +} + +static int +e7000_remove_breakpoint (CORE_ADDR addr, char *shadow) +{ + int i; + char buf[200]; + + for (i = 0; i < MAX_E7000DEBUG_BREAKPOINTS; i++) + if (breakaddr[i] == addr) + { + breakaddr[i] = 0; +#ifdef HARD_BREAKPOINTS + if (BC_BREAKPOINTS) + { + sprintf (buf, "BC%d - \r", i + 1); + puts_e7000debug (buf); + } + else + { + sprintf (buf, "B - %s\r", paddr_nz (addr)); + puts_e7000debug (buf); + } + expect_prompt (); +#else + sprintf (buf, "B - %s\r", paddr_nz (addr)); + puts_e7000debug (buf); + expect_prompt (); + +#if 0 + /* Replace the insn under the break */ + e7000_write_inferior_memory (addr, shadow, 2); +#endif +#endif + + return 0; + } + + warning ("Can't find breakpoint associated with 0x%s\n", paddr_nz (addr)); + return 1; +} + +/* Put a command string, in args, out to STDBUG. Output from STDBUG + is placed on the users terminal until the prompt is seen. */ + +static void +e7000_command (char *args, int fromtty) +{ + /* FIXME: arbitrary limit on length of args. */ + char buf[200]; + + echo = 0; + + if (!e7000_desc) + error ("e7000 target not open."); + if (!args) + { + puts_e7000debug ("\r"); + } + else + { + sprintf (buf, "%s\r", args); + puts_e7000debug (buf); + } + + echo++; + ctrl_c = 2; + expect_full_prompt (); + echo--; + ctrl_c = 0; + printf_unfiltered ("\n"); + + /* Who knows what the command did... */ + registers_changed (); +} + + +static void +e7000_drain_command (char *args, int fromtty) +{ + int c; + + puts_e7000debug ("end\r"); + putchar_e7000 (CTRLC); + + while ((c = readchar (1)) != -1) + { + if (quit_flag) + { + putchar_e7000 (CTRLC); + quit_flag = 0; + } + if (c > ' ' && c < 127) + printf_unfiltered ("%c", c & 0xff); + else + printf_unfiltered ("<%x>", c & 0xff); + } +} + +#define NITEMS 7 + +static int +why_stop (void) +{ + static char *strings[NITEMS] = + { + "STEP NORMAL", + "BREAK POINT", + "BREAK KEY", + "BREAK CONDI", + "CYCLE ACCESS", + "ILLEGAL INSTRUCTION", + "WRITE PROTECT", + }; + char *p[NITEMS]; + int c; + int i; + + for (i = 0; i < NITEMS; ++i) + p[i] = strings[i]; + + c = gch (); + while (1) + { + for (i = 0; i < NITEMS; i++) + { + if (c == *(p[i])) + { + p[i]++; + if (*(p[i]) == 0) + { + /* found one of the choices */ + return i; + } + } + else + p[i] = strings[i]; + } + + c = gch (); + } +} + +/* Suck characters, if a string match, then return the strings index + otherwise echo them. */ + +static int +expect_n (char **strings) +{ + char *(ptr[10]); + int n; + int c; + char saveaway[100]; + char *buffer = saveaway; + /* Count number of expect strings */ + + for (n = 0; strings[n]; n++) + { + ptr[n] = strings[n]; + } + + while (1) + { + int i; + int gotone = 0; + + c = readchar (1); + if (c == -1) + { + printf_unfiltered ("[waiting for e7000...]\n"); + } +#ifdef __GO32__ + if (kbhit ()) + { + int k = getkey (); + + if (k == 1) + quit_flag = 1; + } +#endif + if (quit_flag) + { + putchar_e7000 (CTRLC); /* interrupt the running program */ + quit_flag = 0; + } + + for (i = 0; i < n; i++) + { + if (c == ptr[i][0]) + { + ptr[i]++; + if (ptr[i][0] == 0) + { + /* Gone all the way */ + return i; + } + gotone = 1; + } + else + { + ptr[i] = strings[i]; + } + } + + if (gotone) + { + /* Save it up incase we find that there was no match */ + *buffer++ = c; + } + else + { + if (buffer != saveaway) + { + *buffer++ = 0; + printf_unfiltered ("%s", buffer); + buffer = saveaway; + } + if (c != -1) + { + putchar_unfiltered (c); + gdb_flush (gdb_stdout); + } + } + } +} + +/* We subtract two from the pc here rather than use + DECR_PC_AFTER_BREAK since the e7000 doesn't always add two to the + pc, and the simulators never do. */ + +static void +sub2_from_pc (void) +{ + char buf[4]; + char buf2[200]; + + store_signed_integer (buf, + DEPRECATED_REGISTER_RAW_SIZE (PC_REGNUM), + read_register (PC_REGNUM) - 2); + supply_register (PC_REGNUM, buf); + sprintf (buf2, ".PC %s\r", phex_nz (read_register (PC_REGNUM), 0)); + puts_e7000debug (buf2); +} + +#define WAS_SLEEP 0 +#define WAS_INT 1 +#define WAS_RUNNING 2 +#define WAS_OTHER 3 + +static char *estrings[] = +{ + "** SLEEP", + "BREAK !", + "** PC", + "PC", + NULL +}; + +/* Wait until the remote machine stops, then return, storing status in + STATUS just as `wait' would. */ + +static ptid_t +e7000_wait (ptid_t ptid, struct target_waitstatus *status) +{ + int stop_reason; + int regno; + int running_count = 0; + int had_sleep = 0; + int loop = 1; + char *wanted_nopc = NULL; + + /* Then echo chars until PC= string seen */ + gch (); /* Drop cr */ + gch (); /* and space */ + + while (loop) + { + switch (expect_n (estrings)) + { + case WAS_OTHER: + /* how did this happen ? */ + loop = 0; + break; + case WAS_SLEEP: + had_sleep = 1; + putchar_e7000 (CTRLC); + loop = 0; + break; + case WAS_INT: + loop = 0; + break; + case WAS_RUNNING: + running_count++; + if (running_count == 20) + { + printf_unfiltered ("[running...]\n"); + running_count = 0; + } + break; + default: + /* error? */ + break; + } + } + + /* Skip till the PC= */ + expect ("="); + + if (TARGET_ARCHITECTURE->arch == bfd_arch_sh) + { + wanted_nopc = want_nopc_sh; + switch (TARGET_ARCHITECTURE->mach) + { + case bfd_mach_sh3: + case bfd_mach_sh3e: + case bfd_mach_sh4: + wanted_nopc = want_nopc_sh3; + } + } + if (TARGET_ARCHITECTURE->arch == bfd_arch_h8300) + { + wanted_nopc = want_nopc_h8300h; + switch (TARGET_ARCHITECTURE->mach) + { + case bfd_mach_h8300s: + case bfd_mach_h8300sn: + case bfd_mach_h8300sx: + case bfd_mach_h8300sxn: + wanted_nopc = want_nopc_h8300s; + } + } + fetch_regs_from_dump (gch, wanted_nopc); + + /* And supply the extra ones the simulator uses */ + for (regno = NUM_REALREGS; regno < NUM_REGS; regno++) + { + int buf = 0; + supply_register (regno, (char *) &buf); + } + + stop_reason = why_stop (); + expect_full_prompt (); + + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_TRAP; + + switch (stop_reason) + { + case 1: /* Breakpoint */ + write_pc (read_pc ()); /* PC is always off by 2 for breakpoints */ + status->value.sig = TARGET_SIGNAL_TRAP; + break; + case 0: /* Single step */ + status->value.sig = TARGET_SIGNAL_TRAP; + break; + case 2: /* Interrupt */ + if (had_sleep) + { + status->value.sig = TARGET_SIGNAL_TRAP; + sub2_from_pc (); + } + else + { + status->value.sig = TARGET_SIGNAL_INT; + } + break; + case 3: + break; + case 4: + printf_unfiltered ("a cycle address error?\n"); + status->value.sig = TARGET_SIGNAL_UNKNOWN; + break; + case 5: + status->value.sig = TARGET_SIGNAL_ILL; + break; + case 6: + status->value.sig = TARGET_SIGNAL_SEGV; + break; + case 7: /* Anything else (NITEMS + 1) */ + printf_unfiltered ("a write protect error?\n"); + status->value.sig = TARGET_SIGNAL_UNKNOWN; + break; + default: + /* Get the user's attention - this should never happen. */ + internal_error (__FILE__, __LINE__, "failed internal consistency check"); + } + + return inferior_ptid; +} + +/* Stop the running program. */ + +static void +e7000_stop (void) +{ + /* Sending a ^C is supposed to stop the running program. */ + putchar_e7000 (CTRLC); +} + +/* Define the target subroutine names. */ + +struct target_ops e7000_ops; + +static void +init_e7000_ops (void) +{ + e7000_ops.to_shortname = "e7000"; + e7000_ops.to_longname = "Remote Renesas e7000 target"; + e7000_ops.to_doc = "Use a remote Renesas e7000 ICE connected by a serial line;\n\ +or a network connection.\n\ +Arguments are the name of the device for the serial line,\n\ +the speed to connect at in bits per second.\n\ +eg\n\ +target e7000 /dev/ttya 9600\n\ +target e7000 foobar"; + e7000_ops.to_open = e7000_open; + e7000_ops.to_close = e7000_close; + e7000_ops.to_detach = e7000_detach; + e7000_ops.to_resume = e7000_resume; + e7000_ops.to_wait = e7000_wait; + e7000_ops.to_fetch_registers = e7000_fetch_register; + e7000_ops.to_store_registers = e7000_store_register; + e7000_ops.to_prepare_to_store = e7000_prepare_to_store; + e7000_ops.to_xfer_memory = e7000_xfer_inferior_memory; + e7000_ops.to_files_info = e7000_files_info; + e7000_ops.to_insert_breakpoint = e7000_insert_breakpoint; + e7000_ops.to_remove_breakpoint = e7000_remove_breakpoint; + e7000_ops.to_kill = e7000_kill; + e7000_ops.to_load = e7000_load; + e7000_ops.to_create_inferior = e7000_create_inferior; + e7000_ops.to_mourn_inferior = e7000_mourn_inferior; + e7000_ops.to_stop = e7000_stop; + e7000_ops.to_stratum = process_stratum; + e7000_ops.to_has_all_memory = 1; + e7000_ops.to_has_memory = 1; + e7000_ops.to_has_stack = 1; + e7000_ops.to_has_registers = 1; + e7000_ops.to_has_execution = 1; + e7000_ops.to_magic = OPS_MAGIC; +}; + +extern initialize_file_ftype _initialize_remote_e7000; /* -Wmissing-prototypes */ + +void +_initialize_remote_e7000 (void) +{ + init_e7000_ops (); + add_target (&e7000_ops); + + add_com ("e7000", class_obscure, e7000_command, + "Send a command to the e7000 monitor."); + + add_com ("ftplogin", class_obscure, e7000_login_command, + "Login to machine and change to directory."); + + add_com ("ftpload", class_obscure, e7000_ftp_command, + "Fetch and load a file from previously described place."); + + add_com ("drain", class_obscure, e7000_drain_command, + "Drain pending e7000 text buffers."); + + add_show_from_set (add_set_cmd ("usehardbreakpoints", no_class, + var_integer, (char *) &use_hard_breakpoints, + "Set use of hardware breakpoints for all breakpoints.\n", &setlist), + &showlist); +} diff --git a/contrib/gdb/gdb/remote-est.c b/contrib/gdb/gdb/remote-est.c new file mode 100644 index 0000000..a2c0f7c --- /dev/null +++ b/contrib/gdb/gdb/remote-est.c @@ -0,0 +1,186 @@ +/* Remote debugging interface for EST-300 ICE, for GDB + Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001 + Free Software Foundation, Inc. + Contributed by Cygnus Support. + + Written by Steve Chamberlain for Cygnus Support. + Re-written by Stu Grossman of Cygnus Support + + 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 "defs.h" +#include "gdbcore.h" +#include "target.h" +#include "monitor.h" +#include "serial.h" +#include "regcache.h" + +#include "m68k-tdep.h" + +static void est_open (char *args, int from_tty); + +static void +est_supply_register (char *regname, int regnamelen, char *val, int vallen) +{ + int regno; + + if (regnamelen != 2) + return; + + switch (regname[0]) + { + case 'S': + if (regname[1] != 'R') + return; + regno = PS_REGNUM; + break; + case 'P': + if (regname[1] != 'C') + return; + regno = PC_REGNUM; + break; + case 'D': + if (regname[1] < '0' || regname[1] > '7') + return; + regno = regname[1] - '0' + M68K_D0_REGNUM; + break; + case 'A': + if (regname[1] < '0' || regname[1] > '7') + return; + regno = regname[1] - '0' + M68K_A0_REGNUM; + break; + default: + return; + } + + monitor_supply_register (regno, val); +} + +/* + * This array of registers needs to match the indexes used by GDB. The + * whole reason this exists is because the various ROM monitors use + * different names than GDB does, and don't support all the + * registers either. So, typing "info reg sp" becomes a "r30". + */ + +static const char * +est_regname (int index) +{ + + static char *regnames[] = + { + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", + "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", + "SR", "PC", + }; + + + if ((index >= (sizeof (regnames) / sizeof (regnames[0]))) + || (index < 0) || (index >= NUM_REGS)) + return NULL; + else + return regnames[index]; +} + +/* + * Define the monitor command strings. Since these are passed directly + * through to a printf style function, we need can include formatting + * strings. We also need a CR or LF on the end. + */ + +static struct target_ops est_ops; + +static char *est_inits[] = +{"he\r", /* Resets the prompt, and clears repeated cmds */ + NULL}; + +static struct monitor_ops est_cmds; + +static void +init_est_cmds (void) +{ + est_cmds.flags = MO_CLR_BREAK_USES_ADDR | MO_FILL_USES_ADDR | MO_NEED_REGDUMP_AFTER_CONT | + MO_SREC_ACK | MO_SREC_ACK_PLUS; + est_cmds.init = est_inits; /* Init strings */ + est_cmds.cont = "go\r"; /* continue command */ + est_cmds.step = "sidr\r"; /* single step */ + est_cmds.stop = "\003"; /* ^C interrupts the program */ + est_cmds.set_break = "sb %x\r"; /* set a breakpoint */ + est_cmds.clr_break = "rb %x\r"; /* clear a breakpoint */ + est_cmds.clr_all_break = "rb\r"; /* clear all breakpoints */ + est_cmds.fill = "bfb %x %x %x\r"; /* fill (start end val) */ + est_cmds.setmem.cmdb = "smb %x %x\r"; /* setmem.cmdb (addr, value) */ + est_cmds.setmem.cmdw = "smw %x %x\r"; /* setmem.cmdw (addr, value) */ + est_cmds.setmem.cmdl = "sml %x %x\r"; /* setmem.cmdl (addr, value) */ + est_cmds.setmem.cmdll = NULL; /* setmem.cmdll (addr, value) */ + est_cmds.setmem.resp_delim = NULL; /* setreg.resp_delim */ + est_cmds.setmem.term = NULL; /* setreg.term */ + est_cmds.setmem.term_cmd = NULL; /* setreg.term_cmd */ + est_cmds.getmem.cmdb = "dmb %x %x\r"; /* getmem.cmdb (addr, len) */ + est_cmds.getmem.cmdw = "dmw %x %x\r"; /* getmem.cmdw (addr, len) */ + est_cmds.getmem.cmdl = "dml %x %x\r"; /* getmem.cmdl (addr, len) */ + est_cmds.getmem.cmdll = NULL; /* getmem.cmdll (addr, len) */ + est_cmds.getmem.resp_delim = ": "; /* getmem.resp_delim */ + est_cmds.getmem.term = NULL; /* getmem.term */ + est_cmds.getmem.term_cmd = NULL; /* getmem.term_cmd */ + est_cmds.setreg.cmd = "sr %s %x\r"; /* setreg.cmd (name, value) */ + est_cmds.setreg.resp_delim = NULL; /* setreg.resp_delim */ + est_cmds.setreg.term = NULL; /* setreg.term */ + est_cmds.setreg.term_cmd = NULL; /* setreg.term_cmd */ + est_cmds.getreg.cmd = "dr %s\r"; /* getreg.cmd (name) */ + est_cmds.getreg.resp_delim = " = "; /* getreg.resp_delim */ + est_cmds.getreg.term = NULL; /* getreg.term */ + est_cmds.getreg.term_cmd = NULL; /* getreg.term_cmd */ + est_cmds.dump_registers = "dr\r"; /* dump_registers */ + est_cmds.register_pattern = "\\(\\w+\\) = \\([0-9a-fA-F]+\\)"; /* register_pattern */ + est_cmds.supply_register = est_supply_register; /* supply_register */ + est_cmds.load_routine = NULL; /* load_routine (defaults to SRECs) */ + est_cmds.load = "dl\r"; /* download command */ + est_cmds.loadresp = "+"; /* load response */ + est_cmds.prompt = ">BKM>"; /* monitor command prompt */ + est_cmds.line_term = "\r"; /* end-of-line terminator */ + est_cmds.cmd_end = NULL; /* optional command terminator */ + est_cmds.target = &est_ops; /* target operations */ + est_cmds.stopbits = SERIAL_1_STOPBITS; /* number of stop bits */ + est_cmds.regnames = NULL; + est_cmds.regname = est_regname; /*register names*/ + est_cmds.magic = MONITOR_OPS_MAGIC; /* magic */ +} /* init_est_cmds */ + +static void +est_open (char *args, int from_tty) +{ + monitor_open (args, &est_cmds, from_tty); +} + +extern initialize_file_ftype _initialize_est; /* -Wmissing-prototypes */ + +void +_initialize_est (void) +{ + init_est_cmds (); + init_monitor_ops (&est_ops); + + est_ops.to_shortname = "est"; + est_ops.to_longname = "EST background debug monitor"; + est_ops.to_doc = "Debug via the EST BDM.\n\ +Specify the serial device it is connected to (e.g. /dev/ttya)."; + est_ops.to_open = est_open; + + add_target (&est_ops); +} diff --git a/contrib/gdb/gdb/remote-hms.c b/contrib/gdb/gdb/remote-hms.c new file mode 100644 index 0000000..ee40051 --- /dev/null +++ b/contrib/gdb/gdb/remote-hms.c @@ -0,0 +1,159 @@ +/* Remote debugging interface for Renesas HMS Monitor Version 1.0 + Copyright 1995, 1996, 1998, 1999, 2000, 2001 + Free Software Foundation, Inc. + Contributed by Cygnus Support. Written by Steve Chamberlain + (sac@cygnus.com). + + 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 "defs.h" +#include "gdbcore.h" +#include "target.h" +#include "monitor.h" +#include "serial.h" +#include "regcache.h" + +static void hms_open (char *args, int from_tty); +static void +hms_supply_register (char *regname, int regnamelen, char *val, int vallen) +{ + int regno; + + if (regnamelen != 2) + return; + if (regname[0] != 'P') + return; + /* We scan off all the registers in one go */ + + val = monitor_supply_register (PC_REGNUM, val); + /* Skip the ccr string */ + while (*val != '=' && *val) + val++; + + val = monitor_supply_register (CCR_REGNUM, val + 1); + + /* Skip up to rest of regs */ + while (*val != '=' && *val) + val++; + + for (regno = 0; regno < 7; regno++) + { + val = monitor_supply_register (regno, val + 1); + } +} + +/* + * This array of registers needs to match the indexes used by GDB. The + * whole reason this exists is because the various ROM monitors use + * different names than GDB does, and don't support all the + * registers either. So, typing "info reg sp" becomes a "r30". + */ + +static char *hms_regnames[] = +{ + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "CCR", "PC", "", "", "", "" +}; + +/* + * Define the monitor command strings. Since these are passed directly + * through to a printf style function, we need can include formatting + * strings. We also need a CR or LF on the end. + */ + +static struct target_ops hms_ops; + +static char *hms_inits[] = +{"\003", /* Resets the prompt, and clears repeated cmds */ + NULL}; + +static struct monitor_ops hms_cmds; + +static void +init_hms_cmds (void) +{ + hms_cmds.flags = MO_CLR_BREAK_USES_ADDR | MO_FILL_USES_ADDR | MO_GETMEM_NEEDS_RANGE; + hms_cmds.init = hms_inits; /* Init strings */ + hms_cmds.cont = "g\r"; /* continue command */ + hms_cmds.step = "s\r"; /* single step */ + hms_cmds.stop = "\003"; /* ^C interrupts the program */ + hms_cmds.set_break = "b %x\r"; /* set a breakpoint */ + hms_cmds.clr_break = "b - %x\r"; /* clear a breakpoint */ + hms_cmds.clr_all_break = "b -\r"; /* clear all breakpoints */ + hms_cmds.fill = "f %x %x %x\r"; /* fill (start end val) */ + hms_cmds.setmem.cmdb = "m.b %x=%x\r"; /* setmem.cmdb (addr, value) */ + hms_cmds.setmem.cmdw = "m.w %x=%x\r"; /* setmem.cmdw (addr, value) */ + hms_cmds.setmem.cmdl = NULL; /* setmem.cmdl (addr, value) */ + hms_cmds.setmem.cmdll = NULL; /* setmem.cmdll (addr, value) */ + hms_cmds.setmem.resp_delim = NULL; /* setreg.resp_delim */ + hms_cmds.setmem.term = NULL; /* setreg.term */ + hms_cmds.setmem.term_cmd = NULL; /* setreg.term_cmd */ + hms_cmds.getmem.cmdb = "m.b %x %x\r"; /* getmem.cmdb (addr, addr) */ + hms_cmds.getmem.cmdw = "m.w %x %x\r"; /* getmem.cmdw (addr, addr) */ + hms_cmds.getmem.cmdl = NULL; /* getmem.cmdl (addr, addr) */ + hms_cmds.getmem.cmdll = NULL; /* getmem.cmdll (addr, addr) */ + hms_cmds.getmem.resp_delim = ": "; /* getmem.resp_delim */ + hms_cmds.getmem.term = ">"; /* getmem.term */ + hms_cmds.getmem.term_cmd = "\003"; /* getmem.term_cmd */ + hms_cmds.setreg.cmd = "r %s=%x\r"; /* setreg.cmd (name, value) */ + hms_cmds.setreg.resp_delim = NULL; /* setreg.resp_delim */ + hms_cmds.setreg.term = NULL; /* setreg.term */ + hms_cmds.setreg.term_cmd = NULL; /* setreg.term_cmd */ + hms_cmds.getreg.cmd = "r %s\r"; /* getreg.cmd (name) */ + hms_cmds.getreg.resp_delim = " ("; /* getreg.resp_delim */ + hms_cmds.getreg.term = ":"; /* getreg.term */ + hms_cmds.getreg.term_cmd = "\003"; /* getreg.term_cmd */ + hms_cmds.dump_registers = "r\r"; /* dump_registers */ + hms_cmds.register_pattern = "\\(\\w+\\)=\\([0-9a-fA-F]+\\)"; /* register_pattern */ + hms_cmds.supply_register = hms_supply_register; /* supply_register */ + hms_cmds.load_routine = NULL; /* load_routine (defaults to SRECs) */ + hms_cmds.load = "tl\r"; /* download command */ + hms_cmds.loadresp = NULL; /* load response */ + hms_cmds.prompt = ">"; /* monitor command prompt */ + hms_cmds.line_term = "\r"; /* end-of-command delimitor */ + hms_cmds.cmd_end = NULL; /* optional command terminator */ + hms_cmds.target = &hms_ops; /* target operations */ + hms_cmds.stopbits = SERIAL_1_STOPBITS; /* number of stop bits */ + hms_cmds.regnames = hms_regnames; /* registers names */ + hms_cmds.magic = MONITOR_OPS_MAGIC; /* magic */ +} /* init_hms-cmds */ + +static void +hms_open (char *args, int from_tty) +{ + monitor_open (args, &hms_cmds, from_tty); +} + +int write_dos_tick_delay; + +extern initialize_file_ftype _initialize_remote_hms; /* -Wmissing-prototypes */ + +void +_initialize_remote_hms (void) +{ + init_hms_cmds (); + init_monitor_ops (&hms_ops); + + hms_ops.to_shortname = "hms"; + hms_ops.to_longname = "Renesas Microsystems H8/300 debug monitor"; + hms_ops.to_doc = "Debug via the HMS monitor.\n\ +Specify the serial device it is connected to (e.g. /dev/ttya)."; + hms_ops.to_open = hms_open; + /* By trial and error I've found that this delay doesn't break things */ + write_dos_tick_delay = 1; + add_target (&hms_ops); +} diff --git a/contrib/gdb/gdb/remote-mips.c b/contrib/gdb/gdb/remote-mips.c new file mode 100644 index 0000000..c757684 --- /dev/null +++ b/contrib/gdb/gdb/remote-mips.c @@ -0,0 +1,3421 @@ +/* Remote debugging interface for MIPS remote debugging protocol. + + Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + 2002 Free Software Foundation, Inc. + + Contributed by Cygnus Support. Written by Ian Lance Taylor + <ian@cygnus.com>. + + 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 "defs.h" +#include "inferior.h" +#include "bfd.h" +#include "symfile.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "serial.h" +#include "target.h" +#include "remote-utils.h" +#include "gdb_string.h" +#include "gdb_stat.h" +#include "regcache.h" +#include <ctype.h> +#include "mips-tdep.h" + + +/* Breakpoint types. Values 0, 1, and 2 must agree with the watch + types passed by breakpoint.c to target_insert_watchpoint. + Value 3 is our own invention, and is used for ordinary instruction + breakpoints. Value 4 is used to mark an unused watchpoint in tables. */ +enum break_type + { + BREAK_WRITE, /* 0 */ + BREAK_READ, /* 1 */ + BREAK_ACCESS, /* 2 */ + BREAK_FETCH, /* 3 */ + BREAK_UNUSED /* 4 */ + }; + +/* Prototypes for local functions. */ + +static int mips_readchar (int timeout); + +static int mips_receive_header (unsigned char *hdr, int *pgarbage, + int ch, int timeout); + +static int mips_receive_trailer (unsigned char *trlr, int *pgarbage, + int *pch, int timeout); + +static int mips_cksum (const unsigned char *hdr, + const unsigned char *data, int len); + +static void mips_send_packet (const char *s, int get_ack); + +static void mips_send_command (const char *cmd, int prompt); + +static int mips_receive_packet (char *buff, int throw_error, int timeout); + +static ULONGEST mips_request (int cmd, ULONGEST addr, ULONGEST data, + int *perr, int timeout, char *buff); + +static void mips_initialize (void); + +static void mips_open (char *name, int from_tty); + +static void pmon_open (char *name, int from_tty); + +static void ddb_open (char *name, int from_tty); + +static void lsi_open (char *name, int from_tty); + +static void mips_close (int quitting); + +static void mips_detach (char *args, int from_tty); + +static void mips_resume (ptid_t ptid, int step, + enum target_signal siggnal); + +static ptid_t mips_wait (ptid_t ptid, + struct target_waitstatus *status); + +static int mips_map_regno (int regno); + +static void mips_fetch_registers (int regno); + +static void mips_prepare_to_store (void); + +static void mips_store_registers (int regno); + +static unsigned int mips_fetch_word (CORE_ADDR addr); + +static int mips_store_word (CORE_ADDR addr, unsigned int value, + char *old_contents); + +static int mips_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, + int write, + struct mem_attrib *attrib, + struct target_ops *target); + +static void mips_files_info (struct target_ops *ignore); + +static void mips_create_inferior (char *execfile, char *args, char **env); + +static void mips_mourn_inferior (void); + +static int pmon_makeb64 (unsigned long v, char *p, int n, int *chksum); + +static int pmon_zeroset (int recsize, char **buff, int *amount, + unsigned int *chksum); + +static int pmon_checkset (int recsize, char **buff, int *value); + +static void pmon_make_fastrec (char **outbuf, unsigned char *inbuf, + int *inptr, int inamount, int *recsize, + unsigned int *csum, unsigned int *zerofill); + +static int pmon_check_ack (char *mesg); + +static void pmon_start_download (void); + +static void pmon_end_download (int final, int bintotal); + +static void pmon_download (char *buffer, int length); + +static void pmon_load_fast (char *file); + +static void mips_load (char *file, int from_tty); + +static int mips_make_srec (char *buffer, int type, CORE_ADDR memaddr, + unsigned char *myaddr, int len); + +static int set_breakpoint (CORE_ADDR addr, int len, enum break_type type); + +static int clear_breakpoint (CORE_ADDR addr, int len, enum break_type type); + +static int common_breakpoint (int set, CORE_ADDR addr, int len, + enum break_type type); + +/* Forward declarations. */ +extern struct target_ops mips_ops; +extern struct target_ops pmon_ops; +extern struct target_ops ddb_ops; +/* *INDENT-OFF* */ +/* The MIPS remote debugging interface is built on top of a simple + packet protocol. Each packet is organized as follows: + + SYN The first character is always a SYN (ASCII 026, or ^V). SYN + may not appear anywhere else in the packet. Any time a SYN is + seen, a new packet should be assumed to have begun. + + TYPE_LEN + This byte contains the upper five bits of the logical length + of the data section, plus a single bit indicating whether this + is a data packet or an acknowledgement. The documentation + indicates that this bit is 1 for a data packet, but the actual + board uses 1 for an acknowledgement. The value of the byte is + 0x40 + (ack ? 0x20 : 0) + (len >> 6) + (we always have 0 <= len < 1024). Acknowledgement packets do + not carry data, and must have a data length of 0. + + LEN1 This byte contains the lower six bits of the logical length of + the data section. The value is + 0x40 + (len & 0x3f) + + SEQ This byte contains the six bit sequence number of the packet. + The value is + 0x40 + seq + An acknowlegment packet contains the sequence number of the + packet being acknowledged plus 1 modulo 64. Data packets are + transmitted in sequence. There may only be one outstanding + unacknowledged data packet at a time. The sequence numbers + are independent in each direction. If an acknowledgement for + the previous packet is received (i.e., an acknowledgement with + the sequence number of the packet just sent) the packet just + sent should be retransmitted. If no acknowledgement is + received within a timeout period, the packet should be + retransmitted. This has an unfortunate failure condition on a + high-latency line, as a delayed acknowledgement may lead to an + endless series of duplicate packets. + + DATA The actual data bytes follow. The following characters are + escaped inline with DLE (ASCII 020, or ^P): + SYN (026) DLE S + DLE (020) DLE D + ^C (003) DLE C + ^S (023) DLE s + ^Q (021) DLE q + The additional DLE characters are not counted in the logical + length stored in the TYPE_LEN and LEN1 bytes. + + CSUM1 + CSUM2 + CSUM3 + These bytes contain an 18 bit checksum of the complete + contents of the packet excluding the SEQ byte and the + CSUM[123] bytes. The checksum is simply the twos complement + addition of all the bytes treated as unsigned characters. The + values of the checksum bytes are: + CSUM1: 0x40 + ((cksum >> 12) & 0x3f) + CSUM2: 0x40 + ((cksum >> 6) & 0x3f) + CSUM3: 0x40 + (cksum & 0x3f) + + It happens that the MIPS remote debugging protocol always + communicates with ASCII strings. Because of this, this + implementation doesn't bother to handle the DLE quoting mechanism, + since it will never be required. */ +/* *INDENT-ON* */ + + +/* The SYN character which starts each packet. */ +#define SYN '\026' + +/* The 0x40 used to offset each packet (this value ensures that all of + the header and trailer bytes, other than SYN, are printable ASCII + characters). */ +#define HDR_OFFSET 0x40 + +/* The indices of the bytes in the packet header. */ +#define HDR_INDX_SYN 0 +#define HDR_INDX_TYPE_LEN 1 +#define HDR_INDX_LEN1 2 +#define HDR_INDX_SEQ 3 +#define HDR_LENGTH 4 + +/* The data/ack bit in the TYPE_LEN header byte. */ +#define TYPE_LEN_DA_BIT 0x20 +#define TYPE_LEN_DATA 0 +#define TYPE_LEN_ACK TYPE_LEN_DA_BIT + +/* How to compute the header bytes. */ +#define HDR_SET_SYN(data, len, seq) (SYN) +#define HDR_SET_TYPE_LEN(data, len, seq) \ + (HDR_OFFSET \ + + ((data) ? TYPE_LEN_DATA : TYPE_LEN_ACK) \ + + (((len) >> 6) & 0x1f)) +#define HDR_SET_LEN1(data, len, seq) (HDR_OFFSET + ((len) & 0x3f)) +#define HDR_SET_SEQ(data, len, seq) (HDR_OFFSET + (seq)) + +/* Check that a header byte is reasonable. */ +#define HDR_CHECK(ch) (((ch) & HDR_OFFSET) == HDR_OFFSET) + +/* Get data from the header. These macros evaluate their argument + multiple times. */ +#define HDR_IS_DATA(hdr) \ + (((hdr)[HDR_INDX_TYPE_LEN] & TYPE_LEN_DA_BIT) == TYPE_LEN_DATA) +#define HDR_GET_LEN(hdr) \ + ((((hdr)[HDR_INDX_TYPE_LEN] & 0x1f) << 6) + (((hdr)[HDR_INDX_LEN1] & 0x3f))) +#define HDR_GET_SEQ(hdr) ((unsigned int)(hdr)[HDR_INDX_SEQ] & 0x3f) + +/* The maximum data length. */ +#define DATA_MAXLEN 1023 + +/* The trailer offset. */ +#define TRLR_OFFSET HDR_OFFSET + +/* The indices of the bytes in the packet trailer. */ +#define TRLR_INDX_CSUM1 0 +#define TRLR_INDX_CSUM2 1 +#define TRLR_INDX_CSUM3 2 +#define TRLR_LENGTH 3 + +/* How to compute the trailer bytes. */ +#define TRLR_SET_CSUM1(cksum) (TRLR_OFFSET + (((cksum) >> 12) & 0x3f)) +#define TRLR_SET_CSUM2(cksum) (TRLR_OFFSET + (((cksum) >> 6) & 0x3f)) +#define TRLR_SET_CSUM3(cksum) (TRLR_OFFSET + (((cksum) ) & 0x3f)) + +/* Check that a trailer byte is reasonable. */ +#define TRLR_CHECK(ch) (((ch) & TRLR_OFFSET) == TRLR_OFFSET) + +/* Get data from the trailer. This evaluates its argument multiple + times. */ +#define TRLR_GET_CKSUM(trlr) \ + ((((trlr)[TRLR_INDX_CSUM1] & 0x3f) << 12) \ + + (((trlr)[TRLR_INDX_CSUM2] & 0x3f) << 6) \ + + ((trlr)[TRLR_INDX_CSUM3] & 0x3f)) + +/* The sequence number modulos. */ +#define SEQ_MODULOS (64) + +/* PMON commands to load from the serial port or UDP socket. */ +#define LOAD_CMD "load -b -s tty0\r" +#define LOAD_CMD_UDP "load -b -s udp\r" + +/* The target vectors for the four different remote MIPS targets. + These are initialized with code in _initialize_remote_mips instead + of static initializers, to make it easier to extend the target_ops + vector later. */ +struct target_ops mips_ops, pmon_ops, ddb_ops, lsi_ops; + +enum mips_monitor_type + { + /* IDT/SIM monitor being used: */ + MON_IDT, + /* PMON monitor being used: */ + MON_PMON, /* 3.0.83 [COGENT,EB,FP,NET] Algorithmics Ltd. Nov 9 1995 17:19:50 */ + MON_DDB, /* 2.7.473 [DDBVR4300,EL,FP,NET] Risq Modular Systems, Thu Jun 6 09:28:40 PDT 1996 */ + MON_LSI, /* 4.3.12 [EB,FP], LSI LOGIC Corp. Tue Feb 25 13:22:14 1997 */ + /* Last and unused value, for sizing vectors, etc. */ + MON_LAST + }; +static enum mips_monitor_type mips_monitor = MON_LAST; + +/* The monitor prompt text. If the user sets the PMON prompt + to some new value, the GDB `set monitor-prompt' command must also + be used to inform GDB about the expected prompt. Otherwise, GDB + will not be able to connect to PMON in mips_initialize(). + If the `set monitor-prompt' command is not used, the expected + default prompt will be set according the target: + target prompt + ----- ----- + pmon PMON> + ddb NEC010> + lsi PMON> + */ +static char *mips_monitor_prompt; + +/* Set to 1 if the target is open. */ +static int mips_is_open; + +/* Currently active target description (if mips_is_open == 1) */ +static struct target_ops *current_ops; + +/* Set to 1 while the connection is being initialized. */ +static int mips_initializing; + +/* Set to 1 while the connection is being brought down. */ +static int mips_exiting; + +/* The next sequence number to send. */ +static unsigned int mips_send_seq; + +/* The next sequence number we expect to receive. */ +static unsigned int mips_receive_seq; + +/* The time to wait before retransmitting a packet, in seconds. */ +static int mips_retransmit_wait = 3; + +/* The number of times to try retransmitting a packet before giving up. */ +static int mips_send_retries = 10; + +/* The number of garbage characters to accept when looking for an + SYN for the next packet. */ +static int mips_syn_garbage = 10; + +/* The time to wait for a packet, in seconds. */ +static int mips_receive_wait = 5; + +/* Set if we have sent a packet to the board but have not yet received + a reply. */ +static int mips_need_reply = 0; + +/* Handle used to access serial I/O stream. */ +static struct serial *mips_desc; + +/* UDP handle used to download files to target. */ +static struct serial *udp_desc; +static int udp_in_use; + +/* TFTP filename used to download files to DDB board, in the form + host:filename. */ +static char *tftp_name; /* host:filename */ +static char *tftp_localname; /* filename portion of above */ +static int tftp_in_use; +static FILE *tftp_file; + +/* Counts the number of times the user tried to interrupt the target (usually + via ^C. */ +static int interrupt_count; + +/* If non-zero, means that the target is running. */ +static int mips_wait_flag = 0; + +/* If non-zero, monitor supports breakpoint commands. */ +static int monitor_supports_breakpoints = 0; + +/* Data cache header. */ + +#if 0 /* not used (yet?) */ +static DCACHE *mips_dcache; +#endif + +/* Non-zero means that we've just hit a read or write watchpoint */ +static int hit_watchpoint; + +/* Table of breakpoints/watchpoints (used only on LSI PMON target). + The table is indexed by a breakpoint number, which is an integer + from 0 to 255 returned by the LSI PMON when a breakpoint is set. + */ +#define MAX_LSI_BREAKPOINTS 256 +struct lsi_breakpoint_info + { + enum break_type type; /* type of breakpoint */ + CORE_ADDR addr; /* address of breakpoint */ + int len; /* length of region being watched */ + unsigned long value; /* value to watch */ + } +lsi_breakpoints[MAX_LSI_BREAKPOINTS]; + +/* Error/warning codes returned by LSI PMON for breakpoint commands. + Warning values may be ORed together; error values may not. */ +#define W_WARN 0x100 /* This bit is set if the error code is a warning */ +#define W_MSK 0x101 /* warning: Range feature is supported via mask */ +#define W_VAL 0x102 /* warning: Value check is not supported in hardware */ +#define W_QAL 0x104 /* warning: Requested qualifiers are not supported in hardware */ + +#define E_ERR 0x200 /* This bit is set if the error code is an error */ +#define E_BPT 0x200 /* error: No such breakpoint number */ +#define E_RGE 0x201 /* error: Range is not supported */ +#define E_QAL 0x202 /* error: The requested qualifiers can not be used */ +#define E_OUT 0x203 /* error: Out of hardware resources */ +#define E_NON 0x204 /* error: Hardware breakpoint not supported */ + +struct lsi_error + { + int code; /* error code */ + char *string; /* string associated with this code */ + }; + +struct lsi_error lsi_warning_table[] = +{ + {W_MSK, "Range feature is supported via mask"}, + {W_VAL, "Value check is not supported in hardware"}, + {W_QAL, "Requested qualifiers are not supported in hardware"}, + {0, NULL} +}; + +struct lsi_error lsi_error_table[] = +{ + {E_BPT, "No such breakpoint number"}, + {E_RGE, "Range is not supported"}, + {E_QAL, "The requested qualifiers can not be used"}, + {E_OUT, "Out of hardware resources"}, + {E_NON, "Hardware breakpoint not supported"}, + {0, NULL} +}; + +/* Set to 1 with the 'set monitor-warnings' command to enable printing + of warnings returned by PMON when hardware breakpoints are used. */ +static int monitor_warnings; + + +static void +close_ports (void) +{ + mips_is_open = 0; + serial_close (mips_desc); + + if (udp_in_use) + { + serial_close (udp_desc); + udp_in_use = 0; + } + tftp_in_use = 0; +} + +/* Handle low-level error that we can't recover from. Note that just + error()ing out from target_wait or some such low-level place will cause + all hell to break loose--the rest of GDB will tend to get left in an + inconsistent state. */ + +static NORETURN void +mips_error (char *string,...) +{ + va_list args; + + va_start (args, string); + + target_terminal_ours (); + wrap_here (""); /* Force out any buffered output */ + gdb_flush (gdb_stdout); + if (error_pre_print) + fputs_filtered (error_pre_print, gdb_stderr); + vfprintf_filtered (gdb_stderr, string, args); + fprintf_filtered (gdb_stderr, "\n"); + va_end (args); + gdb_flush (gdb_stderr); + + /* Clean up in such a way that mips_close won't try to talk to the + board (it almost surely won't work since we weren't able to talk to + it). */ + close_ports (); + + printf_unfiltered ("Ending remote MIPS debugging.\n"); + target_mourn_inferior (); + + throw_exception (RETURN_ERROR); +} + +/* putc_readable - print a character, displaying non-printable chars in + ^x notation or in hex. */ + +static void +fputc_readable (int ch, struct ui_file *file) +{ + if (ch == '\n') + fputc_unfiltered ('\n', file); + else if (ch == '\r') + fprintf_unfiltered (file, "\\r"); + else if (ch < 0x20) /* ASCII control character */ + fprintf_unfiltered (file, "^%c", ch + '@'); + else if (ch >= 0x7f) /* non-ASCII characters (rubout or greater) */ + fprintf_unfiltered (file, "[%02x]", ch & 0xff); + else + fputc_unfiltered (ch, file); +} + + +/* puts_readable - print a string, displaying non-printable chars in + ^x notation or in hex. */ + +static void +fputs_readable (const char *string, struct ui_file *file) +{ + int c; + + while ((c = *string++) != '\0') + fputc_readable (c, file); +} + + +/* Wait until STRING shows up in mips_desc. Returns 1 if successful, else 0 if + timed out. TIMEOUT specifies timeout value in seconds. + */ + +static int +mips_expect_timeout (const char *string, int timeout) +{ + const char *p = string; + + if (remote_debug) + { + fprintf_unfiltered (gdb_stdlog, "Expected \""); + fputs_readable (string, gdb_stdlog); + fprintf_unfiltered (gdb_stdlog, "\", got \""); + } + + immediate_quit++; + while (1) + { + int c; + + /* Must use serial_readchar() here cuz mips_readchar would get + confused if we were waiting for the mips_monitor_prompt... */ + + c = serial_readchar (mips_desc, timeout); + + if (c == SERIAL_TIMEOUT) + { + if (remote_debug) + fprintf_unfiltered (gdb_stdlog, "\": FAIL\n"); + return 0; + } + + if (remote_debug) + fputc_readable (c, gdb_stdlog); + + if (c == *p++) + { + if (*p == '\0') + { + immediate_quit--; + if (remote_debug) + fprintf_unfiltered (gdb_stdlog, "\": OK\n"); + return 1; + } + } + else + { + p = string; + if (c == *p) + p++; + } + } +} + +/* Wait until STRING shows up in mips_desc. Returns 1 if successful, else 0 if + timed out. The timeout value is hard-coded to 2 seconds. Use + mips_expect_timeout if a different timeout value is needed. + */ + +static int +mips_expect (const char *string) +{ + return mips_expect_timeout (string, remote_timeout); +} + +/* Read a character from the remote, aborting on error. Returns + SERIAL_TIMEOUT on timeout (since that's what serial_readchar() + returns). FIXME: If we see the string mips_monitor_prompt from the + board, then we are debugging on the main console port, and we have + somehow dropped out of remote debugging mode. In this case, we + automatically go back in to remote debugging mode. This is a hack, + put in because I can't find any way for a program running on the + remote board to terminate without also ending remote debugging + mode. I assume users won't have any trouble with this; for one + thing, the IDT documentation generally assumes that the remote + debugging port is not the console port. This is, however, very + convenient for DejaGnu when you only have one connected serial + port. */ + +static int +mips_readchar (int timeout) +{ + int ch; + static int state = 0; + int mips_monitor_prompt_len = strlen (mips_monitor_prompt); + + { + int i; + + i = timeout; + if (i == -1 && watchdog > 0) + i = watchdog; + } + + if (state == mips_monitor_prompt_len) + timeout = 1; + ch = serial_readchar (mips_desc, timeout); + + if (ch == SERIAL_TIMEOUT && timeout == -1) /* Watchdog went off */ + { + target_mourn_inferior (); + error ("Watchdog has expired. Target detached.\n"); + } + + if (ch == SERIAL_EOF) + mips_error ("End of file from remote"); + if (ch == SERIAL_ERROR) + mips_error ("Error reading from remote: %s", safe_strerror (errno)); + if (remote_debug > 1) + { + /* Don't use _filtered; we can't deal with a QUIT out of + target_wait, and I think this might be called from there. */ + if (ch != SERIAL_TIMEOUT) + fprintf_unfiltered (gdb_stdlog, "Read '%c' %d 0x%x\n", ch, ch, ch); + else + fprintf_unfiltered (gdb_stdlog, "Timed out in read\n"); + } + + /* If we have seen mips_monitor_prompt and we either time out, or + we see a @ (which was echoed from a packet we sent), reset the + board as described above. The first character in a packet after + the SYN (which is not echoed) is always an @ unless the packet is + more than 64 characters long, which ours never are. */ + if ((ch == SERIAL_TIMEOUT || ch == '@') + && state == mips_monitor_prompt_len + && !mips_initializing + && !mips_exiting) + { + if (remote_debug > 0) + /* Don't use _filtered; we can't deal with a QUIT out of + target_wait, and I think this might be called from there. */ + fprintf_unfiltered (gdb_stdlog, "Reinitializing MIPS debugging mode\n"); + + mips_need_reply = 0; + mips_initialize (); + + state = 0; + + /* At this point, about the only thing we can do is abort the command + in progress and get back to command level as quickly as possible. */ + + error ("Remote board reset, debug protocol re-initialized."); + } + + if (ch == mips_monitor_prompt[state]) + ++state; + else + state = 0; + + return ch; +} + +/* Get a packet header, putting the data in the supplied buffer. + PGARBAGE is a pointer to the number of garbage characters received + so far. CH is the last character received. Returns 0 for success, + or -1 for timeout. */ + +static int +mips_receive_header (unsigned char *hdr, int *pgarbage, int ch, int timeout) +{ + int i; + + while (1) + { + /* Wait for a SYN. mips_syn_garbage is intended to prevent + sitting here indefinitely if the board sends us one garbage + character per second. ch may already have a value from the + last time through the loop. */ + while (ch != SYN) + { + ch = mips_readchar (timeout); + if (ch == SERIAL_TIMEOUT) + return -1; + if (ch != SYN) + { + /* Printing the character here lets the user of gdb see + what the program is outputting, if the debugging is + being done on the console port. Don't use _filtered: + we can't deal with a QUIT out of target_wait and + buffered target output confuses the user. */ + if (!mips_initializing || remote_debug > 0) + { + if (isprint (ch) || isspace (ch)) + { + fputc_unfiltered (ch, gdb_stdtarg); + } + else + { + fputc_readable (ch, gdb_stdtarg); + } + gdb_flush (gdb_stdtarg); + } + + /* Only count unprintable characters. */ + if (! (isprint (ch) || isspace (ch))) + (*pgarbage) += 1; + + if (mips_syn_garbage > 0 + && *pgarbage > mips_syn_garbage) + mips_error ("Debug protocol failure: more than %d characters before a sync.", + mips_syn_garbage); + } + } + + /* Get the packet header following the SYN. */ + for (i = 1; i < HDR_LENGTH; i++) + { + ch = mips_readchar (timeout); + if (ch == SERIAL_TIMEOUT) + return -1; + /* Make sure this is a header byte. */ + if (ch == SYN || !HDR_CHECK (ch)) + break; + + hdr[i] = ch; + } + + /* If we got the complete header, we can return. Otherwise we + loop around and keep looking for SYN. */ + if (i >= HDR_LENGTH) + return 0; + } +} + +/* Get a packet header, putting the data in the supplied buffer. + PGARBAGE is a pointer to the number of garbage characters received + so far. The last character read is returned in *PCH. Returns 0 + for success, -1 for timeout, -2 for error. */ + +static int +mips_receive_trailer (unsigned char *trlr, int *pgarbage, int *pch, int timeout) +{ + int i; + int ch; + + for (i = 0; i < TRLR_LENGTH; i++) + { + ch = mips_readchar (timeout); + *pch = ch; + if (ch == SERIAL_TIMEOUT) + return -1; + if (!TRLR_CHECK (ch)) + return -2; + trlr[i] = ch; + } + return 0; +} + +/* Get the checksum of a packet. HDR points to the packet header. + DATA points to the packet data. LEN is the length of DATA. */ + +static int +mips_cksum (const unsigned char *hdr, const unsigned char *data, int len) +{ + const unsigned char *p; + int c; + int cksum; + + cksum = 0; + + /* The initial SYN is not included in the checksum. */ + c = HDR_LENGTH - 1; + p = hdr + 1; + while (c-- != 0) + cksum += *p++; + + c = len; + p = data; + while (c-- != 0) + cksum += *p++; + + return cksum; +} + +/* Send a packet containing the given ASCII string. */ + +static void +mips_send_packet (const char *s, int get_ack) +{ + /* unsigned */ int len; + unsigned char *packet; + int cksum; + int try; + + len = strlen (s); + if (len > DATA_MAXLEN) + mips_error ("MIPS protocol data packet too long: %s", s); + + packet = (unsigned char *) alloca (HDR_LENGTH + len + TRLR_LENGTH + 1); + + packet[HDR_INDX_SYN] = HDR_SET_SYN (1, len, mips_send_seq); + packet[HDR_INDX_TYPE_LEN] = HDR_SET_TYPE_LEN (1, len, mips_send_seq); + packet[HDR_INDX_LEN1] = HDR_SET_LEN1 (1, len, mips_send_seq); + packet[HDR_INDX_SEQ] = HDR_SET_SEQ (1, len, mips_send_seq); + + memcpy (packet + HDR_LENGTH, s, len); + + cksum = mips_cksum (packet, packet + HDR_LENGTH, len); + packet[HDR_LENGTH + len + TRLR_INDX_CSUM1] = TRLR_SET_CSUM1 (cksum); + packet[HDR_LENGTH + len + TRLR_INDX_CSUM2] = TRLR_SET_CSUM2 (cksum); + packet[HDR_LENGTH + len + TRLR_INDX_CSUM3] = TRLR_SET_CSUM3 (cksum); + + /* Increment the sequence number. This will set mips_send_seq to + the sequence number we expect in the acknowledgement. */ + mips_send_seq = (mips_send_seq + 1) % SEQ_MODULOS; + + /* We can only have one outstanding data packet, so we just wait for + the acknowledgement here. Keep retransmitting the packet until + we get one, or until we've tried too many times. */ + for (try = 0; try < mips_send_retries; try++) + { + int garbage; + int ch; + + if (remote_debug > 0) + { + /* Don't use _filtered; we can't deal with a QUIT out of + target_wait, and I think this might be called from there. */ + packet[HDR_LENGTH + len + TRLR_LENGTH] = '\0'; + fprintf_unfiltered (gdb_stdlog, "Writing \"%s\"\n", packet + 1); + } + + if (serial_write (mips_desc, packet, + HDR_LENGTH + len + TRLR_LENGTH) != 0) + mips_error ("write to target failed: %s", safe_strerror (errno)); + + if (!get_ack) + return; + + garbage = 0; + ch = 0; + while (1) + { + unsigned char hdr[HDR_LENGTH + 1]; + unsigned char trlr[TRLR_LENGTH + 1]; + int err; + unsigned int seq; + + /* Get the packet header. If we time out, resend the data + packet. */ + err = mips_receive_header (hdr, &garbage, ch, mips_retransmit_wait); + if (err != 0) + break; + + ch = 0; + + /* If we get a data packet, assume it is a duplicate and + ignore it. FIXME: If the acknowledgement is lost, this + data packet may be the packet the remote sends after the + acknowledgement. */ + if (HDR_IS_DATA (hdr)) + { + int i; + + /* Ignore any errors raised whilst attempting to ignore + packet. */ + + len = HDR_GET_LEN (hdr); + + for (i = 0; i < len; i++) + { + int rch; + + rch = mips_readchar (remote_timeout); + if (rch == SYN) + { + ch = SYN; + break; + } + if (rch == SERIAL_TIMEOUT) + break; + /* ignore the character */ + } + + if (i == len) + (void) mips_receive_trailer (trlr, &garbage, &ch, + remote_timeout); + + /* We don't bother checking the checksum, or providing an + ACK to the packet. */ + continue; + } + + /* If the length is not 0, this is a garbled packet. */ + if (HDR_GET_LEN (hdr) != 0) + continue; + + /* Get the packet trailer. */ + err = mips_receive_trailer (trlr, &garbage, &ch, + mips_retransmit_wait); + + /* If we timed out, resend the data packet. */ + if (err == -1) + break; + + /* If we got a bad character, reread the header. */ + if (err != 0) + continue; + + /* If the checksum does not match the trailer checksum, this + is a bad packet; ignore it. */ + if (mips_cksum (hdr, (unsigned char *) NULL, 0) + != TRLR_GET_CKSUM (trlr)) + continue; + + if (remote_debug > 0) + { + hdr[HDR_LENGTH] = '\0'; + trlr[TRLR_LENGTH] = '\0'; + /* Don't use _filtered; we can't deal with a QUIT out of + target_wait, and I think this might be called from there. */ + fprintf_unfiltered (gdb_stdlog, "Got ack %d \"%s%s\"\n", + HDR_GET_SEQ (hdr), hdr + 1, trlr); + } + + /* If this ack is for the current packet, we're done. */ + seq = HDR_GET_SEQ (hdr); + if (seq == mips_send_seq) + return; + + /* If this ack is for the last packet, resend the current + packet. */ + if ((seq + 1) % SEQ_MODULOS == mips_send_seq) + break; + + /* Otherwise this is a bad ack; ignore it. Increment the + garbage count to ensure that we do not stay in this loop + forever. */ + ++garbage; + } + } + + mips_error ("Remote did not acknowledge packet"); +} + +/* Receive and acknowledge a packet, returning the data in BUFF (which + should be DATA_MAXLEN + 1 bytes). The protocol documentation + implies that only the sender retransmits packets, so this code just + waits silently for a packet. It returns the length of the received + packet. If THROW_ERROR is nonzero, call error() on errors. If not, + don't print an error message and return -1. */ + +static int +mips_receive_packet (char *buff, int throw_error, int timeout) +{ + int ch; + int garbage; + int len; + unsigned char ack[HDR_LENGTH + TRLR_LENGTH + 1]; + int cksum; + + ch = 0; + garbage = 0; + while (1) + { + unsigned char hdr[HDR_LENGTH]; + unsigned char trlr[TRLR_LENGTH]; + int i; + int err; + + if (mips_receive_header (hdr, &garbage, ch, timeout) != 0) + { + if (throw_error) + mips_error ("Timed out waiting for remote packet"); + else + return -1; + } + + ch = 0; + + /* An acknowledgement is probably a duplicate; ignore it. */ + if (!HDR_IS_DATA (hdr)) + { + len = HDR_GET_LEN (hdr); + /* Check if the length is valid for an ACK, we may aswell + try and read the remainder of the packet: */ + if (len == 0) + { + /* Ignore the error condition, since we are going to + ignore the packet anyway. */ + (void) mips_receive_trailer (trlr, &garbage, &ch, timeout); + } + /* Don't use _filtered; we can't deal with a QUIT out of + target_wait, and I think this might be called from there. */ + if (remote_debug > 0) + fprintf_unfiltered (gdb_stdlog, "Ignoring unexpected ACK\n"); + continue; + } + + len = HDR_GET_LEN (hdr); + for (i = 0; i < len; i++) + { + int rch; + + rch = mips_readchar (timeout); + if (rch == SYN) + { + ch = SYN; + break; + } + if (rch == SERIAL_TIMEOUT) + { + if (throw_error) + mips_error ("Timed out waiting for remote packet"); + else + return -1; + } + buff[i] = rch; + } + + if (i < len) + { + /* Don't use _filtered; we can't deal with a QUIT out of + target_wait, and I think this might be called from there. */ + if (remote_debug > 0) + fprintf_unfiltered (gdb_stdlog, + "Got new SYN after %d chars (wanted %d)\n", + i, len); + continue; + } + + err = mips_receive_trailer (trlr, &garbage, &ch, timeout); + if (err == -1) + { + if (throw_error) + mips_error ("Timed out waiting for packet"); + else + return -1; + } + if (err == -2) + { + /* Don't use _filtered; we can't deal with a QUIT out of + target_wait, and I think this might be called from there. */ + if (remote_debug > 0) + fprintf_unfiltered (gdb_stdlog, "Got SYN when wanted trailer\n"); + continue; + } + + /* If this is the wrong sequence number, ignore it. */ + if (HDR_GET_SEQ (hdr) != mips_receive_seq) + { + /* Don't use _filtered; we can't deal with a QUIT out of + target_wait, and I think this might be called from there. */ + if (remote_debug > 0) + fprintf_unfiltered (gdb_stdlog, + "Ignoring sequence number %d (want %d)\n", + HDR_GET_SEQ (hdr), mips_receive_seq); + continue; + } + + if (mips_cksum (hdr, buff, len) == TRLR_GET_CKSUM (trlr)) + break; + + if (remote_debug > 0) + /* Don't use _filtered; we can't deal with a QUIT out of + target_wait, and I think this might be called from there. */ + printf_unfiltered ("Bad checksum; data %d, trailer %d\n", + mips_cksum (hdr, buff, len), + TRLR_GET_CKSUM (trlr)); + + /* The checksum failed. Send an acknowledgement for the + previous packet to tell the remote to resend the packet. */ + ack[HDR_INDX_SYN] = HDR_SET_SYN (0, 0, mips_receive_seq); + ack[HDR_INDX_TYPE_LEN] = HDR_SET_TYPE_LEN (0, 0, mips_receive_seq); + ack[HDR_INDX_LEN1] = HDR_SET_LEN1 (0, 0, mips_receive_seq); + ack[HDR_INDX_SEQ] = HDR_SET_SEQ (0, 0, mips_receive_seq); + + cksum = mips_cksum (ack, (unsigned char *) NULL, 0); + + ack[HDR_LENGTH + TRLR_INDX_CSUM1] = TRLR_SET_CSUM1 (cksum); + ack[HDR_LENGTH + TRLR_INDX_CSUM2] = TRLR_SET_CSUM2 (cksum); + ack[HDR_LENGTH + TRLR_INDX_CSUM3] = TRLR_SET_CSUM3 (cksum); + + if (remote_debug > 0) + { + ack[HDR_LENGTH + TRLR_LENGTH] = '\0'; + /* Don't use _filtered; we can't deal with a QUIT out of + target_wait, and I think this might be called from there. */ + printf_unfiltered ("Writing ack %d \"%s\"\n", mips_receive_seq, + ack + 1); + } + + if (serial_write (mips_desc, ack, HDR_LENGTH + TRLR_LENGTH) != 0) + { + if (throw_error) + mips_error ("write to target failed: %s", safe_strerror (errno)); + else + return -1; + } + } + + if (remote_debug > 0) + { + buff[len] = '\0'; + /* Don't use _filtered; we can't deal with a QUIT out of + target_wait, and I think this might be called from there. */ + printf_unfiltered ("Got packet \"%s\"\n", buff); + } + + /* We got the packet. Send an acknowledgement. */ + mips_receive_seq = (mips_receive_seq + 1) % SEQ_MODULOS; + + ack[HDR_INDX_SYN] = HDR_SET_SYN (0, 0, mips_receive_seq); + ack[HDR_INDX_TYPE_LEN] = HDR_SET_TYPE_LEN (0, 0, mips_receive_seq); + ack[HDR_INDX_LEN1] = HDR_SET_LEN1 (0, 0, mips_receive_seq); + ack[HDR_INDX_SEQ] = HDR_SET_SEQ (0, 0, mips_receive_seq); + + cksum = mips_cksum (ack, (unsigned char *) NULL, 0); + + ack[HDR_LENGTH + TRLR_INDX_CSUM1] = TRLR_SET_CSUM1 (cksum); + ack[HDR_LENGTH + TRLR_INDX_CSUM2] = TRLR_SET_CSUM2 (cksum); + ack[HDR_LENGTH + TRLR_INDX_CSUM3] = TRLR_SET_CSUM3 (cksum); + + if (remote_debug > 0) + { + ack[HDR_LENGTH + TRLR_LENGTH] = '\0'; + /* Don't use _filtered; we can't deal with a QUIT out of + target_wait, and I think this might be called from there. */ + printf_unfiltered ("Writing ack %d \"%s\"\n", mips_receive_seq, + ack + 1); + } + + if (serial_write (mips_desc, ack, HDR_LENGTH + TRLR_LENGTH) != 0) + { + if (throw_error) + mips_error ("write to target failed: %s", safe_strerror (errno)); + else + return -1; + } + + return len; +} + +/* Optionally send a request to the remote system and optionally wait + for the reply. This implements the remote debugging protocol, + which is built on top of the packet protocol defined above. Each + request has an ADDR argument and a DATA argument. The following + requests are defined: + + \0 don't send a request; just wait for a reply + i read word from instruction space at ADDR + d read word from data space at ADDR + I write DATA to instruction space at ADDR + D write DATA to data space at ADDR + r read register number ADDR + R set register number ADDR to value DATA + c continue execution (if ADDR != 1, set pc to ADDR) + s single step (if ADDR != 1, set pc to ADDR) + + The read requests return the value requested. The write requests + return the previous value in the changed location. The execution + requests return a UNIX wait value (the approximate signal which + caused execution to stop is in the upper eight bits). + + If PERR is not NULL, this function waits for a reply. If an error + occurs, it sets *PERR to 1 and sets errno according to what the + target board reports. */ + +static ULONGEST +mips_request (int cmd, + ULONGEST addr, + ULONGEST data, + int *perr, + int timeout, + char *buff) +{ + char myBuff[DATA_MAXLEN + 1]; + int len; + int rpid; + char rcmd; + int rerrflg; + unsigned long rresponse; + + if (buff == (char *) NULL) + buff = myBuff; + + if (cmd != '\0') + { + if (mips_need_reply) + internal_error (__FILE__, __LINE__, + "mips_request: Trying to send command before reply"); + sprintf (buff, "0x0 %c 0x%s 0x%s", cmd, paddr_nz (addr), paddr_nz (data)); + mips_send_packet (buff, 1); + mips_need_reply = 1; + } + + if (perr == (int *) NULL) + return 0; + + if (!mips_need_reply) + internal_error (__FILE__, __LINE__, + "mips_request: Trying to get reply before command"); + + mips_need_reply = 0; + + len = mips_receive_packet (buff, 1, timeout); + buff[len] = '\0'; + + if (sscanf (buff, "0x%x %c 0x%x 0x%lx", + &rpid, &rcmd, &rerrflg, &rresponse) != 4 + || (cmd != '\0' && rcmd != cmd)) + mips_error ("Bad response from remote board"); + + if (rerrflg != 0) + { + *perr = 1; + + /* FIXME: This will returns MIPS errno numbers, which may or may + not be the same as errno values used on other systems. If + they stick to common errno values, they will be the same, but + if they don't, they must be translated. */ + errno = rresponse; + + return 0; + } + + *perr = 0; + return rresponse; +} + +static void +mips_initialize_cleanups (void *arg) +{ + mips_initializing = 0; +} + +static void +mips_exit_cleanups (void *arg) +{ + mips_exiting = 0; +} + +static void +mips_send_command (const char *cmd, int prompt) +{ + serial_write (mips_desc, cmd, strlen (cmd)); + mips_expect (cmd); + mips_expect ("\n"); + if (prompt) + mips_expect (mips_monitor_prompt); +} + +/* Enter remote (dbx) debug mode: */ +static void +mips_enter_debug (void) +{ + /* Reset the sequence numbers, ready for the new debug sequence: */ + mips_send_seq = 0; + mips_receive_seq = 0; + + if (mips_monitor != MON_IDT) + mips_send_command ("debug\r", 0); + else /* assume IDT monitor by default */ + mips_send_command ("db tty0\r", 0); + + sleep (1); + serial_write (mips_desc, "\r", sizeof "\r" - 1); + + /* We don't need to absorb any spurious characters here, since the + mips_receive_header will eat up a reasonable number of characters + whilst looking for the SYN, however this avoids the "garbage" + being displayed to the user. */ + if (mips_monitor != MON_IDT) + mips_expect ("\r"); + + { + char buff[DATA_MAXLEN + 1]; + if (mips_receive_packet (buff, 1, 3) < 0) + mips_error ("Failed to initialize (didn't receive packet)."); + } +} + +/* Exit remote (dbx) debug mode, returning to the monitor prompt: */ +static int +mips_exit_debug (void) +{ + int err; + struct cleanup *old_cleanups = make_cleanup (mips_exit_cleanups, NULL); + + mips_exiting = 1; + + if (mips_monitor != MON_IDT) + { + /* The DDB (NEC) and MiniRISC (LSI) versions of PMON exit immediately, + so we do not get a reply to this command: */ + mips_request ('x', 0, 0, NULL, mips_receive_wait, NULL); + mips_need_reply = 0; + if (!mips_expect (" break!")) + return -1; + } + else + mips_request ('x', 0, 0, &err, mips_receive_wait, NULL); + + if (!mips_expect (mips_monitor_prompt)) + return -1; + + do_cleanups (old_cleanups); + + return 0; +} + +/* Initialize a new connection to the MIPS board, and make sure we are + really connected. */ + +static void +mips_initialize (void) +{ + int err; + struct cleanup *old_cleanups = make_cleanup (mips_initialize_cleanups, NULL); + int j; + + /* What is this code doing here? I don't see any way it can happen, and + it might mean mips_initializing didn't get cleared properly. + So I'll make it a warning. */ + + if (mips_initializing) + { + warning ("internal error: mips_initialize called twice"); + return; + } + + mips_wait_flag = 0; + mips_initializing = 1; + + /* At this point, the packit protocol isn't responding. We'll try getting + into the monitor, and restarting the protocol. */ + + /* Force the system into the monitor. After this we *should* be at + the mips_monitor_prompt. */ + if (mips_monitor != MON_IDT) + j = 0; /* start by checking if we are already at the prompt */ + else + j = 1; /* start by sending a break */ + for (; j <= 4; j++) + { + switch (j) + { + case 0: /* First, try sending a CR */ + serial_flush_input (mips_desc); + serial_write (mips_desc, "\r", 1); + break; + case 1: /* First, try sending a break */ + serial_send_break (mips_desc); + break; + case 2: /* Then, try a ^C */ + serial_write (mips_desc, "\003", 1); + break; + case 3: /* Then, try escaping from download */ + { + if (mips_monitor != MON_IDT) + { + char tbuff[7]; + + /* We shouldn't need to send multiple termination + sequences, since the target performs line (or + block) reads, and then processes those + packets. In-case we were downloading a large packet + we flush the output buffer before inserting a + termination sequence. */ + serial_flush_output (mips_desc); + sprintf (tbuff, "\r/E/E\r"); + serial_write (mips_desc, tbuff, 6); + } + else + { + char srec[10]; + int i; + + /* We are possibly in binary download mode, having + aborted in the middle of an S-record. ^C won't + work because of binary mode. The only reliable way + out is to send enough termination packets (8 bytes) + to fill up and then overflow the largest size + S-record (255 bytes in this case). This amounts to + 256/8 + 1 packets. + */ + + mips_make_srec (srec, '7', 0, NULL, 0); + + for (i = 1; i <= 33; i++) + { + serial_write (mips_desc, srec, 8); + + if (serial_readchar (mips_desc, 0) >= 0) + break; /* Break immediatly if we get something from + the board. */ + } + } + } + break; + case 4: + mips_error ("Failed to initialize."); + } + + if (mips_expect (mips_monitor_prompt)) + break; + } + + if (mips_monitor != MON_IDT) + { + /* Sometimes PMON ignores the first few characters in the first + command sent after a load. Sending a blank command gets + around that. */ + mips_send_command ("\r", -1); + + /* Ensure the correct target state: */ + if (mips_monitor != MON_LSI) + mips_send_command ("set regsize 64\r", -1); + mips_send_command ("set hostport tty0\r", -1); + mips_send_command ("set brkcmd \"\"\r", -1); + /* Delete all the current breakpoints: */ + mips_send_command ("db *\r", -1); + /* NOTE: PMON does not have breakpoint support through the + "debug" mode, only at the monitor command-line. */ + } + + mips_enter_debug (); + + /* Clear all breakpoints: */ + if ((mips_monitor == MON_IDT + && clear_breakpoint (-1, 0, BREAK_UNUSED) == 0) + || mips_monitor == MON_LSI) + monitor_supports_breakpoints = 1; + else + monitor_supports_breakpoints = 0; + + do_cleanups (old_cleanups); + + /* If this doesn't call error, we have connected; we don't care if + the request itself succeeds or fails. */ + + mips_request ('r', 0, 0, &err, mips_receive_wait, NULL); +} + +/* Open a connection to the remote board. */ +static void +common_open (struct target_ops *ops, char *name, int from_tty, + enum mips_monitor_type new_monitor, + const char *new_monitor_prompt) +{ + char *ptype; + char *serial_port_name; + char *remote_name = 0; + char *local_name = 0; + char **argv; + + if (name == 0) + error ( + "To open a MIPS remote debugging connection, you need to specify what serial\n\ +device is attached to the target board (e.g., /dev/ttya).\n" + "If you want to use TFTP to download to the board, specify the name of a\n" + "temporary file to be used by GDB for downloads as the second argument.\n" + "This filename must be in the form host:filename, where host is the name\n" + "of the host running the TFTP server, and the file must be readable by the\n" + "world. If the local name of the temporary file differs from the name as\n" + "seen from the board via TFTP, specify that name as the third parameter.\n"); + + /* Parse the serial port name, the optional TFTP name, and the + optional local TFTP name. */ + if ((argv = buildargv (name)) == NULL) + nomem (0); + make_cleanup_freeargv (argv); + + serial_port_name = xstrdup (argv[0]); + if (argv[1]) /* remote TFTP name specified? */ + { + remote_name = argv[1]; + if (argv[2]) /* local TFTP filename specified? */ + local_name = argv[2]; + } + + target_preopen (from_tty); + + if (mips_is_open) + unpush_target (current_ops); + + /* Open and initialize the serial port. */ + mips_desc = serial_open (serial_port_name); + if (mips_desc == NULL) + perror_with_name (serial_port_name); + + if (baud_rate != -1) + { + if (serial_setbaudrate (mips_desc, baud_rate)) + { + serial_close (mips_desc); + perror_with_name (serial_port_name); + } + } + + serial_raw (mips_desc); + + /* Open and initialize the optional download port. If it is in the form + hostname#portnumber, it's a UDP socket. If it is in the form + hostname:filename, assume it's the TFTP filename that must be + passed to the DDB board to tell it where to get the load file. */ + if (remote_name) + { + if (strchr (remote_name, '#')) + { + udp_desc = serial_open (remote_name); + if (!udp_desc) + perror_with_name ("Unable to open UDP port"); + udp_in_use = 1; + } + else + { + /* Save the remote and local names of the TFTP temp file. If + the user didn't specify a local name, assume it's the same + as the part of the remote name after the "host:". */ + if (tftp_name) + xfree (tftp_name); + if (tftp_localname) + xfree (tftp_localname); + if (local_name == NULL) + if ((local_name = strchr (remote_name, ':')) != NULL) + local_name++; /* skip over the colon */ + if (local_name == NULL) + local_name = remote_name; /* local name same as remote name */ + tftp_name = xstrdup (remote_name); + tftp_localname = xstrdup (local_name); + tftp_in_use = 1; + } + } + + current_ops = ops; + mips_is_open = 1; + + /* Reset the expected monitor prompt if it's never been set before. */ + if (mips_monitor_prompt == NULL) + mips_monitor_prompt = xstrdup (new_monitor_prompt); + mips_monitor = new_monitor; + + mips_initialize (); + + if (from_tty) + printf_unfiltered ("Remote MIPS debugging using %s\n", serial_port_name); + + /* Switch to using remote target now. */ + push_target (ops); + + /* FIXME: Should we call start_remote here? */ + + /* Try to figure out the processor model if possible. */ + deprecated_mips_set_processor_regs_hack (); + + /* This is really the job of start_remote however, that makes an + assumption that the target is about to print out a status message + of some sort. That doesn't happen here (in fact, it may not be + possible to get the monitor to send the appropriate packet). */ + + flush_cached_frames (); + registers_changed (); + stop_pc = read_pc (); + print_stack_frame (get_selected_frame (), -1, 1); + xfree (serial_port_name); +} + +static void +mips_open (char *name, int from_tty) +{ + const char *monitor_prompt = NULL; + if (TARGET_ARCHITECTURE != NULL + && TARGET_ARCHITECTURE->arch == bfd_arch_mips) + { + switch (TARGET_ARCHITECTURE->mach) + { + case bfd_mach_mips4100: + case bfd_mach_mips4300: + case bfd_mach_mips4600: + case bfd_mach_mips4650: + case bfd_mach_mips5000: + monitor_prompt = "<RISQ> "; + break; + } + } + if (monitor_prompt == NULL) + monitor_prompt = "<IDT>"; + common_open (&mips_ops, name, from_tty, MON_IDT, monitor_prompt); +} + +static void +pmon_open (char *name, int from_tty) +{ + common_open (&pmon_ops, name, from_tty, MON_PMON, "PMON> "); +} + +static void +ddb_open (char *name, int from_tty) +{ + common_open (&ddb_ops, name, from_tty, MON_DDB, "NEC010>"); +} + +static void +lsi_open (char *name, int from_tty) +{ + int i; + + /* Clear the LSI breakpoint table. */ + for (i = 0; i < MAX_LSI_BREAKPOINTS; i++) + lsi_breakpoints[i].type = BREAK_UNUSED; + + common_open (&lsi_ops, name, from_tty, MON_LSI, "PMON> "); +} + +/* Close a connection to the remote board. */ + +static void +mips_close (int quitting) +{ + if (mips_is_open) + { + /* Get the board out of remote debugging mode. */ + (void) mips_exit_debug (); + + close_ports (); + } +} + +/* Detach from the remote board. */ + +static void +mips_detach (char *args, int from_tty) +{ + if (args) + error ("Argument given to \"detach\" when remotely debugging."); + + pop_target (); + + mips_close (1); + + if (from_tty) + printf_unfiltered ("Ending remote MIPS debugging.\n"); +} + +/* Tell the target board to resume. This does not wait for a reply + from the board, except in the case of single-stepping on LSI boards, + where PMON does return a reply. */ + +static void +mips_resume (ptid_t ptid, int step, enum target_signal siggnal) +{ + int err; + + /* LSI PMON requires returns a reply packet "0x1 s 0x0 0x57f" after + a single step, so we wait for that. */ + mips_request (step ? 's' : 'c', 1, siggnal, + mips_monitor == MON_LSI && step ? &err : (int *) NULL, + mips_receive_wait, NULL); +} + +/* Return the signal corresponding to SIG, where SIG is the number which + the MIPS protocol uses for the signal. */ +static enum target_signal +mips_signal_from_protocol (int sig) +{ + /* We allow a few more signals than the IDT board actually returns, on + the theory that there is at least *some* hope that perhaps the numbering + for these signals is widely agreed upon. */ + if (sig <= 0 + || sig > 31) + return TARGET_SIGNAL_UNKNOWN; + + /* Don't want to use target_signal_from_host because we are converting + from MIPS signal numbers, not host ones. Our internal numbers + match the MIPS numbers for the signals the board can return, which + are: SIGINT, SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP. */ + return (enum target_signal) sig; +} + +/* Wait until the remote stops, and return a wait status. */ + +static ptid_t +mips_wait (ptid_t ptid, struct target_waitstatus *status) +{ + int rstatus; + int err; + char buff[DATA_MAXLEN]; + int rpc, rfp, rsp; + char flags[20]; + int nfields; + int i; + + interrupt_count = 0; + hit_watchpoint = 0; + + /* If we have not sent a single step or continue command, then the + board is waiting for us to do something. Return a status + indicating that it is stopped. */ + if (!mips_need_reply) + { + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_TRAP; + return inferior_ptid; + } + + /* No timeout; we sit here as long as the program continues to execute. */ + mips_wait_flag = 1; + rstatus = mips_request ('\000', 0, 0, &err, -1, buff); + mips_wait_flag = 0; + if (err) + mips_error ("Remote failure: %s", safe_strerror (errno)); + + /* On returning from a continue, the PMON monitor seems to start + echoing back the messages we send prior to sending back the + ACK. The code can cope with this, but to try and avoid the + unnecessary serial traffic, and "spurious" characters displayed + to the user, we cheat and reset the debug protocol. The problems + seems to be caused by a check on the number of arguments, and the + command length, within the monitor causing it to echo the command + as a bad packet. */ + if (mips_monitor == MON_PMON) + { + mips_exit_debug (); + mips_enter_debug (); + } + + /* See if we got back extended status. If so, pick out the pc, fp, sp, etc... */ + + nfields = sscanf (buff, "0x%*x %*c 0x%*x 0x%*x 0x%x 0x%x 0x%x 0x%*x %s", + &rpc, &rfp, &rsp, flags); + if (nfields >= 3) + { + char buf[MAX_REGISTER_SIZE]; + + store_unsigned_integer (buf, DEPRECATED_REGISTER_RAW_SIZE (PC_REGNUM), rpc); + supply_register (PC_REGNUM, buf); + + store_unsigned_integer (buf, DEPRECATED_REGISTER_RAW_SIZE (PC_REGNUM), rfp); + supply_register (30, buf); /* This register they are avoiding and so it is unnamed */ + + store_unsigned_integer (buf, DEPRECATED_REGISTER_RAW_SIZE (SP_REGNUM), rsp); + supply_register (SP_REGNUM, buf); + + store_unsigned_integer (buf, DEPRECATED_REGISTER_RAW_SIZE (DEPRECATED_FP_REGNUM), 0); + supply_register (DEPRECATED_FP_REGNUM, buf); + + if (nfields == 9) + { + int i; + + for (i = 0; i <= 2; i++) + if (flags[i] == 'r' || flags[i] == 'w') + hit_watchpoint = 1; + else if (flags[i] == '\000') + break; + } + } + + if (strcmp (target_shortname, "lsi") == 0) + { +#if 0 + /* If this is an LSI PMON target, see if we just hit a hardrdware watchpoint. + Right now, PMON doesn't give us enough information to determine which + breakpoint we hit. So we have to look up the PC in our own table + of breakpoints, and if found, assume it's just a normal instruction + fetch breakpoint, not a data watchpoint. FIXME when PMON + provides some way to tell us what type of breakpoint it is. */ + int i; + CORE_ADDR pc = read_pc (); + + hit_watchpoint = 1; + for (i = 0; i < MAX_LSI_BREAKPOINTS; i++) + { + if (lsi_breakpoints[i].addr == pc + && lsi_breakpoints[i].type == BREAK_FETCH) + { + hit_watchpoint = 0; + break; + } + } +#else + /* If a data breakpoint was hit, PMON returns the following packet: + 0x1 c 0x0 0x57f 0x1 + The return packet from an ordinary breakpoint doesn't have the + extra 0x01 field tacked onto the end. */ + if (nfields == 1 && rpc == 1) + hit_watchpoint = 1; +#endif + } + + /* NOTE: The following (sig) numbers are defined by PMON: + SPP_SIGTRAP 5 breakpoint + SPP_SIGINT 2 + SPP_SIGSEGV 11 + SPP_SIGBUS 10 + SPP_SIGILL 4 + SPP_SIGFPE 8 + SPP_SIGTERM 15 */ + + /* Translate a MIPS waitstatus. We use constants here rather than WTERMSIG + and so on, because the constants we want here are determined by the + MIPS protocol and have nothing to do with what host we are running on. */ + if ((rstatus & 0xff) == 0) + { + status->kind = TARGET_WAITKIND_EXITED; + status->value.integer = (((rstatus) >> 8) & 0xff); + } + else if ((rstatus & 0xff) == 0x7f) + { + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = mips_signal_from_protocol (((rstatus) >> 8) & 0xff); + + /* If the stop PC is in the _exit function, assume + we hit the 'break 0x3ff' instruction in _exit, so this + is not a normal breakpoint. */ + if (strcmp (target_shortname, "lsi") == 0) + { + char *func_name; + CORE_ADDR func_start; + CORE_ADDR pc = read_pc (); + + find_pc_partial_function (pc, &func_name, &func_start, NULL); + if (func_name != NULL && strcmp (func_name, "_exit") == 0 + && func_start == pc) + status->kind = TARGET_WAITKIND_EXITED; + } + } + else + { + status->kind = TARGET_WAITKIND_SIGNALLED; + status->value.sig = mips_signal_from_protocol (rstatus & 0x7f); + } + + return inferior_ptid; +} + +/* We have to map between the register numbers used by gdb and the + register numbers used by the debugging protocol. This function + assumes that we are using tm-mips.h. */ + +#define REGNO_OFFSET 96 + +static int +mips_map_regno (int regno) +{ + if (regno < 32) + return regno; + if (regno >= mips_regnum (current_gdbarch)->fp0 + && regno < mips_regnum (current_gdbarch)->fp0 + 32) + return regno - mips_regnum (current_gdbarch)->fp0 + 32; + else if (regno == mips_regnum (current_gdbarch)->pc) + return REGNO_OFFSET + 0; + else if (regno == mips_regnum (current_gdbarch)->cause) + return REGNO_OFFSET + 1; + else if (regno == mips_regnum (current_gdbarch)->hi) + return REGNO_OFFSET + 2; + else if (regno == mips_regnum (current_gdbarch)->lo) + return REGNO_OFFSET + 3; + else if (regno == mips_regnum (current_gdbarch)->fp_control_status) + return REGNO_OFFSET + 4; + else if (regno == mips_regnum (current_gdbarch)->fp_implementation_revision) + return REGNO_OFFSET + 5; + else + /* FIXME: Is there a way to get the status register? */ + return 0; +} + +/* Fetch the remote registers. */ + +static void +mips_fetch_registers (int regno) +{ + unsigned LONGEST val; + int err; + + if (regno == -1) + { + for (regno = 0; regno < NUM_REGS; regno++) + mips_fetch_registers (regno); + return; + } + + if (regno == DEPRECATED_FP_REGNUM || regno == ZERO_REGNUM) + /* DEPRECATED_FP_REGNUM on the mips is a hack which is just + supposed to read zero (see also mips-nat.c). */ + val = 0; + else + { + /* If PMON doesn't support this register, don't waste serial + bandwidth trying to read it. */ + int pmon_reg = mips_map_regno (regno); + if (regno != 0 && pmon_reg == 0) + val = 0; + else + { + /* Unfortunately the PMON version in the Vr4300 board has been + compiled without the 64bit register access commands. This + means we cannot get hold of the full register width. */ + if (mips_monitor == MON_DDB) + val = (unsigned) mips_request ('t', pmon_reg, 0, + &err, mips_receive_wait, NULL); + else + val = mips_request ('r', pmon_reg, 0, + &err, mips_receive_wait, NULL); + if (err) + mips_error ("Can't read register %d: %s", regno, + safe_strerror (errno)); + } + } + + { + char buf[MAX_REGISTER_SIZE]; + + /* We got the number the register holds, but gdb expects to see a + value in the target byte ordering. */ + store_unsigned_integer (buf, DEPRECATED_REGISTER_RAW_SIZE (regno), val); + supply_register (regno, buf); + } +} + +/* Prepare to store registers. The MIPS protocol can store individual + registers, so this function doesn't have to do anything. */ + +static void +mips_prepare_to_store (void) +{ +} + +/* Store remote register(s). */ + +static void +mips_store_registers (int regno) +{ + int err; + + if (regno == -1) + { + for (regno = 0; regno < NUM_REGS; regno++) + mips_store_registers (regno); + return; + } + + mips_request ('R', mips_map_regno (regno), + read_register (regno), + &err, mips_receive_wait, NULL); + if (err) + mips_error ("Can't write register %d: %s", regno, safe_strerror (errno)); +} + +/* Fetch a word from the target board. */ + +static unsigned int +mips_fetch_word (CORE_ADDR addr) +{ + unsigned int val; + int err; + + val = mips_request ('d', addr, 0, &err, mips_receive_wait, NULL); + if (err) + { + /* Data space failed; try instruction space. */ + val = mips_request ('i', addr, 0, &err, + mips_receive_wait, NULL); + if (err) + mips_error ("Can't read address 0x%s: %s", + paddr_nz (addr), safe_strerror (errno)); + } + return val; +} + +/* Store a word to the target board. Returns errno code or zero for + success. If OLD_CONTENTS is non-NULL, put the old contents of that + memory location there. */ + +/* FIXME! make sure only 32-bit quantities get stored! */ +static int +mips_store_word (CORE_ADDR addr, unsigned int val, char *old_contents) +{ + int err; + unsigned int oldcontents; + + oldcontents = mips_request ('D', addr, val, &err, + mips_receive_wait, NULL); + if (err) + { + /* Data space failed; try instruction space. */ + oldcontents = mips_request ('I', addr, val, &err, + mips_receive_wait, NULL); + if (err) + return errno; + } + if (old_contents != NULL) + store_unsigned_integer (old_contents, 4, oldcontents); + return 0; +} + +/* Read or write LEN bytes from inferior memory at MEMADDR, + transferring to or from debugger address MYADDR. Write to inferior + if SHOULD_WRITE is nonzero. Returns length of data written or + read; 0 for error. Note that protocol gives us the correct value + for a longword, since it transfers values in ASCII. We want the + byte values, so we have to swap the longword values. */ + +static int mask_address_p = 1; + +static int +mips_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write, + struct mem_attrib *attrib, struct target_ops *target) +{ + int i; + CORE_ADDR addr; + int count; + char *buffer; + int status; + + /* PMON targets do not cope well with 64 bit addresses. Mask the + value down to 32 bits. */ + if (mask_address_p) + memaddr &= (CORE_ADDR) 0xffffffff; + + /* Round starting address down to longword boundary. */ + addr = memaddr & ~3; + /* Round ending address up; get number of longwords that makes. */ + count = (((memaddr + len) - addr) + 3) / 4; + /* Allocate buffer of that many longwords. */ + buffer = alloca (count * 4); + + if (write) + { + /* Fill start and end extra bytes of buffer with existing data. */ + if (addr != memaddr || len < 4) + { + /* Need part of initial word -- fetch it. */ + store_unsigned_integer (&buffer[0], 4, mips_fetch_word (addr)); + } + + if (count > 1) + { + /* Need part of last word -- fetch it. FIXME: we do this even + if we don't need it. */ + store_unsigned_integer (&buffer[(count - 1) * 4], 4, + mips_fetch_word (addr + (count - 1) * 4)); + } + + /* Copy data to be written over corresponding part of buffer */ + + memcpy ((char *) buffer + (memaddr & 3), myaddr, len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += 4) + { + status = mips_store_word (addr, + extract_unsigned_integer (&buffer[i * 4], 4), + NULL); + /* Report each kilobyte (we download 32-bit words at a time) */ + if (i % 256 == 255) + { + printf_unfiltered ("*"); + gdb_flush (gdb_stdout); + } + if (status) + { + errno = status; + return 0; + } + /* FIXME: Do we want a QUIT here? */ + } + if (count >= 256) + printf_unfiltered ("\n"); + } + else + { + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += 4) + { + store_unsigned_integer (&buffer[i * 4], 4, mips_fetch_word (addr)); + QUIT; + } + + /* Copy appropriate bytes out of the buffer. */ + memcpy (myaddr, buffer + (memaddr & 3), len); + } + return len; +} + +/* Print info on this target. */ + +static void +mips_files_info (struct target_ops *ignore) +{ + printf_unfiltered ("Debugging a MIPS board over a serial line.\n"); +} + +/* Kill the process running on the board. This will actually only + work if we are doing remote debugging over the console input. I + think that if IDT/sim had the remote debug interrupt enabled on the + right port, we could interrupt the process with a break signal. */ + +static void +mips_kill (void) +{ + if (!mips_wait_flag) + return; + + interrupt_count++; + + if (interrupt_count >= 2) + { + interrupt_count = 0; + + target_terminal_ours (); + + if (query ("Interrupted while waiting for the program.\n\ +Give up (and stop debugging it)? ")) + { + /* Clean up in such a way that mips_close won't try to talk to the + board (it almost surely won't work since we weren't able to talk to + it). */ + mips_wait_flag = 0; + close_ports (); + + printf_unfiltered ("Ending remote MIPS debugging.\n"); + target_mourn_inferior (); + + throw_exception (RETURN_QUIT); + } + + target_terminal_inferior (); + } + + if (remote_debug > 0) + printf_unfiltered ("Sending break\n"); + + serial_send_break (mips_desc); + +#if 0 + if (mips_is_open) + { + char cc; + + /* Send a ^C. */ + cc = '\003'; + serial_write (mips_desc, &cc, 1); + sleep (1); + target_mourn_inferior (); + } +#endif +} + +/* Start running on the target board. */ + +static void +mips_create_inferior (char *execfile, char *args, char **env) +{ + CORE_ADDR entry_pt; + + if (args && *args) + { + warning ("\ +Can't pass arguments to remote MIPS board; arguments ignored."); + /* And don't try to use them on the next "run" command. */ + execute_command ("set args", 0); + } + + if (execfile == 0 || exec_bfd == 0) + error ("No executable file specified"); + + entry_pt = (CORE_ADDR) bfd_get_start_address (exec_bfd); + + init_wait_for_inferior (); + + /* FIXME: Should we set inferior_ptid here? */ + + proceed (entry_pt, TARGET_SIGNAL_DEFAULT, 0); +} + +/* Clean up after a process. Actually nothing to do. */ + +static void +mips_mourn_inferior (void) +{ + if (current_ops != NULL) + unpush_target (current_ops); + generic_mourn_inferior (); +} + +/* We can write a breakpoint and read the shadow contents in one + operation. */ + +/* Insert a breakpoint. On targets that don't have built-in + breakpoint support, we read the contents of the target location and + stash it, then overwrite it with a breakpoint instruction. ADDR is + the target location in the target machine. CONTENTS_CACHE is a + pointer to memory allocated for saving the target contents. It is + guaranteed by the caller to be long enough to save the breakpoint + length returned by BREAKPOINT_FROM_PC. */ + +static int +mips_insert_breakpoint (CORE_ADDR addr, char *contents_cache) +{ + if (monitor_supports_breakpoints) + return set_breakpoint (addr, MIPS_INSTLEN, BREAK_FETCH); + else + return memory_insert_breakpoint (addr, contents_cache); +} + +static int +mips_remove_breakpoint (CORE_ADDR addr, char *contents_cache) +{ + if (monitor_supports_breakpoints) + return clear_breakpoint (addr, MIPS_INSTLEN, BREAK_FETCH); + else + return memory_remove_breakpoint (addr, contents_cache); +} + +/* Tell whether this target can support a hardware breakpoint. CNT + is the number of hardware breakpoints already installed. This + implements the TARGET_CAN_USE_HARDWARE_WATCHPOINT macro. */ + +int +mips_can_use_watchpoint (int type, int cnt, int othertype) +{ + return cnt < MAX_LSI_BREAKPOINTS && strcmp (target_shortname, "lsi") == 0; +} + + +/* Compute a don't care mask for the region bounding ADDR and ADDR + LEN - 1. + This is used for memory ref breakpoints. */ + +static unsigned long +calculate_mask (CORE_ADDR addr, int len) +{ + unsigned long mask; + int i; + + mask = addr ^ (addr + len - 1); + + for (i = 32; i >= 0; i--) + if (mask == 0) + break; + else + mask >>= 1; + + mask = (unsigned long) 0xffffffff >> i; + + return mask; +} + + +/* Set a data watchpoint. ADDR and LEN should be obvious. TYPE is 0 + for a write watchpoint, 1 for a read watchpoint, or 2 for a read/write + watchpoint. */ + +int +mips_insert_watchpoint (CORE_ADDR addr, int len, int type) +{ + if (set_breakpoint (addr, len, type)) + return -1; + + return 0; +} + +int +mips_remove_watchpoint (CORE_ADDR addr, int len, int type) +{ + if (clear_breakpoint (addr, len, type)) + return -1; + + return 0; +} + +int +mips_stopped_by_watchpoint (void) +{ + return hit_watchpoint; +} + + +/* Insert a breakpoint. */ + +static int +set_breakpoint (CORE_ADDR addr, int len, enum break_type type) +{ + return common_breakpoint (1, addr, len, type); +} + + +/* Clear a breakpoint. */ + +static int +clear_breakpoint (CORE_ADDR addr, int len, enum break_type type) +{ + return common_breakpoint (0, addr, len, type); +} + + +/* Check the error code from the return packet for an LSI breakpoint + command. If there's no error, just return 0. If it's a warning, + print the warning text and return 0. If it's an error, print + the error text and return 1. <ADDR> is the address of the breakpoint + that was being set. <RERRFLG> is the error code returned by PMON. + This is a helper function for common_breakpoint. */ + +static int +check_lsi_error (CORE_ADDR addr, int rerrflg) +{ + struct lsi_error *err; + char *saddr = paddr_nz (addr); /* printable address string */ + + if (rerrflg == 0) /* no error */ + return 0; + + /* Warnings can be ORed together, so check them all. */ + if (rerrflg & W_WARN) + { + if (monitor_warnings) + { + int found = 0; + for (err = lsi_warning_table; err->code != 0; err++) + { + if ((err->code & rerrflg) == err->code) + { + found = 1; + fprintf_unfiltered (gdb_stderr, + "common_breakpoint (0x%s): Warning: %s\n", + saddr, + err->string); + } + } + if (!found) + fprintf_unfiltered (gdb_stderr, + "common_breakpoint (0x%s): Unknown warning: 0x%x\n", + saddr, + rerrflg); + } + return 0; + } + + /* Errors are unique, i.e. can't be ORed together. */ + for (err = lsi_error_table; err->code != 0; err++) + { + if ((err->code & rerrflg) == err->code) + { + fprintf_unfiltered (gdb_stderr, + "common_breakpoint (0x%s): Error: %s\n", + saddr, + err->string); + return 1; + } + } + fprintf_unfiltered (gdb_stderr, + "common_breakpoint (0x%s): Unknown error: 0x%x\n", + saddr, + rerrflg); + return 1; +} + + +/* This routine sends a breakpoint command to the remote target. + + <SET> is 1 if setting a breakpoint, or 0 if clearing a breakpoint. + <ADDR> is the address of the breakpoint. + <LEN> the length of the region to break on. + <TYPE> is the type of breakpoint: + 0 = write (BREAK_WRITE) + 1 = read (BREAK_READ) + 2 = read/write (BREAK_ACCESS) + 3 = instruction fetch (BREAK_FETCH) + + Return 0 if successful; otherwise 1. */ + +static int +common_breakpoint (int set, CORE_ADDR addr, int len, enum break_type type) +{ + char buf[DATA_MAXLEN + 1]; + char cmd, rcmd; + int rpid, rerrflg, rresponse, rlen; + int nfields; + + addr = ADDR_BITS_REMOVE (addr); + + if (mips_monitor == MON_LSI) + { + if (set == 0) /* clear breakpoint */ + { + /* The LSI PMON "clear breakpoint" has this form: + <pid> 'b' <bptn> 0x0 + reply: + <pid> 'b' 0x0 <code> + + <bptn> is a breakpoint number returned by an earlier 'B' command. + Possible return codes: OK, E_BPT. */ + + int i; + + /* Search for the breakpoint in the table. */ + for (i = 0; i < MAX_LSI_BREAKPOINTS; i++) + if (lsi_breakpoints[i].type == type + && lsi_breakpoints[i].addr == addr + && lsi_breakpoints[i].len == len) + break; + + /* Clear the table entry and tell PMON to clear the breakpoint. */ + if (i == MAX_LSI_BREAKPOINTS) + { + warning ("common_breakpoint: Attempt to clear bogus breakpoint at %s\n", + paddr_nz (addr)); + return 1; + } + + lsi_breakpoints[i].type = BREAK_UNUSED; + sprintf (buf, "0x0 b 0x%x 0x0", i); + mips_send_packet (buf, 1); + + rlen = mips_receive_packet (buf, 1, mips_receive_wait); + buf[rlen] = '\0'; + + nfields = sscanf (buf, "0x%x b 0x0 0x%x", &rpid, &rerrflg); + if (nfields != 2) + mips_error ("common_breakpoint: Bad response from remote board: %s", buf); + + return (check_lsi_error (addr, rerrflg)); + } + else + /* set a breakpoint */ + { + /* The LSI PMON "set breakpoint" command has this form: + <pid> 'B' <addr> 0x0 + reply: + <pid> 'B' <bptn> <code> + + The "set data breakpoint" command has this form: + + <pid> 'A' <addr1> <type> [<addr2> [<value>]] + + where: type= "0x1" = read + "0x2" = write + "0x3" = access (read or write) + + The reply returns two values: + bptn - a breakpoint number, which is a small integer with + possible values of zero through 255. + code - an error return code, a value of zero indicates a + succesful completion, other values indicate various + errors and warnings. + + Possible return codes: OK, W_QAL, E_QAL, E_OUT, E_NON. + + */ + + if (type == BREAK_FETCH) /* instruction breakpoint */ + { + cmd = 'B'; + sprintf (buf, "0x0 B 0x%s 0x0", paddr_nz (addr)); + } + else + /* watchpoint */ + { + cmd = 'A'; + sprintf (buf, "0x0 A 0x%s 0x%x 0x%s", paddr_nz (addr), + type == BREAK_READ ? 1 : (type == BREAK_WRITE ? 2 : 3), + paddr_nz (addr + len - 1)); + } + mips_send_packet (buf, 1); + + rlen = mips_receive_packet (buf, 1, mips_receive_wait); + buf[rlen] = '\0'; + + nfields = sscanf (buf, "0x%x %c 0x%x 0x%x", + &rpid, &rcmd, &rresponse, &rerrflg); + if (nfields != 4 || rcmd != cmd || rresponse > 255) + mips_error ("common_breakpoint: Bad response from remote board: %s", buf); + + if (rerrflg != 0) + if (check_lsi_error (addr, rerrflg)) + return 1; + + /* rresponse contains PMON's breakpoint number. Record the + information for this breakpoint so we can clear it later. */ + lsi_breakpoints[rresponse].type = type; + lsi_breakpoints[rresponse].addr = addr; + lsi_breakpoints[rresponse].len = len; + + return 0; + } + } + else + { + /* On non-LSI targets, the breakpoint command has this form: + 0x0 <CMD> <ADDR> <MASK> <FLAGS> + <MASK> is a don't care mask for addresses. + <FLAGS> is any combination of `r', `w', or `f' for read/write/fetch. + */ + unsigned long mask; + + mask = calculate_mask (addr, len); + addr &= ~mask; + + if (set) /* set a breakpoint */ + { + char *flags; + switch (type) + { + case BREAK_WRITE: /* write */ + flags = "w"; + break; + case BREAK_READ: /* read */ + flags = "r"; + break; + case BREAK_ACCESS: /* read/write */ + flags = "rw"; + break; + case BREAK_FETCH: /* fetch */ + flags = "f"; + break; + default: + internal_error (__FILE__, __LINE__, "failed internal consistency check"); + } + + cmd = 'B'; + sprintf (buf, "0x0 B 0x%s 0x%s %s", paddr_nz (addr), + paddr_nz (mask), flags); + } + else + { + cmd = 'b'; + sprintf (buf, "0x0 b 0x%s", paddr_nz (addr)); + } + + mips_send_packet (buf, 1); + + rlen = mips_receive_packet (buf, 1, mips_receive_wait); + buf[rlen] = '\0'; + + nfields = sscanf (buf, "0x%x %c 0x%x 0x%x", + &rpid, &rcmd, &rerrflg, &rresponse); + + if (nfields != 4 || rcmd != cmd) + mips_error ("common_breakpoint: Bad response from remote board: %s", + buf); + + if (rerrflg != 0) + { + /* Ddb returns "0x0 b 0x16 0x0\000", whereas + Cogent returns "0x0 b 0xffffffff 0x16\000": */ + if (mips_monitor == MON_DDB) + rresponse = rerrflg; + if (rresponse != 22) /* invalid argument */ + fprintf_unfiltered (gdb_stderr, + "common_breakpoint (0x%s): Got error: 0x%x\n", + paddr_nz (addr), rresponse); + return 1; + } + } + return 0; +} + +static void +send_srec (char *srec, int len, CORE_ADDR addr) +{ + while (1) + { + int ch; + + serial_write (mips_desc, srec, len); + + ch = mips_readchar (remote_timeout); + + switch (ch) + { + case SERIAL_TIMEOUT: + error ("Timeout during download."); + break; + case 0x6: /* ACK */ + return; + case 0x15: /* NACK */ + fprintf_unfiltered (gdb_stderr, "Download got a NACK at byte %s! Retrying.\n", paddr_u (addr)); + continue; + default: + error ("Download got unexpected ack char: 0x%x, retrying.\n", ch); + } + } +} + +/* Download a binary file by converting it to S records. */ + +static void +mips_load_srec (char *args) +{ + bfd *abfd; + asection *s; + char *buffer, srec[1024]; + unsigned int i; + unsigned int srec_frame = 200; + int reclen; + static int hashmark = 1; + + buffer = alloca (srec_frame * 2 + 256); + + abfd = bfd_openr (args, 0); + if (!abfd) + { + printf_filtered ("Unable to open file %s\n", args); + return; + } + + if (bfd_check_format (abfd, bfd_object) == 0) + { + printf_filtered ("File is not an object file\n"); + return; + } + +/* This actually causes a download in the IDT binary format: */ + mips_send_command (LOAD_CMD, 0); + + for (s = abfd->sections; s; s = s->next) + { + if (s->flags & SEC_LOAD) + { + unsigned int numbytes; + + /* FIXME! vma too small????? */ + printf_filtered ("%s\t: 0x%4lx .. 0x%4lx ", s->name, + (long) s->vma, + (long) (s->vma + s->_raw_size)); + gdb_flush (gdb_stdout); + + for (i = 0; i < s->_raw_size; i += numbytes) + { + numbytes = min (srec_frame, s->_raw_size - i); + + bfd_get_section_contents (abfd, s, buffer, i, numbytes); + + reclen = mips_make_srec (srec, '3', s->vma + i, buffer, numbytes); + send_srec (srec, reclen, s->vma + i); + + if (ui_load_progress_hook) + ui_load_progress_hook (s->name, i); + + if (hashmark) + { + putchar_unfiltered ('#'); + gdb_flush (gdb_stdout); + } + + } /* Per-packet (or S-record) loop */ + + putchar_unfiltered ('\n'); + } /* Loadable sections */ + } + if (hashmark) + putchar_unfiltered ('\n'); + + /* Write a type 7 terminator record. no data for a type 7, and there + is no data, so len is 0. */ + + reclen = mips_make_srec (srec, '7', abfd->start_address, NULL, 0); + + send_srec (srec, reclen, abfd->start_address); + + serial_flush_input (mips_desc); +} + +/* + * mips_make_srec -- make an srecord. This writes each line, one at a + * time, each with it's own header and trailer line. + * An srecord looks like this: + * + * byte count-+ address + * start ---+ | | data +- checksum + * | | | | + * S01000006F6B692D746573742E73726563E4 + * S315000448600000000000000000FC00005900000000E9 + * S31A0004000023C1400037DE00F023604000377B009020825000348D + * S30B0004485A0000000000004E + * S70500040000F6 + * + * S<type><length><address><data><checksum> + * + * Where + * - length + * is the number of bytes following upto the checksum. Note that + * this is not the number of chars following, since it takes two + * chars to represent a byte. + * - type + * is one of: + * 0) header record + * 1) two byte address data record + * 2) three byte address data record + * 3) four byte address data record + * 7) four byte address termination record + * 8) three byte address termination record + * 9) two byte address termination record + * + * - address + * is the start address of the data following, or in the case of + * a termination record, the start address of the image + * - data + * is the data. + * - checksum + * is the sum of all the raw byte data in the record, from the length + * upwards, modulo 256 and subtracted from 255. + * + * This routine returns the length of the S-record. + * + */ + +static int +mips_make_srec (char *buf, int type, CORE_ADDR memaddr, unsigned char *myaddr, + int len) +{ + unsigned char checksum; + int i; + + /* Create the header for the srec. addr_size is the number of bytes in the address, + and 1 is the number of bytes in the count. */ + + /* FIXME!! bigger buf required for 64-bit! */ + buf[0] = 'S'; + buf[1] = type; + buf[2] = len + 4 + 1; /* len + 4 byte address + 1 byte checksum */ + /* This assumes S3 style downloads (4byte addresses). There should + probably be a check, or the code changed to make it more + explicit. */ + buf[3] = memaddr >> 24; + buf[4] = memaddr >> 16; + buf[5] = memaddr >> 8; + buf[6] = memaddr; + memcpy (&buf[7], myaddr, len); + + /* Note that the checksum is calculated on the raw data, not the + hexified data. It includes the length, address and the data + portions of the packet. */ + checksum = 0; + buf += 2; /* Point at length byte */ + for (i = 0; i < len + 4 + 1; i++) + checksum += *buf++; + + *buf = ~checksum; + + return len + 8; +} + +/* The following manifest controls whether we enable the simple flow + control support provided by the monitor. If enabled the code will + wait for an affirmative ACK between transmitting packets. */ +#define DOETXACK (1) + +/* The PMON fast-download uses an encoded packet format constructed of + 3byte data packets (encoded as 4 printable ASCII characters), and + escape sequences (preceded by a '/'): + + 'K' clear checksum + 'C' compare checksum (12bit value, not included in checksum calculation) + 'S' define symbol name (for addr) terminated with "," and padded to 4char boundary + 'Z' zero fill multiple of 3bytes + 'B' byte (12bit encoded value, of 8bit data) + 'A' address (36bit encoded value) + 'E' define entry as original address, and exit load + + The packets are processed in 4 character chunks, so the escape + sequences that do not have any data (or variable length data) + should be padded to a 4 character boundary. The decoder will give + an error if the complete message block size is not a multiple of + 4bytes (size of record). + + The encoding of numbers is done in 6bit fields. The 6bit value is + used to index into this string to get the specific character + encoding for the value: */ +static char encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,."; + +/* Convert the number of bits required into an encoded number, 6bits + at a time (range 0..63). Keep a checksum if required (passed + pointer non-NULL). The function returns the number of encoded + characters written into the buffer. */ +static int +pmon_makeb64 (unsigned long v, char *p, int n, int *chksum) +{ + int count = (n / 6); + + if ((n % 12) != 0) + { + fprintf_unfiltered (gdb_stderr, + "Fast encoding bitcount must be a multiple of 12bits: %dbit%s\n", n, (n == 1) ? "" : "s"); + return (0); + } + if (n > 36) + { + fprintf_unfiltered (gdb_stderr, + "Fast encoding cannot process more than 36bits at the moment: %dbits\n", n); + return (0); + } + + /* Deal with the checksum: */ + if (chksum != NULL) + { + switch (n) + { + case 36: + *chksum += ((v >> 24) & 0xFFF); + case 24: + *chksum += ((v >> 12) & 0xFFF); + case 12: + *chksum += ((v >> 0) & 0xFFF); + } + } + + do + { + n -= 6; + *p++ = encoding[(v >> n) & 0x3F]; + } + while (n > 0); + + return (count); +} + +/* Shorthand function (that could be in-lined) to output the zero-fill + escape sequence into the data stream. */ +static int +pmon_zeroset (int recsize, char **buff, int *amount, unsigned int *chksum) +{ + int count; + + sprintf (*buff, "/Z"); + count = pmon_makeb64 (*amount, (*buff + 2), 12, chksum); + *buff += (count + 2); + *amount = 0; + return (recsize + count + 2); +} + +static int +pmon_checkset (int recsize, char **buff, int *value) +{ + int count; + + /* Add the checksum (without updating the value): */ + sprintf (*buff, "/C"); + count = pmon_makeb64 (*value, (*buff + 2), 12, NULL); + *buff += (count + 2); + sprintf (*buff, "\n"); + *buff += 2; /* include zero terminator */ + /* Forcing a checksum validation clears the sum: */ + *value = 0; + return (recsize + count + 3); +} + +/* Amount of padding we leave after at the end of the output buffer, + for the checksum and line termination characters: */ +#define CHECKSIZE (4 + 4 + 4 + 2) +/* zero-fill, checksum, transfer end and line termination space. */ + +/* The amount of binary data loaded from the object file in a single + operation: */ +#define BINCHUNK (1024) + +/* Maximum line of data accepted by the monitor: */ +#define MAXRECSIZE (550) +/* NOTE: This constant depends on the monitor being used. This value + is for PMON 5.x on the Cogent Vr4300 board. */ + +static void +pmon_make_fastrec (char **outbuf, unsigned char *inbuf, int *inptr, + int inamount, int *recsize, unsigned int *csum, + unsigned int *zerofill) +{ + int count = 0; + char *p = *outbuf; + + /* This is a simple check to ensure that our data will fit within + the maximum allowable record size. Each record output is 4bytes + in length. We must allow space for a pending zero fill command, + the record, and a checksum record. */ + while ((*recsize < (MAXRECSIZE - CHECKSIZE)) && ((inamount - *inptr) > 0)) + { + /* Process the binary data: */ + if ((inamount - *inptr) < 3) + { + if (*zerofill != 0) + *recsize = pmon_zeroset (*recsize, &p, zerofill, csum); + sprintf (p, "/B"); + count = pmon_makeb64 (inbuf[*inptr], &p[2], 12, csum); + p += (2 + count); + *recsize += (2 + count); + (*inptr)++; + } + else + { + unsigned int value = ((inbuf[*inptr + 0] << 16) | (inbuf[*inptr + 1] << 8) | inbuf[*inptr + 2]); + /* Simple check for zero data. TODO: A better check would be + to check the last, and then the middle byte for being zero + (if the first byte is not). We could then check for + following runs of zeros, and if above a certain size it is + worth the 4 or 8 character hit of the byte insertions used + to pad to the start of the zeroes. NOTE: This also depends + on the alignment at the end of the zero run. */ + if (value == 0x00000000) + { + (*zerofill)++; + if (*zerofill == 0xFFF) /* 12bit counter */ + *recsize = pmon_zeroset (*recsize, &p, zerofill, csum); + } + else + { + if (*zerofill != 0) + *recsize = pmon_zeroset (*recsize, &p, zerofill, csum); + count = pmon_makeb64 (value, p, 24, csum); + p += count; + *recsize += count; + } + *inptr += 3; + } + } + + *outbuf = p; + return; +} + +static int +pmon_check_ack (char *mesg) +{ +#if defined(DOETXACK) + int c; + + if (!tftp_in_use) + { + c = serial_readchar (udp_in_use ? udp_desc : mips_desc, + remote_timeout); + if ((c == SERIAL_TIMEOUT) || (c != 0x06)) + { + fprintf_unfiltered (gdb_stderr, + "Failed to receive valid ACK for %s\n", mesg); + return (-1); /* terminate the download */ + } + } +#endif /* DOETXACK */ + return (0); +} + +/* pmon_download - Send a sequence of characters to the PMON download port, + which is either a serial port or a UDP socket. */ + +static void +pmon_start_download (void) +{ + if (tftp_in_use) + { + /* Create the temporary download file. */ + if ((tftp_file = fopen (tftp_localname, "w")) == NULL) + perror_with_name (tftp_localname); + } + else + { + mips_send_command (udp_in_use ? LOAD_CMD_UDP : LOAD_CMD, 0); + mips_expect ("Downloading from "); + mips_expect (udp_in_use ? "udp" : "tty0"); + mips_expect (", ^C to abort\r\n"); + } +} + +static int +mips_expect_download (char *string) +{ + if (!mips_expect (string)) + { + fprintf_unfiltered (gdb_stderr, "Load did not complete successfully.\n"); + if (tftp_in_use) + remove (tftp_localname); /* Remove temporary file */ + return 0; + } + else + return 1; +} + +static void +pmon_check_entry_address (char *entry_address, int final) +{ + char hexnumber[9]; /* includes '\0' space */ + mips_expect_timeout (entry_address, tftp_in_use ? 15 : remote_timeout); + sprintf (hexnumber, "%x", final); + mips_expect (hexnumber); + mips_expect ("\r\n"); +} + +static int +pmon_check_total (int bintotal) +{ + char hexnumber[9]; /* includes '\0' space */ + mips_expect ("\r\ntotal = 0x"); + sprintf (hexnumber, "%x", bintotal); + mips_expect (hexnumber); + return mips_expect_download (" bytes\r\n"); +} + +static void +pmon_end_download (int final, int bintotal) +{ + char hexnumber[9]; /* includes '\0' space */ + + if (tftp_in_use) + { + static char *load_cmd_prefix = "load -b -s "; + char *cmd; + struct stat stbuf; + + /* Close off the temporary file containing the load data. */ + fclose (tftp_file); + tftp_file = NULL; + + /* Make the temporary file readable by the world. */ + if (stat (tftp_localname, &stbuf) == 0) + chmod (tftp_localname, stbuf.st_mode | S_IROTH); + + /* Must reinitialize the board to prevent PMON from crashing. */ + mips_send_command ("initEther\r", -1); + + /* Send the load command. */ + cmd = xmalloc (strlen (load_cmd_prefix) + strlen (tftp_name) + 2); + strcpy (cmd, load_cmd_prefix); + strcat (cmd, tftp_name); + strcat (cmd, "\r"); + mips_send_command (cmd, 0); + xfree (cmd); + if (!mips_expect_download ("Downloading from ")) + return; + if (!mips_expect_download (tftp_name)) + return; + if (!mips_expect_download (", ^C to abort\r\n")) + return; + } + + /* Wait for the stuff that PMON prints after the load has completed. + The timeout value for use in the tftp case (15 seconds) was picked + arbitrarily but might be too small for really large downloads. FIXME. */ + switch (mips_monitor) + { + case MON_LSI: + pmon_check_ack ("termination"); + pmon_check_entry_address ("Entry address is ", final); + if (!pmon_check_total (bintotal)) + return; + break; + default: + pmon_check_entry_address ("Entry Address = ", final); + pmon_check_ack ("termination"); + if (!pmon_check_total (bintotal)) + return; + break; + } + + if (tftp_in_use) + remove (tftp_localname); /* Remove temporary file */ +} + +static void +pmon_download (char *buffer, int length) +{ + if (tftp_in_use) + fwrite (buffer, 1, length, tftp_file); + else + serial_write (udp_in_use ? udp_desc : mips_desc, buffer, length); +} + +static void +pmon_load_fast (char *file) +{ + bfd *abfd; + asection *s; + unsigned char *binbuf; + char *buffer; + int reclen; + unsigned int csum = 0; + int hashmark = !tftp_in_use; + int bintotal = 0; + int final = 0; + int finished = 0; + + buffer = (char *) xmalloc (MAXRECSIZE + 1); + binbuf = (unsigned char *) xmalloc (BINCHUNK); + + abfd = bfd_openr (file, 0); + if (!abfd) + { + printf_filtered ("Unable to open file %s\n", file); + return; + } + + if (bfd_check_format (abfd, bfd_object) == 0) + { + printf_filtered ("File is not an object file\n"); + return; + } + + /* Setup the required download state: */ + mips_send_command ("set dlproto etxack\r", -1); + mips_send_command ("set dlecho off\r", -1); + /* NOTE: We get a "cannot set variable" message if the variable is + already defined to have the argument we give. The code doesn't + care, since it just scans to the next prompt anyway. */ + /* Start the download: */ + pmon_start_download (); + + /* Zero the checksum */ + sprintf (buffer, "/Kxx\n"); + reclen = strlen (buffer); + pmon_download (buffer, reclen); + finished = pmon_check_ack ("/Kxx"); + + for (s = abfd->sections; s && !finished; s = s->next) + if (s->flags & SEC_LOAD) /* only deal with loadable sections */ + { + bintotal += s->_raw_size; + final = (s->vma + s->_raw_size); + + printf_filtered ("%s\t: 0x%4x .. 0x%4x ", s->name, (unsigned int) s->vma, + (unsigned int) (s->vma + s->_raw_size)); + gdb_flush (gdb_stdout); + + /* Output the starting address */ + sprintf (buffer, "/A"); + reclen = pmon_makeb64 (s->vma, &buffer[2], 36, &csum); + buffer[2 + reclen] = '\n'; + buffer[3 + reclen] = '\0'; + reclen += 3; /* for the initial escape code and carriage return */ + pmon_download (buffer, reclen); + finished = pmon_check_ack ("/A"); + + if (!finished) + { + unsigned int binamount; + unsigned int zerofill = 0; + char *bp = buffer; + unsigned int i; + + reclen = 0; + + for (i = 0; ((i < s->_raw_size) && !finished); i += binamount) + { + int binptr = 0; + + binamount = min (BINCHUNK, s->_raw_size - i); + + bfd_get_section_contents (abfd, s, binbuf, i, binamount); + + /* This keeps a rolling checksum, until we decide to output + the line: */ + for (; ((binamount - binptr) > 0);) + { + pmon_make_fastrec (&bp, binbuf, &binptr, binamount, &reclen, &csum, &zerofill); + if (reclen >= (MAXRECSIZE - CHECKSIZE)) + { + reclen = pmon_checkset (reclen, &bp, &csum); + pmon_download (buffer, reclen); + finished = pmon_check_ack ("data record"); + if (finished) + { + zerofill = 0; /* do not transmit pending zerofills */ + break; + } + + if (ui_load_progress_hook) + ui_load_progress_hook (s->name, i); + + if (hashmark) + { + putchar_unfiltered ('#'); + gdb_flush (gdb_stdout); + } + + bp = buffer; + reclen = 0; /* buffer processed */ + } + } + } + + /* Ensure no out-standing zerofill requests: */ + if (zerofill != 0) + reclen = pmon_zeroset (reclen, &bp, &zerofill, &csum); + + /* and then flush the line: */ + if (reclen > 0) + { + reclen = pmon_checkset (reclen, &bp, &csum); + /* Currently pmon_checkset outputs the line terminator by + default, so we write out the buffer so far: */ + pmon_download (buffer, reclen); + finished = pmon_check_ack ("record remnant"); + } + } + + putchar_unfiltered ('\n'); + } + + /* Terminate the transfer. We know that we have an empty output + buffer at this point. */ + sprintf (buffer, "/E/E\n"); /* include dummy padding characters */ + reclen = strlen (buffer); + pmon_download (buffer, reclen); + + if (finished) + { /* Ignore the termination message: */ + serial_flush_input (udp_in_use ? udp_desc : mips_desc); + } + else + { /* Deal with termination message: */ + pmon_end_download (final, bintotal); + } + + return; +} + +/* mips_load -- download a file. */ + +static void +mips_load (char *file, int from_tty) +{ + /* Get the board out of remote debugging mode. */ + if (mips_exit_debug ()) + error ("mips_load: Couldn't get into monitor mode."); + + if (mips_monitor != MON_IDT) + pmon_load_fast (file); + else + mips_load_srec (file); + + mips_initialize (); + + /* Finally, make the PC point at the start address */ + if (mips_monitor != MON_IDT) + { + /* Work around problem where PMON monitor updates the PC after a load + to a different value than GDB thinks it has. The following ensures + that the write_pc() WILL update the PC value: */ + deprecated_register_valid[PC_REGNUM] = 0; + } + if (exec_bfd) + write_pc (bfd_get_start_address (exec_bfd)); + + inferior_ptid = null_ptid; /* No process now */ + +/* This is necessary because many things were based on the PC at the time that + we attached to the monitor, which is no longer valid now that we have loaded + new code (and just changed the PC). Another way to do this might be to call + normal_stop, except that the stack may not be valid, and things would get + horribly confused... */ + + clear_symtab_users (); +} + + +/* Pass the command argument as a packet to PMON verbatim. */ + +static void +pmon_command (char *args, int from_tty) +{ + char buf[DATA_MAXLEN + 1]; + int rlen; + + sprintf (buf, "0x0 %s", args); + mips_send_packet (buf, 1); + printf_filtered ("Send packet: %s\n", buf); + + rlen = mips_receive_packet (buf, 1, mips_receive_wait); + buf[rlen] = '\0'; + printf_filtered ("Received packet: %s\n", buf); +} + +extern initialize_file_ftype _initialize_remote_mips; /* -Wmissing-prototypes */ + +void +_initialize_remote_mips (void) +{ + /* Initialize the fields in mips_ops that are common to all four targets. */ + mips_ops.to_longname = "Remote MIPS debugging over serial line"; + mips_ops.to_close = mips_close; + mips_ops.to_detach = mips_detach; + mips_ops.to_resume = mips_resume; + mips_ops.to_fetch_registers = mips_fetch_registers; + mips_ops.to_store_registers = mips_store_registers; + mips_ops.to_prepare_to_store = mips_prepare_to_store; + mips_ops.to_xfer_memory = mips_xfer_memory; + mips_ops.to_files_info = mips_files_info; + mips_ops.to_insert_breakpoint = mips_insert_breakpoint; + mips_ops.to_remove_breakpoint = mips_remove_breakpoint; + mips_ops.to_insert_watchpoint = mips_insert_watchpoint; + mips_ops.to_remove_watchpoint = mips_remove_watchpoint; + mips_ops.to_stopped_by_watchpoint = mips_stopped_by_watchpoint; + mips_ops.to_can_use_hw_breakpoint = mips_can_use_watchpoint; + mips_ops.to_kill = mips_kill; + mips_ops.to_load = mips_load; + mips_ops.to_create_inferior = mips_create_inferior; + mips_ops.to_mourn_inferior = mips_mourn_inferior; + mips_ops.to_stratum = process_stratum; + mips_ops.to_has_all_memory = 1; + mips_ops.to_has_memory = 1; + mips_ops.to_has_stack = 1; + mips_ops.to_has_registers = 1; + mips_ops.to_has_execution = 1; + mips_ops.to_magic = OPS_MAGIC; + + /* Copy the common fields to all four target vectors. */ + pmon_ops = ddb_ops = lsi_ops = mips_ops; + + /* Initialize target-specific fields in the target vectors. */ + mips_ops.to_shortname = "mips"; + mips_ops.to_doc = "\ +Debug a board using the MIPS remote debugging protocol over a serial line.\n\ +The argument is the device it is connected to or, if it contains a colon,\n\ +HOST:PORT to access a board over a network"; + mips_ops.to_open = mips_open; + mips_ops.to_wait = mips_wait; + + pmon_ops.to_shortname = "pmon"; + pmon_ops.to_doc = "\ +Debug a board using the PMON MIPS remote debugging protocol over a serial\n\ +line. The argument is the device it is connected to or, if it contains a\n\ +colon, HOST:PORT to access a board over a network"; + pmon_ops.to_open = pmon_open; + pmon_ops.to_wait = mips_wait; + + ddb_ops.to_shortname = "ddb"; + ddb_ops.to_doc = "\ +Debug a board using the PMON MIPS remote debugging protocol over a serial\n\ +line. The first argument is the device it is connected to or, if it contains\n\ +a colon, HOST:PORT to access a board over a network. The optional second\n\ +parameter is the temporary file in the form HOST:FILENAME to be used for\n\ +TFTP downloads to the board. The optional third parameter is the local name\n\ +of the TFTP temporary file, if it differs from the filename seen by the board."; + ddb_ops.to_open = ddb_open; + ddb_ops.to_wait = mips_wait; + + lsi_ops.to_shortname = "lsi"; + lsi_ops.to_doc = pmon_ops.to_doc; + lsi_ops.to_open = lsi_open; + lsi_ops.to_wait = mips_wait; + + /* Add the targets. */ + add_target (&mips_ops); + add_target (&pmon_ops); + add_target (&ddb_ops); + add_target (&lsi_ops); + + add_show_from_set ( + add_set_cmd ("timeout", no_class, var_zinteger, + (char *) &mips_receive_wait, + "Set timeout in seconds for remote MIPS serial I/O.", + &setlist), + &showlist); + + add_show_from_set ( + add_set_cmd ("retransmit-timeout", no_class, var_zinteger, + (char *) &mips_retransmit_wait, + "Set retransmit timeout in seconds for remote MIPS serial I/O.\n\ +This is the number of seconds to wait for an acknowledgement to a packet\n\ +before resending the packet.", &setlist), + &showlist); + + add_show_from_set ( + add_set_cmd ("syn-garbage-limit", no_class, var_zinteger, + (char *) &mips_syn_garbage, + "Set the maximum number of characters to ignore when scanning for a SYN.\n\ +This is the maximum number of characters GDB will ignore when trying to\n\ +synchronize with the remote system. A value of -1 means that there is no limit\n\ +(Note that these characters are printed out even though they are ignored.)", + &setlist), + &showlist); + + add_show_from_set + (add_set_cmd ("monitor-prompt", class_obscure, var_string, + (char *) &mips_monitor_prompt, + "Set the prompt that GDB expects from the monitor.", + &setlist), + &showlist); + + add_show_from_set ( + add_set_cmd ("monitor-warnings", class_obscure, var_zinteger, + (char *) &monitor_warnings, + "Set printing of monitor warnings.\n" + "When enabled, monitor warnings about hardware breakpoints " + "will be displayed.", + &setlist), + &showlist); + + add_com ("pmon <command>", class_obscure, pmon_command, + "Send a packet to PMON (must be in debug mode)."); + + add_show_from_set (add_set_cmd ("mask-address", no_class, + var_boolean, &mask_address_p, + "Set zeroing of upper 32 bits of 64-bit addresses when talking to PMON targets.\n\ +Use \"on\" to enable the masking and \"off\" to disable it.\n", + &setlist), + &showlist); +} diff --git a/contrib/gdb/gdb/remote-rdp.c b/contrib/gdb/gdb/remote-rdp.c new file mode 100644 index 0000000..eab68ea --- /dev/null +++ b/contrib/gdb/gdb/remote-rdp.c @@ -0,0 +1,1431 @@ +/* Remote debugging for the ARM RDP interface. + + Copyright 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003 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. + + + */ + + +/* + Much of this file (in particular the SWI stuff) is based on code by + David Taylor (djt1000@uk.ac.cam.hermes). + + I hacked on and simplified it by removing a lot of sexy features he + had added, and some of the (unix specific) workarounds he'd done + for other GDB problems - which if they still exist should be fixed + in GDB, not in a remote-foo thing . I also made it conform more to + the doc I have; which may be wrong. + + Steve Chamberlain (sac@cygnus.com). + */ + + +#include "defs.h" +#include "inferior.h" +#include "value.h" +#include "gdb/callback.h" +#include "command.h" +#include <ctype.h> +#include <fcntl.h> +#include "symfile.h" +#include "remote-utils.h" +#include "gdb_string.h" +#include "gdbcore.h" +#include "regcache.h" +#include "serial.h" + +#include "arm-tdep.h" + +#ifdef HAVE_TIME_H +#include <time.h> +#endif + +extern struct target_ops remote_rdp_ops; +static struct serial *io; +static host_callback *callback = &default_callback; + +struct + { + int step_info; + int break_info; + int model_info; + int target_info; + int can_step; + char command_line[10]; + int rdi_level; + int rdi_stopped_status; + } +ds; + + + +/* Definitions for the RDP protocol. */ + +#define RDP_MOUTHFULL (1<<6) +#define FPU_COPRO_NUMBER 1 + +#define RDP_OPEN 0 +#define RDP_OPEN_TYPE_COLD 0 +#define RDP_OPEN_TYPE_WARM 1 +#define RDP_OPEN_TYPE_BAUDRATE 2 + +#define RDP_OPEN_BAUDRATE_9600 1 +#define RDP_OPEN_BAUDRATE_19200 2 +#define RDP_OPEN_BAUDRATE_38400 3 + +#define RDP_OPEN_TYPE_RETURN_SEX (1<<3) + +#define RDP_CLOSE 1 + +#define RDP_MEM_READ 2 + +#define RDP_MEM_WRITE 3 + +#define RDP_CPU_READ 4 +#define RDP_CPU_WRITE 5 +#define RDP_CPU_READWRITE_MODE_CURRENT 255 +#define RDP_CPU_READWRITE_MASK_PC (1<<16) +#define RDP_CPU_READWRITE_MASK_CPSR (1<<17) +#define RDP_CPU_READWRITE_MASK_SPSR (1<<18) + +#define RDP_COPRO_READ 6 +#define RDP_COPRO_WRITE 7 +#define RDP_FPU_READWRITE_MASK_FPS (1<<8) + +#define RDP_SET_BREAK 0xa +#define RDP_SET_BREAK_TYPE_PC_EQUAL 0 +#define RDP_SET_BREAK_TYPE_GET_HANDLE (0x10) + +#define RDP_CLEAR_BREAK 0xb + +#define RDP_EXEC 0x10 +#define RDP_EXEC_TYPE_SYNC 0 + +#define RDP_STEP 0x11 + +#define RDP_INFO 0x12 +#define RDP_INFO_ABOUT_STEP 2 +#define RDP_INFO_ABOUT_STEP_GT_1 1 +#define RDP_INFO_ABOUT_STEP_TO_JMP 2 +#define RDP_INFO_ABOUT_STEP_1 4 +#define RDP_INFO_ABOUT_TARGET 0 +#define RDP_INFO_ABOUT_BREAK 1 +#define RDP_INFO_ABOUT_BREAK_COMP 1 +#define RDP_INFO_ABOUT_BREAK_RANGE 2 +#define RDP_INFO_ABOUT_BREAK_BYTE_READ 4 +#define RDP_INFO_ABOUT_BREAK_HALFWORD_READ 8 +#define RDP_INFO_ABOUT_BREAK_WORD_READ (1<<4) +#define RDP_INFO_ABOUT_BREAK_BYTE_WRITE (1<<5) +#define RDP_INFO_ABOUT_BREAK_HALFWORD_WRITE (1<<6) +#define RDP_INFO_ABOUT_BREAK_WORD_WRITE (1<<7) +#define RDP_INFO_ABOUT_BREAK_MASK (1<<8) +#define RDP_INFO_ABOUT_BREAK_THREAD_BREAK (1<<9) +#define RDP_INFO_ABOUT_BREAK_THREAD_WATCH (1<<10) +#define RDP_INFO_ABOUT_BREAK_COND (1<<11) +#define RDP_INFO_VECTOR_CATCH (0x180) +#define RDP_INFO_ICEBREAKER (7) +#define RDP_INFO_SET_CMDLINE (0x300) + +#define RDP_SELECT_CONFIG (0x16) +#define RDI_ConfigCPU 0 +#define RDI_ConfigSystem 1 +#define RDI_MatchAny 0 +#define RDI_MatchExactly 1 +#define RDI_MatchNoEarlier 2 + +#define RDP_RESET 0x7f + +/* Returns from RDP */ +#define RDP_RES_STOPPED 0x20 +#define RDP_RES_SWI 0x21 +#define RDP_RES_FATAL 0x5e +#define RDP_RES_VALUE 0x5f +#define RDP_RES_VALUE_LITTLE_ENDIAN 240 +#define RDP_RES_VALUE_BIG_ENDIAN 241 +#define RDP_RES_RESET 0x7f +#define RDP_RES_AT_BREAKPOINT 143 +#define RDP_RES_IDUNNO 0xe6 +#define RDP_OSOpReply 0x13 +#define RDP_OSOpWord 2 +#define RDP_OSOpNothing 0 + +static int timeout = 2; + +static char *commandline = NULL; + +static int +remote_rdp_xfer_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len, + int write, + struct mem_attrib *attrib, + struct target_ops *target); + + +/* Stuff for talking to the serial layer. */ + +static unsigned char +get_byte (void) +{ + int c = serial_readchar (io, timeout); + + if (remote_debug) + fprintf_unfiltered (gdb_stdlog, "[%02x]\n", c); + + if (c == SERIAL_TIMEOUT) + { + if (timeout == 0) + return (unsigned char) c; + + error ("Timeout reading from remote_system"); + } + + return c; +} + +/* Note that the target always speaks little-endian to us, + even if it's a big endian machine. */ +static unsigned int +get_word (void) +{ + unsigned int val = 0; + unsigned int c; + int n; + for (n = 0; n < 4; n++) + { + c = get_byte (); + val |= c << (n * 8); + } + return val; +} + +static void +put_byte (char val) +{ + if (remote_debug) + fprintf_unfiltered (gdb_stdlog, "(%02x)\n", val); + serial_write (io, &val, 1); +} + +static void +put_word (int val) +{ + /* We always send in little endian */ + unsigned char b[4]; + b[0] = val; + b[1] = val >> 8; + b[2] = val >> 16; + b[3] = val >> 24; + + if (remote_debug) + fprintf_unfiltered (gdb_stdlog, "(%04x)", val); + + serial_write (io, b, 4); +} + + + +/* Stuff for talking to the RDP layer. */ + +/* This is a bit more fancy that need be so that it syncs even in nasty cases. + + I'be been unable to make it reliably sync up with the change + baudrate open command. It likes to sit and say it's been reset, + with no more action. So I took all that code out. I'd rather sync + reliably at 9600 than wait forever for a possible 19200 connection. + + */ +static void +rdp_init (int cold, int tty) +{ + int sync = 0; + int type = cold ? RDP_OPEN_TYPE_COLD : RDP_OPEN_TYPE_WARM; + int baudtry = 9600; + + time_t now = time (0); + time_t stop_time = now + 10; /* Try and sync for 10 seconds, then give up */ + + + while (time (0) < stop_time && !sync) + { + int restype; + QUIT; + + serial_flush_input (io); + serial_flush_output (io); + + if (tty) + printf_unfiltered ("Trying to connect at %d baud.\n", baudtry); + + /* + ** It seems necessary to reset an EmbeddedICE to get it going. + ** This has the side benefit of displaying the startup banner. + */ + if (cold) + { + put_byte (RDP_RESET); + while ((restype = serial_readchar (io, 1)) > 0) + { + switch (restype) + { + case SERIAL_TIMEOUT: + break; + case RDP_RESET: + /* Sent at start of reset process: ignore */ + break; + default: + printf_unfiltered ("%c", isgraph (restype) ? restype : ' '); + break; + } + } + + if (restype == 0) + { + /* Got end-of-banner mark */ + printf_filtered ("\n"); + } + } + + put_byte (RDP_OPEN); + + put_byte (type | RDP_OPEN_TYPE_RETURN_SEX); + put_word (0); + + while (!sync && (restype = serial_readchar (io, 1)) > 0) + { + if (remote_debug) + fprintf_unfiltered (gdb_stdlog, "[%02x]\n", restype); + + switch (restype) + { + case SERIAL_TIMEOUT: + break; + + case RDP_RESET: + while ((restype = serial_readchar (io, 1)) == RDP_RESET) + ; + do + { + printf_unfiltered ("%c", isgraph (restype) ? restype : ' '); + } + while ((restype = serial_readchar (io, 1)) > 0); + + if (tty) + { + printf_unfiltered ("\nThe board has sent notification that it was reset.\n"); + printf_unfiltered ("Waiting for it to settle down...\n"); + } + sleep (3); + if (tty) + printf_unfiltered ("\nTrying again.\n"); + cold = 0; + break; + + default: + break; + + case RDP_RES_VALUE: + { + int resval = serial_readchar (io, 1); + + if (remote_debug) + fprintf_unfiltered (gdb_stdlog, "[%02x]\n", resval); + + switch (resval) + { + case SERIAL_TIMEOUT: + break; + case RDP_RES_VALUE_LITTLE_ENDIAN: +#if 0 + /* FIXME: cagney/2003-11-22: Ever since the ARM + was multi-arched (in 2002-02-08), this + assignment has had no effect. There needs to + be some sort of check/decision based on the + current architecture's byte-order vs the remote + target's byte order. For the moment disable + the assignment to keep things building. */ + target_byte_order = BFD_ENDIAN_LITTLE; +#endif + sync = 1; + break; + case RDP_RES_VALUE_BIG_ENDIAN: +#if 0 + /* FIXME: cagney/2003-11-22: Ever since the ARM + was multi-arched (in 2002-02-08), this + assignment has had no effect. There needs to + be some sort of check/decision based on the + current architecture's byte-order vs the remote + target's byte order. For the moment disable + the assignment to keep things building. */ + target_byte_order = BFD_ENDIAN_BIG; +#endif + sync = 1; + break; + default: + break; + } + } + } + } + } + + if (!sync) + { + error ("Couldn't reset the board, try pressing the reset button"); + } +} + + +static void +send_rdp (char *template,...) +{ + char buf[200]; + char *dst = buf; + va_list alist; + va_start (alist, template); + + while (*template) + { + unsigned int val; + int *pi; + int *pstat; + char *pc; + int i; + switch (*template++) + { + case 'b': + val = va_arg (alist, int); + *dst++ = val; + break; + case 'w': + val = va_arg (alist, int); + *dst++ = val; + *dst++ = val >> 8; + *dst++ = val >> 16; + *dst++ = val >> 24; + break; + case 'S': + val = get_byte (); + if (val != RDP_RES_VALUE) + { + printf_unfiltered ("got bad res value of %d, %x\n", val, val); + } + break; + case 'V': + pstat = va_arg (alist, int *); + pi = va_arg (alist, int *); + + *pstat = get_byte (); + /* Check the result was zero, if not read the syndrome */ + if (*pstat) + { + *pi = get_word (); + } + break; + case 'Z': + /* Check the result code */ + switch (get_byte ()) + { + case 0: + /* Success */ + break; + case 253: + /* Target can't do it; never mind */ + printf_unfiltered ("RDP: Insufficient privilege\n"); + return; + case 254: + /* Target can't do it; never mind */ + printf_unfiltered ("RDP: Unimplemented message\n"); + return; + case 255: + error ("Command garbled"); + break; + default: + error ("Corrupt reply from target"); + break; + } + break; + case 'W': + /* Read a word from the target */ + pi = va_arg (alist, int *); + *pi = get_word (); + break; + case 'P': + /* Read in some bytes from the target. */ + pc = va_arg (alist, char *); + val = va_arg (alist, int); + for (i = 0; i < val; i++) + { + pc[i] = get_byte (); + } + break; + case 'p': + /* send what's being pointed at */ + pc = va_arg (alist, char *); + val = va_arg (alist, int); + dst = buf; + serial_write (io, pc, val); + break; + case '-': + /* Send whats in the queue */ + if (dst != buf) + { + serial_write (io, buf, dst - buf); + dst = buf; + } + break; + case 'B': + pi = va_arg (alist, int *); + *pi = get_byte (); + break; + default: + internal_error (__FILE__, __LINE__, "failed internal consistency check"); + } + } + va_end (alist); + + if (dst != buf) + internal_error (__FILE__, __LINE__, "failed internal consistency check"); +} + + +static int +rdp_write (CORE_ADDR memaddr, char *buf, int len) +{ + int res; + int val; + + send_rdp ("bww-p-SV", RDP_MEM_WRITE, memaddr, len, buf, len, &res, &val); + + if (res) + { + return val; + } + return len; +} + + +static int +rdp_read (CORE_ADDR memaddr, char *buf, int len) +{ + int res; + int val; + send_rdp ("bww-S-P-V", + RDP_MEM_READ, memaddr, len, + buf, len, + &res, &val); + if (res) + { + return val; + } + return len; +} + +static void +rdp_fetch_one_register (int mask, char *buf) +{ + int val; + send_rdp ("bbw-SWZ", RDP_CPU_READ, RDP_CPU_READWRITE_MODE_CURRENT, mask, &val); + store_signed_integer (buf, 4, val); +} + +static void +rdp_fetch_one_fpu_register (int mask, char *buf) +{ +#if 0 + /* !!! Since the PIE board doesn't work as documented, + and it doesn't have FPU hardware anyway and since it + slows everything down, I've disabled this. */ + int val; + if (mask == RDP_FPU_READWRITE_MASK_FPS) + { + /* this guy is only a word */ + send_rdp ("bbw-SWZ", RDP_COPRO_READ, FPU_COPRO_NUMBER, mask, &val); + store_signed_integer (buf, 4, val); + } + else + { + /* There are 12 bytes long + !! fixme about endianness + */ + int dummy; /* I've seen these come back as four words !! */ + send_rdp ("bbw-SWWWWZ", RDP_COPRO_READ, FPU_COPRO_NUMBER, mask, buf + 0, buf + 4, buf + 8, &dummy); + } +#endif + memset (buf, 0, MAX_REGISTER_SIZE); +} + + +static void +rdp_store_one_register (int mask, char *buf) +{ + int val = extract_unsigned_integer (buf, 4); + + send_rdp ("bbww-SZ", + RDP_CPU_WRITE, RDP_CPU_READWRITE_MODE_CURRENT, mask, val); +} + + +static void +rdp_store_one_fpu_register (int mask, char *buf) +{ +#if 0 + /* See comment in fetch_one_fpu_register */ + if (mask == RDP_FPU_READWRITE_MASK_FPS) + { + int val = extract_unsigned_integer (buf, 4); + /* this guy is only a word */ + send_rdp ("bbww-SZ", RDP_COPRO_WRITE, + FPU_COPRO_NUMBER, + mask, val); + } + else + { + /* There are 12 bytes long + !! fixme about endianness + */ + int dummy = 0; + /* I've seen these come as four words, not the three advertized !! */ + printf ("Sending mask %x\n", mask); + send_rdp ("bbwwwww-SZ", + RDP_COPRO_WRITE, + FPU_COPRO_NUMBER, + mask, + *(int *) (buf + 0), + *(int *) (buf + 4), + *(int *) (buf + 8), + 0); + + printf ("done mask %x\n", mask); + } +#endif +} + + +/* Convert between GDB requests and the RDP layer. */ + +static void +remote_rdp_fetch_register (int regno) +{ + if (regno == -1) + { + for (regno = 0; regno < NUM_REGS; regno++) + remote_rdp_fetch_register (regno); + } + else + { + char buf[MAX_REGISTER_SIZE]; + if (regno < 15) + rdp_fetch_one_register (1 << regno, buf); + else if (regno == ARM_PC_REGNUM) + rdp_fetch_one_register (RDP_CPU_READWRITE_MASK_PC, buf); + else if (regno == ARM_PS_REGNUM) + rdp_fetch_one_register (RDP_CPU_READWRITE_MASK_CPSR, buf); + else if (regno == ARM_FPS_REGNUM) + rdp_fetch_one_fpu_register (RDP_FPU_READWRITE_MASK_FPS, buf); + else if (regno >= ARM_F0_REGNUM && regno <= ARM_F7_REGNUM) + rdp_fetch_one_fpu_register (1 << (regno - ARM_F0_REGNUM), buf); + else + { + printf ("Help me with fetch reg %d\n", regno); + } + supply_register (regno, buf); + } +} + + +static void +remote_rdp_store_register (int regno) +{ + if (regno == -1) + { + for (regno = 0; regno < NUM_REGS; regno++) + remote_rdp_store_register (regno); + } + else + { + char tmp[MAX_REGISTER_SIZE]; + deprecated_read_register_gen (regno, tmp); + if (regno < 15) + rdp_store_one_register (1 << regno, tmp); + else if (regno == ARM_PC_REGNUM) + rdp_store_one_register (RDP_CPU_READWRITE_MASK_PC, tmp); + else if (regno == ARM_PS_REGNUM) + rdp_store_one_register (RDP_CPU_READWRITE_MASK_CPSR, tmp); + else if (regno >= ARM_F0_REGNUM && regno <= ARM_F7_REGNUM) + rdp_store_one_fpu_register (1 << (regno - ARM_F0_REGNUM), tmp); + else + { + printf ("Help me with reg %d\n", regno); + } + } +} + +static void +remote_rdp_kill (void) +{ + callback->shutdown (callback); +} + + +static void +rdp_info (void) +{ + send_rdp ("bw-S-W-Z", RDP_INFO, RDP_INFO_ABOUT_STEP, + &ds.step_info); + send_rdp ("bw-S-W-Z", RDP_INFO, RDP_INFO_ABOUT_BREAK, + &ds.break_info); + send_rdp ("bw-S-WW-Z", RDP_INFO, RDP_INFO_ABOUT_TARGET, + &ds.target_info, + &ds.model_info); + + ds.can_step = ds.step_info & RDP_INFO_ABOUT_STEP_1; + + ds.rdi_level = (ds.target_info >> 5) & 3; +} + + +static void +rdp_execute_start (void) +{ + /* Start it off, but don't wait for it */ + send_rdp ("bb-", RDP_EXEC, RDP_EXEC_TYPE_SYNC); +} + + +static void +rdp_set_command_line (char *command, char *args) +{ + /* + ** We could use RDP_INFO_SET_CMDLINE to send this, but EmbeddedICE systems + ** don't implement that, and get all confused at the unexpected text. + ** Instead, just keep a copy, and send it when the target does a SWI_GetEnv + */ + + if (commandline != NULL) + xfree (commandline); + + xasprintf (&commandline, "%s %s", command, args); +} + +static void +rdp_catch_vectors (void) +{ + /* + ** We want the target monitor to intercept the abort vectors + ** i.e. stop the program if any of these are used. + */ + send_rdp ("bww-SZ", RDP_INFO, RDP_INFO_VECTOR_CATCH, + /* + ** Specify a bitmask including + ** the reset vector + ** the undefined instruction vector + ** the prefetch abort vector + ** the data abort vector + ** the address exception vector + */ + (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) + ); +} + + + +#define a_byte 1 +#define a_word 2 +#define a_string 3 + + +typedef struct +{ + CORE_ADDR n; + const char *s; +} +argsin; + +#define ABYTE 1 +#define AWORD 2 +#define ASTRING 3 +#define ADDRLEN 4 + +#define SWI_WriteC 0x0 +#define SWI_Write0 0x2 +#define SWI_ReadC 0x4 +#define SWI_CLI 0x5 +#define SWI_GetEnv 0x10 +#define SWI_Exit 0x11 +#define SWI_EnterOS 0x16 + +#define SWI_GetErrno 0x60 +#define SWI_Clock 0x61 + +#define SWI_Time 0x63 +#define SWI_Remove 0x64 +#define SWI_Rename 0x65 +#define SWI_Open 0x66 + +#define SWI_Close 0x68 +#define SWI_Write 0x69 +#define SWI_Read 0x6a +#define SWI_Seek 0x6b +#define SWI_Flen 0x6c + +#define SWI_IsTTY 0x6e +#define SWI_TmpNam 0x6f +#define SWI_InstallHandler 0x70 +#define SWI_GenerateError 0x71 + + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +static int translate_open_mode[] = +{ + O_RDONLY, /* "r" */ + O_RDONLY + O_BINARY, /* "rb" */ + O_RDWR, /* "r+" */ + O_RDWR + O_BINARY, /* "r+b" */ + O_WRONLY + O_CREAT + O_TRUNC, /* "w" */ + O_WRONLY + O_BINARY + O_CREAT + O_TRUNC, /* "wb" */ + O_RDWR + O_CREAT + O_TRUNC, /* "w+" */ + O_RDWR + O_BINARY + O_CREAT + O_TRUNC, /* "w+b" */ + O_WRONLY + O_APPEND + O_CREAT, /* "a" */ + O_WRONLY + O_BINARY + O_APPEND + O_CREAT, /* "ab" */ + O_RDWR + O_APPEND + O_CREAT, /* "a+" */ + O_RDWR + O_BINARY + O_APPEND + O_CREAT /* "a+b" */ +}; + +static int +exec_swi (int swi, argsin *args) +{ + int i; + char c; + switch (swi) + { + case SWI_WriteC: + callback->write_stdout (callback, &c, 1); + return 0; + case SWI_Write0: + for (i = 0; i < args->n; i++) + callback->write_stdout (callback, args->s, strlen (args->s)); + return 0; + case SWI_ReadC: + callback->read_stdin (callback, &c, 1); + args->n = c; + return 1; + case SWI_CLI: + args->n = callback->system (callback, args->s); + return 1; + case SWI_GetErrno: + args->n = callback->get_errno (callback); + return 1; + case SWI_Time: + args->n = callback->time (callback, NULL); + return 1; + + case SWI_Clock: + /* return number of centi-seconds... */ + args->n = +#ifdef CLOCKS_PER_SEC + (CLOCKS_PER_SEC >= 100) + ? (clock () / (CLOCKS_PER_SEC / 100)) + : ((clock () * 100) / CLOCKS_PER_SEC); +#else + /* presume unix... clock() returns microseconds */ + clock () / 10000; +#endif + return 1; + + case SWI_Remove: + args->n = callback->unlink (callback, args->s); + return 1; + case SWI_Rename: + args->n = callback->rename (callback, args[0].s, args[1].s); + return 1; + + case SWI_Open: + /* Now we need to decode the Demon open mode */ + i = translate_open_mode[args[1].n]; + + /* Filename ":tt" is special: it denotes stdin/out */ + if (strcmp (args->s, ":tt") == 0) + { + if (i == O_RDONLY) /* opening tty "r" */ + args->n = 0 /* stdin */ ; + else + args->n = 1 /* stdout */ ; + } + else + args->n = callback->open (callback, args->s, i); + return 1; + + case SWI_Close: + args->n = callback->close (callback, args->n); + return 1; + + case SWI_Write: + /* Return the number of bytes *not* written */ + args->n = args[1].n - + callback->write (callback, args[0].n, args[1].s, args[1].n); + return 1; + + case SWI_Read: + { + char *copy = alloca (args[2].n); + int done = callback->read (callback, args[0].n, copy, args[2].n); + if (done > 0) + remote_rdp_xfer_inferior_memory (args[1].n, copy, done, 1, 0, 0); + args->n = args[2].n - done; + return 1; + } + + case SWI_Seek: + /* Return non-zero on failure */ + args->n = callback->lseek (callback, args[0].n, args[1].n, 0) < 0; + return 1; + + case SWI_Flen: + { + long old = callback->lseek (callback, args->n, 0, SEEK_CUR); + args->n = callback->lseek (callback, args->n, 0, SEEK_END); + callback->lseek (callback, args->n, old, 0); + return 1; + } + + case SWI_IsTTY: + args->n = callback->isatty (callback, args->n); + return 1; + + case SWI_GetEnv: + if (commandline != NULL) + { + int len = strlen (commandline); + if (len > 255) + { + len = 255; + commandline[255] = '\0'; + } + remote_rdp_xfer_inferior_memory (args[0].n, + commandline, len + 1, 1, 0, 0); + } + else + remote_rdp_xfer_inferior_memory (args[0].n, "", 1, 1, 0, 0); + return 1; + + default: + return 0; + } +} + + +static void +handle_swi (void) +{ + argsin args[3]; + char *buf; + int len; + int count = 0; + + int swino = get_word (); + int type = get_byte (); + while (type != 0) + { + switch (type & 0x3) + { + case ABYTE: + args[count].n = get_byte (); + break; + + case AWORD: + args[count].n = get_word (); + break; + + case ASTRING: + /* If the word is under 32 bytes it will be sent otherwise + an address to it is passed. Also: Special case of 255 */ + + len = get_byte (); + if (len > 32) + { + if (len == 255) + { + len = get_word (); + } + buf = alloca (len); + remote_rdp_xfer_inferior_memory (get_word (), + buf, + len, + 0, + 0, + 0); + } + else + { + int i; + buf = alloca (len + 1); + for (i = 0; i < len; i++) + buf[i] = get_byte (); + buf[i] = 0; + } + args[count].n = len; + args[count].s = buf; + break; + + default: + error ("Unimplemented SWI argument"); + } + + type = type >> 2; + count++; + } + + if (exec_swi (swino, args)) + { + /* We have two options here reply with either a byte or a word + which is stored in args[0].n. There is no harm in replying with + a word all the time, so thats what I do! */ + send_rdp ("bbw-", RDP_OSOpReply, RDP_OSOpWord, args[0].n); + } + else + { + send_rdp ("bb-", RDP_OSOpReply, RDP_OSOpNothing); + } +} + +static void +rdp_execute_finish (void) +{ + int running = 1; + + while (running) + { + int res; + res = serial_readchar (io, 1); + while (res == SERIAL_TIMEOUT) + { + QUIT; + printf_filtered ("Waiting for target..\n"); + res = serial_readchar (io, 1); + } + + switch (res) + { + case RDP_RES_SWI: + handle_swi (); + break; + case RDP_RES_VALUE: + send_rdp ("B", &ds.rdi_stopped_status); + running = 0; + break; + case RDP_RESET: + printf_filtered ("Target reset\n"); + running = 0; + break; + default: + printf_filtered ("Ignoring %x\n", res); + break; + } + } +} + + +static void +rdp_execute (void) +{ + rdp_execute_start (); + rdp_execute_finish (); +} + +static int +remote_rdp_insert_breakpoint (CORE_ADDR addr, char *save) +{ + int res; + if (ds.rdi_level > 0) + { + send_rdp ("bwb-SWB", + RDP_SET_BREAK, + addr, + RDP_SET_BREAK_TYPE_PC_EQUAL | RDP_SET_BREAK_TYPE_GET_HANDLE, + save, + &res); + } + else + { + send_rdp ("bwb-SB", + RDP_SET_BREAK, + addr, + RDP_SET_BREAK_TYPE_PC_EQUAL, + &res); + } + return res; +} + +static int +remote_rdp_remove_breakpoint (CORE_ADDR addr, char *save) +{ + int res; + if (ds.rdi_level > 0) + { + send_rdp ("b-p-S-B", + RDP_CLEAR_BREAK, + save, 4, + &res); + } + else + { + send_rdp ("bw-S-B", + RDP_CLEAR_BREAK, + addr, + &res); + } + return res; +} + +static void +rdp_step (void) +{ + if (ds.can_step && 0) + { + /* The pie board can't do steps so I can't test this, and + the other code will always work. */ + int status; + send_rdp ("bbw-S-B", + RDP_STEP, 0, 1, + &status); + } + else + { + char handle[4]; + CORE_ADDR pc = read_register (ARM_PC_REGNUM); + pc = arm_get_next_pc (pc); + remote_rdp_insert_breakpoint (pc, handle); + rdp_execute (); + remote_rdp_remove_breakpoint (pc, handle); + } +} + +static void +remote_rdp_open (char *args, int from_tty) +{ + int not_icebreaker; + + if (!args) + error_no_arg ("serial port device name"); + + baud_rate = 9600; + + target_preopen (from_tty); + + io = serial_open (args); + + if (!io) + perror_with_name (args); + + serial_raw (io); + + rdp_init (1, from_tty); + + + if (from_tty) + { + printf_unfiltered ("Remote RDP debugging using %s at %d baud\n", args, baud_rate); + } + + rdp_info (); + + /* Need to set up the vector interception state */ + rdp_catch_vectors (); + + /* + ** If it's an EmbeddedICE, we need to set the processor config. + ** Assume we can always have ARM7TDI... + */ + send_rdp ("bw-SB", RDP_INFO, RDP_INFO_ICEBREAKER, ¬_icebreaker); + if (!not_icebreaker) + { + const char *CPU = "ARM7TDI"; + int ICEversion; + int len = strlen (CPU); + + send_rdp ("bbbbw-p-SWZ", + RDP_SELECT_CONFIG, + RDI_ConfigCPU, /* Aspect: set the CPU */ + len, /* The number of bytes in the name */ + RDI_MatchAny, /* We'll take whatever we get */ + 0, /* We'll take whatever version's there */ + CPU, len, + &ICEversion); + } + + /* command line initialised on 'run' */ + + push_target (&remote_rdp_ops); + + callback->init (callback); + flush_cached_frames (); + registers_changed (); + stop_pc = read_pc (); + print_stack_frame (get_selected_frame (), -1, 1); +} + + + +/* Close out all files and local state before this target loses control. */ + +static void +remote_rdp_close (int quitting) +{ + callback->shutdown (callback); + if (io) + serial_close (io); + io = 0; +} + + +/* Resume execution of the target process. STEP says whether to single-step + or to run free; SIGGNAL is the signal value (e.g. SIGINT) to be given + to the target, or zero for no signal. */ + +static void +remote_rdp_resume (ptid_t ptid, int step, enum target_signal siggnal) +{ + if (step) + rdp_step (); + else + rdp_execute (); +} + +/* Wait for inferior process to do something. Return pid of child, + or -1 in case of error; store status through argument pointer STATUS, + just as `wait' would. */ + +static ptid_t +remote_rdp_wait (ptid_t ptid, struct target_waitstatus *status) +{ + switch (ds.rdi_stopped_status) + { + default: + case RDP_RES_RESET: + case RDP_RES_SWI: + status->kind = TARGET_WAITKIND_EXITED; + status->value.integer = read_register (0); + break; + case RDP_RES_AT_BREAKPOINT: + status->kind = TARGET_WAITKIND_STOPPED; + /* The signal in sigrc is a host signal. That probably + should be fixed. */ + status->value.sig = TARGET_SIGNAL_TRAP; + break; +#if 0 + case rdp_signalled: + status->kind = TARGET_WAITKIND_SIGNALLED; + /* The signal in sigrc is a host signal. That probably + should be fixed. */ + status->value.sig = target_signal_from_host (sigrc); + break; +#endif + } + + return inferior_ptid; +} + +/* Get ready to modify the registers array. On machines which store + individual registers, this doesn't need to do anything. On machines + which store all the registers in one fell swoop, this makes sure + that registers contains all the registers from the program being + debugged. */ + +static void +remote_rdp_prepare_to_store (void) +{ + /* Do nothing, since we can store individual regs */ +} + +/* Transfer LEN bytes between GDB address MYADDR and target address + MEMADDR. If WRITE is non-zero, transfer them to the target, + otherwise transfer them from the target. TARGET is unused. + + Returns the number of bytes transferred. */ + +static int +remote_rdp_xfer_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len, + int write, struct mem_attrib *attrib, + struct target_ops *target) +{ + /* I infer from D Taylor's code that there's a limit on the amount + we can transfer in one chunk.. */ + int done = 0; + while (done < len) + { + int justdone; + int thisbite = len - done; + if (thisbite > RDP_MOUTHFULL) + thisbite = RDP_MOUTHFULL; + + QUIT; + + if (write) + { + justdone = rdp_write (memaddr + done, myaddr + done, thisbite); + } + else + { + justdone = rdp_read (memaddr + done, myaddr + done, thisbite); + } + + done += justdone; + + if (justdone != thisbite) + break; + } + return done; +} + + + +struct yn +{ + const char *name; + int bit; +}; +static struct yn stepinfo[] = +{ + {"Step more than one instruction", RDP_INFO_ABOUT_STEP_GT_1}, + {"Step to jump", RDP_INFO_ABOUT_STEP_TO_JMP}, + {"Step one instruction", RDP_INFO_ABOUT_STEP_1}, + {0} +}; + +static struct yn breakinfo[] = +{ + {"comparison breakpoints supported", RDP_INFO_ABOUT_BREAK_COMP}, + {"range breakpoints supported", RDP_INFO_ABOUT_BREAK_RANGE}, + {"watchpoints for byte reads supported", RDP_INFO_ABOUT_BREAK_BYTE_READ}, + {"watchpoints for half-word reads supported", RDP_INFO_ABOUT_BREAK_HALFWORD_READ}, + {"watchpoints for word reads supported", RDP_INFO_ABOUT_BREAK_WORD_READ}, + {"watchpoints for byte writes supported", RDP_INFO_ABOUT_BREAK_BYTE_WRITE}, + {"watchpoints for half-word writes supported", RDP_INFO_ABOUT_BREAK_HALFWORD_WRITE}, + {"watchpoints for word writes supported", RDP_INFO_ABOUT_BREAK_WORD_WRITE}, + {"mask break/watch-points supported", RDP_INFO_ABOUT_BREAK_MASK}, +{"thread-specific breakpoints supported", RDP_INFO_ABOUT_BREAK_THREAD_BREAK}, +{"thread-specific watchpoints supported", RDP_INFO_ABOUT_BREAK_THREAD_WATCH}, + {"conditional breakpoints supported", RDP_INFO_ABOUT_BREAK_COND}, + {0} +}; + + +static void +dump_bits (struct yn *t, int info) +{ + while (t->name) + { + printf_unfiltered (" %-45s : %s\n", t->name, (info & t->bit) ? "Yes" : "No"); + t++; + } +} + +static void +remote_rdp_files_info (struct target_ops *target) +{ + printf_filtered ("Target capabilities:\n"); + dump_bits (stepinfo, ds.step_info); + dump_bits (breakinfo, ds.break_info); + printf_unfiltered ("target level RDI %x\n", (ds.target_info >> 5) & 3); +} + + +static void +remote_rdp_create_inferior (char *exec_file, char *allargs, char **env) +{ + CORE_ADDR entry_point; + + if (exec_file == 0 || exec_bfd == 0) + error ("No executable file specified."); + + entry_point = (CORE_ADDR) bfd_get_start_address (exec_bfd); + + remote_rdp_kill (); + remove_breakpoints (); + init_wait_for_inferior (); + + /* This gives us a chance to set up the command line */ + rdp_set_command_line (exec_file, allargs); + + inferior_ptid = pid_to_ptid (42); + insert_breakpoints (); /* Needed to get correct instruction in cache */ + + /* + ** RDP targets don't provide any facility to set the top of memory, + ** so we don't bother to look for MEMSIZE in the environment. + */ + + /* Let's go! */ + proceed (entry_point, TARGET_SIGNAL_DEFAULT, 0); +} + +/* Attach doesn't need to do anything */ +static void +remote_rdp_attach (char *args, int from_tty) +{ + return; +} + +/* Define the target subroutine names */ + +struct target_ops remote_rdp_ops; + +static void +init_remote_rdp_ops (void) +{ + remote_rdp_ops.to_shortname = "rdp"; + remote_rdp_ops.to_longname = "Remote Target using the RDProtocol"; + remote_rdp_ops.to_doc = "Use a remote ARM system which uses the ARM Remote Debugging Protocol"; + remote_rdp_ops.to_open = remote_rdp_open; + remote_rdp_ops.to_close = remote_rdp_close; + remote_rdp_ops.to_attach = remote_rdp_attach; + remote_rdp_ops.to_resume = remote_rdp_resume; + remote_rdp_ops.to_wait = remote_rdp_wait; + remote_rdp_ops.to_fetch_registers = remote_rdp_fetch_register; + remote_rdp_ops.to_store_registers = remote_rdp_store_register; + remote_rdp_ops.to_prepare_to_store = remote_rdp_prepare_to_store; + remote_rdp_ops.to_xfer_memory = remote_rdp_xfer_inferior_memory; + remote_rdp_ops.to_files_info = remote_rdp_files_info; + remote_rdp_ops.to_insert_breakpoint = remote_rdp_insert_breakpoint; + remote_rdp_ops.to_remove_breakpoint = remote_rdp_remove_breakpoint; + remote_rdp_ops.to_kill = remote_rdp_kill; + remote_rdp_ops.to_load = generic_load; + remote_rdp_ops.to_create_inferior = remote_rdp_create_inferior; + remote_rdp_ops.to_mourn_inferior = generic_mourn_inferior; + remote_rdp_ops.to_stratum = process_stratum; + remote_rdp_ops.to_has_all_memory = 1; + remote_rdp_ops.to_has_memory = 1; + remote_rdp_ops.to_has_stack = 1; + remote_rdp_ops.to_has_registers = 1; + remote_rdp_ops.to_has_execution = 1; + remote_rdp_ops.to_magic = OPS_MAGIC; +} + +extern initialize_file_ftype _initialize_remote_rdp; /* -Wmissing-prototypes */ + +void +_initialize_remote_rdp (void) +{ + init_remote_rdp_ops (); + add_target (&remote_rdp_ops); +} diff --git a/contrib/gdb/gdb/remote-sds.c b/contrib/gdb/gdb/remote-sds.c new file mode 100644 index 0000000..d74fd7d --- /dev/null +++ b/contrib/gdb/gdb/remote-sds.c @@ -0,0 +1,1130 @@ +/* Remote target communications for serial-line targets using SDS' protocol. + + Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2004 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. */ + +/* This interface was written by studying the behavior of the SDS + monitor on an ADS 821/860 board, and by consulting the + documentation of the monitor that is available on Motorola's web + site. -sts 8/13/97 */ + +#include "defs.h" +#include "gdb_string.h" +#include <fcntl.h> +#include "frame.h" +#include "inferior.h" +#include "bfd.h" +#include "symfile.h" +#include "target.h" +#include "gdbcmd.h" +#include "objfiles.h" +#include "gdb-stabs.h" +#include "gdbthread.h" +#include "gdbcore.h" +#include "regcache.h" + +#ifdef USG +#include <sys/types.h> +#endif + +#include <signal.h> +#include "serial.h" + +extern void _initialize_remote_sds (void); + +/* Declarations of local functions. */ + +static int sds_write_bytes (CORE_ADDR, char *, int); + +static int sds_read_bytes (CORE_ADDR, char *, int); + +static void sds_files_info (struct target_ops *ignore); + +static int sds_xfer_memory (CORE_ADDR, char *, int, int, + struct mem_attrib *, struct target_ops *); + +static void sds_prepare_to_store (void); + +static void sds_fetch_registers (int); + +static void sds_resume (ptid_t, int, enum target_signal); + +static int sds_start_remote (void *); + +static void sds_open (char *, int); + +static void sds_close (int); + +static void sds_store_registers (int); + +static void sds_mourn (void); + +static void sds_create_inferior (char *, char *, char **); + +static void sds_load (char *, int); + +static int getmessage (unsigned char *, int); + +static int putmessage (unsigned char *, int); + +static int sds_send (unsigned char *, int); + +static int readchar (int); + +static ptid_t sds_wait (ptid_t, struct target_waitstatus *); + +static void sds_kill (void); + +static int fromhex (int); + +static void sds_detach (char *, int); + +static void sds_interrupt (int); + +static void sds_interrupt_twice (int); + +static void interrupt_query (void); + +static int read_frame (char *); + +static int sds_insert_breakpoint (CORE_ADDR, char *); + +static int sds_remove_breakpoint (CORE_ADDR, char *); + +static void init_sds_ops (void); + +static void sds_command (char *args, int from_tty); + +/* Define the target operations vector. */ + +static struct target_ops sds_ops; + +/* This was 5 seconds, which is a long time to sit and wait. + Unless this is going though some terminal server or multiplexer or + other form of hairy serial connection, I would think 2 seconds would + be plenty. */ + +static int sds_timeout = 2; + +/* Descriptor for I/O to remote machine. Initialize it to NULL so + that sds_open knows that we don't have a file open when the program + starts. */ + +static struct serial *sds_desc = NULL; + +/* This limit comes from the monitor. */ + +#define PBUFSIZ 250 + +/* Maximum number of bytes to read/write at once. The value here + is chosen to fill up a packet (the headers account for the 32). */ +#define MAXBUFBYTES ((PBUFSIZ-32)/2) + +static int next_msg_id; + +static int just_started; + +static int message_pending; + + +/* Clean up connection to a remote debugger. */ + +static void +sds_close (int quitting) +{ + if (sds_desc) + serial_close (sds_desc); + sds_desc = NULL; +} + +/* Stub for catch_errors. */ + +static int +sds_start_remote (void *dummy) +{ + int c; + unsigned char buf[200]; + + immediate_quit++; /* Allow user to interrupt it */ + + /* Ack any packet which the remote side has already sent. */ + serial_write (sds_desc, "{#*\r\n", 5); + serial_write (sds_desc, "{#}\r\n", 5); + + while ((c = readchar (1)) >= 0) + printf_unfiltered ("%c", c); + printf_unfiltered ("\n"); + + next_msg_id = 251; + + buf[0] = 26; + sds_send (buf, 1); + + buf[0] = 0; + sds_send (buf, 1); + + immediate_quit--; + + start_remote (); /* Initialize gdb process mechanisms */ + return 1; +} + +/* Open a connection to a remote debugger. + NAME is the filename used for communication. */ + +static void +sds_open (char *name, int from_tty) +{ + if (name == 0) + error ("To open a remote debug connection, you need to specify what serial\n\ +device is attached to the remote system (e.g. /dev/ttya)."); + + target_preopen (from_tty); + + unpush_target (&sds_ops); + + sds_desc = serial_open (name); + if (!sds_desc) + perror_with_name (name); + + if (baud_rate != -1) + { + if (serial_setbaudrate (sds_desc, baud_rate)) + { + serial_close (sds_desc); + perror_with_name (name); + } + } + + + serial_raw (sds_desc); + + /* If there is something sitting in the buffer we might take it as a + response to a command, which would be bad. */ + serial_flush_input (sds_desc); + + if (from_tty) + { + puts_filtered ("Remote debugging using "); + puts_filtered (name); + puts_filtered ("\n"); + } + push_target (&sds_ops); /* Switch to using remote target now */ + + just_started = 1; + + /* Start the remote connection; if error (0), discard this target. + In particular, if the user quits, be sure to discard it (we'd be + in an inconsistent state otherwise). */ + if (!catch_errors (sds_start_remote, NULL, + "Couldn't establish connection to remote target\n", + RETURN_MASK_ALL)) + pop_target (); +} + +/* This takes a program previously attached to and detaches it. After + this is done, GDB can be used to debug some other program. We + better not have left any breakpoints in the target program or it'll + die when it hits one. */ + +static void +sds_detach (char *args, int from_tty) +{ + char buf[PBUFSIZ]; + + if (args) + error ("Argument given to \"detach\" when remotely debugging."); + +#if 0 + /* Tell the remote target to detach. */ + strcpy (buf, "D"); + sds_send (buf, 1); +#endif + + pop_target (); + if (from_tty) + puts_filtered ("Ending remote debugging.\n"); +} + +/* Convert hex digit A to a number. */ + +static int +fromhex (int a) +{ + if (a >= '0' && a <= '9') + return a - '0'; + else if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + else + error ("Reply contains invalid hex digit %d", a); +} + +static int +tob64 (unsigned char *inbuf, char *outbuf, int len) +{ + int i, sum; + char *p; + + if (len % 3 != 0) + error ("bad length"); + + p = outbuf; + for (i = 0; i < len; i += 3) + { + /* Collect the next three bytes into a number. */ + sum = ((long) *inbuf++) << 16; + sum |= ((long) *inbuf++) << 8; + sum |= ((long) *inbuf++); + + /* Spit out 4 6-bit encodings. */ + *p++ = ((sum >> 18) & 0x3f) + '0'; + *p++ = ((sum >> 12) & 0x3f) + '0'; + *p++ = ((sum >> 6) & 0x3f) + '0'; + *p++ = (sum & 0x3f) + '0'; + } + return (p - outbuf); +} + +static int +fromb64 (char *inbuf, char *outbuf, int len) +{ + int i, sum; + + if (len % 4 != 0) + error ("bad length"); + + for (i = 0; i < len; i += 4) + { + /* Collect 4 6-bit digits. */ + sum = (*inbuf++ - '0') << 18; + sum |= (*inbuf++ - '0') << 12; + sum |= (*inbuf++ - '0') << 6; + sum |= (*inbuf++ - '0'); + + /* Now take the resulting 24-bit number and get three bytes out + of it. */ + *outbuf++ = (sum >> 16) & 0xff; + *outbuf++ = (sum >> 8) & 0xff; + *outbuf++ = sum & 0xff; + } + + return (len / 4) * 3; +} + + +/* Tell the remote machine to resume. */ + +static enum target_signal last_sent_signal = TARGET_SIGNAL_0; +int last_sent_step; + +static void +sds_resume (ptid_t ptid, int step, enum target_signal siggnal) +{ + unsigned char buf[PBUFSIZ]; + + last_sent_signal = siggnal; + last_sent_step = step; + + buf[0] = (step ? 21 : 20); + buf[1] = 0; /* (should be signal?) */ + + sds_send (buf, 2); +} + +/* Send a message to target to halt it. Target will respond, and send + us a message pending notice. */ + +static void +sds_interrupt (int signo) +{ + unsigned char buf[PBUFSIZ]; + + /* If this doesn't work, try more severe steps. */ + signal (signo, sds_interrupt_twice); + + if (remote_debug) + fprintf_unfiltered (gdb_stdlog, "sds_interrupt called\n"); + + buf[0] = 25; + sds_send (buf, 1); +} + +static void (*ofunc) (); + +/* The user typed ^C twice. */ + +static void +sds_interrupt_twice (int signo) +{ + signal (signo, ofunc); + + interrupt_query (); + + signal (signo, sds_interrupt); +} + +/* Ask the user what to do when an interrupt is received. */ + +static void +interrupt_query (void) +{ + target_terminal_ours (); + + if (query ("Interrupted while waiting for the program.\n\ +Give up (and stop debugging it)? ")) + { + target_mourn_inferior (); + throw_exception (RETURN_QUIT); + } + + target_terminal_inferior (); +} + +/* If nonzero, ignore the next kill. */ +int kill_kludge; + +/* Wait until the remote machine stops, then return, storing status in + STATUS just as `wait' would. Returns "pid" (though it's not clear + what, if anything, that means in the case of this target). */ + +static ptid_t +sds_wait (ptid_t ptid, struct target_waitstatus *status) +{ + unsigned char buf[PBUFSIZ]; + int retlen; + + status->kind = TARGET_WAITKIND_EXITED; + status->value.integer = 0; + + ofunc = (void (*)()) signal (SIGINT, sds_interrupt); + + signal (SIGINT, ofunc); + + if (just_started) + { + just_started = 0; + status->kind = TARGET_WAITKIND_STOPPED; + return inferior_ptid; + } + + while (1) + { + getmessage (buf, 1); + + if (message_pending) + { + buf[0] = 26; + retlen = sds_send (buf, 1); + if (remote_debug) + { + fprintf_unfiltered (gdb_stdlog, "Signals: %02x%02x %02x %02x\n", + buf[0], buf[1], + buf[2], buf[3]); + } + message_pending = 0; + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_TRAP; + goto got_status; + } + } +got_status: + return inferior_ptid; +} + +static unsigned char sprs[16]; + +/* Read the remote registers into the block REGS. */ +/* Currently we just read all the registers, so we don't use regno. */ + +static void +sds_fetch_registers (int regno) +{ + unsigned char buf[PBUFSIZ]; + int i, retlen; + char *regs = alloca (DEPRECATED_REGISTER_BYTES); + + /* Unimplemented registers read as all bits zero. */ + memset (regs, 0, DEPRECATED_REGISTER_BYTES); + + buf[0] = 18; + buf[1] = 1; + buf[2] = 0; + retlen = sds_send (buf, 3); + + for (i = 0; i < 4 * 6; ++i) + regs[i + 4 * 32 + 8 * 32] = buf[i]; + for (i = 0; i < 4 * 4; ++i) + sprs[i] = buf[i + 4 * 7]; + + buf[0] = 18; + buf[1] = 2; + buf[2] = 0; + retlen = sds_send (buf, 3); + + for (i = 0; i < retlen; i++) + regs[i] = buf[i]; + + /* (should warn about reply too short) */ + + for (i = 0; i < NUM_REGS; i++) + supply_register (i, ®s[DEPRECATED_REGISTER_BYTE (i)]); +} + +/* Prepare to store registers. Since we may send them all, we have to + read out the ones we don't want to change first. */ + +static void +sds_prepare_to_store (void) +{ + /* Make sure the entire registers array is valid. */ + deprecated_read_register_bytes (0, (char *) NULL, DEPRECATED_REGISTER_BYTES); +} + +/* Store register REGNO, or all registers if REGNO == -1, from the contents + of REGISTERS. FIXME: ignores errors. */ + +static void +sds_store_registers (int regno) +{ + unsigned char *p, buf[PBUFSIZ]; + int i; + + /* Store all the special-purpose registers. */ + p = buf; + *p++ = 19; + *p++ = 1; + *p++ = 0; + *p++ = 0; + for (i = 0; i < 4 * 6; i++) + *p++ = deprecated_registers[i + 4 * 32 + 8 * 32]; + for (i = 0; i < 4 * 1; i++) + *p++ = 0; + for (i = 0; i < 4 * 4; i++) + *p++ = sprs[i]; + + sds_send (buf, p - buf); + + /* Store all the general-purpose registers. */ + p = buf; + *p++ = 19; + *p++ = 2; + *p++ = 0; + *p++ = 0; + for (i = 0; i < 4 * 32; i++) + *p++ = deprecated_registers[i]; + + sds_send (buf, p - buf); + +} + +/* Write memory data directly to the remote machine. This does not + inform the data cache; the data cache uses this. MEMADDR is the + address in the remote memory space. MYADDR is the address of the + buffer in our space. LEN is the number of bytes. + + Returns number of bytes transferred, or 0 for error. */ + +static int +sds_write_bytes (CORE_ADDR memaddr, char *myaddr, int len) +{ + int max_buf_size; /* Max size of packet output buffer */ + int origlen; + unsigned char buf[PBUFSIZ]; + int todo; + int i; + + /* Chop the transfer down if necessary */ + + max_buf_size = 150; + + origlen = len; + while (len > 0) + { + todo = min (len, max_buf_size); + + buf[0] = 13; + buf[1] = 0; + buf[2] = (int) (memaddr >> 24) & 0xff; + buf[3] = (int) (memaddr >> 16) & 0xff; + buf[4] = (int) (memaddr >> 8) & 0xff; + buf[5] = (int) (memaddr) & 0xff; + buf[6] = 1; + buf[7] = 0; + + for (i = 0; i < todo; i++) + buf[i + 8] = myaddr[i]; + + sds_send (buf, 8 + todo); + + /* (should look at result) */ + + myaddr += todo; + memaddr += todo; + len -= todo; + } + return origlen; +} + +/* Read memory data directly from the remote machine. This does not + use the data cache; the data cache uses this. MEMADDR is the + address in the remote memory space. MYADDR is the address of the + buffer in our space. LEN is the number of bytes. + + Returns number of bytes transferred, or 0 for error. */ + +static int +sds_read_bytes (CORE_ADDR memaddr, char *myaddr, int len) +{ + int max_buf_size; /* Max size of packet output buffer */ + int origlen, retlen; + unsigned char buf[PBUFSIZ]; + int todo; + int i; + + /* Chop the transfer down if necessary */ + + max_buf_size = 150; + + origlen = len; + while (len > 0) + { + todo = min (len, max_buf_size); + + buf[0] = 12; + buf[1] = 0; + buf[2] = (int) (memaddr >> 24) & 0xff; + buf[3] = (int) (memaddr >> 16) & 0xff; + buf[4] = (int) (memaddr >> 8) & 0xff; + buf[5] = (int) (memaddr) & 0xff; + buf[6] = (int) (todo >> 8) & 0xff; + buf[7] = (int) (todo) & 0xff; + buf[8] = 1; + + retlen = sds_send (buf, 9); + + if (retlen - 2 != todo) + { + return 0; + } + + /* Reply describes memory byte by byte. */ + + for (i = 0; i < todo; i++) + myaddr[i] = buf[i + 2]; + + myaddr += todo; + memaddr += todo; + len -= todo; + } + + return origlen; +} + +/* Read or write LEN bytes from inferior memory at MEMADDR, + transferring to or from debugger address MYADDR. Write to inferior + if SHOULD_WRITE is nonzero. Returns length of data written or + read; 0 for error. TARGET is unused. */ + +static int +sds_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int should_write, + struct mem_attrib *attrib, struct target_ops *target) +{ + int res; + + if (should_write) + res = sds_write_bytes (memaddr, myaddr, len); + else + res = sds_read_bytes (memaddr, myaddr, len); + + return res; +} + + +static void +sds_files_info (struct target_ops *ignore) +{ + puts_filtered ("Debugging over a serial connection, using SDS protocol.\n"); +} + +/* Stuff for dealing with the packets which are part of this protocol. + See comment at top of file for details. */ + +/* Read a single character from the remote end, masking it down to 7 bits. */ + +static int +readchar (int timeout) +{ + int ch; + + ch = serial_readchar (sds_desc, timeout); + + if (remote_debug > 1 && ch >= 0) + fprintf_unfiltered (gdb_stdlog, "%c(%x)", ch, ch); + + switch (ch) + { + case SERIAL_EOF: + error ("Remote connection closed"); + case SERIAL_ERROR: + perror_with_name ("Remote communication error"); + case SERIAL_TIMEOUT: + return ch; + default: + return ch & 0x7f; + } +} + +/* An SDS-style checksum is a sum of the bytes modulo 253. (Presumably + because 253, 254, and 255 are special flags in the protocol.) */ + +static int +compute_checksum (int csum, char *buf, int len) +{ + int i; + + for (i = 0; i < len; ++i) + csum += (unsigned char) buf[i]; + + csum %= 253; + return csum; +} + +/* Send the command in BUF to the remote machine, and read the reply + into BUF also. */ + +static int +sds_send (unsigned char *buf, int len) +{ + putmessage (buf, len); + + return getmessage (buf, 0); +} + +/* Send a message to the remote machine. */ + +static int +putmessage (unsigned char *buf, int len) +{ + int i, enclen; + unsigned char csum = 0; + char buf2[PBUFSIZ], buf3[PBUFSIZ]; + unsigned char header[3]; + char *p; + + /* Copy the packet into buffer BUF2, encapsulating it + and giving it a checksum. */ + + if (len > 170) /* Prosanity check */ + internal_error (__FILE__, __LINE__, "failed internal consistency check"); + + if (remote_debug) + { + fprintf_unfiltered (gdb_stdlog, "Message to send: \""); + for (i = 0; i < len; ++i) + fprintf_unfiltered (gdb_stdlog, "%02x", buf[i]); + fprintf_unfiltered (gdb_stdlog, "\"\n"); + } + + p = buf2; + *p++ = '$'; + + if (len % 3 != 0) + { + buf[len] = '\0'; + buf[len + 1] = '\0'; + } + + header[1] = next_msg_id; + + header[2] = len; + + csum = compute_checksum (csum, buf, len); + csum = compute_checksum (csum, header + 1, 2); + + header[0] = csum; + + tob64 (header, p, 3); + p += 4; + enclen = tob64 (buf, buf3, ((len + 2) / 3) * 3); + + for (i = 0; i < enclen; ++i) + *p++ = buf3[i]; + *p++ = '\r'; + *p++ = '\n'; + + next_msg_id = (next_msg_id + 3) % 245; + + /* Send it over and over until we get a positive ack. */ + + while (1) + { + if (remote_debug) + { + *p = '\0'; + fprintf_unfiltered (gdb_stdlog, "Sending encoded: \"%s\"", buf2); + fprintf_unfiltered (gdb_stdlog, + " (Checksum %d, id %d, length %d)\n", + header[0], header[1], header[2]); + gdb_flush (gdb_stdlog); + } + if (serial_write (sds_desc, buf2, p - buf2)) + perror_with_name ("putmessage: write failed"); + + return 1; + } +} + +/* Come here after finding the start of the frame. Collect the rest + into BUF. Returns 0 on any error, 1 on success. */ + +static int +read_frame (char *buf) +{ + char *bp; + int c; + + bp = buf; + + while (1) + { + c = readchar (sds_timeout); + + switch (c) + { + case SERIAL_TIMEOUT: + if (remote_debug) + fputs_filtered ("Timeout in mid-message, retrying\n", gdb_stdlog); + return 0; + case '$': + if (remote_debug) + fputs_filtered ("Saw new packet start in middle of old one\n", + gdb_stdlog); + return 0; /* Start a new packet, count retries */ + case '\r': + break; + + case '\n': + { + *bp = '\000'; + if (remote_debug) + fprintf_unfiltered (gdb_stdlog, "Received encoded: \"%s\"\n", + buf); + return 1; + } + + default: + if (bp < buf + PBUFSIZ - 1) + { + *bp++ = c; + continue; + } + + *bp = '\0'; + puts_filtered ("Message too long: "); + puts_filtered (buf); + puts_filtered ("\n"); + + return 0; + } + } +} + +/* Read a packet from the remote machine, with error checking, + and store it in BUF. BUF is expected to be of size PBUFSIZ. + If FOREVER, wait forever rather than timing out; this is used + while the target is executing user code. */ + +static int +getmessage (unsigned char *buf, int forever) +{ + int c, c2, c3; + int tries; + int timeout; + int val, i, len, csum; + unsigned char header[3]; + unsigned char inbuf[500]; + + strcpy (buf, "timeout"); + + if (forever) + { + timeout = watchdog > 0 ? watchdog : -1; + } + + else + timeout = sds_timeout; + +#define MAX_TRIES 3 + + for (tries = 1; tries <= MAX_TRIES; tries++) + { + /* This can loop forever if the remote side sends us characters + continuously, but if it pauses, we'll get a zero from readchar + because of timeout. Then we'll count that as a retry. */ + + /* Note that we will only wait forever prior to the start of a packet. + After that, we expect characters to arrive at a brisk pace. They + should show up within sds_timeout intervals. */ + + do + { + c = readchar (timeout); + + if (c == SERIAL_TIMEOUT) + { + if (forever) /* Watchdog went off. Kill the target. */ + { + target_mourn_inferior (); + error ("Watchdog has expired. Target detached.\n"); + } + if (remote_debug) + fputs_filtered ("Timed out.\n", gdb_stdlog); + goto retry; + } + } + while (c != '$' && c != '{'); + + /* We might have seen a "trigraph", a sequence of three characters + that indicate various sorts of communication state. */ + + if (c == '{') + { + /* Read the other two chars of the trigraph. */ + c2 = readchar (timeout); + c3 = readchar (timeout); + if (remote_debug) + fprintf_unfiltered (gdb_stdlog, "Trigraph %c%c%c received\n", + c, c2, c3); + if (c3 == '+') + { + message_pending = 1; + return 0; /*???? */ + } + continue; + } + + val = read_frame (inbuf); + + if (val == 1) + { + fromb64 (inbuf, header, 4); + /* (should check out other bits) */ + fromb64 (inbuf + 4, buf, strlen (inbuf) - 4); + + len = header[2]; + + csum = 0; + csum = compute_checksum (csum, buf, len); + csum = compute_checksum (csum, header + 1, 2); + + if (csum != header[0]) + fprintf_unfiltered (gdb_stderr, + "Checksum mismatch: computed %d, received %d\n", + csum, header[0]); + + if (header[2] == 0xff) + fprintf_unfiltered (gdb_stderr, "Requesting resend...\n"); + + if (remote_debug) + { + fprintf_unfiltered (gdb_stdlog, + "... (Got checksum %d, id %d, length %d)\n", + header[0], header[1], header[2]); + fprintf_unfiltered (gdb_stdlog, "Message received: \""); + for (i = 0; i < len; ++i) + { + fprintf_unfiltered (gdb_stdlog, "%02x", (unsigned char) buf[i]); + } + fprintf_unfiltered (gdb_stdlog, "\"\n"); + } + + /* no ack required? */ + return len; + } + + /* Try the whole thing again. */ + retry: + /* need to do something here */ + ; + } + + /* We have tried hard enough, and just can't receive the packet. Give up. */ + + printf_unfiltered ("Ignoring packet error, continuing...\n"); + return 0; +} + +static void +sds_kill (void) +{ + /* Don't try to do anything to the target. */ +} + +static void +sds_mourn (void) +{ + unpush_target (&sds_ops); + generic_mourn_inferior (); +} + +static void +sds_create_inferior (char *exec_file, char *args, char **env) +{ + inferior_ptid = pid_to_ptid (42000); + + /* Clean up from the last time we were running. */ + clear_proceed_status (); + + /* Let the remote process run. */ + proceed (bfd_get_start_address (exec_bfd), TARGET_SIGNAL_0, 0); +} + +static void +sds_load (char *filename, int from_tty) +{ + generic_load (filename, from_tty); + + inferior_ptid = null_ptid; +} + +/* The SDS monitor has commands for breakpoint insertion, although it + it doesn't actually manage the breakpoints, it just returns the + replaced instruction back to the debugger. */ + +static int +sds_insert_breakpoint (CORE_ADDR addr, char *contents_cache) +{ + int i, retlen; + unsigned char *p, buf[PBUFSIZ]; + + p = buf; + *p++ = 16; + *p++ = 0; + *p++ = (int) (addr >> 24) & 0xff; + *p++ = (int) (addr >> 16) & 0xff; + *p++ = (int) (addr >> 8) & 0xff; + *p++ = (int) (addr) & 0xff; + + retlen = sds_send (buf, p - buf); + + for (i = 0; i < 4; ++i) + contents_cache[i] = buf[i + 2]; + + return 0; +} + +static int +sds_remove_breakpoint (CORE_ADDR addr, char *contents_cache) +{ + int i, retlen; + unsigned char *p, buf[PBUFSIZ]; + + p = buf; + *p++ = 17; + *p++ = 0; + *p++ = (int) (addr >> 24) & 0xff; + *p++ = (int) (addr >> 16) & 0xff; + *p++ = (int) (addr >> 8) & 0xff; + *p++ = (int) (addr) & 0xff; + for (i = 0; i < 4; ++i) + *p++ = contents_cache[i]; + + retlen = sds_send (buf, p - buf); + + return 0; +} + +static void +init_sds_ops (void) +{ + sds_ops.to_shortname = "sds"; + sds_ops.to_longname = "Remote serial target with SDS protocol"; + sds_ops.to_doc = "Use a remote computer via a serial line; using the SDS protocol.\n\ +Specify the serial device it is connected to (e.g. /dev/ttya)."; + sds_ops.to_open = sds_open; + sds_ops.to_close = sds_close; + sds_ops.to_detach = sds_detach; + sds_ops.to_resume = sds_resume; + sds_ops.to_wait = sds_wait; + sds_ops.to_fetch_registers = sds_fetch_registers; + sds_ops.to_store_registers = sds_store_registers; + sds_ops.to_prepare_to_store = sds_prepare_to_store; + sds_ops.to_xfer_memory = sds_xfer_memory; + sds_ops.to_files_info = sds_files_info; + sds_ops.to_insert_breakpoint = sds_insert_breakpoint; + sds_ops.to_remove_breakpoint = sds_remove_breakpoint; + sds_ops.to_kill = sds_kill; + sds_ops.to_load = sds_load; + sds_ops.to_create_inferior = sds_create_inferior; + sds_ops.to_mourn_inferior = sds_mourn; + sds_ops.to_stratum = process_stratum; + sds_ops.to_has_all_memory = 1; + sds_ops.to_has_memory = 1; + sds_ops.to_has_stack = 1; + sds_ops.to_has_registers = 1; + sds_ops.to_has_execution = 1; + sds_ops.to_magic = OPS_MAGIC; +} + +/* Put a command string, in args, out to the monitor and display the + reply message. */ + +static void +sds_command (char *args, int from_tty) +{ + char *p; + int i, len, retlen; + unsigned char buf[1000]; + + /* Convert hexadecimal chars into a byte buffer. */ + p = args; + len = 0; + while (*p != '\0') + { + buf[len++] = fromhex (p[0]) * 16 + fromhex (p[1]); + if (p[1] == '\0') + break; + p += 2; + } + + retlen = sds_send (buf, len); + + printf_filtered ("Reply is "); + for (i = 0; i < retlen; ++i) + { + printf_filtered ("%02x", buf[i]); + } + printf_filtered ("\n"); +} + +void +_initialize_remote_sds (void) +{ + init_sds_ops (); + add_target (&sds_ops); + + add_show_from_set (add_set_cmd ("sdstimeout", no_class, + var_integer, (char *) &sds_timeout, + "Set timeout value for sds read.\n", &setlist), + &showlist); + + add_com ("sds", class_obscure, sds_command, + "Send a command to the SDS monitor."); +} diff --git a/contrib/gdb/gdb/remote-sim.c b/contrib/gdb/gdb/remote-sim.c new file mode 100644 index 0000000..9b0b3fd --- /dev/null +++ b/contrib/gdb/gdb/remote-sim.c @@ -0,0 +1,900 @@ +/* Generic remote debugging interface for simulators. + + Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + 2002, 2004 Free Software Foundation, Inc. + + Contributed by Cygnus Support. + Steve Chamberlain (sac@cygnus.com). + + 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 "defs.h" +#include "inferior.h" +#include "value.h" +#include "gdb_string.h" +#include <ctype.h> +#include <fcntl.h> +#include <signal.h> +#include <setjmp.h> +#include <errno.h> +#include "terminal.h" +#include "target.h" +#include "gdbcore.h" +#include "gdb/callback.h" +#include "gdb/remote-sim.h" +#include "remote-utils.h" +#include "command.h" +#include "regcache.h" +#include "gdb_assert.h" +#include "sim-regno.h" +#include "arch-utils.h" + +/* Prototypes */ + +extern void _initialize_remote_sim (void); + +extern int (*ui_loop_hook) (int signo); + +static void dump_mem (char *buf, int len); + +static void init_callbacks (void); + +static void end_callbacks (void); + +static int gdb_os_write_stdout (host_callback *, const char *, int); + +static void gdb_os_flush_stdout (host_callback *); + +static int gdb_os_write_stderr (host_callback *, const char *, int); + +static void gdb_os_flush_stderr (host_callback *); + +static int gdb_os_poll_quit (host_callback *); + +/* printf_filtered is depreciated */ +static void gdb_os_printf_filtered (host_callback *, const char *, ...); + +static void gdb_os_vprintf_filtered (host_callback *, const char *, va_list); + +static void gdb_os_evprintf_filtered (host_callback *, const char *, va_list); + +static void gdb_os_error (host_callback *, const char *, ...); + +static void gdbsim_fetch_register (int regno); + +static void gdbsim_store_register (int regno); + +static void gdbsim_kill (void); + +static void gdbsim_load (char *prog, int fromtty); + +static void gdbsim_create_inferior (char *exec_file, char *args, char **env); + +static void gdbsim_open (char *args, int from_tty); + +static void gdbsim_close (int quitting); + +static void gdbsim_detach (char *args, int from_tty); + +static void gdbsim_resume (ptid_t ptid, int step, enum target_signal siggnal); + +static ptid_t gdbsim_wait (ptid_t ptid, struct target_waitstatus *status); + +static void gdbsim_prepare_to_store (void); + +static int gdbsim_xfer_inferior_memory (CORE_ADDR memaddr, char *myaddr, + int len, int write, + struct mem_attrib *attrib, + struct target_ops *target); + +static void gdbsim_files_info (struct target_ops *target); + +static void gdbsim_mourn_inferior (void); + +static void gdbsim_stop (void); + +void simulator_command (char *args, int from_tty); + +/* Naming convention: + + sim_* are the interface to the simulator (see remote-sim.h). + gdbsim_* are stuff which is internal to gdb. */ + +/* Forward data declarations */ +extern struct target_ops gdbsim_ops; + +static int program_loaded = 0; + +/* We must keep track of whether the simulator has been opened or not because + GDB can call a target's close routine twice, but sim_close doesn't allow + this. We also need to record the result of sim_open so we can pass it + back to the other sim_foo routines. */ +static SIM_DESC gdbsim_desc = 0; + +static void +dump_mem (char *buf, int len) +{ + if (len <= 8) + { + if (len == 8 || len == 4) + { + long l[2]; + memcpy (l, buf, len); + printf_filtered ("\t0x%lx", l[0]); + if (len == 8) + printf_filtered (" 0x%lx", l[1]); + printf_filtered ("\n"); + } + else + { + int i; + printf_filtered ("\t"); + for (i = 0; i < len; i++) + printf_filtered ("0x%x ", buf[i]); + printf_filtered ("\n"); + } + } +} + +static host_callback gdb_callback; +static int callbacks_initialized = 0; + +/* Initialize gdb_callback. */ + +static void +init_callbacks (void) +{ + if (!callbacks_initialized) + { + gdb_callback = default_callback; + gdb_callback.init (&gdb_callback); + gdb_callback.write_stdout = gdb_os_write_stdout; + gdb_callback.flush_stdout = gdb_os_flush_stdout; + gdb_callback.write_stderr = gdb_os_write_stderr; + gdb_callback.flush_stderr = gdb_os_flush_stderr; + gdb_callback.printf_filtered = gdb_os_printf_filtered; + gdb_callback.vprintf_filtered = gdb_os_vprintf_filtered; + gdb_callback.evprintf_filtered = gdb_os_evprintf_filtered; + gdb_callback.error = gdb_os_error; + gdb_callback.poll_quit = gdb_os_poll_quit; + gdb_callback.magic = HOST_CALLBACK_MAGIC; + callbacks_initialized = 1; + } +} + +/* Release callbacks (free resources used by them). */ + +static void +end_callbacks (void) +{ + if (callbacks_initialized) + { + gdb_callback.shutdown (&gdb_callback); + callbacks_initialized = 0; + } +} + +/* GDB version of os_write_stdout callback. */ + +static int +gdb_os_write_stdout (host_callback *p, const char *buf, int len) +{ + int i; + char b[2]; + + ui_file_write (gdb_stdtarg, buf, len); + return len; +} + +/* GDB version of os_flush_stdout callback. */ + +static void +gdb_os_flush_stdout (host_callback *p) +{ + gdb_flush (gdb_stdtarg); +} + +/* GDB version of os_write_stderr callback. */ + +static int +gdb_os_write_stderr (host_callback *p, const char *buf, int len) +{ + int i; + char b[2]; + + for (i = 0; i < len; i++) + { + b[0] = buf[i]; + b[1] = 0; + fputs_unfiltered (b, gdb_stdtargerr); + } + return len; +} + +/* GDB version of os_flush_stderr callback. */ + +static void +gdb_os_flush_stderr (host_callback *p) +{ + gdb_flush (gdb_stdtargerr); +} + +/* GDB version of printf_filtered callback. */ + +static void +gdb_os_printf_filtered (host_callback * p, const char *format,...) +{ + va_list args; + va_start (args, format); + + vfprintf_filtered (gdb_stdout, format, args); + + va_end (args); +} + +/* GDB version of error vprintf_filtered. */ + +static void +gdb_os_vprintf_filtered (host_callback * p, const char *format, va_list ap) +{ + vfprintf_filtered (gdb_stdout, format, ap); +} + +/* GDB version of error evprintf_filtered. */ + +static void +gdb_os_evprintf_filtered (host_callback * p, const char *format, va_list ap) +{ + vfprintf_filtered (gdb_stderr, format, ap); +} + +/* GDB version of error callback. */ + +static void +gdb_os_error (host_callback * p, const char *format,...) +{ + if (error_hook) + (*error_hook) (); + else + { + va_list args; + va_start (args, format); + verror (format, args); + va_end (args); + } +} + +int +one2one_register_sim_regno (int regnum) +{ + /* Only makes sense to supply raw registers. */ + gdb_assert (regnum >= 0 && regnum < NUM_REGS); + return regnum; +} + +static void +gdbsim_fetch_register (int regno) +{ + if (regno == -1) + { + for (regno = 0; regno < NUM_REGS; regno++) + gdbsim_fetch_register (regno); + return; + } + + switch (REGISTER_SIM_REGNO (regno)) + { + case LEGACY_SIM_REGNO_IGNORE: + break; + case SIM_REGNO_DOES_NOT_EXIST: + { + /* For moment treat a `does not exist' register the same way + as an ``unavailable'' register. */ + char buf[MAX_REGISTER_SIZE]; + int nr_bytes; + memset (buf, 0, MAX_REGISTER_SIZE); + supply_register (regno, buf); + set_register_cached (regno, -1); + break; + } + default: + { + static int warn_user = 1; + char buf[MAX_REGISTER_SIZE]; + int nr_bytes; + gdb_assert (regno >= 0 && regno < NUM_REGS); + memset (buf, 0, MAX_REGISTER_SIZE); + nr_bytes = sim_fetch_register (gdbsim_desc, + REGISTER_SIM_REGNO (regno), + buf, DEPRECATED_REGISTER_RAW_SIZE (regno)); + if (nr_bytes > 0 && nr_bytes != DEPRECATED_REGISTER_RAW_SIZE (regno) && warn_user) + { + fprintf_unfiltered (gdb_stderr, + "Size of register %s (%d/%d) incorrect (%d instead of %d))", + REGISTER_NAME (regno), + regno, REGISTER_SIM_REGNO (regno), + nr_bytes, DEPRECATED_REGISTER_RAW_SIZE (regno)); + warn_user = 0; + } + /* FIXME: cagney/2002-05-27: Should check `nr_bytes == 0' + indicating that GDB and the SIM have different ideas about + which registers are fetchable. */ + /* Else if (nr_bytes < 0): an old simulator, that doesn't + think to return the register size. Just assume all is ok. */ + supply_register (regno, buf); + if (sr_get_debug ()) + { + printf_filtered ("gdbsim_fetch_register: %d", regno); + /* FIXME: We could print something more intelligible. */ + dump_mem (buf, DEPRECATED_REGISTER_RAW_SIZE (regno)); + } + break; + } + } +} + + +static void +gdbsim_store_register (int regno) +{ + if (regno == -1) + { + for (regno = 0; regno < NUM_REGS; regno++) + gdbsim_store_register (regno); + return; + } + else if (REGISTER_SIM_REGNO (regno) >= 0) + { + char tmp[MAX_REGISTER_SIZE]; + int nr_bytes; + deprecated_read_register_gen (regno, tmp); + nr_bytes = sim_store_register (gdbsim_desc, + REGISTER_SIM_REGNO (regno), + tmp, DEPRECATED_REGISTER_RAW_SIZE (regno)); + if (nr_bytes > 0 && nr_bytes != DEPRECATED_REGISTER_RAW_SIZE (regno)) + internal_error (__FILE__, __LINE__, + "Register size different to expected"); + /* FIXME: cagney/2002-05-27: Should check `nr_bytes == 0' + indicating that GDB and the SIM have different ideas about + which registers are fetchable. */ + if (sr_get_debug ()) + { + printf_filtered ("gdbsim_store_register: %d", regno); + /* FIXME: We could print something more intelligible. */ + dump_mem (tmp, DEPRECATED_REGISTER_RAW_SIZE (regno)); + } + } +} + +/* Kill the running program. This may involve closing any open files + and releasing other resources acquired by the simulated program. */ + +static void +gdbsim_kill (void) +{ + if (sr_get_debug ()) + printf_filtered ("gdbsim_kill\n"); + + /* There is no need to `kill' running simulator - the simulator is + not running */ + inferior_ptid = null_ptid; +} + +/* Load an executable file into the target process. This is expected to + not only bring new code into the target process, but also to update + GDB's symbol tables to match. */ + +static void +gdbsim_load (char *prog, int fromtty) +{ + if (sr_get_debug ()) + printf_filtered ("gdbsim_load: prog \"%s\"\n", prog); + + inferior_ptid = null_ptid; + + /* FIXME: We will print two messages on error. + Need error to either not print anything if passed NULL or need + another routine that doesn't take any arguments. */ + if (sim_load (gdbsim_desc, prog, NULL, fromtty) == SIM_RC_FAIL) + error ("unable to load program"); + + /* FIXME: If a load command should reset the targets registers then + a call to sim_create_inferior() should go here. */ + + program_loaded = 1; +} + + +/* Start an inferior process and set inferior_ptid to its pid. + EXEC_FILE is the file to run. + ARGS is a string containing the arguments to the program. + ENV is the environment vector to pass. Errors reported with error(). + On VxWorks and various standalone systems, we ignore exec_file. */ +/* This is called not only when we first attach, but also when the + user types "run" after having attached. */ + +static void +gdbsim_create_inferior (char *exec_file, char *args, char **env) +{ + int len; + char *arg_buf, **argv; + + if (exec_file == 0 || exec_bfd == 0) + warning ("No executable file specified."); + if (!program_loaded) + warning ("No program loaded."); + + if (sr_get_debug ()) + printf_filtered ("gdbsim_create_inferior: exec_file \"%s\", args \"%s\"\n", + (exec_file ? exec_file : "(NULL)"), + args); + + gdbsim_kill (); + remove_breakpoints (); + init_wait_for_inferior (); + + if (exec_file != NULL) + { + len = strlen (exec_file) + 1 + strlen (args) + 1 + /*slop */ 10; + arg_buf = (char *) alloca (len); + arg_buf[0] = '\0'; + strcat (arg_buf, exec_file); + strcat (arg_buf, " "); + strcat (arg_buf, args); + argv = buildargv (arg_buf); + make_cleanup_freeargv (argv); + } + else + argv = NULL; + sim_create_inferior (gdbsim_desc, exec_bfd, argv, env); + + inferior_ptid = pid_to_ptid (42); + insert_breakpoints (); /* Needed to get correct instruction in cache */ + + clear_proceed_status (); + + /* NB: Entry point already set by sim_create_inferior. */ + proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0); +} + +/* The open routine takes the rest of the parameters from the command, + and (if successful) pushes a new target onto the stack. + Targets should supply this routine, if only to provide an error message. */ +/* Called when selecting the simulator. EG: (gdb) target sim name. */ + +static void +gdbsim_open (char *args, int from_tty) +{ + int len; + char *arg_buf; + char **argv; + + if (sr_get_debug ()) + printf_filtered ("gdbsim_open: args \"%s\"\n", args ? args : "(null)"); + + /* Remove current simulator if one exists. Only do this if the simulator + has been opened because sim_close requires it. + This is important because the call to push_target below will cause + sim_close to be called if the simulator is already open, but push_target + is called after sim_open! We can't move the call to push_target before + the call to sim_open because sim_open may invoke `error'. */ + if (gdbsim_desc != NULL) + unpush_target (&gdbsim_ops); + + len = (7 + 1 /* gdbsim */ + + strlen (" -E little") + + strlen (" --architecture=xxxxxxxxxx") + + (args ? strlen (args) : 0) + + 50) /* slack */ ; + arg_buf = (char *) alloca (len); + strcpy (arg_buf, "gdbsim"); /* 7 */ + /* Specify the byte order for the target when it is both selectable + and explicitly specified by the user (not auto detected). */ + switch (selected_byte_order ()) + { + case BFD_ENDIAN_BIG: + strcat (arg_buf, " -E big"); + break; + case BFD_ENDIAN_LITTLE: + strcat (arg_buf, " -E little"); + break; + case BFD_ENDIAN_UNKNOWN: + break; + } + /* Specify the architecture of the target when it has been + explicitly specified */ + if (selected_architecture_name () != NULL) + { + strcat (arg_buf, " --architecture="); + strcat (arg_buf, selected_architecture_name ()); + } + /* finally, any explicit args */ + if (args) + { + strcat (arg_buf, " "); /* 1 */ + strcat (arg_buf, args); + } + argv = buildargv (arg_buf); + if (argv == NULL) + error ("Insufficient memory available to allocate simulator arg list."); + make_cleanup_freeargv (argv); + + init_callbacks (); + gdbsim_desc = sim_open (SIM_OPEN_DEBUG, &gdb_callback, exec_bfd, argv); + + if (gdbsim_desc == 0) + error ("unable to create simulator instance"); + + push_target (&gdbsim_ops); + target_fetch_registers (-1); + printf_filtered ("Connected to the simulator.\n"); +} + +/* Does whatever cleanup is required for a target that we are no longer + going to be calling. Argument says whether we are quitting gdb and + should not get hung in case of errors, or whether we want a clean + termination even if it takes a while. This routine is automatically + always called just before a routine is popped off the target stack. + Closing file descriptors and freeing memory are typical things it should + do. */ +/* Close out all files and local state before this target loses control. */ + +static void +gdbsim_close (int quitting) +{ + if (sr_get_debug ()) + printf_filtered ("gdbsim_close: quitting %d\n", quitting); + + program_loaded = 0; + + if (gdbsim_desc != NULL) + { + sim_close (gdbsim_desc, quitting); + gdbsim_desc = NULL; + } + + end_callbacks (); + generic_mourn_inferior (); +} + +/* Takes a program previously attached to and detaches it. + The program may resume execution (some targets do, some don't) and will + no longer stop on signals, etc. We better not have left any breakpoints + in the program or it'll die when it hits one. ARGS is arguments + typed by the user (e.g. a signal to send the process). FROM_TTY + says whether to be verbose or not. */ +/* Terminate the open connection to the remote debugger. + Use this when you want to detach and do something else with your gdb. */ + +static void +gdbsim_detach (char *args, int from_tty) +{ + if (sr_get_debug ()) + printf_filtered ("gdbsim_detach: args \"%s\"\n", args); + + pop_target (); /* calls gdbsim_close to do the real work */ + if (from_tty) + printf_filtered ("Ending simulator %s debugging\n", target_shortname); +} + +/* Resume execution of the target process. STEP says whether to single-step + or to run free; SIGGNAL is the signal value (e.g. SIGINT) to be given + to the target, or zero for no signal. */ + +static enum target_signal resume_siggnal; +static int resume_step; + +static void +gdbsim_resume (ptid_t ptid, int step, enum target_signal siggnal) +{ + if (PIDGET (inferior_ptid) != 42) + error ("The program is not being run."); + + if (sr_get_debug ()) + printf_filtered ("gdbsim_resume: step %d, signal %d\n", step, siggnal); + + resume_siggnal = siggnal; + resume_step = step; +} + +/* Notify the simulator of an asynchronous request to stop. + + The simulator shall ensure that the stop request is eventually + delivered to the simulator. If the call is made while the + simulator is not running then the stop request is processed when + the simulator is next resumed. + + For simulators that do not support this operation, just abort */ + +static void +gdbsim_stop (void) +{ + if (!sim_stop (gdbsim_desc)) + { + quit (); + } +} + +/* GDB version of os_poll_quit callback. + Taken from gdb/util.c - should be in a library */ + +static int +gdb_os_poll_quit (host_callback *p) +{ + if (ui_loop_hook != NULL) + ui_loop_hook (0); + + if (quit_flag) /* gdb's idea of quit */ + { + quit_flag = 0; /* we've stolen it */ + return 1; + } + else if (immediate_quit) + { + return 1; + } + return 0; +} + +/* Wait for inferior process to do something. Return pid of child, + or -1 in case of error; store status through argument pointer STATUS, + just as `wait' would. */ + +static void +gdbsim_cntrl_c (int signo) +{ + gdbsim_stop (); +} + +static ptid_t +gdbsim_wait (ptid_t ptid, struct target_waitstatus *status) +{ + static RETSIGTYPE (*prev_sigint) (); + int sigrc = 0; + enum sim_stop reason = sim_running; + + if (sr_get_debug ()) + printf_filtered ("gdbsim_wait\n"); + +#if defined (HAVE_SIGACTION) && defined (SA_RESTART) + { + struct sigaction sa, osa; + sa.sa_handler = gdbsim_cntrl_c; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + sigaction (SIGINT, &sa, &osa); + prev_sigint = osa.sa_handler; + } +#else + prev_sigint = signal (SIGINT, gdbsim_cntrl_c); +#endif + sim_resume (gdbsim_desc, resume_step, + target_signal_to_host (resume_siggnal)); + signal (SIGINT, prev_sigint); + resume_step = 0; + + sim_stop_reason (gdbsim_desc, &reason, &sigrc); + + switch (reason) + { + case sim_exited: + status->kind = TARGET_WAITKIND_EXITED; + status->value.integer = sigrc; + break; + case sim_stopped: + switch (sigrc) + { + case SIGABRT: + quit (); + break; + case SIGINT: + case SIGTRAP: + default: + status->kind = TARGET_WAITKIND_STOPPED; + /* The signal in sigrc is a host signal. That probably + should be fixed. */ + status->value.sig = target_signal_from_host (sigrc); + break; + } + break; + case sim_signalled: + status->kind = TARGET_WAITKIND_SIGNALLED; + /* The signal in sigrc is a host signal. That probably + should be fixed. */ + status->value.sig = target_signal_from_host (sigrc); + break; + case sim_running: + case sim_polling: + /* FIXME: Is this correct? */ + break; + } + + return inferior_ptid; +} + +/* Get ready to modify the registers array. On machines which store + individual registers, this doesn't need to do anything. On machines + which store all the registers in one fell swoop, this makes sure + that registers contains all the registers from the program being + debugged. */ + +static void +gdbsim_prepare_to_store (void) +{ + /* Do nothing, since we can store individual regs */ +} + +/* Transfer LEN bytes between GDB address MYADDR and target address + MEMADDR. If WRITE is non-zero, transfer them to the target, + otherwise transfer them from the target. TARGET is unused. + + Returns the number of bytes transferred. */ + +static int +gdbsim_xfer_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len, + int write, struct mem_attrib *attrib, + struct target_ops *target) +{ + if (!program_loaded) + error ("No program loaded."); + + if (sr_get_debug ()) + { + /* FIXME: Send to something other than STDOUT? */ + printf_filtered ("gdbsim_xfer_inferior_memory: myaddr 0x"); + gdb_print_host_address (myaddr, gdb_stdout); + printf_filtered (", memaddr 0x%s, len %d, write %d\n", + paddr_nz (memaddr), len, write); + if (sr_get_debug () && write) + dump_mem (myaddr, len); + } + + if (write) + { + len = sim_write (gdbsim_desc, memaddr, myaddr, len); + } + else + { + len = sim_read (gdbsim_desc, memaddr, myaddr, len); + if (sr_get_debug () && len > 0) + dump_mem (myaddr, len); + } + return len; +} + +static void +gdbsim_files_info (struct target_ops *target) +{ + char *file = "nothing"; + + if (exec_bfd) + file = bfd_get_filename (exec_bfd); + + if (sr_get_debug ()) + printf_filtered ("gdbsim_files_info: file \"%s\"\n", file); + + if (exec_bfd) + { + printf_filtered ("\tAttached to %s running program %s\n", + target_shortname, file); + sim_info (gdbsim_desc, 0); + } +} + +/* Clear the simulator's notion of what the break points are. */ + +static void +gdbsim_mourn_inferior (void) +{ + if (sr_get_debug ()) + printf_filtered ("gdbsim_mourn_inferior:\n"); + + remove_breakpoints (); + generic_mourn_inferior (); +} + +static int +gdbsim_insert_breakpoint (CORE_ADDR addr, char *contents_cache) +{ + return memory_insert_breakpoint (addr, contents_cache); +} + +static int +gdbsim_remove_breakpoint (CORE_ADDR addr, char *contents_cache) +{ + return memory_remove_breakpoint (addr, contents_cache); +} + +/* Pass the command argument through to the simulator verbatim. The + simulator must do any command interpretation work. */ + +void +simulator_command (char *args, int from_tty) +{ + if (gdbsim_desc == NULL) + { + + /* PREVIOUSLY: The user may give a command before the simulator + is opened. [...] (??? assuming of course one wishes to + continue to allow commands to be sent to unopened simulators, + which isn't entirely unreasonable). */ + + /* The simulator is a builtin abstraction of a remote target. + Consistent with that model, access to the simulator, via sim + commands, is restricted to the period when the channel to the + simulator is open. */ + + error ("Not connected to the simulator target"); + } + + sim_do_command (gdbsim_desc, args); + + /* Invalidate the register cache, in case the simulator command does + something funny. */ + registers_changed (); +} + +/* Define the target subroutine names */ + +struct target_ops gdbsim_ops; + +static void +init_gdbsim_ops (void) +{ + gdbsim_ops.to_shortname = "sim"; + gdbsim_ops.to_longname = "simulator"; + gdbsim_ops.to_doc = "Use the compiled-in simulator."; + gdbsim_ops.to_open = gdbsim_open; + gdbsim_ops.to_close = gdbsim_close; + gdbsim_ops.to_detach = gdbsim_detach; + gdbsim_ops.to_resume = gdbsim_resume; + gdbsim_ops.to_wait = gdbsim_wait; + gdbsim_ops.to_fetch_registers = gdbsim_fetch_register; + gdbsim_ops.to_store_registers = gdbsim_store_register; + gdbsim_ops.to_prepare_to_store = gdbsim_prepare_to_store; + gdbsim_ops.to_xfer_memory = gdbsim_xfer_inferior_memory; + gdbsim_ops.to_files_info = gdbsim_files_info; + gdbsim_ops.to_insert_breakpoint = gdbsim_insert_breakpoint; + gdbsim_ops.to_remove_breakpoint = gdbsim_remove_breakpoint; + gdbsim_ops.to_kill = gdbsim_kill; + gdbsim_ops.to_load = gdbsim_load; + gdbsim_ops.to_create_inferior = gdbsim_create_inferior; + gdbsim_ops.to_mourn_inferior = gdbsim_mourn_inferior; + gdbsim_ops.to_stop = gdbsim_stop; + gdbsim_ops.to_stratum = process_stratum; + gdbsim_ops.to_has_all_memory = 1; + gdbsim_ops.to_has_memory = 1; + gdbsim_ops.to_has_stack = 1; + gdbsim_ops.to_has_registers = 1; + gdbsim_ops.to_has_execution = 1; + gdbsim_ops.to_magic = OPS_MAGIC; + +#ifdef TARGET_REDEFINE_DEFAULT_OPS + TARGET_REDEFINE_DEFAULT_OPS (&gdbsim_ops); +#endif +} + +void +_initialize_remote_sim (void) +{ + init_gdbsim_ops (); + add_target (&gdbsim_ops); + + add_com ("sim <command>", class_obscure, simulator_command, + "Send a command to the simulator."); +} diff --git a/contrib/gdb/gdb/remote-st.c b/contrib/gdb/gdb/remote-st.c new file mode 100644 index 0000000..ce4c7ab --- /dev/null +++ b/contrib/gdb/gdb/remote-st.c @@ -0,0 +1,803 @@ +/* Remote debugging interface for Tandem ST2000 phone switch, for GDB. + + Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1998, 1999, 2000, + 2001, 2002 Free Software Foundation, Inc. + + Contributed by Cygnus Support. Written by Jim Kingdon for Cygnus. + + 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. */ + +/* This file was derived from remote-eb.c, which did a similar job, but for + an AMD-29K running EBMON. That file was in turn derived from remote.c + as mentioned in the following comment (left in for comic relief): + + "This is like remote.c but is for an esoteric situation-- + having an a29k board in a PC hooked up to a unix machine with + a serial line, and running ctty com1 on the PC, through which + the unix machine can run ebmon. Not to mention that the PC + has PC/NFS, so it can access the same executables that gdb can, + over the net in real time." + + In reality, this module talks to a debug monitor called 'STDEBUG', which + runs in a phone switch. We communicate with STDEBUG via either a direct + serial line, or a TCP (or possibly TELNET) stream to a terminal multiplexor, + which in turn talks to the phone switch. */ + +#include "defs.h" +#include "gdbcore.h" +#include "target.h" +#include "gdb_string.h" +#include <sys/types.h> +#include "serial.h" +#include "regcache.h" + +extern struct target_ops st2000_ops; /* Forward declaration */ + +static void st2000_close (); +static void st2000_fetch_register (); +static void st2000_store_register (); + +#define LOG_FILE "st2000.log" +#if defined (LOG_FILE) +FILE *log_file; +#endif + +static int timeout = 24; + +/* Descriptor for I/O to remote machine. Initialize it to -1 so that + st2000_open knows that we don't have a file open when the program + starts. */ + +static struct serial *st2000_desc; + +/* Send data to stdebug. Works just like printf. */ + +static void +printf_stdebug (char *pattern,...) +{ + va_list args; + char buf[200]; + + va_start (args, pattern); + + vsprintf (buf, pattern, args); + va_end (args); + + if (serial_write (st2000_desc, buf, strlen (buf))) + fprintf_unfiltered (gdb_stderr, "serial_write failed: %s\n", + safe_strerror (errno)); +} + +/* Read a character from the remote system, doing all the fancy timeout + stuff. */ + +static int +readchar (int timeout) +{ + int c; + + c = serial_readchar (st2000_desc, timeout); + +#ifdef LOG_FILE + putc (c & 0x7f, log_file); +#endif + + if (c >= 0) + return c & 0x7f; + + if (c == SERIAL_TIMEOUT) + { + if (timeout == 0) + return c; /* Polls shouldn't generate timeout errors */ + + error ("Timeout reading from remote system."); + } + + perror_with_name ("remote-st2000"); +} + +/* Scan input from the remote system, until STRING is found. If DISCARD is + non-zero, then discard non-matching input, else print it out. + Let the user break out immediately. */ +static void +expect (char *string, int discard) +{ + char *p = string; + int c; + + immediate_quit++; + while (1) + { + c = readchar (timeout); + if (c == *p++) + { + if (*p == '\0') + { + immediate_quit--; + return; + } + } + else + { + if (!discard) + { + fwrite (string, 1, (p - 1) - string, stdout); + putchar ((char) c); + fflush (stdout); + } + p = string; + } + } +} + +/* Keep discarding input until we see the STDEBUG prompt. + + The convention for dealing with the prompt is that you + o give your command + o *then* wait for the prompt. + + Thus the last thing that a procedure does with the serial line + will be an expect_prompt(). Exception: st2000_resume does not + wait for the prompt, because the terminal is being handed over + to the inferior. However, the next thing which happens after that + is a st2000_wait which does wait for the prompt. + Note that this includes abnormal exit, e.g. error(). This is + necessary to prevent getting into states from which we can't + recover. */ +static void +expect_prompt (int discard) +{ +#if defined (LOG_FILE) + /* This is a convenient place to do this. The idea is to do it often + enough that we never lose much data if we terminate abnormally. */ + fflush (log_file); +#endif + expect ("dbug> ", discard); +} + +/* Get a hex digit from the remote system & return its value. + If ignore_space is nonzero, ignore spaces (not newline, tab, etc). */ +static int +get_hex_digit (int ignore_space) +{ + int ch; + while (1) + { + ch = readchar (timeout); + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else if (ch == ' ' && ignore_space) + ; + else + { + expect_prompt (1); + error ("Invalid hex digit from remote system."); + } + } +} + +/* Get a byte from stdebug and put it in *BYT. Accept any number + leading spaces. */ +static void +get_hex_byte (char *byt) +{ + int val; + + val = get_hex_digit (1) << 4; + val |= get_hex_digit (0); + *byt = val; +} + +/* Get N 32-bit words from remote, each preceded by a space, + and put them in registers starting at REGNO. */ +static void +get_hex_regs (int n, int regno) +{ + long val; + int i; + + for (i = 0; i < n; i++) + { + int j; + + val = 0; + for (j = 0; j < 8; j++) + val = (val << 4) + get_hex_digit (j == 0); + supply_register (regno++, (char *) &val); + } +} + +/* This is called not only when we first attach, but also when the + user types "run" after having attached. */ +static void +st2000_create_inferior (char *execfile, char *args, char **env) +{ + int entry_pt; + + if (args && *args) + error ("Can't pass arguments to remote STDEBUG process"); + + if (execfile == 0 || exec_bfd == 0) + error ("No executable file specified"); + + entry_pt = (int) bfd_get_start_address (exec_bfd); + +/* The "process" (board) is already stopped awaiting our commands, and + the program is already downloaded. We just set its PC and go. */ + + clear_proceed_status (); + + /* Tell wait_for_inferior that we've started a new process. */ + init_wait_for_inferior (); + + /* Set up the "saved terminal modes" of the inferior + based on what modes we are starting it with. */ + target_terminal_init (); + + /* Install inferior's terminal modes. */ + target_terminal_inferior (); + + /* insert_step_breakpoint (); FIXME, do we need this? */ + /* Let 'er rip... */ + proceed ((CORE_ADDR) entry_pt, TARGET_SIGNAL_DEFAULT, 0); +} + +/* Open a connection to a remote debugger. + NAME is the filename used for communication. */ + +static int baudrate = 9600; +static char dev_name[100]; + +static void +st2000_open (char *args, int from_tty) +{ + int n; + char junk[100]; + + target_preopen (from_tty); + + n = sscanf (args, " %s %d %s", dev_name, &baudrate, junk); + + if (n != 2) + error ("Bad arguments. Usage: target st2000 <device> <speed>\n\ +or target st2000 <host> <port>\n"); + + st2000_close (0); + + st2000_desc = serial_open (dev_name); + + if (!st2000_desc) + perror_with_name (dev_name); + + if (serial_setbaudrate (st2000_desc, baudrate)) + { + serial_close (dev_name); + perror_with_name (dev_name); + } + + serial_raw (st2000_desc); + + push_target (&st2000_ops); + +#if defined (LOG_FILE) + log_file = fopen (LOG_FILE, "w"); + if (log_file == NULL) + perror_with_name (LOG_FILE); +#endif + + /* Hello? Are you there? */ + printf_stdebug ("\003"); /* ^C wakes up dbug */ + + expect_prompt (1); + + if (from_tty) + printf ("Remote %s connected to %s\n", target_shortname, + dev_name); +} + +/* Close out all files and local state before this target loses control. */ + +static void +st2000_close (int quitting) +{ + serial_close (st2000_desc); + +#if defined (LOG_FILE) + if (log_file) + { + if (ferror (log_file)) + fprintf_unfiltered (gdb_stderr, "Error writing log file.\n"); + if (fclose (log_file) != 0) + fprintf_unfiltered (gdb_stderr, "Error closing log file.\n"); + } +#endif +} + +/* Terminate the open connection to the remote debugger. + Use this when you want to detach and do something else + with your gdb. */ +static void +st2000_detach (int from_tty) +{ + pop_target (); /* calls st2000_close to do the real work */ + if (from_tty) + printf ("Ending remote %s debugging\n", target_shortname); +} + +/* Tell the remote machine to resume. */ + +static void +st2000_resume (ptid_t ptid, int step, enum target_signal sig) +{ + if (step) + { + printf_stdebug ("ST\r"); + /* Wait for the echo. */ + expect ("ST\r", 1); + } + else + { + printf_stdebug ("GO\r"); + /* Swallow the echo. */ + expect ("GO\r", 1); + } +} + +/* Wait until the remote machine stops, then return, + storing status in STATUS just as `wait' would. */ + +static ptid_t +st2000_wait (ptid_t ptid, struct target_waitstatus *status) +{ + int old_timeout = timeout; + + status->kind = TARGET_WAITKIND_EXITED; + status->value.integer = 0; + + timeout = 0; /* Don't time out -- user program is running. */ + + expect_prompt (0); /* Wait for prompt, outputting extraneous text */ + + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_TRAP; + + timeout = old_timeout; + + return inferior_ptid; +} + +/* Return the name of register number REGNO in the form input and + output by STDEBUG. Currently, REGISTER_NAME just happens return + exactly what STDEBUG wants. Lets take advantage of that just as + long as possible! */ + +static char * +get_reg_name (int regno) +{ + static char buf[50]; + const char *p; + char *b; + + b = buf; + + for (p = REGISTER_NAME (regno); *p; p++) + *b++ = toupper (*p); + *b = '\000'; + + return buf; +} + +/* Read the remote registers into the block REGS. */ + +static void +st2000_fetch_registers (void) +{ + int regno; + + /* Yeah yeah, I know this is horribly inefficient. But it isn't done + very often... I'll clean it up later. */ + + for (regno = 0; regno <= PC_REGNUM; regno++) + st2000_fetch_register (regno); +} + +/* Fetch register REGNO, or all registers if REGNO is -1. + Returns errno value. */ +static void +st2000_fetch_register (int regno) +{ + if (regno == -1) + st2000_fetch_registers (); + else + { + char *name = get_reg_name (regno); + printf_stdebug ("DR %s\r", name); + expect (name, 1); + expect (" : ", 1); + get_hex_regs (1, regno); + expect_prompt (1); + } + return; +} + +/* Store the remote registers from the contents of the block REGS. */ + +static void +st2000_store_registers (void) +{ + int regno; + + for (regno = 0; regno <= PC_REGNUM; regno++) + st2000_store_register (regno); + + registers_changed (); +} + +/* Store register REGNO, or all if REGNO == 0. + Return errno value. */ +static void +st2000_store_register (int regno) +{ + if (regno == -1) + st2000_store_registers (); + else + { + printf_stdebug ("PR %s %x\r", get_reg_name (regno), + read_register (regno)); + + expect_prompt (1); + } +} + +/* Get ready to modify the registers array. On machines which store + individual registers, this doesn't need to do anything. On machines + which store all the registers in one fell swoop, this makes sure + that registers contains all the registers from the program being + debugged. */ + +static void +st2000_prepare_to_store (void) +{ + /* Do nothing, since we can store individual regs */ +} + +static void +st2000_files_info (void) +{ + printf ("\tAttached to %s at %d baud.\n", + dev_name, baudrate); +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. Returns length moved. */ +static int +st2000_write_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) +{ + int i; + + for (i = 0; i < len; i++) + { + printf_stdebug ("PM.B %x %x\r", memaddr + i, myaddr[i]); + expect_prompt (1); + } + return len; +} + +/* Read LEN bytes from inferior memory at MEMADDR. Put the result + at debugger address MYADDR. Returns length moved. */ +static int +st2000_read_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len) +{ + int i; + + /* Number of bytes read so far. */ + int count; + + /* Starting address of this pass. */ + unsigned long startaddr; + + /* Number of bytes to read in this pass. */ + int len_this_pass; + + /* Note that this code works correctly if startaddr is just less + than UINT_MAX (well, really CORE_ADDR_MAX if there was such a + thing). That is, something like + st2000_read_bytes (CORE_ADDR_MAX - 4, foo, 4) + works--it never adds len to memaddr and gets 0. */ + /* However, something like + st2000_read_bytes (CORE_ADDR_MAX - 3, foo, 4) + doesn't need to work. Detect it and give up if there's an attempt + to do that. */ + if (((memaddr - 1) + len) < memaddr) + { + errno = EIO; + return 0; + } + + startaddr = memaddr; + count = 0; + while (count < len) + { + len_this_pass = 16; + if ((startaddr % 16) != 0) + len_this_pass -= startaddr % 16; + if (len_this_pass > (len - count)) + len_this_pass = (len - count); + + printf_stdebug ("DI.L %x %x\r", startaddr, len_this_pass); + expect (": ", 1); + + for (i = 0; i < len_this_pass; i++) + get_hex_byte (&myaddr[count++]); + + expect_prompt (1); + + startaddr += len_this_pass; + } + return len; +} + +/* Transfer LEN bytes between GDB address MYADDR and target address + MEMADDR. If WRITE is non-zero, transfer them to the target, + otherwise transfer them from the target. TARGET is unused. + + Returns the number of bytes transferred. */ + +static int +st2000_xfer_inferior_memory (CORE_ADDR memaddr, char *myaddr, int len, + int write, struct mem_attrib *attrib, + struct target_ops *target) +{ + if (write) + return st2000_write_inferior_memory (memaddr, myaddr, len); + else + return st2000_read_inferior_memory (memaddr, myaddr, len); +} + +static void +st2000_kill (char *args, int from_tty) +{ + return; /* Ignore attempts to kill target system */ +} + +/* Clean up when a program exits. + + The program actually lives on in the remote processor's RAM, and may be + run again without a download. Don't leave it full of breakpoint + instructions. */ + +static void +st2000_mourn_inferior (void) +{ + remove_breakpoints (); + unpush_target (&st2000_ops); + generic_mourn_inferior (); /* Do all the proper things now */ +} + +#define MAX_STDEBUG_BREAKPOINTS 16 + +static CORE_ADDR breakaddr[MAX_STDEBUG_BREAKPOINTS] = +{0}; + +static int +st2000_insert_breakpoint (CORE_ADDR addr, char *shadow) +{ + int i; + CORE_ADDR bp_addr = addr; + int bp_size = 0; + + BREAKPOINT_FROM_PC (&bp_addr, &bp_size); + + for (i = 0; i <= MAX_STDEBUG_BREAKPOINTS; i++) + if (breakaddr[i] == 0) + { + breakaddr[i] = addr; + + st2000_read_inferior_memory (bp_addr, shadow, bp_size); + printf_stdebug ("BR %x H\r", addr); + expect_prompt (1); + return 0; + } + + fprintf_unfiltered (gdb_stderr, "Too many breakpoints (> 16) for STDBUG\n"); + return 1; +} + +static int +st2000_remove_breakpoint (CORE_ADDR addr, char *shadow) +{ + int i; + + for (i = 0; i < MAX_STDEBUG_BREAKPOINTS; i++) + if (breakaddr[i] == addr) + { + breakaddr[i] = 0; + + printf_stdebug ("CB %d\r", i); + expect_prompt (1); + return 0; + } + + fprintf_unfiltered (gdb_stderr, + "Can't find breakpoint associated with 0x%x\n", addr); + return 1; +} + + +/* Put a command string, in args, out to STDBUG. Output from STDBUG is placed + on the users terminal until the prompt is seen. */ + +static void +st2000_command (char *args, int fromtty) +{ + if (!st2000_desc) + error ("st2000 target not open."); + + if (!args) + error ("Missing command."); + + printf_stdebug ("%s\r", args); + expect_prompt (0); +} + +/* Connect the user directly to STDBUG. This command acts just like the + 'cu' or 'tip' command. Use <CR>~. or <CR>~^D to break out. */ + +/*static struct ttystate ttystate; */ + +static void +cleanup_tty (void) +{ + printf ("\r\n[Exiting connect mode]\r\n"); +/* serial_restore(0, &ttystate); */ +} + +#if 0 +/* This all should now be in serial.c */ + +static void +connect_command (char *args, int fromtty) +{ + fd_set readfds; + int numfds; + int c; + char cur_esc = 0; + + dont_repeat (); + + if (st2000_desc < 0) + error ("st2000 target not open."); + + if (args) + fprintf ("This command takes no args. They have been ignored.\n"); + + printf ("[Entering connect mode. Use ~. or ~^D to escape]\n"); + + serial_raw (0, &ttystate); + + make_cleanup (cleanup_tty, 0); + + FD_ZERO (&readfds); + + while (1) + { + do + { + FD_SET (0, &readfds); + FD_SET (deprecated_serial_fd (st2000_desc), &readfds); + numfds = select (sizeof (readfds) * 8, &readfds, 0, 0, 0); + } + while (numfds == 0); + + if (numfds < 0) + perror_with_name ("select"); + + if (FD_ISSET (0, &readfds)) + { /* tty input, send to stdebug */ + c = getchar (); + if (c < 0) + perror_with_name ("connect"); + + printf_stdebug ("%c", c); + switch (cur_esc) + { + case 0: + if (c == '\r') + cur_esc = c; + break; + case '\r': + if (c == '~') + cur_esc = c; + else + cur_esc = 0; + break; + case '~': + if (c == '.' || c == '\004') + return; + else + cur_esc = 0; + } + } + + if (FD_ISSET (deprecated_serial_fd (st2000_desc), &readfds)) + { + while (1) + { + c = readchar (0); + if (c < 0) + break; + putchar (c); + } + fflush (stdout); + } + } +} +#endif /* 0 */ + +/* Define the target subroutine names */ + +struct target_ops st2000_ops; + +static void +init_st2000_ops (void) +{ + st2000_ops.to_shortname = "st2000"; + st2000_ops.to_longname = "Remote serial Tandem ST2000 target"; + st2000_ops.to_doc = "Use a remote computer running STDEBUG connected by a serial line;\n\ +or a network connection.\n\ +Arguments are the name of the device for the serial line,\n\ +the speed to connect at in bits per second."; + st2000_ops.to_open = st2000_open; + st2000_ops.to_close = st2000_close; + st2000_ops.to_detach = st2000_detach; + st2000_ops.to_resume = st2000_resume; + st2000_ops.to_wait = st2000_wait; + st2000_ops.to_fetch_registers = st2000_fetch_register; + st2000_ops.to_store_registers = st2000_store_register; + st2000_ops.to_prepare_to_store = st2000_prepare_to_store; + st2000_ops.to_xfer_memory = st2000_xfer_inferior_memory; + st2000_ops.to_files_info = st2000_files_info; + st2000_ops.to_insert_breakpoint = st2000_insert_breakpoint; + st2000_ops.to_remove_breakpoint = st2000_remove_breakpoint; /* Breakpoints */ + st2000_ops.to_kill = st2000_kill; + st2000_ops.to_create_inferior = st2000_create_inferior; + st2000_ops.to_mourn_inferior = st2000_mourn_inferior; + st2000_ops.to_stratum = process_stratum; + st2000_ops.to_has_all_memory = 1; + st2000_ops.to_has_memory = 1; + st2000_ops.to_has_stack = 1; + st2000_ops.to_has_registers = 1; + st2000_ops.to_has_execution = 1; /* all mem, mem, stack, regs, exec */ + st2000_ops.to_magic = OPS_MAGIC; /* Always the last thing */ +}; + +void +_initialize_remote_st2000 (void) +{ + init_st2000_ops (); + add_target (&st2000_ops); + add_com ("st2000 <command>", class_obscure, st2000_command, + "Send a command to the STDBUG monitor."); + add_com ("connect", class_obscure, connect_command, + "Connect the terminal directly up to the STDBUG command monitor.\n\ +Use <CR>~. or <CR>~^D to break out."); +} diff --git a/contrib/gdb/gdb/remote-vx.c b/contrib/gdb/gdb/remote-vx.c new file mode 100644 index 0000000..fd51781 --- /dev/null +++ b/contrib/gdb/gdb/remote-vx.c @@ -0,0 +1,1409 @@ +/* Memory-access and commands for remote VxWorks processes, for GDB. + + Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1997, 1998, 1999, + 2000, 2001, 2002 Free Software Foundation, Inc. + + Contributed by Wind River Systems and Cygnus Support. + + 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 "defs.h" +#include "frame.h" +#include "inferior.h" +#include "target.h" +#include "gdbcore.h" +#include "command.h" +#include "symtab.h" +#include "complaints.h" +#include "gdbcmd.h" +#include "bfd.h" /* Required by objfiles.h. */ +#include "symfile.h" +#include "objfiles.h" +#include "gdb-stabs.h" +#include "regcache.h" + +#include "gdb_string.h" +#include <errno.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#define malloc bogon_malloc /* Sun claims "char *malloc()" not void * */ +#define free bogon_free /* Sun claims "int free()" not void */ +#define realloc bogon_realloc /* Sun claims "char *realloc()", not void * */ +#include <rpc/rpc.h> +#undef malloc +#undef free +#undef realloc +#include <sys/time.h> /* UTek's <rpc/rpc.h> doesn't #incl this */ +#include <netdb.h> +#include "vx-share/ptrace.h" +#include "vx-share/xdr_ptrace.h" +#include "vx-share/xdr_ld.h" +#include "vx-share/xdr_rdb.h" +#include "vx-share/dbgRpcLib.h" + +#include <symtab.h> + +/* Maximum number of bytes to transfer in a single + PTRACE_{READ,WRITE}DATA request. */ +#define VX_MEMXFER_MAX 4096 + +extern void vx_read_register (); +extern void vx_write_register (); +extern void symbol_file_command (); +extern enum stop_kind stop_soon; /* for wait_for_inferior */ + +static int net_step (); +static int net_ptrace_clnt_call (); /* Forward decl */ +static enum clnt_stat net_clnt_call (); /* Forward decl */ + +/* Target ops structure for accessing memory and such over the net */ + +static struct target_ops vx_ops; + +/* Target ops structure for accessing VxWorks child processes over the net */ + +static struct target_ops vx_run_ops; + +/* Saved name of target host and called function for "info files". + Both malloc'd. */ + +static char *vx_host; +static char *vx_running; /* Called function */ + +/* Nonzero means target that is being debugged remotely has a floating + point processor. */ + +int target_has_fp; + +/* Default error message when the network is forking up. */ + +static const char rpcerr[] = "network target debugging: rpc error"; + +CLIENT *pClient; /* client used in net debugging */ +static int ptraceSock = RPC_ANYSOCK; + +enum clnt_stat net_clnt_call (); +static void parse_args (); + +static struct timeval rpcTimeout = +{10, 0}; + +static char *skip_white_space (); +static char *find_white_space (); + +/* Tell the VxWorks target system to download a file. + The load addresses of the text, data, and bss segments are + stored in *pTextAddr, *pDataAddr, and *pBssAddr (respectively). + Returns 0 for success, -1 for failure. */ + +static int +net_load (char *filename, CORE_ADDR *pTextAddr, CORE_ADDR *pDataAddr, + CORE_ADDR *pBssAddr) +{ + enum clnt_stat status; + struct ldfile ldstruct; + struct timeval load_timeout; + + memset ((char *) &ldstruct, '\0', sizeof (ldstruct)); + + /* We invoke clnt_call () here directly, instead of through + net_clnt_call (), because we need to set a large timeout value. + The load on the target side can take quite a while, easily + more than 10 seconds. The user can kill this call by typing + CTRL-C if there really is a problem with the load. + + Do not change the tv_sec value without checking -- select() imposes + a limit of 10**8 on it for no good reason that I can see... */ + + load_timeout.tv_sec = 99999999; /* A large number, effectively inf. */ + load_timeout.tv_usec = 0; + + status = clnt_call (pClient, VX_LOAD, xdr_wrapstring, &filename, xdr_ldfile, + &ldstruct, load_timeout); + + if (status == RPC_SUCCESS) + { + if (*ldstruct.name == 0) /* load failed on VxWorks side */ + return -1; + *pTextAddr = ldstruct.txt_addr; + *pDataAddr = ldstruct.data_addr; + *pBssAddr = ldstruct.bss_addr; + return 0; + } + else + return -1; +} + +/* returns 0 if successful, errno if RPC failed or VxWorks complains. */ + +static int +net_break (int addr, u_long procnum) +{ + enum clnt_stat status; + int break_status; + Rptrace ptrace_in; /* XXX This is stupid. It doesn't need to be a ptrace + structure. How about something smaller? */ + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + break_status = 0; + + ptrace_in.addr = addr; + ptrace_in.pid = PIDGET (inferior_ptid); + + status = net_clnt_call (procnum, xdr_rptrace, &ptrace_in, xdr_int, + &break_status); + + if (status != RPC_SUCCESS) + return errno; + + if (break_status == -1) + return ENOMEM; + return break_status; /* probably (FIXME) zero */ +} + +/* returns 0 if successful, errno otherwise */ + +static int +vx_insert_breakpoint (int addr) +{ + return net_break (addr, VX_BREAK_ADD); +} + +/* returns 0 if successful, errno otherwise */ + +static int +vx_remove_breakpoint (int addr) +{ + return net_break (addr, VX_BREAK_DELETE); +} + +/* Start an inferior process and sets inferior_ptid to its pid. + EXEC_FILE is the file to run. + ALLARGS is a string containing the arguments to the program. + ENV is the environment vector to pass. + Returns process id. Errors reported with error(). + On VxWorks, we ignore exec_file. */ + +static void +vx_create_inferior (char *exec_file, char *args, char **env) +{ + enum clnt_stat status; + arg_array passArgs; + TASK_START taskStart; + + memset ((char *) &passArgs, '\0', sizeof (passArgs)); + memset ((char *) &taskStart, '\0', sizeof (taskStart)); + + /* parse arguments, put them in passArgs */ + + parse_args (args, &passArgs); + + if (passArgs.arg_array_len == 0) + error ("You must specify a function name to run, and arguments if any"); + + status = net_clnt_call (PROCESS_START, xdr_arg_array, &passArgs, + xdr_TASK_START, &taskStart); + + if ((status != RPC_SUCCESS) || (taskStart.status == -1)) + error ("Can't create process on remote target machine"); + + /* Save the name of the running function */ + vx_running = savestring (passArgs.arg_array_val[0], + strlen (passArgs.arg_array_val[0])); + + push_target (&vx_run_ops); + inferior_ptid = pid_to_ptid (taskStart.pid); + + /* We will get a trace trap after one instruction. + Insert breakpoints and continue. */ + + init_wait_for_inferior (); + + /* Set up the "saved terminal modes" of the inferior + based on what modes we are starting it with. */ + target_terminal_init (); + + /* Install inferior's terminal modes. */ + target_terminal_inferior (); + + stop_soon = STOP_QUIETLY; + wait_for_inferior (); /* Get the task spawn event */ + stop_soon = NO_STOP_QUIETLY; + + /* insert_step_breakpoint (); FIXME, do we need this? */ + proceed (-1, TARGET_SIGNAL_DEFAULT, 0); +} + +/* Fill ARGSTRUCT in argc/argv form with the arguments from the + argument string ARGSTRING. */ + +static void +parse_args (char *arg_string, arg_array *arg_struct) +{ + int arg_count = 0; /* number of arguments */ + int arg_index = 0; + char *p0; + + memset ((char *) arg_struct, '\0', sizeof (arg_array)); + + /* first count how many arguments there are */ + + p0 = arg_string; + while (*p0 != '\0') + { + if (*(p0 = skip_white_space (p0)) == '\0') + break; + p0 = find_white_space (p0); + arg_count++; + } + + arg_struct->arg_array_len = arg_count; + arg_struct->arg_array_val = (char **) xmalloc ((arg_count + 1) + * sizeof (char *)); + + /* now copy argument strings into arg_struct. */ + + while (*(arg_string = skip_white_space (arg_string))) + { + p0 = find_white_space (arg_string); + arg_struct->arg_array_val[arg_index++] = savestring (arg_string, + p0 - arg_string); + arg_string = p0; + } + + arg_struct->arg_array_val[arg_count] = NULL; +} + +/* Advance a string pointer across whitespace and return a pointer + to the first non-white character. */ + +static char * +skip_white_space (char *p) +{ + while (*p == ' ' || *p == '\t') + p++; + return p; +} + +/* Search for the first unquoted whitespace character in a string. + Returns a pointer to the character, or to the null terminator + if no whitespace is found. */ + +static char * +find_white_space (char *p) +{ + int c; + + while ((c = *p) != ' ' && c != '\t' && c) + { + if (c == '\'' || c == '"') + { + while (*++p != c && *p) + { + if (*p == '\\') + p++; + } + if (!*p) + break; + } + p++; + } + return p; +} + +/* Poll the VxWorks target system for an event related + to the debugged task. + Returns -1 if remote wait failed, task status otherwise. */ + +static int +net_wait (RDB_EVENT *pEvent) +{ + int pid; + enum clnt_stat status; + + memset ((char *) pEvent, '\0', sizeof (RDB_EVENT)); + + pid = PIDGET (inferior_ptid); + status = net_clnt_call (PROCESS_WAIT, xdr_int, &pid, xdr_RDB_EVENT, + pEvent); + + /* return (status == RPC_SUCCESS)? pEvent->status: -1; */ + if (status == RPC_SUCCESS) + return ((pEvent->status) ? 1 : 0); + else if (status == RPC_TIMEDOUT) + return (1); + else + return (-1); +} + +/* Suspend the remote task. + Returns -1 if suspend fails on target system, 0 otherwise. */ + +static int +net_quit (void) +{ + int pid; + int quit_status; + enum clnt_stat status; + + quit_status = 0; + + /* don't let rdbTask suspend itself by passing a pid of 0 */ + + if ((pid = PIDGET (inferior_ptid)) == 0) + return -1; + + status = net_clnt_call (VX_TASK_SUSPEND, xdr_int, &pid, xdr_int, + &quit_status); + + return (status == RPC_SUCCESS) ? quit_status : -1; +} + +/* Read a register or registers from the remote system. */ + +void +net_read_registers (char *reg_buf, int len, u_long procnum) +{ + int status; + Rptrace ptrace_in; + Ptrace_return ptrace_out; + C_bytes out_data; + char message[100]; + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out)); + + /* Initialize RPC input argument structure. */ + + ptrace_in.pid = PIDGET (inferior_ptid); + ptrace_in.info.ttype = NOINFO; + + /* Initialize RPC return value structure. */ + + out_data.bytes = reg_buf; + out_data.len = len; + ptrace_out.info.more_data = (caddr_t) & out_data; + + /* Call RPC; take an error exit if appropriate. */ + + status = net_ptrace_clnt_call (procnum, &ptrace_in, &ptrace_out); + if (status) + error (rpcerr); + if (ptrace_out.status == -1) + { + errno = ptrace_out.errno_num; + sprintf (message, "reading %s registers", (procnum == PTRACE_GETREGS) + ? "general-purpose" + : "floating-point"); + perror_with_name (message); + } +} + +/* Write register values to a VxWorks target. REG_BUF points to a buffer + containing the raw register values, LEN is the length of REG_BUF in + bytes, and PROCNUM is the RPC procedure number (PTRACE_SETREGS or + PTRACE_SETFPREGS). An error exit is taken if the RPC call fails or + if an error status is returned by the remote debug server. This is + a utility routine used by vx_write_register (). */ + +void +net_write_registers (char *reg_buf, int len, u_long procnum) +{ + int status; + Rptrace ptrace_in; + Ptrace_return ptrace_out; + C_bytes in_data; + char message[100]; + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out)); + + /* Initialize RPC input argument structure. */ + + in_data.bytes = reg_buf; + in_data.len = len; + + ptrace_in.pid = PIDGET (inferior_ptid); + ptrace_in.info.ttype = DATA; + ptrace_in.info.more_data = (caddr_t) & in_data; + + /* Call RPC; take an error exit if appropriate. */ + + status = net_ptrace_clnt_call (procnum, &ptrace_in, &ptrace_out); + if (status) + error (rpcerr); + if (ptrace_out.status == -1) + { + errno = ptrace_out.errno_num; + sprintf (message, "writing %s registers", (procnum == PTRACE_SETREGS) + ? "general-purpose" + : "floating-point"); + perror_with_name (message); + } +} + +/* Prepare to store registers. Since we will store all of them, + read out their current values now. */ + +static void +vx_prepare_to_store (void) +{ + /* Fetch all registers, if any of them are not yet fetched. */ + deprecated_read_register_bytes (0, NULL, DEPRECATED_REGISTER_BYTES); +} + +/* Copy LEN bytes to or from remote inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. WRITE is true if writing to the + inferior. TARGET is unused. + Result is the number of bytes written or read (zero if error). The + protocol allows us to return a negative count, indicating that we can't + handle the current address but can handle one N bytes further, but + vxworks doesn't give us that information. */ + +static int +vx_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write, + struct mem_attrib *attrib, struct target_ops *target) +{ + int status; + Rptrace ptrace_in; + Ptrace_return ptrace_out; + C_bytes data; + enum ptracereq request; + int nleft, nxfer; + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out)); + + ptrace_in.pid = PIDGET (inferior_ptid); /* XXX pid unnecessary for READDATA */ + ptrace_in.addr = (int) memaddr; /* Where from */ + ptrace_in.data = len; /* How many bytes */ + + if (write) + { + ptrace_in.info.ttype = DATA; + ptrace_in.info.more_data = (caddr_t) & data; + + data.bytes = (caddr_t) myaddr; /* Where from */ + data.len = len; /* How many bytes (again, for XDR) */ + request = PTRACE_WRITEDATA; + } + else + { + ptrace_out.info.more_data = (caddr_t) & data; + request = PTRACE_READDATA; + } + /* Loop until the entire request has been satisfied, transferring + at most VX_MEMXFER_MAX bytes per iteration. Break from the loop + if an error status is returned by the remote debug server. */ + + nleft = len; + status = 0; + + while (nleft > 0 && status == 0) + { + nxfer = min (nleft, VX_MEMXFER_MAX); + + ptrace_in.addr = (int) memaddr; + ptrace_in.data = nxfer; + data.bytes = (caddr_t) myaddr; + data.len = nxfer; + + /* Request a block from the remote debug server; if RPC fails, + report an error and return to debugger command level. */ + + if (net_ptrace_clnt_call (request, &ptrace_in, &ptrace_out)) + error (rpcerr); + + status = ptrace_out.status; + if (status == 0) + { + memaddr += nxfer; + myaddr += nxfer; + nleft -= nxfer; + } + else + { + /* A target-side error has ocurred. Set errno to the error + code chosen by the target so that a later perror () will + say something meaningful. */ + + errno = ptrace_out.errno_num; + } + } + + /* Return the number of bytes transferred. */ + + return (len - nleft); +} + +static void +vx_files_info (void) +{ + printf_unfiltered ("\tAttached to host `%s'", vx_host); + printf_unfiltered (", which has %sfloating point", target_has_fp ? "" : "no "); + printf_unfiltered (".\n"); +} + +static void +vx_run_files_info (void) +{ + printf_unfiltered ("\tRunning %s VxWorks process %s", + vx_running ? "child" : "attached", + local_hex_string (PIDGET (inferior_ptid))); + if (vx_running) + printf_unfiltered (", function `%s'", vx_running); + printf_unfiltered (".\n"); +} + +static void +vx_resume (ptid_t ptid, int step, enum target_signal siggnal) +{ + int status; + Rptrace ptrace_in; + Ptrace_return ptrace_out; + CORE_ADDR cont_addr; + + if (ptid_equal (ptid, minus_one_ptid)) + ptid = inferior_ptid; + + if (siggnal != 0 && siggnal != stop_signal) + error ("Cannot send signals to VxWorks processes"); + + /* Set CONT_ADDR to the address at which we are continuing, + or to 1 if we are continuing from where the program stopped. + This conforms to traditional ptrace () usage, but at the same + time has special meaning for the VxWorks remote debug server. + If the address is not 1, the server knows that the target + program is jumping to a new address, which requires special + handling if there is a breakpoint at the new address. */ + + cont_addr = read_register (PC_REGNUM); + if (cont_addr == stop_pc) + cont_addr = 1; + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out)); + + ptrace_in.pid = PIDGET (ptid); + ptrace_in.addr = cont_addr; /* Target side insists on this, or it panics. */ + + if (step) + status = net_step (); + else + status = net_ptrace_clnt_call (PTRACE_CONT, &ptrace_in, &ptrace_out); + + if (status) + error (rpcerr); + if (ptrace_out.status == -1) + { + errno = ptrace_out.errno_num; + perror_with_name ("Resuming remote process"); + } +} + +static void +vx_mourn_inferior (void) +{ + pop_target (); /* Pop back to no-child state */ + generic_mourn_inferior (); +} + + +static void vx_add_symbols (char *, int, CORE_ADDR, CORE_ADDR, CORE_ADDR); + +struct find_sect_args + { + CORE_ADDR text_start; + CORE_ADDR data_start; + CORE_ADDR bss_start; + }; + +static void find_sect (bfd *, asection *, void *); + +static void +find_sect (bfd *abfd, asection *sect, void *obj) +{ + struct find_sect_args *args = (struct find_sect_args *) obj; + + if (bfd_get_section_flags (abfd, sect) & (SEC_CODE & SEC_READONLY)) + args->text_start = bfd_get_section_vma (abfd, sect); + else if (bfd_get_section_flags (abfd, sect) & SEC_ALLOC) + { + if (bfd_get_section_flags (abfd, sect) & SEC_LOAD) + { + /* Exclude .ctor and .dtor sections which have SEC_CODE set but not + SEC_DATA. */ + if (bfd_get_section_flags (abfd, sect) & SEC_DATA) + args->data_start = bfd_get_section_vma (abfd, sect); + } + else + args->bss_start = bfd_get_section_vma (abfd, sect); + } +} + +static void +vx_add_symbols (char *name, int from_tty, CORE_ADDR text_addr, + CORE_ADDR data_addr, CORE_ADDR bss_addr) +{ + struct section_offsets *offs; + struct objfile *objfile; + struct find_sect_args ss; + + /* It might be nice to suppress the breakpoint_re_set which happens here + because we are going to do one again after the objfile_relocate. */ + objfile = symbol_file_add (name, from_tty, NULL, 0, 0); + + /* This is a (slightly cheesy) way of superceding the old symbols. A less + cheesy way would be to find the objfile with the same name and + free_objfile it. */ + objfile_to_front (objfile); + + offs = + (struct section_offsets *) + alloca (SIZEOF_N_SECTION_OFFSETS (objfile->num_sections)); + memcpy (offs, objfile->section_offsets, + SIZEOF_N_SECTION_OFFSETS (objfile->num_sections)); + + ss.text_start = 0; + ss.data_start = 0; + ss.bss_start = 0; + bfd_map_over_sections (objfile->obfd, find_sect, &ss); + + /* Both COFF and b.out frontends use these SECT_OFF_* values. */ + offs->offsets[SECT_OFF_TEXT (objfile)] = text_addr - ss.text_start; + offs->offsets[SECT_OFF_DATA (objfile)] = data_addr - ss.data_start; + offs->offsets[SECT_OFF_BSS (objfile)] = bss_addr - ss.bss_start; + objfile_relocate (objfile, offs); +} + +/* This function allows the addition of incrementally linked object files. */ + +static void +vx_load_command (char *arg_string, int from_tty) +{ + CORE_ADDR text_addr; + CORE_ADDR data_addr; + CORE_ADDR bss_addr; + + if (arg_string == 0) + error ("The load command takes a file name"); + + arg_string = tilde_expand (arg_string); + make_cleanup (xfree, arg_string); + + dont_repeat (); + + /* Refuse to load the module if a debugged task is running. Doing so + can have a number of unpleasant consequences to the running task. */ + + if (PIDGET (inferior_ptid) != 0 && target_has_execution) + { + if (query ("You may not load a module while the target task is running.\n\ +Kill the target task? ")) + target_kill (); + else + error ("Load canceled."); + } + + QUIT; + immediate_quit++; + if (net_load (arg_string, &text_addr, &data_addr, &bss_addr) == -1) + error ("Load failed on target machine"); + immediate_quit--; + + vx_add_symbols (arg_string, from_tty, text_addr, data_addr, bss_addr); + + /* Getting new symbols may change our opinion about what is + frameless. */ + reinit_frame_cache (); +} + +/* Single step the target program at the source or machine level. + Takes an error exit if rpc fails. + Returns -1 if remote single-step operation fails, else 0. */ + +static int +net_step (void) +{ + enum clnt_stat status; + int step_status; + SOURCE_STEP source_step; + + source_step.taskId = PIDGET (inferior_ptid); + + if (step_range_end) + { + source_step.startAddr = step_range_start; + source_step.endAddr = step_range_end; + } + else + { + source_step.startAddr = 0; + source_step.endAddr = 0; + } + + status = net_clnt_call (VX_SOURCE_STEP, xdr_SOURCE_STEP, &source_step, + xdr_int, &step_status); + + if (status == RPC_SUCCESS) + return step_status; + else + error (rpcerr); +} + +/* Emulate ptrace using RPC calls to the VxWorks target system. + Returns nonzero (-1) if RPC status to VxWorks is bad, 0 otherwise. */ + +static int +net_ptrace_clnt_call (enum ptracereq request, Rptrace *pPtraceIn, + Ptrace_return *pPtraceOut) +{ + enum clnt_stat status; + + status = net_clnt_call (request, xdr_rptrace, pPtraceIn, xdr_ptrace_return, + pPtraceOut); + + if (status != RPC_SUCCESS) + return -1; + + return 0; +} + +/* Query the target for the name of the file from which VxWorks was + booted. pBootFile is the address of a pointer to the buffer to + receive the file name; if the pointer pointed to by pBootFile is + NULL, memory for the buffer will be allocated by XDR. + Returns -1 if rpc failed, 0 otherwise. */ + +static int +net_get_boot_file (char **pBootFile) +{ + enum clnt_stat status; + + status = net_clnt_call (VX_BOOT_FILE_INQ, xdr_void, (char *) 0, + xdr_wrapstring, pBootFile); + return (status == RPC_SUCCESS) ? 0 : -1; +} + +/* Fetch a list of loaded object modules from the VxWorks target + and store in PLOADTABLE. + Returns -1 if rpc failed, 0 otherwise + There's no way to check if the returned loadTable is correct. + VxWorks doesn't check it. */ + +static int +net_get_symbols (ldtabl *pLoadTable) +{ + enum clnt_stat status; + + memset ((char *) pLoadTable, '\0', sizeof (struct ldtabl)); + + status = net_clnt_call (VX_STATE_INQ, xdr_void, 0, xdr_ldtabl, pLoadTable); + return (status == RPC_SUCCESS) ? 0 : -1; +} + +/* Look up a symbol in the VxWorks target's symbol table. + Returns status of symbol read on target side (0=success, -1=fail) + Returns -1 and complain()s if rpc fails. */ + +static int +vx_lookup_symbol (char *name, /* symbol name */ + CORE_ADDR *pAddr) +{ + enum clnt_stat status; + SYMBOL_ADDR symbolAddr; + + *pAddr = 0; + memset ((char *) &symbolAddr, '\0', sizeof (symbolAddr)); + + status = net_clnt_call (VX_SYMBOL_INQ, xdr_wrapstring, &name, + xdr_SYMBOL_ADDR, &symbolAddr); + if (status != RPC_SUCCESS) + { + complaint (&symfile_complaints, "Lost contact with VxWorks target"); + return -1; + } + + *pAddr = symbolAddr.addr; + return symbolAddr.status; +} + +/* Check to see if the VxWorks target has a floating point coprocessor. + Returns 1 if target has floating point processor, 0 otherwise. + Calls error() if rpc fails. */ + +static int +net_check_for_fp (void) +{ + enum clnt_stat status; + bool_t fp = 0; /* true if fp processor is present on target board */ + + status = net_clnt_call (VX_FP_INQUIRE, xdr_void, 0, xdr_bool, &fp); + if (status != RPC_SUCCESS) + error (rpcerr); + + return (int) fp; +} + +/* Establish an RPC connection with the VxWorks target system. + Calls error () if unable to establish connection. */ + +static void +net_connect (char *host) +{ + struct sockaddr_in destAddr; + struct hostent *destHost; + unsigned long addr; + + /* Get the internet address for the given host. Allow a numeric + IP address or a hostname. */ + + addr = inet_addr (host); + if (addr == -1) + { + destHost = (struct hostent *) gethostbyname (host); + if (destHost == NULL) + /* FIXME: Probably should include hostname here in quotes. + For example if the user types "target vxworks vx960 " it should + say "Invalid host `vx960 '." not just "Invalid hostname". */ + error ("Invalid hostname. Couldn't find remote host address."); + addr = *(unsigned long *) destHost->h_addr; + } + + memset (&destAddr, '\0', sizeof (destAddr)); + + destAddr.sin_addr.s_addr = addr; + destAddr.sin_family = AF_INET; + destAddr.sin_port = 0; /* set to actual port that remote + ptrace is listening on. */ + + /* Create a tcp client transport on which to issue + calls to the remote ptrace server. */ + + ptraceSock = RPC_ANYSOCK; + pClient = clnttcp_create (&destAddr, RDBPROG, RDBVERS, &ptraceSock, 0, 0); + /* FIXME, here is where we deal with different version numbers of the + proto */ + + if (pClient == NULL) + { + clnt_pcreateerror ("\tnet_connect"); + error ("Couldn't connect to remote target."); + } +} + +/* Sleep for the specified number of milliseconds + * (assumed to be less than 1000). + * If select () is interrupted, returns immediately; + * takes an error exit if select () fails for some other reason. + */ + +static void +sleep_ms (long ms) +{ + struct timeval select_timeout; + int status; + + select_timeout.tv_sec = 0; + select_timeout.tv_usec = ms * 1000; + + status = select (0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, + &select_timeout); + + if (status < 0 && errno != EINTR) + perror_with_name ("select"); +} + +static ptid_t +vx_wait (ptid_t ptid_to_wait_for, struct target_waitstatus *status) +{ + int pid; + RDB_EVENT rdbEvent; + int quit_failed; + + do + { + /* If CTRL-C is hit during this loop, + suspend the inferior process. */ + + quit_failed = 0; + if (quit_flag) + { + quit_failed = (net_quit () == -1); + quit_flag = 0; + } + + /* If a net_quit () or net_wait () call has failed, + allow the user to break the connection with the target. + We can't simply error () out of this loop, since the + data structures representing the state of the inferior + are in an inconsistent state. */ + + if (quit_failed || net_wait (&rdbEvent) == -1) + { + terminal_ours (); + if (query ("Can't %s. Disconnect from target system? ", + (quit_failed) ? "suspend remote task" + : "get status of remote task")) + { + target_mourn_inferior (); + error ("Use the \"target\" command to reconnect."); + } + else + { + terminal_inferior (); + continue; + } + } + + pid = rdbEvent.taskId; + if (pid == 0) + { + sleep_ms (200); /* FIXME Don't kill the network too badly */ + } + else if (pid != PIDGET (inferior_ptid)) + internal_error (__FILE__, __LINE__, + "Bad pid for debugged task: %s\n", + local_hex_string ((unsigned long) pid)); + } + while (pid == 0); + + /* The mostly likely kind. */ + status->kind = TARGET_WAITKIND_STOPPED; + + switch (rdbEvent.eventType) + { + case EVENT_EXIT: + status->kind = TARGET_WAITKIND_EXITED; + /* FIXME is it possible to distinguish between a + normal vs abnormal exit in VxWorks? */ + status->value.integer = 0; + break; + + case EVENT_START: + /* Task was just started. */ + status->value.sig = TARGET_SIGNAL_TRAP; + break; + + case EVENT_STOP: + status->value.sig = TARGET_SIGNAL_TRAP; + /* XXX was it stopped by a signal? act accordingly */ + break; + + case EVENT_BREAK: /* Breakpoint was hit. */ + status->value.sig = TARGET_SIGNAL_TRAP; + break; + + case EVENT_SUSPEND: /* Task was suspended, probably by ^C. */ + status->value.sig = TARGET_SIGNAL_INT; + break; + + case EVENT_BUS_ERR: /* Task made evil nasty reference. */ + status->value.sig = TARGET_SIGNAL_BUS; + break; + + case EVENT_ZERO_DIV: /* Division by zero */ + status->value.sig = TARGET_SIGNAL_FPE; + break; + + case EVENT_SIGNAL: +#ifdef I80960 + status->value.sig = i960_fault_to_signal (rdbEvent.sigType); +#else + /* Back in the old days, before enum target_signal, this code used + to add NSIG to the signal number and claim that PRINT_RANDOM_SIGNAL + would take care of it. But PRINT_RANDOM_SIGNAL has never been + defined except on the i960, so I don't really know what we are + supposed to do on other architectures. */ + status->value.sig = TARGET_SIGNAL_UNKNOWN; +#endif + break; + } /* switch */ + return pid_to_ptid (pid); +} + +static int +symbol_stub (char *arg) +{ + symbol_file_add_main (arg, 0); + return 1; +} + +static int +add_symbol_stub (char *arg) +{ + struct ldfile *pLoadFile = (struct ldfile *) arg; + + printf_unfiltered ("\t%s: ", pLoadFile->name); + vx_add_symbols (pLoadFile->name, 0, pLoadFile->txt_addr, + pLoadFile->data_addr, pLoadFile->bss_addr); + printf_unfiltered ("ok\n"); + return 1; +} +/* Target command for VxWorks target systems. + + Used in vxgdb. Takes the name of a remote target machine + running vxWorks and connects to it to initialize remote network + debugging. */ + +static void +vx_open (char *args, int from_tty) +{ + extern int close (); + char *bootFile; + extern char *source_path; + struct ldtabl loadTable; + struct ldfile *pLoadFile; + int i; + extern CLIENT *pClient; + int symbols_added = 0; + + if (!args) + error_no_arg ("target machine name"); + + target_preopen (from_tty); + + unpush_target (&vx_ops); + printf_unfiltered ("Attaching remote machine across net...\n"); + gdb_flush (gdb_stdout); + + /* Allow the user to kill the connect attempt by typing ^C. + Wait until the call to target_has_fp () completes before + disallowing an immediate quit, since even if net_connect () + is successful, the remote debug server might be hung. */ + + immediate_quit++; + + net_connect (args); + target_has_fp = net_check_for_fp (); + printf_filtered ("Connected to %s.\n", args); + + immediate_quit--; + + push_target (&vx_ops); + + /* Save a copy of the target host's name. */ + vx_host = savestring (args, strlen (args)); + + /* Find out the name of the file from which the target was booted + and load its symbol table. */ + + printf_filtered ("Looking in Unix path for all loaded modules:\n"); + bootFile = NULL; + if (!net_get_boot_file (&bootFile)) + { + if (*bootFile) + { + printf_filtered ("\t%s: ", bootFile); + /* This assumes that the kernel is never relocated. Hope that is an + accurate assumption. */ + if (catch_errors + (symbol_stub, + bootFile, + "Error while reading symbols from boot file:\n", + RETURN_MASK_ALL)) + puts_filtered ("ok\n"); + } + else if (from_tty) + printf_unfiltered ("VxWorks kernel symbols not loaded.\n"); + } + else + error ("Can't retrieve boot file name from target machine."); + + clnt_freeres (pClient, xdr_wrapstring, &bootFile); + + if (net_get_symbols (&loadTable) != 0) + error ("Can't read loaded modules from target machine"); + + i = 0 - 1; + while (++i < loadTable.tbl_size) + { + QUIT; /* FIXME, avoids clnt_freeres below: mem leak */ + pLoadFile = &loadTable.tbl_ent[i]; +#ifdef WRS_ORIG + { + int desc; + struct cleanup *old_chain; + char *fullname = NULL; + + desc = openp (source_path, 0, pLoadFile->name, O_RDONLY, 0, &fullname); + if (desc < 0) + perror_with_name (pLoadFile->name); + old_chain = make_cleanup (close, desc); + add_file_at_addr (fullname, desc, pLoadFile->txt_addr, pLoadFile->data_addr, + pLoadFile->bss_addr); + do_cleanups (old_chain); + } +#else + /* FIXME: Is there something better to search than the PATH? (probably + not the source path, since source might be in different directories + than objects. */ + + if (catch_errors (add_symbol_stub, (char *) pLoadFile, (char *) 0, + RETURN_MASK_ALL)) + symbols_added = 1; +#endif + } + printf_filtered ("Done.\n"); + + clnt_freeres (pClient, xdr_ldtabl, &loadTable); + + /* Getting new symbols may change our opinion about what is + frameless. */ + if (symbols_added) + reinit_frame_cache (); +} + +/* Takes a task started up outside of gdb and ``attaches'' to it. + This stops it cold in its tracks and allows us to start tracing it. */ + +static void +vx_attach (char *args, int from_tty) +{ + unsigned long pid; + char *cptr = 0; + Rptrace ptrace_in; + Ptrace_return ptrace_out; + int status; + + if (!args) + error_no_arg ("process-id to attach"); + + pid = strtoul (args, &cptr, 0); + if ((cptr == args) || (*cptr != '\0')) + error ("Invalid process-id -- give a single number in decimal or 0xhex"); + + if (from_tty) + printf_unfiltered ("Attaching pid %s.\n", + local_hex_string ((unsigned long) pid)); + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out)); + ptrace_in.pid = pid; + + status = net_ptrace_clnt_call (PTRACE_ATTACH, &ptrace_in, &ptrace_out); + if (status == -1) + error (rpcerr); + if (ptrace_out.status == -1) + { + errno = ptrace_out.errno_num; + perror_with_name ("Attaching remote process"); + } + + /* It worked... */ + + inferior_ptid = pid_to_ptid (pid); + push_target (&vx_run_ops); + + if (vx_running) + xfree (vx_running); + vx_running = 0; +} + +/* detach_command -- + takes a program previously attached to and detaches it. + The program resumes execution and will no longer stop + on signals, etc. We better not have left any breakpoints + in the program or it'll die when it hits one. For this + to work, it may be necessary for the process to have been + previously attached. It *might* work if the program was + started via the normal ptrace (PTRACE_TRACEME). */ + +static void +vx_detach (char *args, int from_tty) +{ + Rptrace ptrace_in; + Ptrace_return ptrace_out; + int signal = 0; + int status; + + if (args) + error ("Argument given to VxWorks \"detach\"."); + + if (from_tty) + printf_unfiltered ("Detaching pid %s.\n", + local_hex_string ( + (unsigned long) PIDGET (inferior_ptid))); + + if (args) /* FIXME, should be possible to leave suspended */ + signal = atoi (args); + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out)); + ptrace_in.pid = PIDGET (inferior_ptid); + + status = net_ptrace_clnt_call (PTRACE_DETACH, &ptrace_in, &ptrace_out); + if (status == -1) + error (rpcerr); + if (ptrace_out.status == -1) + { + errno = ptrace_out.errno_num; + perror_with_name ("Detaching VxWorks process"); + } + + inferior_ptid = null_ptid; + pop_target (); /* go back to non-executing VxWorks connection */ +} + +/* vx_kill -- takes a running task and wipes it out. */ + +static void +vx_kill (void) +{ + Rptrace ptrace_in; + Ptrace_return ptrace_out; + int status; + + printf_unfiltered ("Killing pid %s.\n", local_hex_string ((unsigned long) PIDGET (inferior_ptid))); + + memset ((char *) &ptrace_in, '\0', sizeof (ptrace_in)); + memset ((char *) &ptrace_out, '\0', sizeof (ptrace_out)); + ptrace_in.pid = PIDGET (inferior_ptid); + + status = net_ptrace_clnt_call (PTRACE_KILL, &ptrace_in, &ptrace_out); + if (status == -1) + warning (rpcerr); + else if (ptrace_out.status == -1) + { + errno = ptrace_out.errno_num; + perror_with_name ("Killing VxWorks process"); + } + + /* If it gives good status, the process is *gone*, no events remain. + If the kill failed, assume the process is gone anyhow. */ + inferior_ptid = null_ptid; + pop_target (); /* go back to non-executing VxWorks connection */ +} + +/* Clean up from the VxWorks process target as it goes away. */ + +static void +vx_proc_close (int quitting) +{ + inferior_ptid = null_ptid; /* No longer have a process. */ + if (vx_running) + xfree (vx_running); + vx_running = 0; +} + +/* Make an RPC call to the VxWorks target. + Returns RPC status. */ + +static enum clnt_stat +net_clnt_call (enum ptracereq procNum, xdrproc_t inProc, char *in, + xdrproc_t outProc, char *out) +{ + enum clnt_stat status; + + status = clnt_call (pClient, procNum, inProc, in, outProc, out, rpcTimeout); + + if (status != RPC_SUCCESS) + clnt_perrno (status); + + return status; +} + +/* Clean up before losing control. */ + +static void +vx_close (int quitting) +{ + if (pClient) + clnt_destroy (pClient); /* The net connection */ + pClient = 0; + + if (vx_host) + xfree (vx_host); /* The hostname */ + vx_host = 0; +} + +/* A vxprocess target should be started via "run" not "target". */ +static void +vx_proc_open (char *name, int from_tty) +{ + error ("Use the \"run\" command to start a VxWorks process."); +} + +static void +init_vx_ops (void) +{ + vx_ops.to_shortname = "vxworks"; + vx_ops.to_longname = "VxWorks target memory via RPC over TCP/IP"; + vx_ops.to_doc = "Use VxWorks target memory. \n\ +Specify the name of the machine to connect to."; + vx_ops.to_open = vx_open; + vx_ops.to_close = vx_close; + vx_ops.to_attach = vx_attach; + vx_ops.to_xfer_memory = vx_xfer_memory; + vx_ops.to_files_info = vx_files_info; + vx_ops.to_load = vx_load_command; + vx_ops.to_lookup_symbol = vx_lookup_symbol; + vx_ops.to_create_inferior = vx_create_inferior; + vx_ops.to_stratum = core_stratum; + vx_ops.to_has_all_memory = 1; + vx_ops.to_has_memory = 1; + vx_ops.to_magic = OPS_MAGIC; /* Always the last thing */ +}; + +static void +init_vx_run_ops (void) +{ + vx_run_ops.to_shortname = "vxprocess"; + vx_run_ops.to_longname = "VxWorks process"; + vx_run_ops.to_doc = "VxWorks process; started by the \"run\" command."; + vx_run_ops.to_open = vx_proc_open; + vx_run_ops.to_close = vx_proc_close; + vx_run_ops.to_detach = vx_detach; + vx_run_ops.to_resume = vx_resume; + vx_run_ops.to_wait = vx_wait; + vx_run_ops.to_fetch_registers = vx_read_register; + vx_run_ops.to_store_registers = vx_write_register; + vx_run_ops.to_prepare_to_store = vx_prepare_to_store; + vx_run_ops.to_xfer_memory = vx_xfer_memory; + vx_run_ops.to_files_info = vx_run_files_info; + vx_run_ops.to_insert_breakpoint = vx_insert_breakpoint; + vx_run_ops.to_remove_breakpoint = vx_remove_breakpoint; + vx_run_ops.to_kill = vx_kill; + vx_run_ops.to_load = vx_load_command; + vx_run_ops.to_lookup_symbol = vx_lookup_symbol; + vx_run_ops.to_mourn_inferior = vx_mourn_inferior; + vx_run_ops.to_stratum = process_stratum; + vx_run_ops.to_has_memory = 1; + vx_run_ops.to_has_stack = 1; + vx_run_ops.to_has_registers = 1; + vx_run_ops.to_has_execution = 1; + vx_run_ops.to_magic = OPS_MAGIC; +} + +void +_initialize_vx (void) +{ + init_vx_ops (); + add_target (&vx_ops); + init_vx_run_ops (); + add_target (&vx_run_ops); + + add_show_from_set + (add_set_cmd ("vxworks-timeout", class_support, var_uinteger, + (char *) &rpcTimeout.tv_sec, + "Set seconds to wait for rpc calls to return.\n\ +Set the number of seconds to wait for rpc calls to return.", &setlist), + &showlist); +} diff --git a/contrib/gdb/gdb/remote-vx68.c b/contrib/gdb/gdb/remote-vx68.c new file mode 100644 index 0000000..8cdac6f --- /dev/null +++ b/contrib/gdb/gdb/remote-vx68.c @@ -0,0 +1,160 @@ +/* 68k-dependent portions of the RPC protocol + used with a VxWorks target + + Contributed by Wind River Systems. + + 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 <stdio.h> +#include "defs.h" + +#include "vx-share/regPacket.h" +#include "frame.h" +#include "inferior.h" +#include "target.h" +#include "gdbcore.h" +#include "command.h" +#include "symtab.h" +#include "symfile.h" +#include "regcache.h" + +#include "gdb_string.h" +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> + +#ifdef _AIX /* IBM claims "void *malloc()" not char * */ +#define malloc bogon_malloc +#endif + +#include <rpc/rpc.h> + +#ifdef _AIX +#undef malloc +#endif + +#include <sys/time.h> /* UTek's <rpc/rpc.h> doesn't #incl this */ +#include <netdb.h> +#include "vx-share/ptrace.h" +#include "vx-share/xdr_ptrace.h" +#include "vx-share/xdr_ld.h" +#include "vx-share/xdr_rdb.h" +#include "vx-share/dbgRpcLib.h" + +/* get rid of value.h if possible */ +#include <value.h> +#include <symtab.h> + +/* Flag set if target has fpu */ + +extern int target_has_fp; + +/* Generic register read/write routines in remote-vx.c. */ + +extern void net_read_registers (); +extern void net_write_registers (); + +/* Read a register or registers from the VxWorks target. + REGNO is the register to read, or -1 for all; currently, + it is ignored. FIXME look at regno to improve efficiency. */ + +void +vx_read_register (int regno) +{ + char mc68k_greg_packet[MC68K_GREG_PLEN]; + char mc68k_fpreg_packet[MC68K_FPREG_PLEN]; + + /* Get general-purpose registers. */ + + net_read_registers (mc68k_greg_packet, MC68K_GREG_PLEN, PTRACE_GETREGS); + + bcopy (&mc68k_greg_packet[MC68K_R_D0], deprecated_registers, + 16 * MC68K_GREG_SIZE); + bcopy (&mc68k_greg_packet[MC68K_R_SR], + &deprecated_registers[DEPRECATED_REGISTER_BYTE (PS_REGNUM)], + MC68K_GREG_SIZE); + bcopy (&mc68k_greg_packet[MC68K_R_PC], + &deprecated_registers[DEPRECATED_REGISTER_BYTE (PC_REGNUM)], + MC68K_GREG_SIZE); + + /* Get floating-point registers, if the target system has them. + Otherwise, zero them. */ + + if (target_has_fp) + { + net_read_registers (mc68k_fpreg_packet, MC68K_FPREG_PLEN, + PTRACE_GETFPREGS); + + bcopy (&mc68k_fpreg_packet[MC68K_R_FP0], + &deprecated_registers[DEPRECATED_REGISTER_BYTE (FP0_REGNUM)], + MC68K_FPREG_SIZE * 8); + bcopy (&mc68k_fpreg_packet[MC68K_R_FPCR], + &deprecated_registers[DEPRECATED_REGISTER_BYTE (FPC_REGNUM)], + MC68K_FPREG_PLEN - (MC68K_FPREG_SIZE * 8)); + } + else + { + memset (&deprecated_registers[DEPRECATED_REGISTER_BYTE (FP0_REGNUM)], + 0, MC68K_FPREG_SIZE * 8); + memset (&deprecated_registers[DEPRECATED_REGISTER_BYTE (FPC_REGNUM)], + 0, MC68K_FPREG_PLEN - (MC68K_FPREG_SIZE * 8)); + } + + /* Mark the register cache valid. */ + + deprecated_registers_fetched (); +} + +/* Store a register or registers into the VxWorks target. + REGNO is the register to store, or -1 for all; currently, + it is ignored. FIXME look at regno to improve efficiency. */ + +void +vx_write_register (int regno) +{ + char mc68k_greg_packet[MC68K_GREG_PLEN]; + char mc68k_fpreg_packet[MC68K_FPREG_PLEN]; + + /* Store general-purpose registers. */ + + bcopy (deprecated_registers, &mc68k_greg_packet[MC68K_R_D0], + 16 * MC68K_GREG_SIZE); + bcopy (&deprecated_registers[DEPRECATED_REGISTER_BYTE (PS_REGNUM)], + &mc68k_greg_packet[MC68K_R_SR], MC68K_GREG_SIZE); + bcopy (&deprecated_registers[DEPRECATED_REGISTER_BYTE (PC_REGNUM)], + &mc68k_greg_packet[MC68K_R_PC], MC68K_GREG_SIZE); + + net_write_registers (mc68k_greg_packet, MC68K_GREG_PLEN, PTRACE_SETREGS); + + /* Store floating point registers if the target has them. */ + + if (target_has_fp) + { + bcopy (&deprecated_registers[DEPRECATED_REGISTER_BYTE (FP0_REGNUM)], + &mc68k_fpreg_packet[MC68K_R_FP0], + MC68K_FPREG_SIZE * 8); + bcopy (&deprecated_registers[DEPRECATED_REGISTER_BYTE (FPC_REGNUM)], + &mc68k_fpreg_packet[MC68K_R_FPCR], + MC68K_FPREG_PLEN - (MC68K_FPREG_SIZE * 8)); + + net_write_registers (mc68k_fpreg_packet, MC68K_FPREG_PLEN, + PTRACE_SETFPREGS); + } +} diff --git a/contrib/gdb/gdb/remote-vxmips.c b/contrib/gdb/gdb/remote-vxmips.c new file mode 100644 index 0000000..55ba49b --- /dev/null +++ b/contrib/gdb/gdb/remote-vxmips.c @@ -0,0 +1,201 @@ +/* MIPS-dependent portions of the RPC protocol + used with a VxWorks target + + Contributed by Wind River Systems. + + 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 <stdio.h> +#include "defs.h" + +#include "vx-share/regPacket.h" +#include "frame.h" +#include "inferior.h" +#include "target.h" +#include "gdbcore.h" +#include "command.h" +#include "symtab.h" +#include "symfile.h" +#include "regcache.h" + +#include "gdb_string.h" +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <rpc/rpc.h> +#include <sys/time.h> /* UTek's <rpc/rpc.h> doesn't #incl this */ +#include <netdb.h> +#include "vx-share/ptrace.h" +#include "vx-share/xdr_ptrace.h" +#include "vx-share/xdr_ld.h" +#include "vx-share/xdr_rdb.h" +#include "vx-share/dbgRpcLib.h" + +/* get rid of value.h if possible */ +#include <value.h> +#include <symtab.h> + +/* Flag set if target has fpu */ + +extern int target_has_fp; + +/* Generic register read/write routines in remote-vx.c. */ + +extern void net_read_registers (); +extern void net_write_registers (); + +/* Read a register or registers from the VxWorks target. + REGNO is the register to read, or -1 for all; currently, + it is ignored. FIXME look at regno to improve efficiency. */ + +void +vx_read_register (int regno) +{ + char mips_greg_packet[MIPS_GREG_PLEN]; + char mips_fpreg_packet[MIPS_FPREG_PLEN]; + + /* Get general-purpose registers. */ + + net_read_registers (mips_greg_packet, MIPS_GREG_PLEN, PTRACE_GETREGS); + + /* this code copies the registers obtained by RPC + stored in a structure(s) like this : + + Register(s) Offset(s) + gp 0-31 0x00 + hi 0x80 + lo 0x84 + sr 0x88 + pc 0x8c + + into a stucture like this: + + 0x00 GP 0-31 + 0x80 SR + 0x84 LO + 0x88 HI + 0x8C BAD --- Not available currently + 0x90 CAUSE --- Not available currently + 0x94 PC + 0x98 FP 0-31 + 0x118 FCSR + 0x11C FIR --- Not available currently + 0x120 FP --- Not available currently + + structure is 0x124 (292) bytes in length */ + + /* Copy the general registers. */ + + bcopy (&mips_greg_packet[MIPS_R_GP0], &deprecated_registers[0], + 32 * MIPS_GREG_SIZE); + + /* Copy SR, LO, HI, and PC. */ + + bcopy (&mips_greg_packet[MIPS_R_SR], + &deprecated_registers[DEPRECATED_REGISTER_BYTE (PS_REGNUM)], MIPS_GREG_SIZE); + bcopy (&mips_greg_packet[MIPS_R_LO], + &deprecated_registers[DEPRECATED_REGISTER_BYTE (mips_regnum (current_gdbarch)->lo)], MIPS_GREG_SIZE); + bcopy (&mips_greg_packet[MIPS_R_HI], + &deprecated_registers[DEPRECATED_REGISTER_BYTE (mips_regnum (current_gdbarch)->hi)], MIPS_GREG_SIZE); + bcopy (&mips_greg_packet[MIPS_R_PC], + &deprecated_registers[DEPRECATED_REGISTER_BYTE (mips_regnum (current_gdbarch)->pc)], MIPS_GREG_SIZE); + + /* If the target has floating point registers, fetch them. + Otherwise, zero the floating point register values in + registers[] for good measure, even though we might not + need to. */ + + if (target_has_fp) + { + net_read_registers (mips_fpreg_packet, MIPS_FPREG_PLEN, + PTRACE_GETFPREGS); + + /* Copy the floating point registers. */ + + bcopy (&mips_fpreg_packet[MIPS_R_FP0], + &deprecated_registers[DEPRECATED_REGISTER_BYTE (FP0_REGNUM)], + DEPRECATED_REGISTER_RAW_SIZE (FP0_REGNUM) * 32); + + /* Copy the floating point control/status register (fpcsr). */ + + bcopy (&mips_fpreg_packet[MIPS_R_FPCSR], + &deprecated_registers[DEPRECATED_REGISTER_BYTE (mips_regnum (current_gdbarch)->fp_control_status)], + DEPRECATED_REGISTER_RAW_SIZE (mips_regnum (current_gdbarch)->fp_control_status)); + } + else + { + memset (&deprecated_registers[DEPRECATED_REGISTER_BYTE (FP0_REGNUM)], + 0, DEPRECATED_REGISTER_RAW_SIZE (FP0_REGNUM) * 32); + memset (&deprecated_registers[DEPRECATED_REGISTER_BYTE (mips_regnum (current_gdbarch)->fp_control_status)], + 0, DEPRECATED_REGISTER_RAW_SIZE (mips_regnum (current_gdbarch)->fp_control_status)); + } + + /* Mark the register cache valid. */ + + deprecated_registers_fetched (); +} + +/* Store a register or registers into the VxWorks target. + REGNO is the register to store, or -1 for all; currently, + it is ignored. FIXME look at regno to improve efficiency. */ + +vx_write_register (int regno) +{ + char mips_greg_packet[MIPS_GREG_PLEN]; + char mips_fpreg_packet[MIPS_FPREG_PLEN]; + + /* Store general registers. */ + + bcopy (&deprecated_registers[0], &mips_greg_packet[MIPS_R_GP0], + 32 * MIPS_GREG_SIZE); + + /* Copy SR, LO, HI, and PC. */ + + bcopy (&deprecated_registers[DEPRECATED_REGISTER_BYTE (PS_REGNUM)], + &mips_greg_packet[MIPS_R_SR], MIPS_GREG_SIZE); + bcopy (&deprecated_registers[DEPRECATED_REGISTER_BYTE (mips_regnum (current_gdbarch)->lo)], + &mips_greg_packet[MIPS_R_LO], MIPS_GREG_SIZE); + bcopy (&deprecated_registers[DEPRECATED_REGISTER_BYTE (mips_regnum (current_gdbarch)->hi)], + &mips_greg_packet[MIPS_R_HI], MIPS_GREG_SIZE); + bcopy (&deprecated_registers[DEPRECATED_REGISTER_BYTE (mips_regnum (current_gdbarch)->pc)], + &mips_greg_packet[MIPS_R_PC], MIPS_GREG_SIZE); + + net_write_registers (mips_greg_packet, MIPS_GREG_PLEN, PTRACE_SETREGS); + + /* Store floating point registers if the target has them. */ + + if (target_has_fp) + { + /* Copy the floating point data registers. */ + + bcopy (&deprecated_registers[DEPRECATED_REGISTER_BYTE (FP0_REGNUM)], + &mips_fpreg_packet[MIPS_R_FP0], + DEPRECATED_REGISTER_RAW_SIZE (FP0_REGNUM) * 32); + + /* Copy the floating point control/status register (fpcsr). */ + + bcopy (&deprecated_registers[DEPRECATED_REGISTER_BYTE (mips_regnum (current_gdbarch)->fp_control_status)], + &mips_fpreg_packet[MIPS_R_FPCSR], + DEPRECATED_REGISTER_RAW_SIZE (mips_regnum (current_gdbarch)->fp_control_status)); + + net_write_registers (mips_fpreg_packet, MIPS_FPREG_PLEN, + PTRACE_SETFPREGS); + } +} diff --git a/contrib/gdb/gdb/remote-vxsparc.c b/contrib/gdb/gdb/remote-vxsparc.c new file mode 100644 index 0000000..118e517 --- /dev/null +++ b/contrib/gdb/gdb/remote-vxsparc.c @@ -0,0 +1,128 @@ +/* SPARC-specific portions of the RPC protocol for VxWorks. + + Contributed by Wind River Systems. + + 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 "defs.h" +#include "regcache.h" + +#include "gdb_string.h" + +#include "sparc-tdep.h" + +#include "vx-share/ptrace.h" +#include "vx-share/regPacket.h" + +#define SPARC_R_G1 (SPARC_R_G0 + SPARC_GREG_SIZE) + +const struct sparc_gregset vxsparc_gregset = +{ + SPARC_R_PSR, /* %psr */ + SPARC_R_PC, /* %pc */ + SPARC_R_NPC, /* %npc */ + SPARC_R_Y, /* %y */ + SPARC_R_WIM, /* %wim */ + SPARC_R_TBR, /* %tbr */ + SPARC_R_G1, /* %g1 */ + SPARC_R_I0 /* %l0 */ +}; + +/* Flag set if target has an FPU. */ + +extern int target_has_fp; + +/* Generic register read/write routines in remote-vx.c. */ + +extern void net_read_registers (); +extern void net_write_registers (); + +/* Read a register or registers from the VxWorks target. REGNUM is + the register to read, or -1 for all; currently, it is ignored. + FIXME: Look at REGNUM to improve efficiency. */ + +void +vx_read_register (int regnum) +{ + struct regcache *regcache = current_regcache; + char gregs[SPARC_GREG_PLEN]; + char fpregs[SPARC_FPREG_PLEN]; + CORE_ADDR sp; + + /* Get the general-purpose registers. */ + net_read_registers (gregs, SPARC_GREG_PLEN, PTRACE_GETREGS); + sparc32_supply_gregset (&vxsparc_gregset, regcache, -1, gregs); + + /* If the target has floating-point registers, fetch them. + Otherwise, zero the floating-point register values in GDB's + register cache for good measure, even though we might not need + to. */ + if (target_has_fp) + net_read_registers (fpregs, SPARC_FPREG_PLEN, PTRACE_GETFPREGS); + else + memset (fpregs, 0, SPARC_FPREG_PLEN); + sparc32_supply_fpregset (regcache, -1, fpregs); +} + +/* Store a register or registers into the VxWorks target. REGNUM is + the register to store, or -1 for all; currently, it is ignored. + FIXME: Look at REGNUM to improve efficiency. */ + +void +vx_write_register (int regnum) +{ + struct regcache *regcache = current_regcache; + char gregs[SPARC_GREG_PLEN]; + char fpregs[SPARC_FPREG_PLEN]; + int gregs_p = 1; + int fpregs_p = 1; + CORE_ADDR sp; + + if (regnum != -1) + { + if ((SPARC_G0_REGNUM <= regnum && regnum <= SPARC_I7_REGNUM) + || (SPARC32_Y_REGNUM <= regnum && regnum <= SPARC32_NPC_REGNUM)) + fpregs_p = 0; + else + gregs_p = 0; + } + + /* Store the general-purpose registers. */ + if (gregs_p) + { + sparc32_collect_gregset (&vxsparc_gregset, regcache, -1, gregs); + net_write_registers (gregs, SPARC_GREG_PLEN, PTRACE_SETREGS); + + /* Deal with the stack regs. */ + if (regnum == -1 || regnum == SPARC_SP_REGNUM + || (regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM)) + { + ULONGEST sp; + + regcache_cooked_read_unsigned (regcache, SPARC_SP_REGNUM, &sp); + sparc_collect_rwindow (regcache, sp, regnum); + } + } + + /* Store the floating-point registers if the target has them. */ + if (fpregs_p && target_has_fp) + { + sparc32_collect_fpregset (regcache, -1, fpregs); + net_write_registers (fpregs, SPARC_FPREG_PLEN, PTRACE_SETFPREGS); + } +} diff --git a/contrib/gdb/gdb/ser-e7kpc.c b/contrib/gdb/gdb/ser-e7kpc.c new file mode 100644 index 0000000..1efe142 --- /dev/null +++ b/contrib/gdb/gdb/ser-e7kpc.c @@ -0,0 +1,436 @@ +/* Remote serial interface using Renesas E7000 PC ISA card in a PC + Copyright 1994, 1996, 1997, 1998, 1999, 2000 + 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 "defs.h" +#if defined __GO32__ || defined _WIN32 +#include "serial.h" +#include "gdb_string.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + +#ifdef __GO32__ +#include <sys/dos.h> +#endif + +static int e7000pc_open (struct serial *scb, const char *name); +static void e7000pc_raw (struct serial *scb); +static int e7000pc_readchar (struct serial *scb, int timeout); +static int e7000pc_setbaudrate (struct serial *scb, int rate); +static int e7000pc_write (struct serial *scb, const char *str, int len); +static void e7000pc_close (struct serial *scb); +static serial_ttystate e7000pc_get_tty_state (struct serial *scb); +static int e7000pc_set_tty_state (struct serial *scb, serial_ttystate state); + +#define OFF_DPD 0x0000 +#define OFF_DDP 0x1000 +#define OFF_CPD 0x2000 +#define OFF_CDP 0x2400 +#define OFF_FA 0x3000 +#define OFF_FB 0x3002 +#define OFF_FC 0x3004 +#define OFF_IRQTOD 0x3008 +#define OFF_IRQTOP 0x300a +#define OFF_READY 0x300c +#define OFF_PON 0x300e + +#define IDLE 0x0000 +#define CMD_CI 0x4349 +#define CMD_CO 0x434f +#define CMD_LO 0x4c4f +#define CMD_LS 0x4c53 +#define CMD_SV 0x5356 +#define CMD_SS 0x5353 +#define CMD_OK 0x4f4b +#define CMD_ER 0x4552 +#define CMD_NF 0x4e46 +#define CMD_AB 0x4142 +#define CMD_ED 0x4544 +#define CMD_CE 0x4345 + +static unsigned long fa; +static unsigned long irqtod; +static unsigned long ready; +static unsigned long fb; +static unsigned long cpd; +static unsigned long cdp; +static unsigned long ready; +static unsigned long pon; +static unsigned long irqtop; +static unsigned long board_at; + +#ifdef __GO32__ + +#define SET_BYTE(x,y) { char _buf = y;dosmemput(&_buf,1, x);} +#define SET_WORD(x,y) { short _buf = y;dosmemput(&_buf,2, x);} +#define GET_BYTE(x) ( dosmemget(x,1,&bb), bb) +#define GET_WORD(x) ( dosmemget(x,2,&sb), sb) +static unsigned char bb; +static unsigned short sb; + +#else /* win32 */ + +#define SET_BYTE(x,y) *(volatile unsigned char *)(x) = (y) +#define SET_WORD(x,y) *(volatile unsigned short *)(x) = (y) +#define GET_BYTE(x) (*(volatile unsigned char *)(x)) +#define GET_WORD(x) (*(volatile unsigned short *)(x)) +#define dosmemget(FROM, LEN, TO) memcpy ((void *)(TO), (void *)(FROM), (LEN)) +#define dosmemput(FROM, LEN, TO) memcpy ((void *)(TO), (void *)(FROM), (LEN)) +#endif + +static struct sw + { + int sw; + int addr; + } +sigs[] = +{ + { + 0x14, 0xd0000 + } + , + { + 0x15, 0xd4000 + } + , + { + 0x16, 0xd8000 + } + , + { + 0x17, 0xdc000 + } + , + 0 +}; + +#define get_ds_base() 0 + +static int +e7000pc_init (void) +{ + int try; + unsigned long dsbase; + + dsbase = get_ds_base (); + + /* Look around in memory for the board's signature */ + + for (try = 0; sigs[try].sw; try++) + { + int val; + board_at = sigs[try].addr - dsbase; + fa = board_at + OFF_FA; + fb = board_at + OFF_FB; + cpd = board_at + OFF_CPD; + cdp = board_at + OFF_CDP; + ready = board_at + OFF_READY; + pon = board_at + OFF_PON; + irqtop = board_at + OFF_IRQTOP; + irqtod = board_at + OFF_IRQTOD; + + val = GET_WORD (ready); + + if (val == (0xaaa0 | sigs[try].sw)) + { + if (GET_WORD (pon) & 0xf) + { + SET_WORD (fa, 0); + SET_WORD (fb, 0); + + SET_WORD (irqtop, 1); /* Disable interrupts from e7000 */ + SET_WORD (ready, 1); + printf_filtered ("\nConnected to the E7000PC at address 0x%x\n", + sigs[try].addr); + return 1; + } + error ("The E7000 PC board is working, but the E7000 is turned off.\n"); + return 0; + } + } + + error ("GDB cannot connect to the E7000 PC board, check that it is installed\n\ +and that the switch settings are correct. Some other DOS programs can \n\ +stop the board from working. Try starting from a very minimal boot, \n\ +perhaps you need to disable EMM386 over the region where the board has\n\ +its I/O space, remove other unneeded cards, etc etc\n"); + return 0; + +} + +static int pbuf_size; +static int pbuf_index; + +/* Return next byte from cdp. If no more, then return -1. */ + +static int +e7000_get (void) +{ + static char pbuf[1000]; + char tmp[1000]; + int x; + + if (pbuf_index < pbuf_size) + { + x = pbuf[pbuf_index++]; + } + else if ((GET_WORD (fb) & 1)) + { + int i; + pbuf_size = GET_WORD (cdp + 2); + + dosmemget (cdp + 8, pbuf_size + 1, tmp); + + /* Tell the E7000 we've eaten */ + SET_WORD (fb, 0); + /* Swap it around */ + for (i = 0; i < pbuf_size; i++) + { + pbuf[i] = tmp[i ^ 1]; + } + pbuf_index = 0; + x = pbuf[pbuf_index++]; + } + else + { + x = -1; + } + return x; +} + +/* Works just like read(), except that it takes a TIMEOUT in seconds. Note + that TIMEOUT == 0 is a poll, and TIMEOUT == -1 means wait forever. */ + +static int +dosasync_read (int fd, char *buf, int len, int timeout) +{ + long now; + long then; + int i = 0; + + /* Then look for some more if we're still hungry */ + time (&now); + then = now + timeout; + while (i < len) + { + int ch = e7000_get (); + + /* While there's room in the buffer, and we've already + read the stuff in, suck it over */ + if (ch != -1) + { + buf[i++] = ch; + while (i < len && pbuf_index < pbuf_size) + { + ch = e7000_get (); + if (ch == -1) + break; + buf[i++] = ch; + } + } + + time (&now); + + if (timeout == 0) + return i; + if (now >= then && timeout > 0) + { + return i; + } + } + return len; +} + + +static int +dosasync_write (int fd, const char *buf, int len) +{ + int i; + char dummy[1000]; + + /* Construct copy locally */ + ((short *) dummy)[0] = CMD_CI; + ((short *) dummy)[1] = len; + ((short *) dummy)[2] = 0; + ((short *) dummy)[3] = 0; + for (i = 0; i < len; i++) + { + dummy[(8 + i) ^ 1] = buf[i]; + } + + /* Wait for the card to get ready */ + while (GET_WORD (fa) & 1); + + /* Blast onto the ISA card */ + dosmemput (dummy, 8 + len + 1, cpd); + + SET_WORD (fa, 1); + SET_WORD (irqtod, 1); /* Interrupt the E7000 */ + + return len; +} + +static int +e7000pc_open (struct serial *scb, const char *name) +{ + if (strncasecmp (name, "pc", 2) != 0) + { + errno = ENOENT; + return -1; + } + + scb->fd = e7000pc_init (); + + if (!scb->fd) + return -1; + + return 0; +} + +static int +e7000pc_noop (struct serial *scb) +{ + return 0; +} + +static void +e7000pc_raw (struct serial *scb) +{ + /* Always in raw mode */ +} + +static int +e7000pc_readchar (struct serial *scb, int timeout) +{ + char buf; + +top: + + if (dosasync_read (scb->fd, &buf, 1, timeout)) + { + if (buf == 0) + goto top; + return buf; + } + else + return SERIAL_TIMEOUT; +} + +struct e7000pc_ttystate +{ + int dummy; +}; + +/* e7000pc_{get set}_tty_state() are both dummys to fill out the function + vector. Someday, they may do something real... */ + +static serial_ttystate +e7000pc_get_tty_state (struct serial *scb) +{ + struct e7000pc_ttystate *state; + + state = (struct e7000pc_ttystate *) xmalloc (sizeof *state); + + return (serial_ttystate) state; +} + +static int +e7000pc_set_tty_state (struct serial *scb, serial_ttystate ttystate) +{ + return 0; +} + +static int +e7000pc_noflush_set_tty_state (struct serial *scb, + serial_ttystate new_ttystate, + serial_ttystate old_ttystate) +{ + return 0; +} + +static void +e7000pc_print_tty_state (struct serial *scb, + serial_ttystate ttystate, + struct ui_file *stream) +{ + /* Nothing to print. */ + return; +} + +static int +e7000pc_setbaudrate (struct serial *scb, int rate) +{ + return 0; +} + +static int +e7000pc_setstopbits (struct serial *scb, int rate) +{ + return 0; +} + +static int +e7000pc_write (struct serial *scb, const char *str, int len) +{ + dosasync_write (scb->fd, str, len); + + return 0; +} + +static void +e7000pc_close (struct serial *scb) +{ +} + +static struct serial_ops e7000pc_ops = +{ + "pc", + 0, + e7000pc_open, + e7000pc_close, + e7000pc_readchar, + e7000pc_write, + e7000pc_noop, /* flush output */ + e7000pc_noop, /* flush input */ + e7000pc_noop, /* send break -- currently used only for nindy */ + e7000pc_raw, + e7000pc_get_tty_state, + e7000pc_set_tty_state, + e7000pc_print_tty_state, + e7000pc_noflush_set_tty_state, + e7000pc_setbaudrate, + e7000pc_setstopbits, + e7000pc_noop, /* wait for output to drain */ +}; + +#endif /*_WIN32 or __GO32__*/ + +extern initialize_file_ftype _initialize_ser_e7000pc; /* -Wmissing-prototypes */ + +void +_initialize_ser_e7000pc (void) +{ +#if defined __GO32__ || defined _WIN32 + serial_add_interface (&e7000pc_ops); +#endif +} diff --git a/contrib/gdb/gdb/ser-go32.c b/contrib/gdb/gdb/ser-go32.c new file mode 100644 index 0000000..cea01cd --- /dev/null +++ b/contrib/gdb/gdb/ser-go32.c @@ -0,0 +1,964 @@ +/* Remote serial interface for local (hardwired) serial ports for GO32. + Copyright 1992, 1993, 2000, 2001 Free Software Foundation, Inc. + + Contributed by Nigel Stephens, Algorithmics Ltd. (nigel@algor.co.uk). + + This version uses DPMI interrupts to handle buffered i/o + without the separate "asynctsr" program. + + 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 "defs.h" +#include "gdbcmd.h" +#include "serial.h" +#include "gdb_string.h" + + +/* + * NS16550 UART registers + */ + +#define COM1ADDR 0x3f8 +#define COM2ADDR 0x2f8 +#define COM3ADDR 0x3e8 +#define COM4ADDR 0x3e0 + +#define com_data 0 /* data register (R/W) */ +#define com_dlbl 0 /* divisor latch low (W) */ +#define com_ier 1 /* interrupt enable (W) */ +#define com_dlbh 1 /* divisor latch high (W) */ +#define com_iir 2 /* interrupt identification (R) */ +#define com_fifo 2 /* FIFO control (W) */ +#define com_lctl 3 /* line control register (R/W) */ +#define com_cfcr 3 /* line control register (R/W) */ +#define com_mcr 4 /* modem control register (R/W) */ +#define com_lsr 5 /* line status register (R/W) */ +#define com_msr 6 /* modem status register (R/W) */ + +/* + * Constants for computing 16 bit baud rate divisor (lower byte + * in com_dlbl, upper in com_dlbh) from 1.8432MHz crystal. Divisor is + * 1.8432 MHz / (16 * X) for X bps. If the baud rate can't be set + * to within +- (desired_rate*SPEED_TOLERANCE/1000) bps, we fail. + */ +#define COMTICK (1843200/16) +#define SPEED_TOLERANCE 30 /* thousandths; real == desired +- 3.0% */ + +/* interrupt enable register */ +#define IER_ERXRDY 0x1 /* int on rx ready */ +#define IER_ETXRDY 0x2 /* int on tx ready */ +#define IER_ERLS 0x4 /* int on line status change */ +#define IER_EMSC 0x8 /* int on modem status change */ + +/* interrupt identification register */ +#define IIR_FIFO_MASK 0xc0 /* set if FIFOs are enabled */ +#define IIR_IMASK 0xf /* interrupt cause mask */ +#define IIR_NOPEND 0x1 /* nothing pending */ +#define IIR_RLS 0x6 /* receive line status */ +#define IIR_RXRDY 0x4 /* receive ready */ +#define IIR_RXTOUT 0xc /* receive timeout */ +#define IIR_TXRDY 0x2 /* transmit ready */ +#define IIR_MLSC 0x0 /* modem status */ + + +/* fifo control register */ +#define FIFO_ENABLE 0x01 /* enable fifo */ +#define FIFO_RCV_RST 0x02 /* reset receive fifo */ +#define FIFO_XMT_RST 0x04 /* reset transmit fifo */ +#define FIFO_DMA_MODE 0x08 /* enable dma mode */ +#define FIFO_TRIGGER_1 0x00 /* trigger at 1 char */ +#define FIFO_TRIGGER_4 0x40 /* trigger at 4 chars */ +#define FIFO_TRIGGER_8 0x80 /* trigger at 8 chars */ +#define FIFO_TRIGGER_14 0xc0 /* trigger at 14 chars */ + +/* character format control register */ +#define CFCR_DLAB 0x80 /* divisor latch */ +#define CFCR_SBREAK 0x40 /* send break */ +#define CFCR_PZERO 0x30 /* zero parity */ +#define CFCR_PONE 0x20 /* one parity */ +#define CFCR_PEVEN 0x10 /* even parity */ +#define CFCR_PODD 0x00 /* odd parity */ +#define CFCR_PENAB 0x08 /* parity enable */ +#define CFCR_STOPB 0x04 /* 2 stop bits */ +#define CFCR_8BITS 0x03 /* 8 data bits */ +#define CFCR_7BITS 0x02 /* 7 data bits */ +#define CFCR_6BITS 0x01 /* 6 data bits */ +#define CFCR_5BITS 0x00 /* 5 data bits */ + +/* modem control register */ +#define MCR_LOOPBACK 0x10 /* loopback */ +#define MCR_IENABLE 0x08 /* output 2 = int enable */ +#define MCR_DRS 0x04 /* output 1 = xxx */ +#define MCR_RTS 0x02 /* enable RTS */ +#define MCR_DTR 0x01 /* enable DTR */ + +/* line status register */ +#define LSR_RCV_FIFO 0x80 /* error in receive fifo */ +#define LSR_TSRE 0x40 /* transmitter empty */ +#define LSR_TXRDY 0x20 /* transmitter ready */ +#define LSR_BI 0x10 /* break detected */ +#define LSR_FE 0x08 /* framing error */ +#define LSR_PE 0x04 /* parity error */ +#define LSR_OE 0x02 /* overrun error */ +#define LSR_RXRDY 0x01 /* receiver ready */ +#define LSR_RCV_MASK 0x1f + +/* modem status register */ +#define MSR_DCD 0x80 +#define MSR_RI 0x40 +#define MSR_DSR 0x20 +#define MSR_CTS 0x10 +#define MSR_DDCD 0x08 +#define MSR_TERI 0x04 +#define MSR_DDSR 0x02 +#define MSR_DCTS 0x01 + +#include <time.h> +#include <dos.h> +#include <go32.h> +#include <dpmi.h> +typedef unsigned long u_long; + +/* 16550 rx fifo trigger point */ +#define FIFO_TRIGGER FIFO_TRIGGER_4 + +/* input buffer size */ +#define CBSIZE 4096 + +#define RAWHZ 18 + +#ifdef DOS_STATS +#define CNT_RX 16 +#define CNT_TX 17 +#define CNT_STRAY 18 +#define CNT_ORUN 19 +#define NCNT 20 + +static int intrcnt; +static int cnts[NCNT]; +static char *cntnames[NCNT] = +{ + /* h/w interrupt counts. */ + "mlsc", "nopend", "txrdy", "?3", + "rxrdy", "?5", "rls", "?7", + "?8", "?9", "?a", "?b", + "rxtout", "?d", "?e", "?f", + /* s/w counts. */ + "rxcnt", "txcnt", "stray", "swoflo" +}; + +#define COUNT(x) cnts[x]++ +#else +#define COUNT(x) +#endif + +/* Main interrupt controller port addresses. */ +#define ICU_BASE 0x20 +#define ICU_OCW2 (ICU_BASE + 0) +#define ICU_MASK (ICU_BASE + 1) + +/* Original interrupt controller mask register. */ +unsigned char icu_oldmask; + +/* Maximum of 8 interrupts (we don't handle the slave icu yet). */ +#define NINTR 8 + +static struct intrupt + { + char inuse; + struct dos_ttystate *port; + _go32_dpmi_seginfo old_rmhandler; + _go32_dpmi_seginfo old_pmhandler; + _go32_dpmi_seginfo new_rmhandler; + _go32_dpmi_seginfo new_pmhandler; + _go32_dpmi_registers regs; + } +intrupts[NINTR]; + + +static struct dos_ttystate + { + int base; + int irq; + int refcnt; + struct intrupt *intrupt; + int fifo; + int baudrate; + unsigned char cbuf[CBSIZE]; + unsigned int first; + unsigned int count; + int txbusy; + unsigned char old_mcr; + int ferr; + int perr; + int oflo; + int msr; + } +ports[4] = +{ + { + COM1ADDR, 4, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0 + } + , + { + COM2ADDR, 3, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0 + } + , + { + COM3ADDR, 4, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0 + } + , + { + COM4ADDR, 3, 0, NULL, 0, 0, "", 0, 0, 0, 0, 0, 0, 0, 0 + } +}; + +static int dos_open (struct serial *scb, const char *name); +static void dos_raw (struct serial *scb); +static int dos_readchar (struct serial *scb, int timeout); +static int dos_setbaudrate (struct serial *scb, int rate); +static int dos_write (struct serial *scb, const char *str, int len); +static void dos_close (struct serial *scb); +static serial_ttystate dos_get_tty_state (struct serial *scb); +static int dos_set_tty_state (struct serial *scb, serial_ttystate state); +static int dos_baudconv (int rate); + +#define inb(p,a) inportb((p)->base + (a)) +#define outb(p,a,v) outportb((p)->base + (a), (v)) +#define disable() asm volatile ("cli"); +#define enable() asm volatile ("sti"); + + +static int +dos_getc (volatile struct dos_ttystate *port) +{ + int c; + + if (port->count == 0) + return -1; + + c = port->cbuf[port->first]; + disable (); + port->first = (port->first + 1) & (CBSIZE - 1); + port->count--; + enable (); + return c; +} + + +static int +dos_putc (int c, struct dos_ttystate *port) +{ + if (port->count >= CBSIZE - 1) + return -1; + port->cbuf[(port->first + port->count) & (CBSIZE - 1)] = c; + port->count++; + return 0; +} + + + +static void +dos_comisr (int irq) +{ + struct dos_ttystate *port; + unsigned char iir, lsr, c; + + disable (); /* Paranoia */ + outportb (ICU_OCW2, 0x20); /* End-Of-Interrupt */ +#ifdef DOS_STATS + ++intrcnt; +#endif + + port = intrupts[irq].port; + if (!port) + { + COUNT (CNT_STRAY); + return; /* not open */ + } + + while (1) + { + iir = inb (port, com_iir) & IIR_IMASK; + switch (iir) + { + + case IIR_RLS: + lsr = inb (port, com_lsr); + goto rx; + + case IIR_RXTOUT: + case IIR_RXRDY: + lsr = 0; + + rx: + do + { + c = inb (port, com_data); + if (lsr & (LSR_BI | LSR_FE | LSR_PE | LSR_OE)) + { + if (lsr & (LSR_BI | LSR_FE)) + port->ferr++; + else if (lsr & LSR_PE) + port->perr++; + if (lsr & LSR_OE) + port->oflo++; + } + + if (dos_putc (c, port) < 0) + { + COUNT (CNT_ORUN); + } + else + { + COUNT (CNT_RX); + } + } + while ((lsr = inb (port, com_lsr)) & LSR_RXRDY); + break; + + case IIR_MLSC: + /* could be used to flowcontrol Tx */ + port->msr = inb (port, com_msr); + break; + + case IIR_TXRDY: + port->txbusy = 0; + break; + + case IIR_NOPEND: + /* no more pending interrupts, all done */ + return; + + default: + /* unexpected interrupt, ignore */ + break; + } + COUNT (iir); + } +} + +#define ISRNAME(x) dos_comisr##x +#define ISR(x) static void ISRNAME(x)(void) {dos_comisr(x);} + +ISR (0) ISR (1) ISR (2) ISR (3) /* OK */ +ISR (4) ISR (5) ISR (6) ISR (7) /* OK */ + +typedef void (*isr_t) (void); + +static isr_t isrs[NINTR] = + { + ISRNAME (0), ISRNAME (1), ISRNAME (2), ISRNAME (3), + ISRNAME (4), ISRNAME (5), ISRNAME (6), ISRNAME (7) + }; + + + +static struct intrupt * +dos_hookirq (unsigned int irq) +{ + struct intrupt *intr; + unsigned int vec; + isr_t isr; + + if (irq >= NINTR) + return 0; + + intr = &intrupts[irq]; + if (intr->inuse) + return 0; + + vec = 0x08 + irq; + isr = isrs[irq]; + + /* setup real mode handler */ + _go32_dpmi_get_real_mode_interrupt_vector (vec, &intr->old_rmhandler); + + intr->new_rmhandler.pm_selector = _go32_my_cs (); + intr->new_rmhandler.pm_offset = (u_long) isr; + if (_go32_dpmi_allocate_real_mode_callback_iret (&intr->new_rmhandler, + &intr->regs)) + { + return 0; + } + + if (_go32_dpmi_set_real_mode_interrupt_vector (vec, &intr->new_rmhandler)) + { + return 0; + } + + /* setup protected mode handler */ + _go32_dpmi_get_protected_mode_interrupt_vector (vec, &intr->old_pmhandler); + + intr->new_pmhandler.pm_selector = _go32_my_cs (); + intr->new_pmhandler.pm_offset = (u_long) isr; + _go32_dpmi_allocate_iret_wrapper (&intr->new_pmhandler); + + if (_go32_dpmi_set_protected_mode_interrupt_vector (vec, + &intr->new_pmhandler)) + { + return 0; + } + + /* setup interrupt controller mask */ + disable (); + outportb (ICU_MASK, inportb (ICU_MASK) & ~(1 << irq)); + enable (); + + intr->inuse = 1; + return intr; +} + + +static void +dos_unhookirq (struct intrupt *intr) +{ + unsigned int irq, vec; + unsigned char mask; + + irq = intr - intrupts; + vec = 0x08 + irq; + + /* restore old interrupt mask bit */ + mask = 1 << irq; + disable (); + outportb (ICU_MASK, inportb (ICU_MASK) | (mask & icu_oldmask)); + enable (); + + /* remove real mode handler */ + _go32_dpmi_set_real_mode_interrupt_vector (vec, &intr->old_rmhandler); + _go32_dpmi_free_real_mode_callback (&intr->new_rmhandler); + + /* remove protected mode handler */ + _go32_dpmi_set_protected_mode_interrupt_vector (vec, &intr->old_pmhandler); + _go32_dpmi_free_iret_wrapper (&intr->new_pmhandler); + intr->inuse = 0; +} + + + +static int +dos_open (struct serial *scb, const char *name) +{ + struct dos_ttystate *port; + int fd, i; + + if (strncasecmp (name, "/dev/", 5) == 0) + name += 5; + else if (strncasecmp (name, "\\dev\\", 5) == 0) + name += 5; + + if (strlen (name) != 4 || strncasecmp (name, "com", 3) != 0) + { + errno = ENOENT; + return -1; + } + + if (name[3] < '1' || name[3] > '4') + { + errno = ENOENT; + return -1; + } + + /* FIXME: this is a Bad Idea (tm)! One should *never* invent file + handles, since they might be already used by other files/devices. + The Right Way to do this is to create a real handle by dup()'ing + some existing one. */ + fd = name[3] - '1'; + port = &ports[fd]; + if (port->refcnt++ > 0) + { + /* Device already opened another user. Just point at it. */ + scb->fd = fd; + return 0; + } + + /* force access to ID reg */ + outb (port, com_cfcr, 0); + outb (port, com_iir, 0); + for (i = 0; i < 17; i++) + { + if ((inb (port, com_iir) & 0x38) == 0) + goto ok; + (void) inb (port, com_data); /* clear recv */ + } + errno = ENODEV; + return -1; + +ok: + /* disable all interrupts in chip */ + outb (port, com_ier, 0); + + /* tentatively enable 16550 fifo, and see if it responds */ + outb (port, com_fifo, + FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER); + sleep (1); + port->fifo = ((inb (port, com_iir) & IIR_FIFO_MASK) == IIR_FIFO_MASK); + + /* clear pending status reports. */ + (void) inb (port, com_lsr); + (void) inb (port, com_msr); + + /* enable external interrupt gate (to avoid floating IRQ) */ + outb (port, com_mcr, MCR_IENABLE); + + /* hook up interrupt handler and initialise icu */ + port->intrupt = dos_hookirq (port->irq); + if (!port->intrupt) + { + outb (port, com_mcr, 0); + outb (port, com_fifo, 0); + errno = ENODEV; + return -1; + } + + disable (); + + /* record port */ + port->intrupt->port = port; + scb->fd = fd; + + /* clear rx buffer, tx busy flag and overflow count */ + port->first = port->count = 0; + port->txbusy = 0; + port->oflo = 0; + + /* set default baud rate and mode: 9600,8,n,1 */ + i = dos_baudconv (port->baudrate = 9600); + outb (port, com_cfcr, CFCR_DLAB); + outb (port, com_dlbl, i & 0xff); + outb (port, com_dlbh, i >> 8); + outb (port, com_cfcr, CFCR_8BITS); + + /* enable all interrupts */ + outb (port, com_ier, IER_ETXRDY | IER_ERXRDY | IER_ERLS | IER_EMSC); + + /* enable DTR & RTS */ + outb (port, com_mcr, MCR_DTR | MCR_RTS | MCR_IENABLE); + + enable (); + + return 0; +} + + +static void +dos_close (struct serial *scb) +{ + struct dos_ttystate *port; + struct intrupt *intrupt; + + if (!scb) + return; + + port = &ports[scb->fd]; + + if (port->refcnt-- > 1) + return; + + if (!(intrupt = port->intrupt)) + return; + + /* disable interrupts, fifo, flow control */ + disable (); + port->intrupt = 0; + intrupt->port = 0; + outb (port, com_fifo, 0); + outb (port, com_ier, 0); + enable (); + + /* unhook handler, and disable interrupt gate */ + dos_unhookirq (intrupt); + outb (port, com_mcr, 0); + + /* Check for overflow errors */ + if (port->oflo) + { + fprintf_unfiltered (gdb_stderr, + "Serial input overruns occurred.\n"); + fprintf_unfiltered (gdb_stderr, "This system %s handle %d baud.\n", + port->fifo ? "cannot" : "needs a 16550 to", + port->baudrate); + } +} + + + +static int +dos_noop (struct serial *scb) +{ + return 0; +} + +static void +dos_raw (struct serial *scb) +{ + /* Always in raw mode */ +} + +static int +dos_readchar (struct serial *scb, int timeout) +{ + struct dos_ttystate *port = &ports[scb->fd]; + long then; + int c; + + then = rawclock () + (timeout * RAWHZ); + while ((c = dos_getc (port)) < 0) + { + if (timeout >= 0 && (rawclock () - then) >= 0) + return SERIAL_TIMEOUT; + } + + return c; +} + + +static serial_ttystate +dos_get_tty_state (struct serial *scb) +{ + struct dos_ttystate *port = &ports[scb->fd]; + struct dos_ttystate *state; + + /* Are they asking about a port we opened? */ + if (port->refcnt <= 0) + { + /* We've never heard about this port. We should fail this call, + unless they are asking about one of the 3 standard handles, + in which case we pretend the handle was open by us if it is + connected to a terminal device. This is beacuse Unix + terminals use the serial interface, so GDB expects the + standard handles to go through here. */ + if (scb->fd >= 3 || !isatty (scb->fd)) + return NULL; + } + + state = (struct dos_ttystate *) xmalloc (sizeof *state); + *state = *port; + return (serial_ttystate) state; +} + +static int +dos_set_tty_state (struct serial *scb, serial_ttystate ttystate) +{ + struct dos_ttystate *state; + + state = (struct dos_ttystate *) ttystate; + dos_setbaudrate (scb, state->baudrate); + return 0; +} + +static int +dos_noflush_set_tty_state (struct serial *scb, serial_ttystate new_ttystate, + serial_ttystate old_ttystate) +{ + struct dos_ttystate *state; + + state = (struct dos_ttystate *) new_ttystate; + dos_setbaudrate (scb, state->baudrate); + return 0; +} + +static int +dos_flush_input (struct serial *scb) +{ + struct dos_ttystate *port = &ports[scb->fd]; + disable (); + port->first = port->count = 0; + if (port->fifo) + outb (port, com_fifo, FIFO_ENABLE | FIFO_RCV_RST | FIFO_TRIGGER); + enable (); + return 0; +} + +static void +dos_print_tty_state (struct serial *scb, serial_ttystate ttystate, + struct ui_file *stream) +{ + /* Nothing to print */ + return; +} + +static int +dos_baudconv (int rate) +{ + long x, err; + + if (rate <= 0) + return -1; + +#define divrnd(n, q) (((n) * 2 / (q) + 1) / 2) /* divide and round off */ + x = divrnd (COMTICK, rate); + if (x <= 0) + return -1; + + err = divrnd (1000 * COMTICK, x * rate) - 1000; + if (err < 0) + err = -err; + if (err > SPEED_TOLERANCE) + return -1; +#undef divrnd + return x; +} + + +static int +dos_setbaudrate (struct serial *scb, int rate) +{ + struct dos_ttystate *port = &ports[scb->fd]; + + if (port->baudrate != rate) + { + int x; + unsigned char cfcr; + + x = dos_baudconv (rate); + if (x <= 0) + { + fprintf_unfiltered (gdb_stderr, "%d: impossible baudrate\n", rate); + errno = EINVAL; + return -1; + } + + disable (); + cfcr = inb (port, com_cfcr); + + outb (port, com_cfcr, CFCR_DLAB); + outb (port, com_dlbl, x & 0xff); + outb (port, com_dlbh, x >> 8); + outb (port, com_cfcr, cfcr); + port->baudrate = rate; + enable (); + } + + return 0; +} + +static int +dos_setstopbits (struct serial *scb, int num) +{ + struct dos_ttystate *port = &ports[scb->fd]; + unsigned char cfcr; + + disable (); + cfcr = inb (port, com_cfcr); + + switch (num) + { + case SERIAL_1_STOPBITS: + outb (port, com_cfcr, cfcr & ~CFCR_STOPB); + break; + case SERIAL_1_AND_A_HALF_STOPBITS: + case SERIAL_2_STOPBITS: + outb (port, com_cfcr, cfcr | CFCR_STOPB); + break; + default: + enable (); + return 1; + } + enable (); + + return 0; +} + +static int +dos_write (struct serial *scb, const char *str, int len) +{ + volatile struct dos_ttystate *port = &ports[scb->fd]; + int fifosize = port->fifo ? 16 : 1; + long then; + int cnt; + + while (len > 0) + { + /* send the data, fifosize bytes at a time */ + cnt = fifosize > len ? len : fifosize; + port->txbusy = 1; + /* Francisco Pastor <fpastor.etra-id@etra.es> says OUTSB messes + up the communications with UARTs with FIFOs. */ +#ifdef UART_FIFO_WORKS + outportsb (port->base + com_data, str, cnt); + str += cnt; + len -= cnt; +#else + for ( ; cnt > 0; cnt--, len--) + outportb (port->base + com_data, *str++); +#endif +#ifdef DOS_STATS + cnts[CNT_TX] += cnt; +#endif + /* wait for transmission to complete (max 1 sec) */ + then = rawclock () + RAWHZ; + while (port->txbusy) + { + if ((rawclock () - then) >= 0) + { + errno = EIO; + return SERIAL_ERROR; + } + } + } + return 0; +} + + +static int +dos_sendbreak (struct serial *scb) +{ + volatile struct dos_ttystate *port = &ports[scb->fd]; + unsigned char cfcr; + long then; + + cfcr = inb (port, com_cfcr); + outb (port, com_cfcr, cfcr | CFCR_SBREAK); + + /* 0.25 sec delay */ + then = rawclock () + RAWHZ / 4; + while ((rawclock () - then) < 0) + continue; + + outb (port, com_cfcr, cfcr); + return 0; +} + + +static struct serial_ops dos_ops = +{ + "hardwire", + 0, + dos_open, + dos_close, + dos_readchar, + dos_write, + dos_noop, /* flush output */ + dos_flush_input, + dos_sendbreak, + dos_raw, + dos_get_tty_state, + dos_set_tty_state, + dos_print_tty_state, + dos_noflush_set_tty_state, + dos_setbaudrate, + dos_setstopbits, + dos_noop, /* wait for output to drain */ + (void (*)(struct serial *, int))NULL /* change into async mode */ +}; + + +static void +dos_info (char *arg, int from_tty) +{ + struct dos_ttystate *port; +#ifdef DOS_STATS + int i; +#endif + + for (port = ports; port < &ports[4]; port++) + { + if (port->baudrate == 0) + continue; + printf_filtered ("Port:\tCOM%ld (%sactive)\n", (long)(port - ports) + 1, + port->intrupt ? "" : "not "); + printf_filtered ("Addr:\t0x%03x (irq %d)\n", port->base, port->irq); + printf_filtered ("16550:\t%s\n", port->fifo ? "yes" : "no"); + printf_filtered ("Speed:\t%d baud\n", port->baudrate); + printf_filtered ("Errs:\tframing %d parity %d overflow %d\n\n", + port->ferr, port->perr, port->oflo); + } + +#ifdef DOS_STATS + printf_filtered ("\nTotal interrupts: %d\n", intrcnt); + for (i = 0; i < NCNT; i++) + if (cnts[i]) + printf_filtered ("%s:\t%d\n", cntnames[i], cnts[i]); +#endif +} + + +void +_initialize_ser_dos (void) +{ + serial_add_interface (&dos_ops); + + /* Save original interrupt mask register. */ + icu_oldmask = inportb (ICU_MASK); + + /* Mark fixed motherboard irqs as inuse. */ + intrupts[0].inuse = /* timer tick */ + intrupts[1].inuse = /* keyboard */ + intrupts[2].inuse = 1; /* slave icu */ + + add_show_from_set ( + add_set_cmd ("com1base", class_obscure, var_zinteger, + (char *) &ports[0].base, + "Set COM1 base i/o port address.", + &setlist), + &showlist); + + add_show_from_set ( + add_set_cmd ("com1irq", class_obscure, var_zinteger, + (char *) &ports[0].irq, + "Set COM1 interrupt request.", + &setlist), + &showlist); + + add_show_from_set ( + add_set_cmd ("com2base", class_obscure, var_zinteger, + (char *) &ports[1].base, + "Set COM2 base i/o port address.", + &setlist), + &showlist); + + add_show_from_set ( + add_set_cmd ("com2irq", class_obscure, var_zinteger, + (char *) &ports[1].irq, + "Set COM2 interrupt request.", + &setlist), + &showlist); + + add_show_from_set ( + add_set_cmd ("com3base", class_obscure, var_zinteger, + (char *) &ports[2].base, + "Set COM3 base i/o port address.", + &setlist), + &showlist); + + add_show_from_set ( + add_set_cmd ("com3irq", class_obscure, var_zinteger, + (char *) &ports[2].irq, + "Set COM3 interrupt request.", + &setlist), + &showlist); + + add_show_from_set ( + add_set_cmd ("com4base", class_obscure, var_zinteger, + (char *) &ports[3].base, + "Set COM4 base i/o port address.", + &setlist), + &showlist); + + add_show_from_set ( + add_set_cmd ("com4irq", class_obscure, var_zinteger, + (char *) &ports[3].irq, + "Set COM4 interrupt request.", + &setlist), + &showlist); + + add_info ("serial", dos_info, + "Print DOS serial port status."); +} diff --git a/contrib/gdb/gdb/somread.c b/contrib/gdb/gdb/somread.c new file mode 100644 index 0000000..4ffa4c1 --- /dev/null +++ b/contrib/gdb/gdb/somread.c @@ -0,0 +1,736 @@ +/* Read HP PA/Risc object files for GDB. + Copyright 1991, 1992, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, + 2004 Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support. + + 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 "defs.h" +#include "bfd.h" +#include <syms.h> +#include "symtab.h" +#include "symfile.h" +#include "objfiles.h" +#include "buildsym.h" +#include "stabsread.h" +#include "gdb-stabs.h" +#include "complaints.h" +#include "gdb_string.h" +#include "demangle.h" +#include "som.h" +#include "libhppa.h" + +/* Various things we might complain about... */ + +static int init_import_symbols (struct objfile *objfile); + +static void som_symfile_init (struct objfile *); + +static void som_new_init (struct objfile *); + +static void som_symfile_read (struct objfile *, int); + +static void som_symfile_finish (struct objfile *); + +static void som_symtab_read (bfd *, struct objfile *, + struct section_offsets *); + +static void som_symfile_offsets (struct objfile *, struct section_addr_info *); + +/* FIXME: These should really be in a common header somewhere */ + +extern void hpread_build_psymtabs (struct objfile *, int); + +extern void hpread_symfile_finish (struct objfile *); + +extern void hpread_symfile_init (struct objfile *); + +extern void do_pxdb (bfd *); + +/* + + LOCAL FUNCTION + + som_symtab_read -- read the symbol table of a SOM file + + SYNOPSIS + + void som_symtab_read (bfd *abfd, struct objfile *objfile, + struct section_offsets *section_offsets) + + DESCRIPTION + + Given an open bfd, a base address to relocate symbols to, and a + flag that specifies whether or not this bfd is for an executable + or not (may be shared library for example), add all the global + function and data symbols to the minimal symbol table. + */ + +static void +som_symtab_read (bfd *abfd, struct objfile *objfile, + struct section_offsets *section_offsets) +{ + unsigned int number_of_symbols; + int val, dynamic; + char *stringtab; + asection *shlib_info; + struct symbol_dictionary_record *buf, *bufp, *endbufp; + char *symname; + CONST int symsize = sizeof (struct symbol_dictionary_record); + CORE_ADDR text_offset, data_offset; + + + text_offset = ANOFFSET (section_offsets, 0); + data_offset = ANOFFSET (section_offsets, 1); + + number_of_symbols = bfd_get_symcount (abfd); + + /* FIXME (alloca): could be quite large. */ + buf = alloca (symsize * number_of_symbols); + bfd_seek (abfd, obj_som_sym_filepos (abfd), SEEK_SET); + val = bfd_bread (buf, symsize * number_of_symbols, abfd); + if (val != symsize * number_of_symbols) + error ("Couldn't read symbol dictionary!"); + + /* FIXME (alloca): could be quite large. */ + stringtab = alloca (obj_som_stringtab_size (abfd)); + bfd_seek (abfd, obj_som_str_filepos (abfd), SEEK_SET); + val = bfd_bread (stringtab, obj_som_stringtab_size (abfd), abfd); + if (val != obj_som_stringtab_size (abfd)) + error ("Can't read in HP string table."); + + /* We need to determine if objfile is a dynamic executable (so we + can do the right thing for ST_ENTRY vs ST_CODE symbols). + + There's nothing in the header which easily allows us to do + this. + + This code used to rely upon the existence of a $SHLIB_INFO$ + section to make this determination. HP claims that it is + more accurate to check for a nonzero text offset, but they + have not provided any information about why that test is + more accurate. */ + dynamic = (text_offset != 0); + + endbufp = buf + number_of_symbols; + for (bufp = buf; bufp < endbufp; ++bufp) + { + enum minimal_symbol_type ms_type; + + QUIT; + + switch (bufp->symbol_scope) + { + case SS_UNIVERSAL: + case SS_EXTERNAL: + switch (bufp->symbol_type) + { + case ST_SYM_EXT: + case ST_ARG_EXT: + continue; + + case ST_CODE: + case ST_PRI_PROG: + case ST_SEC_PROG: + case ST_MILLICODE: + symname = bufp->name.n_strx + stringtab; + ms_type = mst_text; + bufp->symbol_value += text_offset; + bufp->symbol_value = SMASH_TEXT_ADDRESS (bufp->symbol_value); + break; + + case ST_ENTRY: + symname = bufp->name.n_strx + stringtab; + /* For a dynamic executable, ST_ENTRY symbols are + the stubs, while the ST_CODE symbol is the real + function. */ + if (dynamic) + ms_type = mst_solib_trampoline; + else + ms_type = mst_text; + bufp->symbol_value += text_offset; + bufp->symbol_value = SMASH_TEXT_ADDRESS (bufp->symbol_value); + break; + + case ST_STUB: + symname = bufp->name.n_strx + stringtab; + ms_type = mst_solib_trampoline; + bufp->symbol_value += text_offset; + bufp->symbol_value = SMASH_TEXT_ADDRESS (bufp->symbol_value); + break; + + case ST_DATA: + symname = bufp->name.n_strx + stringtab; + bufp->symbol_value += data_offset; + ms_type = mst_data; + break; + default: + continue; + } + break; + +#if 0 + /* SS_GLOBAL and SS_LOCAL are two names for the same thing (!). */ + case SS_GLOBAL: +#endif + case SS_LOCAL: + switch (bufp->symbol_type) + { + case ST_SYM_EXT: + case ST_ARG_EXT: + continue; + + case ST_CODE: + symname = bufp->name.n_strx + stringtab; + ms_type = mst_file_text; + bufp->symbol_value += text_offset; + bufp->symbol_value = SMASH_TEXT_ADDRESS (bufp->symbol_value); + + check_strange_names: + /* Utah GCC 2.5, FSF GCC 2.6 and later generate correct local + label prefixes for stabs, constant data, etc. So we need + only filter out L$ symbols which are left in due to + limitations in how GAS generates SOM relocations. + + When linking in the HPUX C-library the HP linker has + the nasty habit of placing section symbols from the literal + subspaces in the middle of the program's text. Filter + those out as best we can. Check for first and last character + being '$'. + + And finally, the newer HP compilers emit crud like $PIC_foo$N + in some circumstance (PIC code I guess). It's also claimed + that they emit D$ symbols too. What stupidity. */ + if ((symname[0] == 'L' && symname[1] == '$') + || (symname[0] == '$' && symname[strlen (symname) - 1] == '$') + || (symname[0] == 'D' && symname[1] == '$') + || (strncmp (symname, "$PIC", 4) == 0)) + continue; + break; + + case ST_PRI_PROG: + case ST_SEC_PROG: + case ST_MILLICODE: + symname = bufp->name.n_strx + stringtab; + ms_type = mst_file_text; + bufp->symbol_value += text_offset; + bufp->symbol_value = SMASH_TEXT_ADDRESS (bufp->symbol_value); + break; + + case ST_ENTRY: + symname = bufp->name.n_strx + stringtab; + /* SS_LOCAL symbols in a shared library do not have + export stubs, so we do not have to worry about + using mst_file_text vs mst_solib_trampoline here like + we do for SS_UNIVERSAL and SS_EXTERNAL symbols above. */ + ms_type = mst_file_text; + bufp->symbol_value += text_offset; + bufp->symbol_value = SMASH_TEXT_ADDRESS (bufp->symbol_value); + break; + + case ST_STUB: + symname = bufp->name.n_strx + stringtab; + ms_type = mst_solib_trampoline; + bufp->symbol_value += text_offset; + bufp->symbol_value = SMASH_TEXT_ADDRESS (bufp->symbol_value); + break; + + + case ST_DATA: + symname = bufp->name.n_strx + stringtab; + bufp->symbol_value += data_offset; + ms_type = mst_file_data; + goto check_strange_names; + + default: + continue; + } + break; + + /* This can happen for common symbols when -E is passed to the + final link. No idea _why_ that would make the linker force + common symbols to have an SS_UNSAT scope, but it does. + + This also happens for weak symbols, but their type is + ST_DATA. */ + case SS_UNSAT: + switch (bufp->symbol_type) + { + case ST_STORAGE: + case ST_DATA: + symname = bufp->name.n_strx + stringtab; + bufp->symbol_value += data_offset; + ms_type = mst_data; + break; + + default: + continue; + } + break; + + default: + continue; + } + + if (bufp->name.n_strx > obj_som_stringtab_size (abfd)) + error ("Invalid symbol data; bad HP string table offset: %d", + bufp->name.n_strx); + + prim_record_minimal_symbol (symname, bufp->symbol_value, ms_type, + objfile); + } +} + +/* Scan and build partial symbols for a symbol file. + We have been initialized by a call to som_symfile_init, which + currently does nothing. + + SECTION_OFFSETS is a set of offsets to apply to relocate the symbols + in each section. This is ignored, as it isn't needed for SOM. + + MAINLINE is true if we are reading the main symbol + table (as opposed to a shared lib or dynamically loaded file). + + This function only does the minimum work necessary for letting the + user "name" things symbolically; it does not read the entire symtab. + Instead, it reads the external and static symbols and puts them in partial + symbol tables. When more extensive information is requested of a + file, the corresponding partial symbol table is mutated into a full + fledged symbol table by going back and reading the symbols + for real. + + We look for sections with specific names, to tell us what debug + format to look for: FIXME!!! + + somstab_build_psymtabs() handles STABS symbols. + + Note that SOM files have a "minimal" symbol table, which is vaguely + reminiscent of a COFF symbol table, but has only the minimal information + necessary for linking. We process this also, and use the information to + build gdb's minimal symbol table. This gives us some minimal debugging + capability even for files compiled without -g. */ + +static void +som_symfile_read (struct objfile *objfile, int mainline) +{ + bfd *abfd = objfile->obfd; + struct cleanup *back_to; + + do_pxdb (symfile_bfd_open (objfile->name)); + + init_minimal_symbol_collection (); + back_to = make_cleanup_discard_minimal_symbols (); + + /* Read in the import list and the export list. Currently + the export list isn't used; the import list is used in + hp-symtab-read.c to handle static vars declared in other + shared libraries. */ + init_import_symbols (objfile); +#if 0 /* Export symbols not used today 1997-08-05 */ + init_export_symbols (objfile); +#else + objfile->export_list = NULL; + objfile->export_list_size = 0; +#endif + + /* Process the normal SOM symbol table first. + This reads in the DNTT and string table, but doesn't + actually scan the DNTT. It does scan the linker symbol + table and thus build up a "minimal symbol table". */ + + som_symtab_read (abfd, objfile, objfile->section_offsets); + + /* Install any minimal symbols that have been collected as the current + minimal symbols for this objfile. + Further symbol-reading is done incrementally, file-by-file, + in a step known as "psymtab-to-symtab" expansion. hp-symtab-read.c + contains the code to do the actual DNTT scanning and symtab building. */ + install_minimal_symbols (objfile); + do_cleanups (back_to); + + /* Now read information from the stabs debug sections. + This is a no-op for SOM. + Perhaps it is intended for some kind of mixed STABS/SOM + situation? */ + stabsect_build_psymtabs (objfile, mainline, + "$GDB_SYMBOLS$", "$GDB_STRINGS$", "$TEXT$"); + + /* Now read the native debug information. + This builds the psymtab. This used to be done via a scan of + the DNTT, but is now done via the PXDB-built quick-lookup tables + together with a scan of the GNTT. See hp-psymtab-read.c. */ + hpread_build_psymtabs (objfile, mainline); + + /* Force hppa-tdep.c to re-read the unwind descriptors. */ + objfile->obj_private = NULL; +} + +/* Initialize anything that needs initializing when a completely new symbol + file is specified (not just adding some symbols from another file, e.g. a + shared library). + + We reinitialize buildsym, since we may be reading stabs from a SOM file. */ + +static void +som_new_init (struct objfile *ignore) +{ + stabsread_new_init (); + buildsym_new_init (); +} + +/* Perform any local cleanups required when we are done with a particular + objfile. I.E, we are in the process of discarding all symbol information + for an objfile, freeing up all memory held for it, and unlinking the + objfile struct from the global list of known objfiles. */ + +static void +som_symfile_finish (struct objfile *objfile) +{ + if (objfile->sym_stab_info != NULL) + { + xmfree (objfile->md, objfile->sym_stab_info); + } + hpread_symfile_finish (objfile); +} + +/* SOM specific initialization routine for reading symbols. */ + +static void +som_symfile_init (struct objfile *objfile) +{ + /* SOM objects may be reordered, so set OBJF_REORDERED. If we + find this causes a significant slowdown in gdb then we could + set it in the debug symbol readers only when necessary. */ + objfile->flags |= OBJF_REORDERED; + hpread_symfile_init (objfile); +} + +/* SOM specific parsing routine for section offsets. + + Plain and simple for now. */ + +static void +som_symfile_offsets (struct objfile *objfile, struct section_addr_info *addrs) +{ + int i; + CORE_ADDR text_addr; + + objfile->num_sections = bfd_count_sections (objfile->obfd); + objfile->section_offsets = (struct section_offsets *) + obstack_alloc (&objfile->objfile_obstack, + SIZEOF_N_SECTION_OFFSETS (objfile->num_sections)); + + /* FIXME: ezannoni 2000-04-20 The section names in SOM are not + .text, .data, etc, but $TEXT$, $DATA$,... We should initialize + SET_OFF_* from bfd. (See default_symfile_offsets()). But I don't + know the correspondence between SOM sections and GDB's idea of + section names. So for now we default to what is was before these + changes.*/ + objfile->sect_index_text = 0; + objfile->sect_index_data = 1; + objfile->sect_index_bss = 2; + objfile->sect_index_rodata = 3; + + /* First see if we're a shared library. If so, get the section + offsets from the library, else get them from addrs. */ + if (!som_solib_section_offsets (objfile, objfile->section_offsets)) + { + /* Note: Here is OK to compare with ".text" because this is the + name that gdb itself gives to that section, not the SOM + name. */ + for (i = 0; i < objfile->num_sections && addrs->other[i].name; i++) + if (strcmp (addrs->other[i].name, ".text") == 0) + break; + text_addr = addrs->other[i].addr; + + for (i = 0; i < objfile->num_sections; i++) + (objfile->section_offsets)->offsets[i] = text_addr; + } +} + +/* Read in and initialize the SOM import list which is present + for all executables and shared libraries. The import list + consists of the symbols that are referenced in OBJFILE but + not defined there. (Variables that are imported are dealt + with as "loc_indirect" vars.) + Return value = number of import symbols read in. */ +static int +init_import_symbols (struct objfile *objfile) +{ + unsigned int import_list; + unsigned int import_list_size; + unsigned int string_table; + unsigned int string_table_size; + char *string_buffer; + int i; + int j; + int k; + asection *text_section; /* section handle */ + unsigned int dl_header[12]; /* SOM executable header */ + + /* A struct for an entry in the SOM import list */ + typedef struct + { + int name; /* index into the string table */ + short dont_care1; /* we don't use this */ + unsigned char type; /* 0 = NULL, 2 = Data, 3 = Code, 7 = Storage, 13 = Plabel */ + unsigned int reserved2:8; /* not used */ + } + SomImportEntry; + + /* We read 100 entries in at a time from the disk file. */ +#define SOM_READ_IMPORTS_NUM 100 +#define SOM_READ_IMPORTS_CHUNK_SIZE (sizeof (SomImportEntry) * SOM_READ_IMPORTS_NUM) + SomImportEntry buffer[SOM_READ_IMPORTS_NUM]; + + /* Initialize in case we error out */ + objfile->import_list = NULL; + objfile->import_list_size = 0; + + /* It doesn't work, for some reason, to read in space $TEXT$; + the subspace $SHLIB_INFO$ has to be used. Some BFD quirk? pai/1997-08-05 */ + text_section = bfd_get_section_by_name (objfile->obfd, "$SHLIB_INFO$"); + if (!text_section) + return 0; + /* Get the SOM executable header */ + bfd_get_section_contents (objfile->obfd, text_section, dl_header, 0, 12 * sizeof (int)); + + /* Check header version number for 10.x HP-UX */ + /* Currently we deal only with 10.x systems; on 9.x the version # is 89060912. + FIXME: Change for future HP-UX releases and mods to the SOM executable format */ + if (dl_header[0] != 93092112) + return 0; + + import_list = dl_header[4]; + import_list_size = dl_header[5]; + if (!import_list_size) + return 0; + string_table = dl_header[10]; + string_table_size = dl_header[11]; + if (!string_table_size) + return 0; + + /* Suck in SOM string table */ + string_buffer = (char *) xmalloc (string_table_size); + bfd_get_section_contents (objfile->obfd, text_section, string_buffer, + string_table, string_table_size); + + /* Allocate import list in the psymbol obstack; this has nothing + to do with psymbols, just a matter of convenience. We want the + import list to be freed when the objfile is deallocated */ + objfile->import_list + = (ImportEntry *) obstack_alloc (&objfile->objfile_obstack, + import_list_size * sizeof (ImportEntry)); + + /* Read in the import entries, a bunch at a time */ + for (j = 0, k = 0; + j < (import_list_size / SOM_READ_IMPORTS_NUM); + j++) + { + bfd_get_section_contents (objfile->obfd, text_section, buffer, + import_list + j * SOM_READ_IMPORTS_CHUNK_SIZE, + SOM_READ_IMPORTS_CHUNK_SIZE); + for (i = 0; i < SOM_READ_IMPORTS_NUM; i++, k++) + { + if (buffer[i].type != (unsigned char) 0) + { + objfile->import_list[k] + = (char *) obstack_alloc (&objfile->objfile_obstack, strlen (string_buffer + buffer[i].name) + 1); + strcpy (objfile->import_list[k], string_buffer + buffer[i].name); + /* Some day we might want to record the type and other information too */ + } + else /* null type */ + objfile->import_list[k] = NULL; + + } + } + + /* Get the leftovers */ + if (k < import_list_size) + bfd_get_section_contents (objfile->obfd, text_section, buffer, + import_list + k * sizeof (SomImportEntry), + (import_list_size - k) * sizeof (SomImportEntry)); + for (i = 0; k < import_list_size; i++, k++) + { + if (buffer[i].type != (unsigned char) 0) + { + objfile->import_list[k] + = (char *) obstack_alloc (&objfile->objfile_obstack, strlen (string_buffer + buffer[i].name) + 1); + strcpy (objfile->import_list[k], string_buffer + buffer[i].name); + /* Some day we might want to record the type and other information too */ + } + else + objfile->import_list[k] = NULL; + } + + objfile->import_list_size = import_list_size; + xfree (string_buffer); + return import_list_size; +} + +/* Read in and initialize the SOM export list which is present + for all executables and shared libraries. The import list + consists of the symbols that are referenced in OBJFILE but + not defined there. (Variables that are imported are dealt + with as "loc_indirect" vars.) + Return value = number of import symbols read in. */ +int +init_export_symbols (struct objfile *objfile) +{ + unsigned int export_list; + unsigned int export_list_size; + unsigned int string_table; + unsigned int string_table_size; + char *string_buffer; + int i; + int j; + int k; + asection *text_section; /* section handle */ + unsigned int dl_header[12]; /* SOM executable header */ + + /* A struct for an entry in the SOM export list */ + typedef struct + { + int next; /* for hash table use -- we don't use this */ + int name; /* index into string table */ + int value; /* offset or plabel */ + int dont_care1; /* not used */ + unsigned char type; /* 0 = NULL, 2 = Data, 3 = Code, 7 = Storage, 13 = Plabel */ + char dont_care2; /* not used */ + short dont_care3; /* not used */ + } + SomExportEntry; + + /* We read 100 entries in at a time from the disk file. */ +#define SOM_READ_EXPORTS_NUM 100 +#define SOM_READ_EXPORTS_CHUNK_SIZE (sizeof (SomExportEntry) * SOM_READ_EXPORTS_NUM) + SomExportEntry buffer[SOM_READ_EXPORTS_NUM]; + + /* Initialize in case we error out */ + objfile->export_list = NULL; + objfile->export_list_size = 0; + + /* It doesn't work, for some reason, to read in space $TEXT$; + the subspace $SHLIB_INFO$ has to be used. Some BFD quirk? pai/1997-08-05 */ + text_section = bfd_get_section_by_name (objfile->obfd, "$SHLIB_INFO$"); + if (!text_section) + return 0; + /* Get the SOM executable header */ + bfd_get_section_contents (objfile->obfd, text_section, dl_header, 0, 12 * sizeof (int)); + + /* Check header version number for 10.x HP-UX */ + /* Currently we deal only with 10.x systems; on 9.x the version # is 89060912. + FIXME: Change for future HP-UX releases and mods to the SOM executable format */ + if (dl_header[0] != 93092112) + return 0; + + export_list = dl_header[8]; + export_list_size = dl_header[9]; + if (!export_list_size) + return 0; + string_table = dl_header[10]; + string_table_size = dl_header[11]; + if (!string_table_size) + return 0; + + /* Suck in SOM string table */ + string_buffer = (char *) xmalloc (string_table_size); + bfd_get_section_contents (objfile->obfd, text_section, string_buffer, + string_table, string_table_size); + + /* Allocate export list in the psymbol obstack; this has nothing + to do with psymbols, just a matter of convenience. We want the + export list to be freed when the objfile is deallocated */ + objfile->export_list + = (ExportEntry *) obstack_alloc (&objfile->objfile_obstack, + export_list_size * sizeof (ExportEntry)); + + /* Read in the export entries, a bunch at a time */ + for (j = 0, k = 0; + j < (export_list_size / SOM_READ_EXPORTS_NUM); + j++) + { + bfd_get_section_contents (objfile->obfd, text_section, buffer, + export_list + j * SOM_READ_EXPORTS_CHUNK_SIZE, + SOM_READ_EXPORTS_CHUNK_SIZE); + for (i = 0; i < SOM_READ_EXPORTS_NUM; i++, k++) + { + if (buffer[i].type != (unsigned char) 0) + { + objfile->export_list[k].name + = (char *) obstack_alloc (&objfile->objfile_obstack, strlen (string_buffer + buffer[i].name) + 1); + strcpy (objfile->export_list[k].name, string_buffer + buffer[i].name); + objfile->export_list[k].address = buffer[i].value; + /* Some day we might want to record the type and other information too */ + } + else + /* null type */ + { + objfile->export_list[k].name = NULL; + objfile->export_list[k].address = 0; + } + } + } + + /* Get the leftovers */ + if (k < export_list_size) + bfd_get_section_contents (objfile->obfd, text_section, buffer, + export_list + k * sizeof (SomExportEntry), + (export_list_size - k) * sizeof (SomExportEntry)); + for (i = 0; k < export_list_size; i++, k++) + { + if (buffer[i].type != (unsigned char) 0) + { + objfile->export_list[k].name + = (char *) obstack_alloc (&objfile->objfile_obstack, strlen (string_buffer + buffer[i].name) + 1); + strcpy (objfile->export_list[k].name, string_buffer + buffer[i].name); + /* Some day we might want to record the type and other information too */ + objfile->export_list[k].address = buffer[i].value; + } + else + { + objfile->export_list[k].name = NULL; + objfile->export_list[k].address = 0; + } + } + + objfile->export_list_size = export_list_size; + xfree (string_buffer); + return export_list_size; +} + + + +/* Register that we are able to handle SOM object file formats. */ + +static struct sym_fns som_sym_fns = +{ + bfd_target_som_flavour, + som_new_init, /* sym_new_init: init anything gbl to entire symtab */ + som_symfile_init, /* sym_init: read initial info, setup for sym_read() */ + som_symfile_read, /* sym_read: read a symbol file into symtab */ + som_symfile_finish, /* sym_finish: finished with file, cleanup */ + som_symfile_offsets, /* sym_offsets: Translate ext. to int. relocation */ + NULL /* next: pointer to next struct sym_fns */ +}; + +void +_initialize_somread (void) +{ + add_symtab_fns (&som_sym_fns); +} diff --git a/contrib/gdb/gdb/somsolib.c b/contrib/gdb/gdb/somsolib.c new file mode 100644 index 0000000..d8c971b --- /dev/null +++ b/contrib/gdb/gdb/somsolib.c @@ -0,0 +1,1624 @@ +/* Handle HP SOM shared libraries for GDB, the GNU Debugger. + + Copyright 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, + 2003, 2004 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. + + Written by the Center for Software Science at the Univerity of Utah + and by Cygnus Support. */ + + +#include "defs.h" + +#include "frame.h" +#include "bfd.h" +#include "som.h" +#include "libhppa.h" +#include "gdbcore.h" +#include "symtab.h" +#include "breakpoint.h" +#include "symfile.h" +#include "objfiles.h" +#include "inferior.h" +#include "gdb-stabs.h" +#include "gdb_stat.h" +#include "gdbcmd.h" +#include "language.h" +#include "regcache.h" +#include "gdb_assert.h" +#include "exec.h" + +#include <fcntl.h> + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* Uncomment this to turn on some debugging output. + */ + +/* #define SOLIB_DEBUG + */ + +/* This lives in hppa-tdep.c. */ +extern struct unwind_table_entry *find_unwind_entry (CORE_ADDR pc); + +/* These ought to be defined in some public interface, but aren't. They + define the meaning of the various bits in the distinguished __dld_flags + variable that is declared in every debuggable a.out on HP-UX, and that + is shared between the debugger and the dynamic linker. + */ +#define DLD_FLAGS_MAPPRIVATE 0x1 +#define DLD_FLAGS_HOOKVALID 0x2 +#define DLD_FLAGS_LISTVALID 0x4 +#define DLD_FLAGS_BOR_ENABLE 0x8 + +/* TODO: + + * Support for hpux8 dynamic linker. */ + +/* The basic structure which describes a dynamically loaded object. This + data structure is private to the dynamic linker and isn't found in + any HPUX include file. */ + +struct som_solib_mapped_entry + { + /* The name of the library. */ + char *name; + + /* Version of this structure (it is expected to change again in hpux10). */ + unsigned char struct_version; + + /* Binding mode for this library. */ + unsigned char bind_mode; + + /* Version of this library. */ + short library_version; + + /* Start of text address, + * link-time text location (length of text area), + * end of text address. */ + CORE_ADDR text_addr; + CORE_ADDR text_link_addr; + CORE_ADDR text_end; + + /* Start of data, start of bss and end of data. */ + CORE_ADDR data_start; + CORE_ADDR bss_start; + CORE_ADDR data_end; + + /* Value of linkage pointer (%r19). */ + CORE_ADDR got_value; + + /* Next entry. */ + struct som_solib_mapped_entry *next; + + /* There are other fields, but I don't have information as to what is + contained in them. */ + + /* For versions from HPUX-10.30 and up */ + + /* Address in target of offset from thread-local register of + * start of this thread's data. I.e., the first thread-local + * variable in this shared library starts at *(tsd_start_addr) + * from that area pointed to by cr27 (mpsfu_hi). + * + * We do the indirection as soon as we read it, so from then + * on it's the offset itself. + */ + CORE_ADDR tsd_start_addr; + + /* Following this are longwords holding: + + * ?, ?, ?, ptr to -1, ptr to-1, ptr to lib name (leaf name), + * ptr to __data_start, ptr to __data_end + */ + + + }; + +/* A structure to keep track of all the known shared objects. */ +struct so_list + { + struct som_solib_mapped_entry som_solib; + struct objfile *objfile; + bfd *abfd; + struct section_table *sections; + struct section_table *sections_end; +/* elz: added this field to store the address in target space (in the + library) of the library descriptor (handle) which we read into + som_solib_mapped_entry structure */ + CORE_ADDR solib_addr; + struct so_list *next; + + }; + +static struct so_list *so_list_head; + + +/* This is the cumulative size in bytes of the symbol tables of all + shared objects on the so_list_head list. (When we say size, here + we mean of the information before it is brought into memory and + potentially expanded by GDB.) When adding a new shlib, this value + is compared against the threshold size, held by auto_solib_limit + (in megabytes). If adding symbols for the new shlib would cause + the total size to exceed the threshold, then the new shlib's + symbols are not loaded. */ +static LONGEST som_solib_total_st_size; + +/* When the threshold is reached for any shlib, we refuse to add + symbols for subsequent shlibs, even if those shlibs' symbols would + be small enough to fit under the threshold. (Although this may + result in one, early large shlib preventing the loading of later, + smalller shlibs' symbols, it allows us to issue one informational + message. The alternative, to issue a message for each shlib whose + symbols aren't loaded, could be a big annoyance where the threshold + is exceeded due to a very large number of shlibs.) + */ +static int som_solib_st_size_threshold_exceeded; + +/* These addresses should be filled in by som_solib_create_inferior_hook. + They are also used elsewhere in this module. + */ +typedef struct + { + CORE_ADDR address; + struct unwind_table_entry *unwind; + } +addr_and_unwind_t; + +/* When adding fields, be sure to clear them in _initialize_som_solib. */ +static struct + { + int is_valid; + addr_and_unwind_t hook; + addr_and_unwind_t hook_stub; + addr_and_unwind_t load; + addr_and_unwind_t load_stub; + addr_and_unwind_t unload; + addr_and_unwind_t unload2; + addr_and_unwind_t unload_stub; + } +dld_cache; + + + +static void som_sharedlibrary_info_command (char *, int); + +static void som_solib_sharedlibrary_command (char *, int); + +static LONGEST +som_solib_sizeof_symbol_table (char *filename) +{ + bfd *abfd; + int desc; + char *absolute_name; + LONGEST st_size = (LONGEST) 0; + asection *sect; + + /* We believe that filename was handed to us by the dynamic linker, and + is therefore always an absolute path. + */ + desc = openp (getenv ("PATH"), 1, filename, O_RDONLY | O_BINARY, 0, &absolute_name); + if (desc < 0) + { + perror_with_name (filename); + } + filename = absolute_name; + + abfd = bfd_fdopenr (filename, gnutarget, desc); + if (!abfd) + { + close (desc); + make_cleanup (xfree, filename); + error ("\"%s\": can't open to read symbols: %s.", filename, + bfd_errmsg (bfd_get_error ())); + } + + if (!bfd_check_format (abfd, bfd_object)) /* Reads in section info */ + { + bfd_close (abfd); /* This also closes desc */ + make_cleanup (xfree, filename); + error ("\"%s\": can't read symbols: %s.", filename, + bfd_errmsg (bfd_get_error ())); + } + + /* Sum the sizes of the various sections that compose debug info. */ + + /* This contains non-DOC information. */ + sect = bfd_get_section_by_name (abfd, "$DEBUG$"); + if (sect) + st_size += (LONGEST) bfd_section_size (abfd, sect); + + /* This contains DOC information. */ + sect = bfd_get_section_by_name (abfd, "$PINFO$"); + if (sect) + st_size += (LONGEST) bfd_section_size (abfd, sect); + + bfd_close (abfd); /* This also closes desc */ + xfree (filename); + + /* Unfortunately, just summing the sizes of various debug info + sections isn't a very accurate measurement of how much heap + space the debugger will need to hold them. It also doesn't + account for space needed by linker (aka "minimal") symbols. + + Anecdotal evidence suggests that just summing the sizes of + debug-info-related sections understates the heap space needed + to represent it internally by about an order of magnitude. + + Since it's not exactly brain surgery we're doing here, rather + than attempt to more accurately measure the size of a shlib's + symbol table in GDB's heap, we'll just apply a 10x fudge- + factor to the debug info sections' size-sum. No, this doesn't + account for minimal symbols in non-debuggable shlibs. But it + all roughly washes out in the end. + */ + return st_size * (LONGEST) 10; +} + + +static void +som_solib_add_solib_objfile (struct so_list *so, char *name, int from_tty, + CORE_ADDR text_addr) +{ + obj_private_data_t *obj_private; + struct obj_section *s; + + so->objfile = symbol_file_add (name, from_tty, NULL, 0, OBJF_SHARED); + so->abfd = so->objfile->obfd; + + /* syms_from_objfile has bizarre section offset code, + so I do my own right here. */ + for (s = so->objfile->sections; s < so->objfile->sections_end; s++) + { + flagword aflag = bfd_get_section_flags(so->abfd, s->the_bfd_section); + if (aflag & SEC_CODE) + { + s->addr += so->som_solib.text_addr - so->som_solib.text_link_addr; + s->endaddr += so->som_solib.text_addr - so->som_solib.text_link_addr; + } + else if (aflag & SEC_DATA) + { + s->addr += so->som_solib.data_start; + s->endaddr += so->som_solib.data_start; + } + else + ; + } + + /* Mark this as a shared library and save private data. + */ + so->objfile->flags |= OBJF_SHARED; + + if (so->objfile->obj_private == NULL) + { + obj_private = (obj_private_data_t *) + obstack_alloc (&so->objfile->objfile_obstack, + sizeof (obj_private_data_t)); + obj_private->unwind_info = NULL; + obj_private->so_info = NULL; + so->objfile->obj_private = obj_private; + } + + obj_private = (obj_private_data_t *) so->objfile->obj_private; + obj_private->so_info = so; + + if (!bfd_check_format (so->abfd, bfd_object)) + { + error ("\"%s\": not in executable format: %s.", + name, bfd_errmsg (bfd_get_error ())); + } +} + + +static void +som_solib_load_symbols (struct so_list *so, char *name, int from_tty, + CORE_ADDR text_addr, struct target_ops *target) +{ + struct section_table *p; + int status; + char buf[4]; + CORE_ADDR presumed_data_start; + +#ifdef SOLIB_DEBUG + printf ("--Adding symbols for shared library \"%s\"\n", name); +#endif + + som_solib_add_solib_objfile (so, name, from_tty, text_addr); + + /* Now we need to build a section table for this library since + we might be debugging a core file from a dynamically linked + executable in which the libraries were not privately mapped. */ + if (build_section_table (so->abfd, + &so->sections, + &so->sections_end)) + { + error ("Unable to build section table for shared library\n."); + return; + } + + /* Relocate all the sections based on where they got loaded. */ + for (p = so->sections; p < so->sections_end; p++) + { + if (p->the_bfd_section->flags & SEC_CODE) + { + p->addr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_TEXT (so->objfile)); + p->endaddr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_TEXT (so->objfile)); + } + else if (p->the_bfd_section->flags & SEC_DATA) + { + p->addr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_DATA (so->objfile)); + p->endaddr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_DATA (so->objfile)); + } + } + + /* Now see if we need to map in the text and data for this shared + library (for example debugging a core file which does not use + private shared libraries.). + + Carefully peek at the first text address in the library. If the + read succeeds, then the libraries were privately mapped and were + included in the core dump file. + + If the peek failed, then the libraries were not privately mapped + and are not in the core file, we'll have to read them in ourselves. */ + status = target_read_memory (text_addr, buf, 4); + if (status != 0) + { + int old, new; + + new = so->sections_end - so->sections; + + old = target_resize_to_sections (target, new); + + /* Copy over the old data before it gets clobbered. */ + memcpy ((char *) (target->to_sections + old), + so->sections, + ((sizeof (struct section_table)) * new)); + } +} + + +/* FIXME: cagney/2003-02-01: This just isn't right. Given an address + within the target's address space, this converts the value into an + address within the host's (i.e., GDB's) address space. Given that + the host/target address spaces are separate, this can't be right. */ + +static void * +hpux_address_to_host_pointer_hack (CORE_ADDR addr) +{ + void *ptr; + + gdb_assert (sizeof (ptr) == TYPE_LENGTH (builtin_type_void_data_ptr)); + ADDRESS_TO_POINTER (builtin_type_void_data_ptr, &ptr, addr); + return ptr; +} + +/* Add symbols from shared libraries into the symtab list, unless the + size threshold specified by auto_solib_limit (in megabytes) would + be exceeded. */ + +void +som_solib_add (char *arg_string, int from_tty, struct target_ops *target, int readsyms) +{ + struct minimal_symbol *msymbol; + struct so_list *so_list_tail; + CORE_ADDR addr; + asection *shlib_info; + int status; + unsigned int dld_flags; + char buf[4], *re_err; + int threshold_warning_given = 0; + + /* First validate our arguments. */ + re_err = re_comp (arg_string ? arg_string : "."); + if (re_err != NULL) + { + error ("Invalid regexp: %s", re_err); + } + + /* If we're debugging a core file, or have attached to a running + process, then som_solib_create_inferior_hook will not have been + called. + + We need to first determine if we're dealing with a dynamically + linked executable. If not, then return without an error or warning. + + We also need to examine __dld_flags to determine if the shared library + list is valid and to determine if the libraries have been privately + mapped. */ + if (symfile_objfile == NULL) + return; + + /* First see if the objfile was dynamically linked. */ + shlib_info = bfd_get_section_by_name (symfile_objfile->obfd, "$SHLIB_INFO$"); + if (!shlib_info) + return; + + /* It's got a $SHLIB_INFO$ section, make sure it's not empty. */ + if (bfd_section_size (symfile_objfile->obfd, shlib_info) == 0) + return; + + msymbol = lookup_minimal_symbol ("__dld_flags", NULL, NULL); + if (msymbol == NULL) + { + error ("Unable to find __dld_flags symbol in object file.\n"); + return; + } + + addr = SYMBOL_VALUE_ADDRESS (msymbol); + /* Read the current contents. */ + status = target_read_memory (addr, buf, 4); + if (status != 0) + { + error ("Unable to read __dld_flags\n"); + return; + } + dld_flags = extract_unsigned_integer (buf, 4); + + /* __dld_list may not be valid. If not, then we punt, warning the user if + we were called as a result of the add-symfile command. + */ + if ((dld_flags & DLD_FLAGS_LISTVALID) == 0) + { + if (from_tty) + error ("__dld_list is not valid according to __dld_flags.\n"); + return; + } + + /* If the libraries were not mapped private, warn the user. */ + if ((dld_flags & DLD_FLAGS_MAPPRIVATE) == 0) + warning ("The shared libraries were not privately mapped; setting a\nbreakpoint in a shared library will not work until you rerun the program.\n"); + + msymbol = lookup_minimal_symbol ("__dld_list", NULL, NULL); + if (!msymbol) + { + /* Older crt0.o files (hpux8) don't have __dld_list as a symbol, + but the data is still available if you know where to look. */ + msymbol = lookup_minimal_symbol ("__dld_flags", NULL, NULL); + if (!msymbol) + { + error ("Unable to find dynamic library list.\n"); + return; + } + addr = SYMBOL_VALUE_ADDRESS (msymbol) - 8; + } + else + addr = SYMBOL_VALUE_ADDRESS (msymbol); + + status = target_read_memory (addr, buf, 4); + if (status != 0) + { + error ("Unable to find dynamic library list.\n"); + return; + } + + addr = extract_unsigned_integer (buf, 4); + + /* If addr is zero, then we're using an old dynamic loader which + doesn't maintain __dld_list. We'll have to use a completely + different approach to get shared library information. */ + if (addr == 0) + goto old_dld; + + /* Using the information in __dld_list is the preferred method + to get at shared library information. It doesn't depend on + any functions in /opt/langtools/lib/end.o and has a chance of working + with hpux10 when it is released. */ + status = target_read_memory (addr, buf, 4); + if (status != 0) + { + error ("Unable to find dynamic library list.\n"); + return; + } + + /* addr now holds the address of the first entry in the dynamic + library list. */ + addr = extract_unsigned_integer (buf, 4); + + /* Now that we have a pointer to the dynamic library list, walk + through it and add the symbols for each library. */ + + so_list_tail = so_list_head; + /* Find the end of the list of shared objects. */ + while (so_list_tail && so_list_tail->next) + so_list_tail = so_list_tail->next; + +#ifdef SOLIB_DEBUG + printf ("--About to read shared library list data\n"); +#endif + + /* "addr" will always point to the base of the + * current data entry describing the current + * shared library. + */ + while (1) + { + CORE_ADDR name_addr, text_addr; + unsigned int name_len; + char *name; + struct so_list *new_so; + struct so_list *so_list = so_list_head; + struct stat statbuf; + LONGEST st_size; + int is_main_program; + + if (addr == 0) + break; + + /* Get a pointer to the name of this library. */ + status = target_read_memory (addr, buf, 4); + if (status != 0) + goto err; + + name_addr = extract_unsigned_integer (buf, 4); + name_len = 0; + while (1) + { + target_read_memory (name_addr + name_len, buf, 1); + if (status != 0) + goto err; + + name_len++; + if (*buf == '\0') + break; + } + name = alloca (name_len); + status = target_read_memory (name_addr, name, name_len); + if (status != 0) + goto err; + + /* See if we've already loaded something with this name. */ + while (so_list) + { + if (!strcmp (so_list->som_solib.name, name)) + break; + so_list = so_list->next; + } + + /* See if the file exists. If not, give a warning, but don't + die. */ + status = stat (name, &statbuf); + if (status == -1) + { + warning ("Can't find file %s referenced in dld_list.", name); + + status = target_read_memory (addr + 36, buf, 4); + if (status != 0) + goto err; + + addr = (CORE_ADDR) extract_unsigned_integer (buf, 4); + continue; + } + + /* If we've already loaded this one or it's the main program, skip it. */ + is_main_program = (strcmp (name, symfile_objfile->name) == 0); + if (so_list || is_main_program) + { + /* This is the "next" pointer in the strcuture. + */ + status = target_read_memory (addr + 36, buf, 4); + if (status != 0) + goto err; + + addr = (CORE_ADDR) extract_unsigned_integer (buf, 4); + + /* Record the main program's symbol table size. */ + if (is_main_program && !so_list) + { + st_size = som_solib_sizeof_symbol_table (name); + som_solib_total_st_size += st_size; + } + + /* Was this a shlib that we noted but didn't load the symbols for? + If so, were we invoked this time from the command-line, via + a 'sharedlibrary' or 'add-symbol-file' command? If yes to + both, we'd better load the symbols this time. + */ + if (from_tty && so_list && !is_main_program && (so_list->objfile == NULL)) + som_solib_load_symbols (so_list, + name, + from_tty, + so_list->som_solib.text_addr, + target); + + continue; + } + + name = obsavestring (name, name_len - 1, + &symfile_objfile->objfile_obstack); + + status = target_read_memory (addr + 8, buf, 4); + if (status != 0) + goto err; + + text_addr = extract_unsigned_integer (buf, 4); + + new_so = (struct so_list *) xmalloc (sizeof (struct so_list)); + memset ((char *) new_so, 0, sizeof (struct so_list)); + if (so_list_head == NULL) + { + so_list_head = new_so; + so_list_tail = new_so; + } + else + { + so_list_tail->next = new_so; + so_list_tail = new_so; + } + + /* Fill in all the entries in GDB's shared library list. + */ + + new_so->solib_addr = addr; + new_so->som_solib.name = name; + status = target_read_memory (addr + 4, buf, 4); + if (status != 0) + goto err; + + new_so->som_solib.struct_version = extract_unsigned_integer (buf + 3, 1); + new_so->som_solib.bind_mode = extract_unsigned_integer (buf + 2, 1); + /* Following is "high water mark", highest version number + * seen, rather than plain version number. + */ + new_so->som_solib.library_version = extract_unsigned_integer (buf, 2); + new_so->som_solib.text_addr = text_addr; + + /* Q: What about longword at "addr + 8"? + * A: It's read above, out of order, into "text_addr". + */ + + status = target_read_memory (addr + 12, buf, 4); + if (status != 0) + goto err; + + new_so->som_solib.text_link_addr = extract_unsigned_integer (buf, 4); + + status = target_read_memory (addr + 16, buf, 4); + if (status != 0) + goto err; + + new_so->som_solib.text_end = extract_unsigned_integer (buf, 4); + + status = target_read_memory (addr + 20, buf, 4); + if (status != 0) + goto err; + + new_so->som_solib.data_start = extract_unsigned_integer (buf, 4); + + status = target_read_memory (addr + 24, buf, 4); + if (status != 0) + goto err; + + new_so->som_solib.bss_start = extract_unsigned_integer (buf, 4); + + status = target_read_memory (addr + 28, buf, 4); + if (status != 0) + goto err; + + new_so->som_solib.data_end = extract_unsigned_integer (buf, 4); + + status = target_read_memory (addr + 32, buf, 4); + if (status != 0) + goto err; + + new_so->som_solib.got_value = extract_unsigned_integer (buf, 4); + + status = target_read_memory (addr + 36, buf, 4); + if (status != 0) + goto err; + + /* FIXME: cagney/2003-02-01: I think som_solib.next should be a + CORE_ADDR. */ + new_so->som_solib.next = + hpux_address_to_host_pointer_hack (extract_unsigned_integer (buf, 4)); + + /* Note that we don't re-set "addr" to the next pointer + * until after we've read the trailing data. + */ + + status = target_read_memory (addr + 40, buf, 4); + new_so->som_solib.tsd_start_addr = extract_unsigned_integer (buf, 4); + if (status != 0) + goto err; + + /* Now indirect via that value! + */ + status = target_read_memory (new_so->som_solib.tsd_start_addr, buf, 4); + new_so->som_solib.tsd_start_addr = extract_unsigned_integer (buf, 4); + if (status != 0) + goto err; +#ifdef SOLIB_DEBUG + printf ("\n+ library \"%s\" is described at 0x%x\n", name, addr); + printf (" 'version' is %d\n", new_so->som_solib.struct_version); + printf (" 'bind_mode' is %d\n", new_so->som_solib.bind_mode); + printf (" 'library_version' is %d\n", new_so->som_solib.library_version); + printf (" 'text_addr' is 0x%x\n", new_so->som_solib.text_addr); + printf (" 'text_link_addr' is 0x%x\n", new_so->som_solib.text_link_addr); + printf (" 'text_end' is 0x%x\n", new_so->som_solib.text_end); + printf (" 'data_start' is 0x%x\n", new_so->som_solib.data_start); + printf (" 'bss_start' is 0x%x\n", new_so->som_solib.bss_start); + printf (" 'data_end' is 0x%x\n", new_so->som_solib.data_end); + printf (" 'got_value' is %x\n", new_so->som_solib.got_value); + printf (" 'next' is 0x%x\n", new_so->som_solib.next); + printf (" 'tsd_start_addr' is 0x%x\n", new_so->som_solib.tsd_start_addr); +#endif + + /* Go on to the next shared library descriptor. + */ + addr = (CORE_ADDR) new_so->som_solib.next; + + + + /* At this point, we have essentially hooked the shlib into the + "info share" command. However, we haven't yet loaded its + symbol table. We must now decide whether we ought to, i.e., + whether doing so would exceed the symbol table size threshold. + + If the threshold has just now been exceeded, then we'll issue + a warning message (which explains how to load symbols manually, + if the user so desires). + + If the threshold has just now or previously been exceeded, + we'll just add the shlib to the list of object files, but won't + actually load its symbols. (This is more useful than it might + sound, for it allows us to e.g., still load and use the shlibs' + unwind information for stack tracebacks.) + */ + + /* Note that we DON'T want to preclude the user from using the + add-symbol-file command! Thus, we only worry about the threshold + when we're invoked for other reasons. + */ + st_size = som_solib_sizeof_symbol_table (name); + som_solib_st_size_threshold_exceeded = + !from_tty && + auto_solib_limit > 0 && + readsyms && + ((st_size + som_solib_total_st_size) > (auto_solib_limit * (LONGEST) (1024 * 1024))); + + if (som_solib_st_size_threshold_exceeded) + { + if (!threshold_warning_given) + warning ("Symbols for some libraries have not been loaded, because\ndoing so would exceed the size threshold specified by auto-solib-limit.\nTo manually load symbols, use the 'sharedlibrary' command.\nTo raise the threshold, set auto-solib-limit to a larger value and rerun\nthe program.\n"); + threshold_warning_given = 1; + + /* We'll still make note of this shlib, even if we don't + read its symbols. This allows us to use its unwind + information well enough to know how to e.g., correctly + do a traceback from a PC within the shlib, even if we + can't symbolize those PCs... + */ + som_solib_add_solib_objfile (new_so, name, from_tty, text_addr); + continue; + } + + som_solib_total_st_size += st_size; + + /* This fills in new_so->objfile, among others. */ + som_solib_load_symbols (new_so, name, from_tty, text_addr, target); + } + +#ifdef SOLIB_DEBUG + printf ("--Done reading shared library data\n"); +#endif + + /* Getting new symbols may change our opinion about what is + frameless. */ + reinit_frame_cache (); + return; + +old_dld: + error ("Debugging dynamic executables loaded via the hpux8 dld.sl is not supported.\n"); + return; + +err: + error ("Error while reading dynamic library list.\n"); + return; +} + + +/* This hook gets called just before the first instruction in the + inferior process is executed. + + This is our opportunity to set magic flags in the inferior so + that GDB can be notified when a shared library is mapped in and + to tell the dynamic linker that a private copy of the library is + needed (so GDB can set breakpoints in the library). + + __dld_flags is the location of the magic flags; as of this implementation + there are 3 flags of interest: + + bit 0 when set indicates that private copies of the libraries are needed + bit 1 when set indicates that the callback hook routine is valid + bit 2 when set indicates that the dynamic linker should maintain the + __dld_list structure when loading/unloading libraries. + + Note that shared libraries are not mapped in at this time, so we have + run the inferior until the libraries are mapped in. Typically this + means running until the "_start" is called. */ + +void +som_solib_create_inferior_hook (void) +{ + struct minimal_symbol *msymbol; + unsigned int dld_flags, status, have_endo; + asection *shlib_info; + char buf[4]; + struct objfile *objfile; + CORE_ADDR anaddr; + + /* First, remove all the solib event breakpoints. Their addresses + may have changed since the last time we ran the program. */ + remove_solib_event_breakpoints (); + + if (symfile_objfile == NULL) + return; + + /* First see if the objfile was dynamically linked. */ + shlib_info = bfd_get_section_by_name (symfile_objfile->obfd, "$SHLIB_INFO$"); + if (!shlib_info) + return; + + /* It's got a $SHLIB_INFO$ section, make sure it's not empty. */ + if (bfd_section_size (symfile_objfile->obfd, shlib_info) == 0) + return; + + have_endo = 0; + /* Slam the pid of the process into __d_pid. + + We used to warn when this failed, but that warning is only useful + on very old HP systems (hpux9 and older). The warnings are an + annoyance to users of modern systems and foul up the testsuite as + well. As a result, the warnings have been disabled. */ + msymbol = lookup_minimal_symbol ("__d_pid", NULL, symfile_objfile); + if (msymbol == NULL) + goto keep_going; + + anaddr = SYMBOL_VALUE_ADDRESS (msymbol); + store_unsigned_integer (buf, 4, PIDGET (inferior_ptid)); + status = target_write_memory (anaddr, buf, 4); + if (status != 0) + { + warning ("Unable to write __d_pid"); + warning ("Suggest linking with /opt/langtools/lib/end.o."); + warning ("GDB will be unable to track shl_load/shl_unload calls"); + goto keep_going; + } + + /* Get the value of _DLD_HOOK (an export stub) and put it in __dld_hook; + This will force the dynamic linker to call __d_trap when significant + events occur. + + Note that the above is the pre-HP-UX 9.0 behaviour. At 9.0 and above, + the dld provides an export stub named "__d_trap" as well as the + function named "__d_trap" itself, but doesn't provide "_DLD_HOOK". + We'll look first for the old flavor and then the new. + */ + msymbol = lookup_minimal_symbol ("_DLD_HOOK", NULL, symfile_objfile); + if (msymbol == NULL) + msymbol = lookup_minimal_symbol ("__d_trap", NULL, symfile_objfile); + if (msymbol == NULL) + { + warning ("Unable to find _DLD_HOOK symbol in object file."); + warning ("Suggest linking with /opt/langtools/lib/end.o."); + warning ("GDB will be unable to track shl_load/shl_unload calls"); + goto keep_going; + } + anaddr = SYMBOL_VALUE_ADDRESS (msymbol); + dld_cache.hook.address = anaddr; + + /* Grrr, this might not be an export symbol! We have to find the + export stub. */ + ALL_OBJFILES (objfile) + { + struct unwind_table_entry *u; + struct minimal_symbol *msymbol2; + + /* What a crock. */ + msymbol2 = lookup_minimal_symbol_solib_trampoline (DEPRECATED_SYMBOL_NAME (msymbol), + objfile); + /* Found a symbol with the right name. */ + if (msymbol2) + { + struct unwind_table_entry *u; + /* It must be a shared library trampoline. */ + if (SYMBOL_TYPE (msymbol2) != mst_solib_trampoline) + continue; + + /* It must also be an export stub. */ + u = find_unwind_entry (SYMBOL_VALUE (msymbol2)); + if (!u || u->stub_unwind.stub_type != EXPORT) + continue; + + /* OK. Looks like the correct import stub. */ + anaddr = SYMBOL_VALUE (msymbol2); + dld_cache.hook_stub.address = anaddr; + } + } + store_unsigned_integer (buf, 4, anaddr); + + msymbol = lookup_minimal_symbol ("__dld_hook", NULL, symfile_objfile); + if (msymbol == NULL) + { + warning ("Unable to find __dld_hook symbol in object file."); + warning ("Suggest linking with /opt/langtools/lib/end.o."); + warning ("GDB will be unable to track shl_load/shl_unload calls"); + goto keep_going; + } + anaddr = SYMBOL_VALUE_ADDRESS (msymbol); + status = target_write_memory (anaddr, buf, 4); + + /* Now set a shlib_event breakpoint at __d_trap so we can track + significant shared library events. */ + msymbol = lookup_minimal_symbol ("__d_trap", NULL, symfile_objfile); + if (msymbol == NULL) + { + warning ("Unable to find __dld_d_trap symbol in object file."); + warning ("Suggest linking with /opt/langtools/lib/end.o."); + warning ("GDB will be unable to track shl_load/shl_unload calls"); + goto keep_going; + } + create_solib_event_breakpoint (SYMBOL_VALUE_ADDRESS (msymbol)); + + /* We have all the support usually found in end.o, so we can track + shl_load and shl_unload calls. */ + have_endo = 1; + +keep_going: + + /* Get the address of __dld_flags, if no such symbol exists, then we can + not debug the shared code. */ + msymbol = lookup_minimal_symbol ("__dld_flags", NULL, NULL); + if (msymbol == NULL) + { + error ("Unable to find __dld_flags symbol in object file.\n"); + } + + anaddr = SYMBOL_VALUE_ADDRESS (msymbol); + + /* Read the current contents. */ + status = target_read_memory (anaddr, buf, 4); + if (status != 0) + { + error ("Unable to read __dld_flags\n"); + } + dld_flags = extract_unsigned_integer (buf, 4); + + /* Turn on the flags we care about. */ + dld_flags |= DLD_FLAGS_MAPPRIVATE; + if (have_endo) + dld_flags |= DLD_FLAGS_HOOKVALID; + store_unsigned_integer (buf, 4, dld_flags); + status = target_write_memory (anaddr, buf, 4); + if (status != 0) + { + error ("Unable to write __dld_flags\n"); + } + + /* Now find the address of _start and set a breakpoint there. + We still need this code for two reasons: + + * Not all sites have /opt/langtools/lib/end.o, so it's not always + possible to track the dynamic linker's events. + + * At this time no events are triggered for shared libraries + loaded at startup time (what a crock). */ + + msymbol = lookup_minimal_symbol ("_start", NULL, symfile_objfile); + if (msymbol == NULL) + { + error ("Unable to find _start symbol in object file.\n"); + } + + anaddr = SYMBOL_VALUE_ADDRESS (msymbol); + + /* Make the breakpoint at "_start" a shared library event breakpoint. */ + create_solib_event_breakpoint (anaddr); + + /* Wipe out all knowledge of old shared libraries since their + mapping can change from one exec to another! */ + while (so_list_head) + { + struct so_list *temp; + + temp = so_list_head; + xfree (so_list_head); + so_list_head = temp->next; + } + clear_symtab_users (); +} + +/* This operation removes the "hook" between GDB and the dynamic linker, + which causes the dld to notify GDB of shared library events. + + After this operation completes, the dld will no longer notify GDB of + shared library events. To resume notifications, GDB must call + som_solib_create_inferior_hook. + + This operation does not remove any knowledge of shared libraries which + GDB may already have been notified of. + */ +void +som_solib_remove_inferior_hook (int pid) +{ + CORE_ADDR addr; + struct minimal_symbol *msymbol; + int status; + char dld_flags_buffer[4]; + unsigned int dld_flags_value; + struct cleanup *old_cleanups = save_inferior_ptid (); + + /* Ensure that we're really operating on the specified process. */ + inferior_ptid = pid_to_ptid (pid); + + /* We won't bother to remove the solib breakpoints from this process. + + In fact, on PA64 the breakpoint is hard-coded into the dld callback, + and thus we're not supposed to remove it. + + Rather, we'll merely clear the dld_flags bit that enables callbacks. + */ + msymbol = lookup_minimal_symbol ("__dld_flags", NULL, NULL); + + addr = SYMBOL_VALUE_ADDRESS (msymbol); + status = target_read_memory (addr, dld_flags_buffer, 4); + + dld_flags_value = extract_unsigned_integer (dld_flags_buffer, 4); + + dld_flags_value &= ~DLD_FLAGS_HOOKVALID; + store_unsigned_integer (dld_flags_buffer, 4, dld_flags_value); + status = target_write_memory (addr, dld_flags_buffer, 4); + + do_cleanups (old_cleanups); +} + + +/* This function creates a breakpoint on the dynamic linker hook, which + is called when e.g., a shl_load or shl_unload call is made. This + breakpoint will only trigger when a shl_load call is made. + + If filename is NULL, then loads of any dll will be caught. Else, + only loads of the file whose pathname is the string contained by + filename will be caught. + + Undefined behaviour is guaranteed if this function is called before + som_solib_create_inferior_hook. + */ +void +som_solib_create_catch_load_hook (int pid, int tempflag, char *filename, + char *cond_string) +{ + create_solib_load_event_breakpoint ("__d_trap", tempflag, filename, cond_string); +} + +/* This function creates a breakpoint on the dynamic linker hook, which + is called when e.g., a shl_load or shl_unload call is made. This + breakpoint will only trigger when a shl_unload call is made. + + If filename is NULL, then unloads of any dll will be caught. Else, + only unloads of the file whose pathname is the string contained by + filename will be caught. + + Undefined behaviour is guaranteed if this function is called before + som_solib_create_inferior_hook. + */ +void +som_solib_create_catch_unload_hook (int pid, int tempflag, char *filename, + char *cond_string) +{ + create_solib_unload_event_breakpoint ("__d_trap", tempflag, filename, cond_string); +} + +int +som_solib_have_load_event (int pid) +{ + CORE_ADDR event_kind; + + event_kind = read_register (ARG0_REGNUM); + return (event_kind == SHL_LOAD); +} + +int +som_solib_have_unload_event (int pid) +{ + CORE_ADDR event_kind; + + event_kind = read_register (ARG0_REGNUM); + return (event_kind == SHL_UNLOAD); +} + +static char * +som_solib_library_pathname (int pid) +{ + CORE_ADDR dll_handle_address; + CORE_ADDR dll_pathname_address; + struct som_solib_mapped_entry dll_descriptor; + char *p; + static char dll_pathname[1024]; + + /* Read the descriptor of this newly-loaded library. */ + dll_handle_address = read_register (ARG1_REGNUM); + read_memory (dll_handle_address, (char *) &dll_descriptor, sizeof (dll_descriptor)); + + /* We can find a pointer to the dll's pathname within the descriptor. */ + dll_pathname_address = (CORE_ADDR) dll_descriptor.name; + + /* Read the pathname, one byte at a time. */ + p = dll_pathname; + for (;;) + { + char b; + read_memory (dll_pathname_address++, (char *) &b, 1); + *p++ = b; + if (b == '\0') + break; + } + + return dll_pathname; +} + +char * +som_solib_loaded_library_pathname (int pid) +{ + if (!som_solib_have_load_event (pid)) + error ("Must have a load event to use this query"); + + return som_solib_library_pathname (pid); +} + +char * +som_solib_unloaded_library_pathname (int pid) +{ + if (!som_solib_have_unload_event (pid)) + error ("Must have an unload event to use this query"); + + return som_solib_library_pathname (pid); +} + +static void +som_solib_desire_dynamic_linker_symbols (void) +{ + struct objfile *objfile; + struct unwind_table_entry *u; + struct minimal_symbol *dld_msymbol; + + /* Do we already know the value of these symbols? If so, then + we've no work to do. + + (If you add clauses to this test, be sure to likewise update the + test within the loop.) + */ + if (dld_cache.is_valid) + return; + + ALL_OBJFILES (objfile) + { + dld_msymbol = lookup_minimal_symbol ("shl_load", NULL, objfile); + if (dld_msymbol != NULL) + { + dld_cache.load.address = SYMBOL_VALUE (dld_msymbol); + dld_cache.load.unwind = find_unwind_entry (dld_cache.load.address); + } + + dld_msymbol = lookup_minimal_symbol_solib_trampoline ("shl_load", + objfile); + if (dld_msymbol != NULL) + { + if (SYMBOL_TYPE (dld_msymbol) == mst_solib_trampoline) + { + u = find_unwind_entry (SYMBOL_VALUE (dld_msymbol)); + if ((u != NULL) && (u->stub_unwind.stub_type == EXPORT)) + { + dld_cache.load_stub.address = SYMBOL_VALUE (dld_msymbol); + dld_cache.load_stub.unwind = u; + } + } + } + + dld_msymbol = lookup_minimal_symbol ("shl_unload", NULL, objfile); + if (dld_msymbol != NULL) + { + dld_cache.unload.address = SYMBOL_VALUE (dld_msymbol); + dld_cache.unload.unwind = find_unwind_entry (dld_cache.unload.address); + + /* ??rehrauer: I'm not sure exactly what this is, but it appears + that on some HPUX 10.x versions, there's two unwind regions to + cover the body of "shl_unload", the second being 4 bytes past + the end of the first. This is a large hack to handle that + case, but since I don't seem to have any legitimate way to + look for this thing via the symbol table... + */ + if (dld_cache.unload.unwind != NULL) + { + u = find_unwind_entry (dld_cache.unload.unwind->region_end + 4); + if (u != NULL) + { + dld_cache.unload2.address = u->region_start; + dld_cache.unload2.unwind = u; + } + } + } + + dld_msymbol = lookup_minimal_symbol_solib_trampoline ("shl_unload", + objfile); + if (dld_msymbol != NULL) + { + if (SYMBOL_TYPE (dld_msymbol) == mst_solib_trampoline) + { + u = find_unwind_entry (SYMBOL_VALUE (dld_msymbol)); + if ((u != NULL) && (u->stub_unwind.stub_type == EXPORT)) + { + dld_cache.unload_stub.address = SYMBOL_VALUE (dld_msymbol); + dld_cache.unload_stub.unwind = u; + } + } + } + + /* Did we find everything we were looking for? If so, stop. */ + if ((dld_cache.load.address != 0) + && (dld_cache.load_stub.address != 0) + && (dld_cache.unload.address != 0) + && (dld_cache.unload_stub.address != 0)) + { + dld_cache.is_valid = 1; + break; + } + } + + dld_cache.hook.unwind = find_unwind_entry (dld_cache.hook.address); + dld_cache.hook_stub.unwind = find_unwind_entry (dld_cache.hook_stub.address); + + /* We're prepared not to find some of these symbols, which is why + this function is a "desire" operation, and not a "require". + */ +} + +int +som_solib_in_dynamic_linker (int pid, CORE_ADDR pc) +{ + struct unwind_table_entry *u_pc; + + /* Are we in the dld itself? + + ??rehrauer: Large hack -- We'll assume that any address in a + shared text region is the dld's text. This would obviously + fall down if the user attached to a process, whose shlibs + weren't mapped to a (writeable) private region. However, in + that case the debugger probably isn't able to set the fundamental + breakpoint in the dld callback anyways, so this hack should be + safe. + */ + if ((pc & (CORE_ADDR) 0xc0000000) == (CORE_ADDR) 0xc0000000) + return 1; + + /* Cache the address of some symbols that are part of the dynamic + linker, if not already known. + */ + som_solib_desire_dynamic_linker_symbols (); + + /* Are we in the dld callback? Or its export stub? */ + u_pc = find_unwind_entry (pc); + if (u_pc == NULL) + return 0; + + if ((u_pc == dld_cache.hook.unwind) || (u_pc == dld_cache.hook_stub.unwind)) + return 1; + + /* Or the interface of the dld (i.e., "shl_load" or friends)? */ + if ((u_pc == dld_cache.load.unwind) + || (u_pc == dld_cache.unload.unwind) + || (u_pc == dld_cache.unload2.unwind) + || (u_pc == dld_cache.load_stub.unwind) + || (u_pc == dld_cache.unload_stub.unwind)) + return 1; + + /* Apparently this address isn't part of the dld's text. */ + return 0; +} + + +/* Return the GOT value for the shared library in which ADDR belongs. If + ADDR isn't in any known shared library, return zero. */ + +CORE_ADDR +som_solib_get_got_by_pc (CORE_ADDR addr) +{ + struct so_list *so_list = so_list_head; + CORE_ADDR got_value = 0; + + while (so_list) + { + if (so_list->som_solib.text_addr <= addr + && so_list->som_solib.text_end > addr) + { + got_value = so_list->som_solib.got_value; + break; + } + so_list = so_list->next; + } + return got_value; +} + +/* elz: + Return the address of the handle of the shared library + in which ADDR belongs. If + ADDR isn't in any known shared library, return zero. */ +/* this function is used in hppa_fix_call_dummy in hppa-tdep.c */ + +CORE_ADDR +som_solib_get_solib_by_pc (CORE_ADDR addr) +{ + struct so_list *so_list = so_list_head; + + while (so_list) + { + if (so_list->som_solib.text_addr <= addr + && so_list->som_solib.text_end > addr) + { + break; + } + so_list = so_list->next; + } + if (so_list) + return so_list->solib_addr; + else + return 0; +} + + +int +som_solib_section_offsets (struct objfile *objfile, + struct section_offsets *offsets) +{ + struct so_list *so_list = so_list_head; + + while (so_list) + { + /* Oh what a pain! We need the offsets before so_list->objfile + is valid. The BFDs will never match. Make a best guess. */ + if (strstr (objfile->name, so_list->som_solib.name)) + { + asection *private_section; + + /* The text offset is easy. */ + offsets->offsets[SECT_OFF_TEXT (objfile)] + = (so_list->som_solib.text_addr + - so_list->som_solib.text_link_addr); + offsets->offsets[SECT_OFF_RODATA (objfile)] + = ANOFFSET (offsets, SECT_OFF_TEXT (objfile)); + + /* We should look at presumed_dp in the SOM header, but + that's not easily available. This should be OK though. */ + private_section = bfd_get_section_by_name (objfile->obfd, + "$PRIVATE$"); + if (!private_section) + { + warning ("Unable to find $PRIVATE$ in shared library!"); + offsets->offsets[SECT_OFF_DATA (objfile)] = 0; + offsets->offsets[SECT_OFF_BSS (objfile)] = 0; + return 1; + } + offsets->offsets[SECT_OFF_DATA (objfile)] + = (so_list->som_solib.data_start - private_section->vma); + offsets->offsets[SECT_OFF_BSS (objfile)] + = ANOFFSET (offsets, SECT_OFF_DATA (objfile)); + return 1; + } + so_list = so_list->next; + } + return 0; +} + +/* Dump information about all the currently loaded shared libraries. */ + +static void +som_sharedlibrary_info_command (char *ignore, int from_tty) +{ + struct so_list *so_list = so_list_head; + + if (exec_bfd == NULL) + { + printf_unfiltered ("No executable file.\n"); + return; + } + + if (so_list == NULL) + { + printf_unfiltered ("No shared libraries loaded at this time.\n"); + return; + } + + printf_unfiltered ("Shared Object Libraries\n"); + printf_unfiltered (" %-12s%-12s%-12s%-12s%-12s%-12s\n", + " flags", " tstart", " tend", " dstart", " dend", " dlt"); + while (so_list) + { + unsigned int flags; + + flags = so_list->som_solib.struct_version << 24; + flags |= so_list->som_solib.bind_mode << 16; + flags |= so_list->som_solib.library_version; + printf_unfiltered ("%s", so_list->som_solib.name); + if (so_list->objfile == NULL) + printf_unfiltered (" (symbols not loaded)"); + printf_unfiltered ("\n"); + printf_unfiltered (" %-12s", local_hex_string_custom (flags, "08l")); + printf_unfiltered ("%-12s", + local_hex_string_custom (so_list->som_solib.text_addr, "08l")); + printf_unfiltered ("%-12s", + local_hex_string_custom (so_list->som_solib.text_end, "08l")); + printf_unfiltered ("%-12s", + local_hex_string_custom (so_list->som_solib.data_start, "08l")); + printf_unfiltered ("%-12s", + local_hex_string_custom (so_list->som_solib.data_end, "08l")); + printf_unfiltered ("%-12s\n", + local_hex_string_custom (so_list->som_solib.got_value, "08l")); + so_list = so_list->next; + } +} + +static void +som_solib_sharedlibrary_command (char *args, int from_tty) +{ + dont_repeat (); + som_solib_add (args, from_tty, (struct target_ops *) 0, 1); +} + + + +char * +som_solib_address (CORE_ADDR addr) +{ + struct so_list *so = so_list_head; + + while (so) + { + /* Is this address within this shlib's text range? If so, + return the shlib's name. + */ + if ((addr >= so->som_solib.text_addr) && (addr <= so->som_solib.text_end)) + return so->som_solib.name; + + /* Nope, keep looking... */ + so = so->next; + } + + /* No, we couldn't prove that the address is within a shlib. */ + return NULL; +} + + +void +som_solib_restart (void) +{ + struct so_list *sl = so_list_head; + + /* Before the shlib info vanishes, use it to disable any breakpoints + that may still be active in those shlibs. + */ + disable_breakpoints_in_shlibs (0); + + /* Discard all the shlib descriptors. + */ + while (sl) + { + struct so_list *next_sl = sl->next; + xfree (sl); + sl = next_sl; + } + so_list_head = NULL; + + som_solib_total_st_size = (LONGEST) 0; + som_solib_st_size_threshold_exceeded = 0; + + dld_cache.is_valid = 0; + + dld_cache.hook.address = 0; + dld_cache.hook.unwind = NULL; + + dld_cache.hook_stub.address = 0; + dld_cache.hook_stub.unwind = NULL; + + dld_cache.load.address = 0; + dld_cache.load.unwind = NULL; + + dld_cache.load_stub.address = 0; + dld_cache.load_stub.unwind = NULL; + + dld_cache.unload.address = 0; + dld_cache.unload.unwind = NULL; + + dld_cache.unload2.address = 0; + dld_cache.unload2.unwind = NULL; + + dld_cache.unload_stub.address = 0; + dld_cache.unload_stub.unwind = NULL; +} + + +/* LOCAL FUNCTION + + no_shared_libraries -- handle command to explicitly discard symbols + from shared libraries. + + DESCRIPTION + + Implements the command "nosharedlibrary", which discards symbols + that have been auto-loaded from shared libraries. Symbols from + shared libraries that were added by explicit request of the user + are not discarded. Also called from remote.c. */ + +void +no_shared_libraries (char *ignored, int from_tty) +{ + /* FIXME */ +} + + +void +_initialize_som_solib (void) +{ + add_com ("sharedlibrary", class_files, som_solib_sharedlibrary_command, + "Load shared object library symbols for files matching REGEXP."); + add_info ("sharedlibrary", som_sharedlibrary_info_command, + "Status of loaded shared object libraries."); + + add_show_from_set + (add_set_cmd ("auto-solib-add", class_support, var_boolean, + (char *) &auto_solib_add, + "Set autoloading of shared library symbols.\n\ +If \"on\", symbols from all shared object libraries will be loaded\n\ +automatically when the inferior begins execution, when the dynamic linker\n\ +informs gdb that a new library has been loaded, or when attaching to the\n\ +inferior. Otherwise, symbols must be loaded manually, using `sharedlibrary'.", + &setlist), + &showlist); + + add_show_from_set + (add_set_cmd ("auto-solib-limit", class_support, var_zinteger, + (char *) &auto_solib_limit, + "Set threshold (in Mb) for autoloading shared library symbols.\n\ +When shared library autoloading is enabled, new libraries will be loaded\n\ +only until the total size of shared library symbols exceeds this\n\ +threshold in megabytes. Is ignored when using `sharedlibrary'.", + &setlist), + &showlist); + + /* ??rehrauer: On HP-UX, the kernel parameter MAXDSIZ limits how + much data space a process can use. We ought to be reading + MAXDSIZ and setting auto_solib_limit to some large fraction of + that value. If not that, we maybe ought to be setting it smaller + than the default for MAXDSIZ (that being 64Mb, I believe). + However, [1] this threshold is only crudely approximated rather + than actually measured, and [2] 50 Mbytes is too small for + debugging gdb itself. Thus, the arbitrary 100 figure. */ + auto_solib_limit = 100; /* Megabytes */ + + som_solib_restart (); +} + +/* Get some HPUX-specific data from a shared lib. + */ +CORE_ADDR +so_lib_thread_start_addr (struct so_list *so) +{ + return so->som_solib.tsd_start_addr; +} diff --git a/contrib/gdb/gdb/somsolib.h b/contrib/gdb/gdb/somsolib.h new file mode 100644 index 0000000..c241411 --- /dev/null +++ b/contrib/gdb/gdb/somsolib.h @@ -0,0 +1,178 @@ +/* HP SOM Shared library declarations for GDB, the GNU Debugger. + + Copyright 1992, 1994, 1995, 1998, 1999, 2000, 2003 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. + + Written by the Center for Software Science at the Univerity of Utah + and by Cygnus Support. */ + +#ifndef SOMSOLIB_H +#define SOMSOLIB_H + +/* Forward decl's for prototypes */ +struct target_ops; +struct objfile; +struct section_offsets; + +/* Called to add symbols from a shared library to gdb's symbol table. */ + +#define SOLIB_ADD(filename, from_tty, targ, readsyms) \ + som_solib_add (filename, from_tty, targ, readsyms) + +extern void som_solib_add (char *, int, struct target_ops *, int); + +extern CORE_ADDR som_solib_get_got_by_pc (CORE_ADDR); + +extern int som_solib_section_offsets (struct objfile *, + struct section_offsets *); + +/* Function to be called when the inferior starts up, to discover the names + of shared libraries that are dynamically linked, the base addresses to + which they are linked, and sufficient information to read in their symbols + at a later time. */ + +#define SOLIB_CREATE_INFERIOR_HOOK(PID) som_solib_create_inferior_hook() + +extern void som_solib_create_inferior_hook (void); + +/* Function to be called to remove the connection between debugger and + dynamic linker that was established by SOLIB_CREATE_INFERIOR_HOOK. + (This operation does not remove shared library information from + the debugger, as CLEAR_SOLIB does.) + */ +#define SOLIB_REMOVE_INFERIOR_HOOK(PID) som_solib_remove_inferior_hook(PID) + +extern void som_solib_remove_inferior_hook (int); + +/* This function is called by the "catch load" command. It allows + the debugger to be notified by the dynamic linker when a specified + library file (or any library file, if filename is NULL) is loaded. + */ +#define SOLIB_CREATE_CATCH_LOAD_HOOK(pid,tempflag, filename,cond_string) \ + som_solib_create_catch_load_hook (pid, tempflag, filename, cond_string) + +extern void som_solib_create_catch_load_hook (int, int, char *, char *); + +/* This function is called by the "catch unload" command. It allows + the debugger to be notified by the dynamic linker when a specified + library file (or any library file, if filename is NULL) is unloaded. + */ +#define SOLIB_CREATE_CATCH_UNLOAD_HOOK(pid,tempflag,filename, cond_string) \ + som_solib_create_catch_unload_hook (pid, tempflag, filename, cond_string) + +extern void som_solib_create_catch_unload_hook (int, int, char *, char *); + +/* This function returns TRUE if the dynamic linker has just reported + a load of a library. + + This function must be used only when the inferior has stopped in + the dynamic linker hook, or undefined results are guaranteed. + */ +#define SOLIB_HAVE_LOAD_EVENT(pid) \ + som_solib_have_load_event (pid) + +extern int som_solib_have_load_event (int); + +/* This function returns a pointer to the string representation of the + pathname of the dynamically-linked library that has just been loaded. + + This function must be used only when SOLIB_HAVE_LOAD_EVENT is TRUE, + or undefined results are guaranteed. + + This string's contents are only valid immediately after the inferior + has stopped in the dynamic linker hook, and becomes invalid as soon + as the inferior is continued. Clients should make a copy of this + string if they wish to continue the inferior and then access the string. + */ +#define SOLIB_LOADED_LIBRARY_PATHNAME(pid) \ + som_solib_loaded_library_pathname (pid) + +extern char *som_solib_loaded_library_pathname (int); + +/* This function returns TRUE if the dynamic linker has just reported + an unload of a library. + + This function must be used only when the inferior has stopped in + the dynamic linker hook, or undefined results are guaranteed. + */ +#define SOLIB_HAVE_UNLOAD_EVENT(pid) \ + som_solib_have_unload_event (pid) + +extern int som_solib_have_unload_event (int); + +/* This function returns a pointer to the string representation of the + pathname of the dynamically-linked library that has just been unloaded. + + This function must be used only when SOLIB_HAVE_UNLOAD_EVENT is TRUE, + or undefined results are guaranteed. + + This string's contents are only valid immediately after the inferior + has stopped in the dynamic linker hook, and becomes invalid as soon + as the inferior is continued. Clients should make a copy of this + string if they wish to continue the inferior and then access the string. + */ +#define SOLIB_UNLOADED_LIBRARY_PATHNAME(pid) \ + som_solib_unloaded_library_pathname (pid) + +extern char *som_solib_unloaded_library_pathname (int); + +/* This function returns TRUE if pc is the address of an instruction that + lies within the dynamic linker (such as the event hook, or the dld + itself). + + This function must be used only when a dynamic linker event has been + caught, and the inferior is being stepped out of the hook, or undefined + results are guaranteed. + */ +#define SOLIB_IN_DYNAMIC_LINKER(pid,pc) \ + som_solib_in_dynamic_linker (pid, pc) + +extern int som_solib_in_dynamic_linker (int, CORE_ADDR); + +/* This function must be called when the inferior is killed, and the program + restarted. This is not the same as CLEAR_SOLIB, in that it doesn't discard + any symbol tables. + + Presently, this functionality is not implemented. + */ +#define SOLIB_RESTART() \ + som_solib_restart () + +extern void som_solib_restart (void); + +/* If we can't set a breakpoint, and it's in a shared library, just + disable it. */ + +#define DISABLE_UNSETTABLE_BREAK(addr) (som_solib_address(addr) != NULL) + +extern char *som_solib_address (CORE_ADDR); /* somsolib.c */ + +/* If ADDR lies in a shared library, return its name. */ + +#define PC_SOLIB(addr) som_solib_address (addr) + +extern CORE_ADDR som_solib_get_solib_by_pc (CORE_ADDR addr); + +struct so_list; +extern CORE_ADDR so_lib_thread_start_addr (struct so_list *so); + +extern void no_shared_libraries (char *ignored, int from_tty); + +#endif diff --git a/contrib/gdb/gdb/srec.h b/contrib/gdb/gdb/srec.h new file mode 100644 index 0000000..8189a9b --- /dev/null +++ b/contrib/gdb/gdb/srec.h @@ -0,0 +1,39 @@ +/* S-record download support for GDB, the GNU debugger. + Copyright 1995, 1996, 2000 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. */ + +struct serial; + +void load_srec (struct serial *desc, const char *file, bfd_vma load_offset, + int maxrecsize, int flags, int hashmark, + int (*waitack) (void)); + +/* S-record capability flags */ + +/* Which record types are supported */ +#define SREC_2_BYTE_ADDR 0x00000001 +#define SREC_3_BYTE_ADDR 0x00000002 +#define SREC_4_BYTE_ADDR 0x00000004 +#define SREC_TERM_SHIFT 3 + +#define SREC_ALL (SREC_2_BYTE_ADDR | SREC_3_BYTE_ADDR | SREC_4_BYTE_ADDR \ + | ((SREC_2_BYTE_ADDR | SREC_3_BYTE_ADDR | SREC_4_BYTE_ADDR) \ + << SREC_TERM_SHIFT)) + +#define SREC_BINARY 0x00000040 /* Supports binary form of S-records */ diff --git a/contrib/gdb/gdb/standalone.c b/contrib/gdb/gdb/standalone.c new file mode 100644 index 0000000..906e37a --- /dev/null +++ b/contrib/gdb/gdb/standalone.c @@ -0,0 +1,580 @@ +/* Interface to bare machine for GDB running as kernel debugger. + + Copyright 1986, 1989, 1991, 1992, 1993, 1995, 1996, 2000, 2001, + 2003 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 <stdio.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <sys/types.h> +#include "gdb_stat.h" + +#if defined (SIGTSTP) && defined (SIGIO) +#include <sys/time.h> +#include <sys/resource.h> +#endif /* SIGTSTP and SIGIO defined (must be 4.2) */ + +#include "defs.h" +#include <signal.h> +#include "symtab.h" +#include "frame.h" +#include "inferior.h" +#include "gdb_wait.h" + + +/* Random system calls, mostly no-ops to prevent link problems */ + +ioctl (int desc, int code, int arg) +{ +} + +int (*signal ()) () +{ +} + +kill (void) +{ +} + +getpid (void) +{ + return 0; +} + +sigsetmask (void) +{ +} + +chdir (void) +{ +} + +char * +getcwd (char *buf, unsigned int len) +{ + buf[0] = '/'; + buf[1] = 0; + return buf; +} + +/* Used to check for existence of .gdbinit. Say no. */ + +access (void) +{ + return -1; +} + +exit (void) +{ + error ("Fatal error; restarting."); +} + +/* Reading "files". The contents of some files are written into kdb's + data area before it is run. These files are used to contain the + symbol table for kdb to load, and the source files (in case the + kdb user wants to print them). The symbols are stored in a file + named "kdb-symbols" in a.out format (except that all the text and + data have been stripped to save room). + + The files are stored in the following format: + int number of bytes of data for this file, including these four. + char[] name of the file, ending with a null. + padding to multiple of 4 boundary. + char[] file contents. The length can be deduced from what was + specified before. There is no terminating null here. + + If the int at the front is zero, it means there are no more files. + + Opening a file in kdb returns a nonzero value to indicate success, + but the value does not matter. Only one file can be open, and only + for reading. All the primitives for input from the file know + which file is open and ignore what is specified for the descriptor + or for the stdio stream. + + Input with fgetc can be done either on the file that is open + or on stdin (which reads from the terminal through tty_input () */ + +/* Address of data for the files stored in format described above. */ +char *files_start; + +/* The file stream currently open: */ + +char *sourcebeg; /* beginning of contents */ +int sourcesize; /* size of contents */ +char *sourceptr; /* current read pointer */ +int sourceleft; /* number of bytes to eof */ + +/* "descriptor" for the file now open. + Incremented at each close. + If specified descriptor does not match this, + it means the program is trying to use a closed descriptor. + We report an error for that. */ + +int sourcedesc; + +open (char *filename, int modes) +{ + char *next; + + if (modes) + { + errno = EROFS; + return -1; + } + + if (sourceptr) + { + errno = EMFILE; + return -1; + } + + for (next = files_start; *(int *) next; next += *(int *) next) + { + if (!strcmp (next + 4, filename)) + { + sourcebeg = next + 4 + strlen (next + 4) + 1; + sourcebeg = (char *) (((int) sourcebeg + 3) & (-4)); + sourceptr = sourcebeg; + sourcesize = next + *(int *) next - sourceptr; + sourceleft = sourcesize; + return sourcedesc; + } + } + return 0; +} + +close (int desc) +{ + sourceptr = 0; + sourcedesc++; + /* Don't let sourcedesc get big enough to be confused with stdin. */ + if (sourcedesc == 100) + sourcedesc = 5; +} + +FILE * +fopen (char *filename, char *modes) +{ + return (FILE *) open (filename, *modes == 'w'); +} + +FILE * +fdopen (int desc) +{ + return (FILE *) desc; +} + +fclose (int desc) +{ + close (desc); +} + +fstat (int desc, struct stat *statbuf) +{ + if (desc != sourcedesc) + { + errno = EBADF; + return -1; + } + statbuf->st_size = sourcesize; +} + +myread (int desc, char *destptr, int size, char *filename) +{ + int len = min (sourceleft, size); + + if (desc != sourcedesc) + { + errno = EBADF; + return -1; + } + + memcpy (destptr, sourceptr, len); + sourceleft -= len; + return len; +} + +int +fread (int bufp, int numelts, int eltsize, int stream) +{ + int elts = min (numelts, sourceleft / eltsize); + int len = elts * eltsize; + + if (stream != sourcedesc) + { + errno = EBADF; + return -1; + } + + memcpy (bufp, sourceptr, len); + sourceleft -= len; + return elts; +} + +int +fgetc (int desc) +{ + + if (desc == (int) stdin) + return tty_input (); + + if (desc != sourcedesc) + { + errno = EBADF; + return -1; + } + + if (sourceleft-- <= 0) + return EOF; + return *sourceptr++; +} + +lseek (int desc, int pos) +{ + + if (desc != sourcedesc) + { + errno = EBADF; + return -1; + } + + if (pos < 0 || pos > sourcesize) + { + errno = EINVAL; + return -1; + } + + sourceptr = sourcebeg + pos; + sourceleft = sourcesize - pos; +} + +/* Output in kdb can go only to the terminal, so the stream + specified may be ignored. */ + +printf (int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) +{ + char buffer[1024]; + sprintf (buffer, a1, a2, a3, a4, a5, a6, a7, a8, a9); + display_string (buffer); +} + +fprintf (int ign, int a1, int a2, int a3, int a4, int a5, int a6, int a7, + int a8, int a9) +{ + char buffer[1024]; + sprintf (buffer, a1, a2, a3, a4, a5, a6, a7, a8, a9); + display_string (buffer); +} + +fwrite (char *buf, int numelts, int size, int stream) +{ + int i = numelts * size; + while (i-- > 0) + fputc (*buf++, stream); +} + +fputc (int c, int ign) +{ + char buf[2]; + buf[0] = c; + buf[1] = 0; + display_string (buf); +} + +/* sprintf refers to this, but loading this from the + library would cause fflush to be loaded from it too. + In fact there should be no need to call this (I hope). */ + +_flsbuf (void) +{ + error ("_flsbuf was actually called."); +} + +fflush (int ign) +{ +} + +/* Entries into core and inflow, needed only to make things link ok. */ + +exec_file_command (void) +{ +} + +core_file_command (void) +{ +} + +char * +get_exec_file (int err) +{ + /* Makes one printout look reasonable; value does not matter otherwise. */ + return "run"; +} + +/* Nonzero if there is a core file. */ + +have_core_file_p (void) +{ + return 0; +} + +kill_command (void) +{ + inferior_ptid = null_ptid; +} + +terminal_inferior (void) +{ +} + +terminal_ours (void) +{ +} + +terminal_init_inferior (void) +{ +} + +write_inferior_register (void) +{ +} + +read_inferior_register (void) +{ +} + +read_memory (CORE_ADDR memaddr, char *myaddr, int len) +{ + memcpy (myaddr, memaddr, len); +} + +/* Always return 0 indicating success. */ + +write_memory (CORE_ADDR memaddr, char *myaddr, int len) +{ + memcpy (memaddr, myaddr, len); + return 0; +} + +static REGISTER_TYPE saved_regs[NUM_REGS]; + +REGISTER_TYPE +read_register (int regno) +{ + if (regno < 0 || regno >= NUM_REGS) + error ("Register number %d out of range.", regno); + return saved_regs[regno]; +} + +void +write_register (int regno, REGISTER_TYPE value) +{ + if (regno < 0 || regno >= NUM_REGS) + error ("Register number %d out of range.", regno); + saved_regs[regno] = value; +} + +/* System calls needed in relation to running the "inferior". */ + +vfork (void) +{ + /* Just appear to "succeed". Say the inferior's pid is 1. */ + return 1; +} + +/* These are called by code that normally runs in the inferior + that has just been forked. That code never runs, when standalone, + and these definitions are so it will link without errors. */ + +ptrace (void) +{ +} + +setpgrp (void) +{ +} + +execle (void) +{ +} + +_exit (void) +{ +} + +/* Malloc calls these. */ + +malloc_warning (char *str) +{ + printf ("\n%s.\n\n", str); +} + +char *next_free; +char *memory_limit; + +char * +sbrk (int amount) +{ + if (next_free + amount > memory_limit) + return (char *) -1; + next_free += amount; + return next_free - amount; +} + +/* Various ways malloc might ask where end of memory is. */ + +char * +ulimit (void) +{ + return memory_limit; +} + +int +vlimit (void) +{ + return memory_limit - next_free; +} + +getrlimit (struct rlimit *addr) +{ + addr->rlim_cur = memory_limit - next_free; +} + +/* Context switching to and from program being debugged. */ + +/* GDB calls here to run the user program. + The frame pointer for this function is saved in + gdb_stack by save_frame_pointer; then we restore + all of the user program's registers, including PC and PS. */ + +static int fault_code; +static REGISTER_TYPE gdb_stack; + +resume (void) +{ + REGISTER_TYPE restore[NUM_REGS]; + + PUSH_FRAME_PTR; + save_frame_pointer (); + + memcpy (restore, saved_regs, sizeof restore); + POP_REGISTERS; + /* Control does not drop through here! */ +} + +save_frame_pointer (CORE_ADDR val) +{ + gdb_stack = val; +} + +/* Fault handlers call here, running in the user program stack. + They must first push a fault code, + old PC, old PS, and any other info about the fault. + The exact format is machine-dependent and is known only + in the definition of PUSH_REGISTERS. */ + +fault (void) +{ + /* Transfer all registers and fault code to the stack + in canonical order: registers in order of GDB register number, + followed by fault code. */ + PUSH_REGISTERS; + + /* Transfer them to saved_regs and fault_code. */ + save_registers (); + + restore_gdb (); + /* Control does not reach here */ +} + +restore_gdb (void) +{ + CORE_ADDR new_fp = gdb_stack; + /* Switch to GDB's stack */ + POP_FRAME_PTR; + /* Return from the function `resume'. */ +} + +/* Assuming register contents and fault code have been pushed on the stack as + arguments to this function, copy them into the standard place + for the program's registers while GDB is running. */ + +save_registers (int firstreg) +{ + memcpy (saved_regs, &firstreg, sizeof saved_regs); + fault_code = (&firstreg)[NUM_REGS]; +} + +/* Store into the structure such as `wait' would return + the information on why the program faulted, + converted into a machine-independent signal number. */ + +static int fault_table[] = FAULT_TABLE; + +int +wait (WAITTYPE *w) +{ + WSETSTOP (*w, fault_table[fault_code / FAULT_CODE_UNITS]); + return PIDGET (inferior_ptid); +} + +/* Allocate a big space in which files for kdb to read will be stored. + Whatever is left is where malloc can allocate storage. + + Initialize it, so that there will be space in the executable file + for it. Then the files can be put into kdb by writing them into + kdb's executable file. */ + +/* The default size is as much space as we expect to be available + for kdb to use! */ + +#ifndef HEAP_SIZE +#define HEAP_SIZE 400000 +#endif + +char heap[HEAP_SIZE] = +{0}; + +#ifndef STACK_SIZE +#define STACK_SIZE 100000 +#endif + +int kdb_stack_beg[STACK_SIZE / sizeof (int)]; +int kdb_stack_end; + +_initialize_standalone (void) +{ + char *next; + + /* Find start of data on files. */ + + files_start = heap; + + /* Find the end of the data on files. */ + + for (next = files_start; *(int *) next; next += *(int *) next) + { + } + + /* That is where free storage starts for sbrk to give out. */ + next_free = next; + + memory_limit = heap + sizeof heap; +} diff --git a/contrib/gdb/gdb/stop-gdb.c b/contrib/gdb/gdb/stop-gdb.c new file mode 100644 index 0000000..ee84609 --- /dev/null +++ b/contrib/gdb/gdb/stop-gdb.c @@ -0,0 +1,109 @@ +/* A client to make GDB return to command level in Mach 3. + Copyright 1992, 1993, 1994, 2000 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. */ + +/* Authors: Jukka Virtanen <jtv@hut.fi> and Peter Stout <pds@cs.cmu.edu>. + + A simple client to make GDB (versions 4.4 and later) on Mach 3 return + to the command level when it is waiting for the inferior to stop. + + Actions: Lookup the send right to the GDB message port from the + NetMsgServer. + + Send an asynchronous message with msgh_id + GDB_MESSAGE_ID_STOP to that port. + */ + +#include <stdio.h> + +#include "defs.h" + +#include <mach.h> +#include <mach/message.h> +#include <mach_error.h> +#include <servers/netname.h> +#include <servers/netname_defs.h> + +void +main (int argc, char **argv) +{ + kern_return_t kr; + mach_msg_header_t msg; + mach_port_t gdb_port; + char *host; + char *name; + + if (argc == 1) + argv[argc++] = GDB_DEF_NAME; + + if (argc != 2) + { + fprintf (stderr, "Usage : %s <GDB name>\n", argv[0]); + exit (1); + } + + /* Allow the user to specify a remote host. */ + host = strchr (argv[1], '@'); + if (host) + *(host++) = '\0'; + else + host = (char *) ""; + + name = malloc (strlen (argv[1]) + sizeof (GDB_NAME_PREFIX)); + if (name == NULL) + { + fprintf (stderr, "Unable to allocate memory for name."); + exit (1); + } + + strcpy (name, GDB_NAME_PREFIX); + strcat (name, argv[1]); + + /* Look up the GDB service port. For convenience, add the + GDB_NAME_PREFIX the argument before looking up the name. + For backwards compatibility, do it without. */ + + kr = netname_look_up (name_server_port, host, name, &gdb_port); + if (kr == NETNAME_NOT_CHECKED_IN) + kr = netname_look_up (name_server_port, host, argv[1], &gdb_port); + if (kr != KERN_SUCCESS) + { + fprintf (stderr, "Unable to lookup the GDB service port: %s.\n", + mach_error_string (kr)); + exit (1); + } + + /* Code generated by mig stub generator, with minor cleanups :-) + + simpleroutine stop_inferior(gdb_port : mach_port_t); */ + + msg.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0); + msg.msgh_remote_port = gdb_port; + msg.msgh_local_port = MACH_PORT_NULL; + msg.msgh_size = sizeof (msg); + msg.msgh_seqno = 0; + msg.msgh_id = GDB_MESSAGE_ID_STOP; + + kr = mach_msg_send (&msg); + if (kr != KERN_SUCCESS) + fprintf (stderr, "Message not sent, return code %d : %s\n", kr, + mach_error_string (kr)); + + exit (kr != KERN_SUCCESS); +} diff --git a/contrib/gdb/gdb/tui/tui.c b/contrib/gdb/gdb/tui/tui.c new file mode 100644 index 0000000..d6f344f --- /dev/null +++ b/contrib/gdb/gdb/tui/tui.c @@ -0,0 +1,590 @@ +/* General functions for the WDB TUI. + + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software + Foundation, Inc. + + Contributed by Hewlett-Packard Company. + + 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 "defs.h" +#include "gdbcmd.h" +#include "tui/tui.h" +#include "tui/tui-hooks.h" +#include "tui/tui-data.h" +#include "tui/tui-layout.h" +#include "tui/tui-io.h" +#include "tui/tui-regs.h" +#include "tui/tui-stack.h" +#include "tui/tui-win.h" +#include "tui/tui-winsource.h" +#include "tui/tui-windata.h" +#include "target.h" +#include "frame.h" +#include "breakpoint.h" +#include "inferior.h" +#include "symtab.h" +#include "source.h" + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#ifdef HAVE_TERM_H +#include <term.h> +#endif +#include <signal.h> +#include <fcntl.h> +#if 0 +#include <termio.h> +#endif +#include <setjmp.h> + +#include "gdb_curses.h" + +/* This redefines CTRL if it is not already defined, so it must come + after terminal state releated include files like <term.h> and + "gdb_ncurses.h". */ +#include "readline/readline.h" + +/* Tells whether the TUI is active or not. */ +int tui_active = 0; +static int tui_finish_init = 1; + +enum tui_key_mode tui_current_key_mode = TUI_COMMAND_MODE; + +struct tui_char_command +{ + unsigned char key; + const char* cmd; +}; + +/* Key mapping to gdb commands when the TUI is using the single key mode. */ +static const struct tui_char_command tui_commands[] = { + { 'c', "continue" }, + { 'd', "down" }, + { 'f', "finish" }, + { 'n', "next" }, + { 'r', "run" }, + { 's', "step" }, + { 'u', "up" }, + { 'v', "info locals" }, + { 'w', "where" }, + { 0, 0 }, +}; + +static Keymap tui_keymap; +static Keymap tui_readline_standard_keymap; + +/* TUI readline command. + Switch the output mode between TUI/standard gdb. */ +static int +tui_rl_switch_mode (int notused1, int notused2) +{ + if (tui_active) + { + tui_disable (); + rl_prep_terminal (0); + } + else + { + rl_deprep_terminal (); + tui_enable (); + } + + /* Clear the readline in case switching occurred in middle of something. */ + if (rl_end) + rl_kill_text (0, rl_end); + + /* Since we left the curses mode, the terminal mode is restored to + some previous state. That state may not be suitable for readline + to work correctly (it may be restored in line mode). We force an + exit of the current readline so that readline is re-entered and it + will be able to setup the terminal for its needs. By re-entering + in readline, we also redisplay its prompt in the non-curses mode. */ + rl_newline (1, '\n'); + + /* Make sure the \n we are returning does not repeat the last command. */ + dont_repeat (); + return 0; +} + +/* TUI readline command. + Change the TUI layout to show a next layout. + This function is bound to CTRL-X 2. It is intended to provide + a functionality close to the Emacs split-window command. We always + show two windows (src+asm), (src+regs) or (asm+regs). */ +static int +tui_rl_change_windows (int notused1, int notused2) +{ + if (!tui_active) + tui_rl_switch_mode (0/*notused*/, 0/*notused*/); + + if (tui_active) + { + enum tui_layout_type new_layout; + enum tui_register_display_type regs_type = TUI_UNDEFINED_REGS; + + new_layout = tui_current_layout (); + + /* Select a new layout to have a rolling layout behavior + with always two windows (except when undefined). */ + switch (new_layout) + { + case SRC_COMMAND: + new_layout = SRC_DISASSEM_COMMAND; + break; + + case DISASSEM_COMMAND: + new_layout = SRC_DISASSEM_COMMAND; + break; + + case SRC_DATA_COMMAND: + new_layout = SRC_DISASSEM_COMMAND; + break; + + case SRC_DISASSEM_COMMAND: + new_layout = DISASSEM_DATA_COMMAND; + break; + + case DISASSEM_DATA_COMMAND: + new_layout = SRC_DATA_COMMAND; + break; + + default: + new_layout = SRC_COMMAND; + break; + } + tui_set_layout (new_layout, regs_type); + } + return 0; +} + +/* TUI readline command. + Delete the second TUI window to only show one. */ +static int +tui_rl_delete_other_windows (int notused1, int notused2) +{ + if (!tui_active) + tui_rl_switch_mode (0/*notused*/, 0/*notused*/); + + if (tui_active) + { + enum tui_layout_type new_layout; + enum tui_register_display_type regs_type = TUI_UNDEFINED_REGS; + + new_layout = tui_current_layout (); + + /* Kill one window. */ + switch (new_layout) + { + case SRC_COMMAND: + case SRC_DATA_COMMAND: + case SRC_DISASSEM_COMMAND: + default: + new_layout = SRC_COMMAND; + break; + + case DISASSEM_COMMAND: + case DISASSEM_DATA_COMMAND: + new_layout = DISASSEM_COMMAND; + break; + } + tui_set_layout (new_layout, regs_type); + } + return 0; +} + +/* TUI readline command. + Switch the active window to give the focus to a next window. */ +static int +tui_rl_other_window (int count, int key) +{ + struct tui_win_info * win_info; + + if (!tui_active) + tui_rl_switch_mode (0/*notused*/, 0/*notused*/); + + win_info = tui_next_win (tui_win_with_focus ()); + if (win_info) + { + tui_set_win_focus_to (win_info); + if (TUI_DATA_WIN && TUI_DATA_WIN->generic.is_visible) + tui_refresh_data_win (); + keypad (TUI_CMD_WIN->generic.handle, (win_info != TUI_CMD_WIN)); + } + return 0; +} + +/* TUI readline command. + Execute the gdb command bound to the specified key. */ +static int +tui_rl_command_key (int count, int key) +{ + int i; + + reinitialize_more_filter (); + for (i = 0; tui_commands[i].cmd; i++) + { + if (tui_commands[i].key == key) + { + /* Must save the command because it can be modified + by execute_command. */ + char* cmd = alloca (strlen (tui_commands[i].cmd) + 1); + strcpy (cmd, tui_commands[i].cmd); + execute_command (cmd, TRUE); + return 0; + } + } + return 0; +} + +/* TUI readline command. + Temporarily leave the TUI SingleKey mode to allow editing + a gdb command with the normal readline. Once the command + is executed, the TUI SingleKey mode is installed back. */ +static int +tui_rl_command_mode (int count, int key) +{ + tui_set_key_mode (TUI_ONE_COMMAND_MODE); + return rl_insert (count, key); +} + +/* TUI readline command. + Switch between TUI SingleKey mode and gdb readline editing. */ +static int +tui_rl_next_keymap (int notused1, int notused2) +{ + if (!tui_active) + tui_rl_switch_mode (0/*notused*/, 0/*notused*/); + + tui_set_key_mode (tui_current_key_mode == TUI_COMMAND_MODE + ? TUI_SINGLE_KEY_MODE : TUI_COMMAND_MODE); + return 0; +} + +/* Readline hook to redisplay ourself the gdb prompt. + In the SingleKey mode, the prompt is not printed so that + the command window is cleaner. It will be displayed if + we temporarily leave the SingleKey mode. */ +static int +tui_rl_startup_hook (void) +{ + rl_already_prompted = 1; + if (tui_current_key_mode != TUI_COMMAND_MODE) + tui_set_key_mode (TUI_SINGLE_KEY_MODE); + tui_redisplay_readline (); + return 0; +} + +/* Change the TUI key mode by installing the appropriate readline keymap. */ +void +tui_set_key_mode (enum tui_key_mode mode) +{ + tui_current_key_mode = mode; + rl_set_keymap (mode == TUI_SINGLE_KEY_MODE + ? tui_keymap : tui_readline_standard_keymap); + tui_show_locator_content (); +} + +/* Initialize readline and configure the keymap for the switching + key shortcut. */ +void +tui_initialize_readline (void) +{ + int i; + Keymap tui_ctlx_keymap; + + rl_initialize (); + + rl_add_defun ("tui-switch-mode", tui_rl_switch_mode, -1); + rl_add_defun ("gdb-command", tui_rl_command_key, -1); + rl_add_defun ("next-keymap", tui_rl_next_keymap, -1); + + tui_keymap = rl_make_bare_keymap (); + tui_ctlx_keymap = rl_make_bare_keymap (); + tui_readline_standard_keymap = rl_get_keymap (); + + for (i = 0; tui_commands[i].cmd; i++) + rl_bind_key_in_map (tui_commands[i].key, tui_rl_command_key, tui_keymap); + + rl_generic_bind (ISKMAP, "\\C-x", (char*) tui_ctlx_keymap, tui_keymap); + + /* Bind all other keys to tui_rl_command_mode so that we switch + temporarily from SingleKey mode and can enter a gdb command. */ + for (i = ' '; i < 0x7f; i++) + { + int j; + + for (j = 0; tui_commands[j].cmd; j++) + if (tui_commands[j].key == i) + break; + + if (tui_commands[j].cmd) + continue; + + rl_bind_key_in_map (i, tui_rl_command_mode, tui_keymap); + } + + rl_bind_key_in_map ('a', tui_rl_switch_mode, emacs_ctlx_keymap); + rl_bind_key_in_map ('a', tui_rl_switch_mode, tui_ctlx_keymap); + rl_bind_key_in_map ('A', tui_rl_switch_mode, emacs_ctlx_keymap); + rl_bind_key_in_map ('A', tui_rl_switch_mode, tui_ctlx_keymap); + rl_bind_key_in_map (CTRL ('A'), tui_rl_switch_mode, emacs_ctlx_keymap); + rl_bind_key_in_map (CTRL ('A'), tui_rl_switch_mode, tui_ctlx_keymap); + rl_bind_key_in_map ('1', tui_rl_delete_other_windows, emacs_ctlx_keymap); + rl_bind_key_in_map ('1', tui_rl_delete_other_windows, tui_ctlx_keymap); + rl_bind_key_in_map ('2', tui_rl_change_windows, emacs_ctlx_keymap); + rl_bind_key_in_map ('2', tui_rl_change_windows, tui_ctlx_keymap); + rl_bind_key_in_map ('o', tui_rl_other_window, emacs_ctlx_keymap); + rl_bind_key_in_map ('o', tui_rl_other_window, tui_ctlx_keymap); + rl_bind_key_in_map ('q', tui_rl_next_keymap, tui_keymap); + rl_bind_key_in_map ('s', tui_rl_next_keymap, emacs_ctlx_keymap); + rl_bind_key_in_map ('s', tui_rl_next_keymap, tui_ctlx_keymap); +} + +/* Enter in the tui mode (curses). + When in normal mode, it installs the tui hooks in gdb, redirects + the gdb output, configures the readline to work in tui mode. + When in curses mode, it does nothing. */ +void +tui_enable (void) +{ + if (tui_active) + return; + + /* To avoid to initialize curses when gdb starts, there is a defered + curses initialization. This initialization is made only once + and the first time the curses mode is entered. */ + if (tui_finish_init) + { + WINDOW *w; + + w = initscr (); + + cbreak (); + noecho (); + /*timeout (1);*/ + nodelay(w, FALSE); + nl(); + keypad (w, TRUE); + rl_initialize (); + tui_set_term_height_to (LINES); + tui_set_term_width_to (COLS); + def_prog_mode (); + + tui_show_frame_info (0); + tui_set_layout (SRC_COMMAND, TUI_UNDEFINED_REGS); + tui_set_win_focus_to (TUI_SRC_WIN); + keypad (TUI_CMD_WIN->generic.handle, TRUE); + wrefresh (TUI_CMD_WIN->generic.handle); + tui_finish_init = 0; + } + else + { + /* Save the current gdb setting of the terminal. + Curses will restore this state when endwin() is called. */ + def_shell_mode (); + clearok (stdscr, TRUE); + } + + /* Install the TUI specific hooks. */ + tui_install_hooks (); + rl_startup_hook = tui_rl_startup_hook; + + tui_update_variables (); + + tui_setup_io (1); + + tui_active = 1; + if (deprecated_selected_frame) + tui_show_frame_info (deprecated_selected_frame); + + /* Restore TUI keymap. */ + tui_set_key_mode (tui_current_key_mode); + tui_refresh_all_win (); + + /* Update gdb's knowledge of its terminal. */ + target_terminal_save_ours (); + tui_update_gdb_sizes (); +} + +/* Leave the tui mode. + Remove the tui hooks and configure the gdb output and readline + back to their original state. The curses mode is left so that + the terminal setting is restored to the point when we entered. */ +void +tui_disable (void) +{ + if (!tui_active) + return; + + /* Restore initial readline keymap. */ + rl_set_keymap (tui_readline_standard_keymap); + + /* Remove TUI hooks. */ + tui_remove_hooks (); + rl_startup_hook = 0; + rl_already_prompted = 0; + + /* Leave curses and restore previous gdb terminal setting. */ + endwin (); + + /* gdb terminal has changed, update gdb internal copy of it + so that terminal management with the inferior works. */ + tui_setup_io (0); + + /* Update gdb's knowledge of its terminal. */ + target_terminal_save_ours (); + + tui_active = 0; + tui_update_gdb_sizes (); +} + +void +strcat_to_buf (char *buf, int buflen, const char *item_to_add) +{ + if (item_to_add != (char *) NULL && buf != (char *) NULL) + { + if ((strlen (buf) + strlen (item_to_add)) <= buflen) + strcat (buf, item_to_add); + else + strncat (buf, item_to_add, (buflen - strlen (buf))); + } +} + +#if 0 +/* Solaris <sys/termios.h> defines CTRL. */ +#ifndef CTRL +#define CTRL(x) (x & ~0140) +#endif + +#define FILEDES 2 +#define CHK(val, dft) (val<=0 ? dft : val) + +static void +tui_reset (void) +{ + struct termio mode; + + /* + ** reset the teletype mode bits to a sensible state. + ** Copied tset.c + */ +#if ! defined (USG) && defined (TIOCGETC) + struct tchars tbuf; +#endif /* !USG && TIOCGETC */ +#ifdef UCB_NTTY + struct ltchars ltc; + + if (ldisc == NTTYDISC) + { + ioctl (FILEDES, TIOCGLTC, <c); + ltc.t_suspc = CHK (ltc.t_suspc, CTRL ('Z')); + ltc.t_dsuspc = CHK (ltc.t_dsuspc, CTRL ('Y')); + ltc.t_rprntc = CHK (ltc.t_rprntc, CTRL ('R')); + ltc.t_flushc = CHK (ltc.t_flushc, CTRL ('O')); + ltc.t_werasc = CHK (ltc.t_werasc, CTRL ('W')); + ltc.t_lnextc = CHK (ltc.t_lnextc, CTRL ('V')); + ioctl (FILEDES, TIOCSLTC, <c); + } +#endif /* UCB_NTTY */ +#ifndef USG +#ifdef TIOCGETC + ioctl (FILEDES, TIOCGETC, &tbuf); + tbuf.t_intrc = CHK (tbuf.t_intrc, CTRL ('?')); + tbuf.t_quitc = CHK (tbuf.t_quitc, CTRL ('\\')); + tbuf.t_startc = CHK (tbuf.t_startc, CTRL ('Q')); + tbuf.t_stopc = CHK (tbuf.t_stopc, CTRL ('S')); + tbuf.t_eofc = CHK (tbuf.t_eofc, CTRL ('D')); + /* brkc is left alone */ + ioctl (FILEDES, TIOCSETC, &tbuf); +#endif /* TIOCGETC */ + mode.sg_flags &= ~(RAW +#ifdef CBREAK + | CBREAK +#endif /* CBREAK */ + | VTDELAY | ALLDELAY); + mode.sg_flags |= XTABS | ECHO | CRMOD | ANYP; +#else /*USG */ + ioctl (FILEDES, TCGETA, &mode); + mode.c_cc[VINTR] = CHK (mode.c_cc[VINTR], CTRL ('?')); + mode.c_cc[VQUIT] = CHK (mode.c_cc[VQUIT], CTRL ('\\')); + mode.c_cc[VEOF] = CHK (mode.c_cc[VEOF], CTRL ('D')); + + mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | INLCR | IGNCR | IUCLC | IXOFF); + mode.c_iflag |= (BRKINT | ISTRIP | ICRNL | IXON); + mode.c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | OFDEL | + NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY); + mode.c_oflag |= (OPOST | ONLCR); + mode.c_cflag &= ~(CSIZE | PARODD | CLOCAL); +#ifndef hp9000s800 + mode.c_cflag |= (CS8 | CREAD); +#else /*hp9000s800 */ + mode.c_cflag |= (CS8 | CSTOPB | CREAD); +#endif /* hp9000s800 */ + mode.c_lflag &= ~(XCASE | ECHONL | NOFLSH); + mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOK); + ioctl (FILEDES, TCSETAW, &mode); +#endif /* USG */ + + return; +} +#endif + +void +tui_show_source (const char *file, int line) +{ + struct symtab_and_line cursal = get_current_source_symtab_and_line (); + /* make sure that the source window is displayed */ + tui_add_win_to_layout (SRC_WIN); + + tui_update_source_windows_with_line (cursal.symtab, line); + tui_update_locator_filename (file); +} + +void +tui_show_assembly (CORE_ADDR addr) +{ + tui_add_win_to_layout (DISASSEM_WIN); + tui_update_source_windows_with_addr (addr); +} + +int +tui_is_window_visible (enum tui_win_type type) +{ + if (tui_active == 0) + return 0; + + if (tui_win_list[type] == 0) + return 0; + + return tui_win_list[type]->generic.is_visible; +} + +int +tui_get_command_dimension (int *width, int *height) +{ + if (!tui_active || (TUI_CMD_WIN == NULL)) + { + return 0; + } + + *width = TUI_CMD_WIN->generic.width; + *height = TUI_CMD_WIN->generic.height; + return 1; +} diff --git a/contrib/gdb/gdb/tui/tui.h b/contrib/gdb/gdb/tui/tui.h new file mode 100644 index 0000000..d7b741c --- /dev/null +++ b/contrib/gdb/gdb/tui/tui.h @@ -0,0 +1,100 @@ +/* External/Public TUI Header File. + + Copyright 1998, 1999, 2000, 2001, 2004 Free Software Foundation, + Inc. + + Contributed by Hewlett-Packard Company. + + 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. */ + +#ifndef TUI_H +#define TUI_H + +struct ui_file; + +extern void strcat_to_buf (char *, int, const char *); + +/* Types of error returns. */ +enum tui_status +{ + TUI_SUCCESS, + TUI_FAILURE +}; + +/* Types of windows */ +enum tui_win_type +{ + SRC_WIN = 0, + DISASSEM_WIN, + DATA_WIN, + CMD_WIN, + /* This must ALWAYS be AFTER the major windows last. */ + MAX_MAJOR_WINDOWS, + /* Auxillary windows. */ + LOCATOR_WIN, + EXEC_INFO_WIN, + DATA_ITEM_WIN, + /* This must ALWAYS be next to last. */ + MAX_WINDOWS, + UNDEFINED_WIN /* LAST */ +}; + +/* GENERAL TUI FUNCTIONS */ +/* tui.c */ +extern CORE_ADDR tui_get_low_disassembly_address (CORE_ADDR, CORE_ADDR); +extern void tui_show_assembly (CORE_ADDR addr); +extern int tui_is_window_visible (enum tui_win_type type); +extern int tui_get_command_dimension (int *width, int *height); + +/* Initialize readline and configure the keymap for the switching + key shortcut. */ +extern void tui_initialize_readline (void); + +/* Enter in the tui mode (curses). */ +extern void tui_enable (void); + +/* Leave the tui mode. */ +extern void tui_disable (void); + +enum tui_key_mode +{ + /* Plain command mode to enter gdb commands. */ + TUI_COMMAND_MODE, + + /* SingleKey mode with some keys bound to gdb commands. */ + TUI_SINGLE_KEY_MODE, + + /* Read/edit one command and return to SingleKey after it's processed. */ + TUI_ONE_COMMAND_MODE +}; + +extern enum tui_key_mode tui_current_key_mode; + +/* Change the TUI key mode by installing the appropriate readline keymap. */ +extern void tui_set_key_mode (enum tui_key_mode mode); + +extern int tui_active; + +extern void tui_show_source (const char *file, int line); + +extern struct ui_out *tui_out_new (struct ui_file *stream); + +/* tui-layout.c */ +extern enum tui_status tui_set_layout_for_display_command (const char *name); + +#endif diff --git a/contrib/gdb/gdb/xcoffread.c b/contrib/gdb/gdb/xcoffread.c new file mode 100644 index 0000000..759dfcb --- /dev/null +++ b/contrib/gdb/gdb/xcoffread.c @@ -0,0 +1,3033 @@ +/* Read AIX xcoff symbol tables and convert to internal format, for GDB. + Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, + 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 + Free Software Foundation, Inc. + Derived from coffread.c, dbxread.c, and a lot of hacking. + Contributed by IBM Corporation. + + 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 "defs.h" +#include "bfd.h" + +#include <sys/types.h> +#include <fcntl.h> +#include <ctype.h> +#include "gdb_string.h" + +#include <sys/param.h> +#ifndef NO_SYS_FILE +#include <sys/file.h> +#endif +#include "gdb_stat.h" + +#include "coff/internal.h" +#include "libcoff.h" /* FIXME, internal data from BFD */ +#include "coff/xcoff.h" +#include "libxcoff.h" +#include "coff/rs6000.h" + +#include "symtab.h" +#include "gdbtypes.h" +/* FIXME: ezannoni/2004-02-13 Verify if the include below is really needed. */ +#include "symfile.h" +#include "objfiles.h" +#include "buildsym.h" +#include "stabsread.h" +#include "expression.h" +#include "complaints.h" + +#include "gdb-stabs.h" + +/* For interface with stabsread.c. */ +#include "aout/stab_gnu.h" + + +/* We put a pointer to this structure in the read_symtab_private field + of the psymtab. */ + +struct symloc + { + + /* First symbol number for this file. */ + + int first_symnum; + + /* Number of symbols in the section of the symbol table devoted to + this file's symbols (actually, the section bracketed may contain + more than just this file's symbols). If numsyms is 0, the only + reason for this thing's existence is the dependency list. Nothing + else will happen when it is read in. */ + + int numsyms; + + /* Position of the start of the line number information for this psymtab. */ + unsigned int lineno_off; + }; + +/* Remember what we deduced to be the source language of this psymtab. */ + +static enum language psymtab_language = language_unknown; + + +/* Simplified internal version of coff symbol table information */ + +struct coff_symbol + { + char *c_name; + int c_symnum; /* symbol number of this entry */ + int c_naux; /* 0 if syment only, 1 if syment + auxent */ + long c_value; + unsigned char c_sclass; + int c_secnum; + unsigned int c_type; + }; + +/* last function's saved coff symbol `cs' */ + +static struct coff_symbol fcn_cs_saved; + +static bfd *symfile_bfd; + +/* Core address of start and end of text of current source file. + This is calculated from the first function seen after a C_FILE + symbol. */ + + +static CORE_ADDR cur_src_end_addr; + +/* Core address of the end of the first object file. */ + +static CORE_ADDR first_object_file_end; + +/* initial symbol-table-debug-string vector length */ + +#define INITIAL_STABVECTOR_LENGTH 40 + +/* Nonzero if within a function (so symbols should be local, + if nothing says specifically). */ + +int within_function; + +/* Size of a COFF symbol. I think it is always 18, so I'm not sure + there is any reason not to just use a #define, but might as well + ask BFD for the size and store it here, I guess. */ + +static unsigned local_symesz; + +struct coff_symfile_info + { + file_ptr min_lineno_offset; /* Where in file lowest line#s are */ + file_ptr max_lineno_offset; /* 1+last byte of line#s in file */ + + /* Pointer to the string table. */ + char *strtbl; + + /* Pointer to debug section. */ + char *debugsec; + + /* Pointer to the a.out symbol table. */ + char *symtbl; + + /* Number of symbols in symtbl. */ + int symtbl_num_syms; + + /* Offset in data section to TOC anchor. */ + CORE_ADDR toc_offset; + }; + +static void +bf_notfound_complaint (void) +{ + complaint (&symfile_complaints, "line numbers off, `.bf' symbol not found"); +} + +static void +ef_complaint (int arg1) +{ + complaint (&symfile_complaints, + "Mismatched .ef symbol ignored starting at symnum %d", arg1); +} + +static void +eb_complaint (int arg1) +{ + complaint (&symfile_complaints, + "Mismatched .eb symbol ignored starting at symnum %d", arg1); +} + +static void xcoff_initial_scan (struct objfile *, int); + +static void scan_xcoff_symtab (struct objfile *); + +static char *xcoff_next_symbol_text (struct objfile *); + +static void record_include_begin (struct coff_symbol *); + +static void +enter_line_range (struct subfile *, unsigned, unsigned, + CORE_ADDR, CORE_ADDR, unsigned *); + +static void init_stringtab (bfd *, file_ptr, struct objfile *); + +static void xcoff_symfile_init (struct objfile *); + +static void xcoff_new_init (struct objfile *); + +static void xcoff_symfile_finish (struct objfile *); + +static void xcoff_symfile_offsets (struct objfile *, + struct section_addr_info *addrs); + +static char *coff_getfilename (union internal_auxent *, struct objfile *); + +static void read_symbol (struct internal_syment *, int); + +static int read_symbol_lineno (int); + +static CORE_ADDR read_symbol_nvalue (int); + +static struct symbol *process_xcoff_symbol (struct coff_symbol *, + struct objfile *); + +static void read_xcoff_symtab (struct partial_symtab *); + +#if 0 +static void add_stab_to_list (char *, struct pending_stabs **); +#endif + +static int compare_lte (const void *, const void *); + +static struct linetable *arrange_linetable (struct linetable *); + +static void record_include_end (struct coff_symbol *); + +static void process_linenos (CORE_ADDR, CORE_ADDR); + + +/* Translate from a COFF section number (target_index) to a SECT_OFF_* + code. */ +static int secnum_to_section (int, struct objfile *); +static asection *secnum_to_bfd_section (int, struct objfile *); + +struct find_targ_sec_arg + { + int targ_index; + int *resultp; + asection **bfd_sect; + struct objfile *objfile; + }; + +static void find_targ_sec (bfd *, asection *, void *); + +static void +find_targ_sec (bfd *abfd, asection *sect, void *obj) +{ + struct find_targ_sec_arg *args = (struct find_targ_sec_arg *) obj; + struct objfile *objfile = args->objfile; + if (sect->target_index == args->targ_index) + { + /* This is the section. Figure out what SECT_OFF_* code it is. */ + if (bfd_get_section_flags (abfd, sect) & SEC_CODE) + *args->resultp = SECT_OFF_TEXT (objfile); + else if (bfd_get_section_flags (abfd, sect) & SEC_LOAD) + *args->resultp = SECT_OFF_DATA (objfile); + else + *args->resultp = sect->index; + *args->bfd_sect = sect; + } +} + +/* Return the section number (SECT_OFF_*) that CS points to. */ +static int +secnum_to_section (int secnum, struct objfile *objfile) +{ + int off = SECT_OFF_TEXT (objfile); + asection *sect = NULL; + struct find_targ_sec_arg args; + args.targ_index = secnum; + args.resultp = &off; + args.bfd_sect = § + args.objfile = objfile; + bfd_map_over_sections (objfile->obfd, find_targ_sec, &args); + return off; +} + +/* Return the BFD section that CS points to. */ +static asection * +secnum_to_bfd_section (int secnum, struct objfile *objfile) +{ + int off = SECT_OFF_TEXT (objfile); + asection *sect = NULL; + struct find_targ_sec_arg args; + args.targ_index = secnum; + args.resultp = &off; + args.bfd_sect = § + args.objfile = objfile; + bfd_map_over_sections (objfile->obfd, find_targ_sec, &args); + return sect; +} + +/* add a given stab string into given stab vector. */ + +#if 0 + +static void +add_stab_to_list (char *stabname, struct pending_stabs **stabvector) +{ + if (*stabvector == NULL) + { + *stabvector = (struct pending_stabs *) + xmalloc (sizeof (struct pending_stabs) + + INITIAL_STABVECTOR_LENGTH * sizeof (char *)); + (*stabvector)->count = 0; + (*stabvector)->length = INITIAL_STABVECTOR_LENGTH; + } + else if ((*stabvector)->count >= (*stabvector)->length) + { + (*stabvector)->length += INITIAL_STABVECTOR_LENGTH; + *stabvector = (struct pending_stabs *) + xrealloc ((char *) *stabvector, sizeof (struct pending_stabs) + + (*stabvector)->length * sizeof (char *)); + } + (*stabvector)->stab[(*stabvector)->count++] = stabname; +} + +#endif +/* *INDENT-OFF* */ +/* Linenos are processed on a file-by-file basis. + + Two reasons: + + 1) xlc (IBM's native c compiler) postpones static function code + emission to the end of a compilation unit. This way it can + determine if those functions (statics) are needed or not, and + can do some garbage collection (I think). This makes line + numbers and corresponding addresses unordered, and we end up + with a line table like: + + + lineno addr + foo() 10 0x100 + 20 0x200 + 30 0x300 + + foo3() 70 0x400 + 80 0x500 + 90 0x600 + + static foo2() + 40 0x700 + 50 0x800 + 60 0x900 + + and that breaks gdb's binary search on line numbers, if the + above table is not sorted on line numbers. And that sort + should be on function based, since gcc can emit line numbers + like: + + 10 0x100 - for the init/test part of a for stmt. + 20 0x200 + 30 0x300 + 10 0x400 - for the increment part of a for stmt. + + arrange_linetable() will do this sorting. + + 2) aix symbol table might look like: + + c_file // beginning of a new file + .bi // beginning of include file + .ei // end of include file + .bi + .ei + + basically, .bi/.ei pairs do not necessarily encapsulate + their scope. They need to be recorded, and processed later + on when we come the end of the compilation unit. + Include table (inclTable) and process_linenos() handle + that. */ +/* *INDENT-ON* */ + + + +/* compare line table entry addresses. */ + +static int +compare_lte (const void *lte1p, const void *lte2p) +{ + struct linetable_entry *lte1 = (struct linetable_entry *) lte1p; + struct linetable_entry *lte2 = (struct linetable_entry *) lte2p; + return lte1->pc - lte2->pc; +} + +/* Given a line table with function entries are marked, arrange its functions + in ascending order and strip off function entry markers and return it in + a newly created table. If the old one is good enough, return the old one. */ +/* FIXME: I think all this stuff can be replaced by just passing + sort_linevec = 1 to end_symtab. */ + +static struct linetable * +arrange_linetable (struct linetable *oldLineTb) +{ + int ii, jj, newline, /* new line count */ + function_count; /* # of functions */ + + struct linetable_entry *fentry; /* function entry vector */ + int fentry_size; /* # of function entries */ + struct linetable *newLineTb; /* new line table */ + +#define NUM_OF_FUNCTIONS 20 + + fentry_size = NUM_OF_FUNCTIONS; + fentry = (struct linetable_entry *) + xmalloc (fentry_size * sizeof (struct linetable_entry)); + + for (function_count = 0, ii = 0; ii < oldLineTb->nitems; ++ii) + { + + if (oldLineTb->item[ii].line == 0) + { /* function entry found. */ + + if (function_count >= fentry_size) + { /* make sure you have room. */ + fentry_size *= 2; + fentry = (struct linetable_entry *) + xrealloc (fentry, fentry_size * sizeof (struct linetable_entry)); + } + fentry[function_count].line = ii; + fentry[function_count].pc = oldLineTb->item[ii].pc; + ++function_count; + } + } + + if (function_count == 0) + { + xfree (fentry); + return oldLineTb; + } + else if (function_count > 1) + qsort (fentry, function_count, sizeof (struct linetable_entry), compare_lte); + + /* allocate a new line table. */ + newLineTb = (struct linetable *) + xmalloc + (sizeof (struct linetable) + + (oldLineTb->nitems - function_count) * sizeof (struct linetable_entry)); + + /* if line table does not start with a function beginning, copy up until + a function begin. */ + + newline = 0; + if (oldLineTb->item[0].line != 0) + for (newline = 0; + newline < oldLineTb->nitems && oldLineTb->item[newline].line; ++newline) + newLineTb->item[newline] = oldLineTb->item[newline]; + + /* Now copy function lines one by one. */ + + for (ii = 0; ii < function_count; ++ii) + { + for (jj = fentry[ii].line + 1; + jj < oldLineTb->nitems && oldLineTb->item[jj].line != 0; + ++jj, ++newline) + newLineTb->item[newline] = oldLineTb->item[jj]; + } + xfree (fentry); + newLineTb->nitems = oldLineTb->nitems - function_count; + return newLineTb; +} + +/* include file support: C_BINCL/C_EINCL pairs will be kept in the + following `IncludeChain'. At the end of each symtab (end_symtab), + we will determine if we should create additional symtab's to + represent if (the include files. */ + + +typedef struct _inclTable +{ + char *name; /* include filename */ + + /* Offsets to the line table. end points to the last entry which is + part of this include file. */ + int begin, end; + + struct subfile *subfile; + unsigned funStartLine; /* start line # of its function */ +} +InclTable; + +#define INITIAL_INCLUDE_TABLE_LENGTH 20 +static InclTable *inclTable; /* global include table */ +static int inclIndx; /* last entry to table */ +static int inclLength; /* table length */ +static int inclDepth; /* nested include depth */ + +static void allocate_include_entry (void); + +static void +record_include_begin (struct coff_symbol *cs) +{ + if (inclDepth) + { + /* In xcoff, we assume include files cannot be nested (not in .c files + of course, but in corresponding .s files.). */ + + /* This can happen with old versions of GCC. + GCC 2.3.3-930426 does not exhibit this on a test case which + a user said produced the message for him. */ + complaint (&symfile_complaints, "Nested C_BINCL symbols"); + } + ++inclDepth; + + allocate_include_entry (); + + inclTable[inclIndx].name = cs->c_name; + inclTable[inclIndx].begin = cs->c_value; +} + +static void +record_include_end (struct coff_symbol *cs) +{ + InclTable *pTbl; + + if (inclDepth == 0) + { + complaint (&symfile_complaints, "Mismatched C_BINCL/C_EINCL pair"); + } + + allocate_include_entry (); + + pTbl = &inclTable[inclIndx]; + pTbl->end = cs->c_value; + + --inclDepth; + ++inclIndx; +} + +static void +allocate_include_entry (void) +{ + if (inclTable == NULL) + { + inclTable = (InclTable *) + xmalloc (sizeof (InclTable) * INITIAL_INCLUDE_TABLE_LENGTH); + memset (inclTable, + '\0', sizeof (InclTable) * INITIAL_INCLUDE_TABLE_LENGTH); + inclLength = INITIAL_INCLUDE_TABLE_LENGTH; + inclIndx = 0; + } + else if (inclIndx >= inclLength) + { + inclLength += INITIAL_INCLUDE_TABLE_LENGTH; + inclTable = (InclTable *) + xrealloc (inclTable, sizeof (InclTable) * inclLength); + memset (inclTable + inclLength - INITIAL_INCLUDE_TABLE_LENGTH, + '\0', sizeof (InclTable) * INITIAL_INCLUDE_TABLE_LENGTH); + } +} + +/* Global variable to pass the psymtab down to all the routines involved + in psymtab to symtab processing. */ +static struct partial_symtab *this_symtab_psymtab; + +/* given the start and end addresses of a compilation unit (or a csect, + at times) process its lines and create appropriate line vectors. */ + +static void +process_linenos (CORE_ADDR start, CORE_ADDR end) +{ + int offset, ii; + file_ptr max_offset = + ((struct coff_symfile_info *) this_symtab_psymtab->objfile->sym_private) + ->max_lineno_offset; + + /* subfile structure for the main compilation unit. */ + struct subfile main_subfile; + + /* In the main source file, any time we see a function entry, we + reset this variable to function's absolute starting line number. + All the following line numbers in the function are relative to + this, and we record absolute line numbers in record_line(). */ + + unsigned int main_source_baseline = 0; + + unsigned *firstLine; + + offset = + ((struct symloc *) this_symtab_psymtab->read_symtab_private)->lineno_off; + if (offset == 0) + goto return_after_cleanup; + + memset (&main_subfile, '\0', sizeof (main_subfile)); + + if (inclIndx == 0) + /* All source lines were in the main source file. None in include files. */ + + enter_line_range (&main_subfile, offset, 0, start, end, + &main_source_baseline); + + else + { + /* There was source with line numbers in include files. */ + + int linesz = + coff_data (this_symtab_psymtab->objfile->obfd)->local_linesz; + main_source_baseline = 0; + + for (ii = 0; ii < inclIndx; ++ii) + { + struct subfile *tmpSubfile; + + /* If there is main file source before include file, enter it. */ + if (offset < inclTable[ii].begin) + { + enter_line_range + (&main_subfile, offset, inclTable[ii].begin - linesz, + start, 0, &main_source_baseline); + } + + /* Have a new subfile for the include file. */ + + tmpSubfile = inclTable[ii].subfile = + (struct subfile *) xmalloc (sizeof (struct subfile)); + + memset (tmpSubfile, '\0', sizeof (struct subfile)); + firstLine = &(inclTable[ii].funStartLine); + + /* Enter include file's lines now. */ + enter_line_range (tmpSubfile, inclTable[ii].begin, + inclTable[ii].end, start, 0, firstLine); + + if (offset <= inclTable[ii].end) + offset = inclTable[ii].end + linesz; + } + + /* All the include files' line have been processed at this point. Now, + enter remaining lines of the main file, if any left. */ + if (offset < max_offset + 1 - linesz) + { + enter_line_range (&main_subfile, offset, 0, start, end, + &main_source_baseline); + } + } + + /* Process main file's line numbers. */ + if (main_subfile.line_vector) + { + struct linetable *lineTb, *lv; + + lv = main_subfile.line_vector; + + /* Line numbers are not necessarily ordered. xlc compilation will + put static function to the end. */ + + lineTb = arrange_linetable (lv); + if (lv == lineTb) + { + current_subfile->line_vector = (struct linetable *) + xrealloc (lv, (sizeof (struct linetable) + + lv->nitems * sizeof (struct linetable_entry))); + } + else + { + xfree (lv); + current_subfile->line_vector = lineTb; + } + + current_subfile->line_vector_length = + current_subfile->line_vector->nitems; + } + + /* Now, process included files' line numbers. */ + + for (ii = 0; ii < inclIndx; ++ii) + { + if ((inclTable[ii].subfile)->line_vector) /* Useless if!!! FIXMEmgo */ + { + struct linetable *lineTb, *lv; + + lv = (inclTable[ii].subfile)->line_vector; + + /* Line numbers are not necessarily ordered. xlc compilation will + put static function to the end. */ + + lineTb = arrange_linetable (lv); + + push_subfile (); + + /* For the same include file, we might want to have more than one + subfile. This happens if we have something like: + + ...... + #include "foo.h" + ...... + #include "foo.h" + ...... + + while foo.h including code in it. (stupid but possible) + Since start_subfile() looks at the name and uses an + existing one if finds, we need to provide a fake name and + fool it. */ + +#if 0 + start_subfile (inclTable[ii].name, (char *) 0); +#else + { + /* Pick a fake name that will produce the same results as this + one when passed to deduce_language_from_filename. Kludge on + top of kludge. */ + char *fakename = strrchr (inclTable[ii].name, '.'); + if (fakename == NULL) + fakename = " ?"; + start_subfile (fakename, (char *) 0); + xfree (current_subfile->name); + } + current_subfile->name = xstrdup (inclTable[ii].name); +#endif + + if (lv == lineTb) + { + current_subfile->line_vector = + (struct linetable *) xrealloc + (lv, (sizeof (struct linetable) + + lv->nitems * sizeof (struct linetable_entry))); + + } + else + { + xfree (lv); + current_subfile->line_vector = lineTb; + } + + current_subfile->line_vector_length = + current_subfile->line_vector->nitems; + start_subfile (pop_subfile (), (char *) 0); + } + } + +return_after_cleanup: + + /* We don't want to keep alloc/free'ing the global include file table. */ + inclIndx = 0; + + /* Start with a fresh subfile structure for the next file. */ + memset (&main_subfile, '\0', sizeof (struct subfile)); +} + +void +aix_process_linenos (void) +{ + /* process line numbers and enter them into line vector */ + process_linenos (last_source_start_addr, cur_src_end_addr); +} + + +/* Enter a given range of lines into the line vector. + can be called in the following two ways: + enter_line_range (subfile, beginoffset, endoffset, startaddr, 0, firstLine) or + enter_line_range (subfile, beginoffset, 0, startaddr, endaddr, firstLine) + + endoffset points to the last line table entry that we should pay + attention to. */ + +static void +enter_line_range (struct subfile *subfile, unsigned beginoffset, unsigned endoffset, /* offsets to line table */ + CORE_ADDR startaddr, /* offsets to line table */ + CORE_ADDR endaddr, unsigned *firstLine) +{ + unsigned int curoffset; + CORE_ADDR addr; + void *ext_lnno; + struct internal_lineno int_lnno; + unsigned int limit_offset; + bfd *abfd; + int linesz; + + if (endoffset == 0 && startaddr == 0 && endaddr == 0) + return; + curoffset = beginoffset; + limit_offset = + ((struct coff_symfile_info *) this_symtab_psymtab->objfile->sym_private) + ->max_lineno_offset; + + if (endoffset != 0) + { + if (endoffset >= limit_offset) + { + complaint (&symfile_complaints, + "Bad line table offset in C_EINCL directive"); + return; + } + limit_offset = endoffset; + } + else + limit_offset -= 1; + + abfd = this_symtab_psymtab->objfile->obfd; + linesz = coff_data (abfd)->local_linesz; + ext_lnno = alloca (linesz); + + while (curoffset <= limit_offset) + { + bfd_seek (abfd, curoffset, SEEK_SET); + bfd_bread (ext_lnno, linesz, abfd); + bfd_coff_swap_lineno_in (abfd, ext_lnno, &int_lnno); + + /* Find the address this line represents. */ + addr = (int_lnno.l_lnno + ? int_lnno.l_addr.l_paddr + : read_symbol_nvalue (int_lnno.l_addr.l_symndx)); + addr += ANOFFSET (this_symtab_psymtab->objfile->section_offsets, + SECT_OFF_TEXT (this_symtab_psymtab->objfile)); + + if (addr < startaddr || (endaddr && addr >= endaddr)) + return; + + if (int_lnno.l_lnno == 0) + { + *firstLine = read_symbol_lineno (int_lnno.l_addr.l_symndx); + record_line (subfile, 0, addr); + --(*firstLine); + } + else + record_line (subfile, *firstLine + int_lnno.l_lnno, addr); + curoffset += linesz; + } +} + + +/* Save the vital information for use when closing off the current file. + NAME is the file name the symbols came from, START_ADDR is the first + text address for the file, and SIZE is the number of bytes of text. */ + +#define complete_symtab(name, start_addr) { \ + last_source_file = savestring (name, strlen (name)); \ + last_source_start_addr = start_addr; \ +} + + +/* Refill the symbol table input buffer + and set the variables that control fetching entries from it. + Reports an error if no data available. + This function can read past the end of the symbol table + (into the string table) but this does no harm. */ + +/* Reading symbol table has to be fast! Keep the followings as macros, rather + than functions. */ + +#define RECORD_MINIMAL_SYMBOL(NAME, ADDR, TYPE, SECTION, OBJFILE) \ +{ \ + char *namestr; \ + namestr = (NAME); \ + if (namestr[0] == '.') ++namestr; \ + prim_record_minimal_symbol_and_info (namestr, (ADDR), (TYPE), \ + (char *)NULL, (SECTION), (asection *)NULL, (OBJFILE)); \ + misc_func_recorded = 1; \ +} + + +/* xcoff has static blocks marked in `.bs', `.es' pairs. They cannot be + nested. At any given time, a symbol can only be in one static block. + This is the base address of current static block, zero if non exists. */ + +static int static_block_base = 0; + +/* Section number for the current static block. */ + +static int static_block_section = -1; + +/* true if space for symbol name has been allocated. */ + +static int symname_alloced = 0; + +/* Next symbol to read. Pointer into raw seething symbol table. */ + +static char *raw_symbol; + +/* This is the function which stabsread.c calls to get symbol + continuations. */ + +static char * +xcoff_next_symbol_text (struct objfile *objfile) +{ + struct internal_syment symbol; + char *retval; + /* FIXME: is this the same as the passed arg? */ + objfile = this_symtab_psymtab->objfile; + + bfd_coff_swap_sym_in (objfile->obfd, raw_symbol, &symbol); + if (symbol.n_zeroes) + { + complaint (&symfile_complaints, "Unexpected symbol continuation"); + + /* Return something which points to '\0' and hope the symbol reading + code does something reasonable. */ + retval = ""; + } + else if (symbol.n_sclass & 0x80) + { + retval = + ((struct coff_symfile_info *) objfile->sym_private)->debugsec + + symbol.n_offset; + raw_symbol += + coff_data (objfile->obfd)->local_symesz; + ++symnum; + } + else + { + complaint (&symfile_complaints, "Unexpected symbol continuation"); + + /* Return something which points to '\0' and hope the symbol reading + code does something reasonable. */ + retval = ""; + } + return retval; +} + +/* Read symbols for a given partial symbol table. */ + +static void +read_xcoff_symtab (struct partial_symtab *pst) +{ + struct objfile *objfile = pst->objfile; + bfd *abfd = objfile->obfd; + char *raw_auxptr; /* Pointer to first raw aux entry for sym */ + char *strtbl = ((struct coff_symfile_info *) objfile->sym_private)->strtbl; + char *debugsec = + ((struct coff_symfile_info *) objfile->sym_private)->debugsec; + char *debugfmt = bfd_xcoff_is_xcoff64 (abfd) ? "XCOFF64" : "XCOFF"; + + struct internal_syment symbol[1]; + union internal_auxent main_aux; + struct coff_symbol cs[1]; + CORE_ADDR file_start_addr = 0; + CORE_ADDR file_end_addr = 0; + + int next_file_symnum = -1; + unsigned int max_symnum; + int just_started = 1; + int depth = 0; + int fcn_start_addr = 0; + + struct coff_symbol fcn_stab_saved; + + /* fcn_cs_saved is global because process_xcoff_symbol needs it. */ + union internal_auxent fcn_aux_saved; + struct context_stack *new; + + char *filestring = " _start_ "; /* Name of the current file. */ + + char *last_csect_name; /* last seen csect's name and value */ + CORE_ADDR last_csect_val; + int last_csect_sec; + + this_symtab_psymtab = pst; + + /* Get the appropriate COFF "constants" related to the file we're + handling. */ + local_symesz = coff_data (abfd)->local_symesz; + + last_source_file = NULL; + last_csect_name = 0; + last_csect_val = 0; + + start_stabs (); + start_symtab (filestring, (char *) NULL, file_start_addr); + record_debugformat (debugfmt); + symnum = ((struct symloc *) pst->read_symtab_private)->first_symnum; + max_symnum = + symnum + ((struct symloc *) pst->read_symtab_private)->numsyms; + first_object_file_end = 0; + + raw_symbol = + ((struct coff_symfile_info *) objfile->sym_private)->symtbl + + symnum * local_symesz; + + while (symnum < max_symnum) + { + + QUIT; /* make this command interruptable. */ + + /* READ_ONE_SYMBOL (symbol, cs, symname_alloced); */ + /* read one symbol into `cs' structure. After processing the + whole symbol table, only string table will be kept in memory, + symbol table and debug section of xcoff will be freed. Thus + we can mark symbols with names in string table as + `alloced'. */ + { + int ii; + + /* Swap and align the symbol into a reasonable C structure. */ + bfd_coff_swap_sym_in (abfd, raw_symbol, symbol); + + cs->c_symnum = symnum; + cs->c_naux = symbol->n_numaux; + if (symbol->n_zeroes) + { + symname_alloced = 0; + /* We must use the original, unswapped, name here so the name field + pointed to by cs->c_name will persist throughout xcoffread. If + we use the new field, it gets overwritten for each symbol. */ + cs->c_name = ((struct external_syment *) raw_symbol)->e.e_name; + /* If it's exactly E_SYMNMLEN characters long it isn't + '\0'-terminated. */ + if (cs->c_name[E_SYMNMLEN - 1] != '\0') + { + char *p; + p = obstack_alloc (&objfile->objfile_obstack, E_SYMNMLEN + 1); + strncpy (p, cs->c_name, E_SYMNMLEN); + p[E_SYMNMLEN] = '\0'; + cs->c_name = p; + symname_alloced = 1; + } + } + else if (symbol->n_sclass & 0x80) + { + cs->c_name = debugsec + symbol->n_offset; + symname_alloced = 0; + } + else + { + /* in string table */ + cs->c_name = strtbl + (int) symbol->n_offset; + symname_alloced = 1; + } + cs->c_value = symbol->n_value; + cs->c_sclass = symbol->n_sclass; + cs->c_secnum = symbol->n_scnum; + cs->c_type = (unsigned) symbol->n_type; + + raw_symbol += local_symesz; + ++symnum; + + /* Save addr of first aux entry. */ + raw_auxptr = raw_symbol; + + /* Skip all the auxents associated with this symbol. */ + for (ii = symbol->n_numaux; ii; --ii) + { + raw_symbol += coff_data (abfd)->local_auxesz; + ++symnum; + } + } + + /* if symbol name starts with ".$" or "$", ignore it. */ + if (cs->c_name[0] == '$' + || (cs->c_name[1] == '$' && cs->c_name[0] == '.')) + continue; + + if (cs->c_symnum == next_file_symnum && cs->c_sclass != C_FILE) + { + if (last_source_file) + { + pst->symtab = + end_symtab (cur_src_end_addr, objfile, SECT_OFF_TEXT (objfile)); + end_stabs (); + } + + start_stabs (); + start_symtab ("_globals_", (char *) NULL, (CORE_ADDR) 0); + record_debugformat (debugfmt); + cur_src_end_addr = first_object_file_end; + /* done with all files, everything from here on is globals */ + } + + if ((cs->c_sclass == C_EXT || cs->c_sclass == C_HIDEXT) + && cs->c_naux == 1) + { + /* Dealing with a symbol with a csect entry. */ + +#define CSECT(PP) ((PP)->x_csect) +#define CSECT_LEN(PP) (CSECT(PP).x_scnlen.l) +#define CSECT_ALIGN(PP) (SMTYP_ALIGN(CSECT(PP).x_smtyp)) +#define CSECT_SMTYP(PP) (SMTYP_SMTYP(CSECT(PP).x_smtyp)) +#define CSECT_SCLAS(PP) (CSECT(PP).x_smclas) + + /* Convert the auxent to something we can access. */ + bfd_coff_swap_aux_in (abfd, raw_auxptr, cs->c_type, cs->c_sclass, + 0, cs->c_naux, &main_aux); + + switch (CSECT_SMTYP (&main_aux)) + { + + case XTY_ER: + /* Ignore all external references. */ + continue; + + case XTY_SD: + /* A section description. */ + { + switch (CSECT_SCLAS (&main_aux)) + { + + case XMC_PR: + { + + /* A program csect is seen. We have to allocate one + symbol table for each program csect. Normally gdb + prefers one symtab for each source file. In case + of AIX, one source file might include more than one + [PR] csect, and they don't have to be adjacent in + terms of the space they occupy in memory. Thus, one + single source file might get fragmented in the + memory and gdb's file start and end address + approach does not work! GCC (and I think xlc) seem + to put all the code in the unnamed program csect. */ + + if (last_csect_name) + { + complete_symtab (filestring, file_start_addr); + cur_src_end_addr = file_end_addr; + end_symtab (file_end_addr, objfile, SECT_OFF_TEXT (objfile)); + end_stabs (); + start_stabs (); + /* Give all csects for this source file the same + name. */ + start_symtab (filestring, NULL, (CORE_ADDR) 0); + record_debugformat (debugfmt); + } + + /* If this is the very first csect seen, + basically `__start'. */ + if (just_started) + { + first_object_file_end + = cs->c_value + CSECT_LEN (&main_aux); + just_started = 0; + } + + file_start_addr = + cs->c_value + ANOFFSET (objfile->section_offsets, + SECT_OFF_TEXT (objfile)); + file_end_addr = file_start_addr + CSECT_LEN (&main_aux); + + if (cs->c_name && (cs->c_name[0] == '.' + || cs->c_name[0] == '@')) + { + last_csect_name = cs->c_name; + last_csect_val = cs->c_value; + last_csect_sec = secnum_to_section (cs->c_secnum, objfile); + } + } + continue; + + /* All other symbols are put into the minimal symbol + table only. */ + + case XMC_RW: + continue; + + case XMC_TC0: + continue; + + case XMC_TC: + continue; + + default: + /* Ignore the symbol. */ + continue; + } + } + break; + + case XTY_LD: + + switch (CSECT_SCLAS (&main_aux)) + { + case XMC_PR: + /* a function entry point. */ + function_entry_point: + + fcn_start_addr = cs->c_value; + + /* save the function header info, which will be used + when `.bf' is seen. */ + fcn_cs_saved = *cs; + fcn_aux_saved = main_aux; + continue; + + case XMC_GL: + /* shared library function trampoline code entry point. */ + continue; + + case XMC_DS: + /* The symbols often have the same names as debug symbols for + functions, and confuse lookup_symbol. */ + continue; + + default: + /* xlc puts each variable in a separate csect, so we get + an XTY_SD for each variable. But gcc puts several + variables in a csect, so that each variable only gets + an XTY_LD. This will typically be XMC_RW; I suspect + XMC_RO and XMC_BS might be possible too. + These variables are put in the minimal symbol table + only. */ + continue; + } + break; + + case XTY_CM: + /* Common symbols are put into the minimal symbol table only. */ + continue; + + default: + break; + } + } + + /* If explicitly specified as a function, treat is as one. This check + evaluates to true for @FIX* bigtoc CSECT symbols, so it must occur + after the above CSECT check. */ + if (ISFCN (cs->c_type) && cs->c_sclass != C_TPDEF) + { + bfd_coff_swap_aux_in (abfd, raw_auxptr, cs->c_type, cs->c_sclass, + 0, cs->c_naux, &main_aux); + goto function_entry_point; + } + + switch (cs->c_sclass) + { + + case C_FILE: + + /* c_value field contains symnum of next .file entry in table + or symnum of first global after last .file. */ + + next_file_symnum = cs->c_value; + + /* Complete symbol table for last object file containing + debugging information. */ + + /* Whether or not there was a csect in the previous file, we + have to call `end_stabs' and `start_stabs' to reset + type_vector, line_vector, etc. structures. */ + + complete_symtab (filestring, file_start_addr); + cur_src_end_addr = file_end_addr; + end_symtab (file_end_addr, objfile, SECT_OFF_TEXT (objfile)); + end_stabs (); + + /* XCOFF, according to the AIX 3.2 documentation, puts the filename + in cs->c_name. But xlc 1.3.0.2 has decided to do things the + standard COFF way and put it in the auxent. We use the auxent if + the symbol is ".file" and an auxent exists, otherwise use the symbol + itself. Simple enough. */ + if (!strcmp (cs->c_name, ".file") && cs->c_naux > 0) + { + bfd_coff_swap_aux_in (abfd, raw_auxptr, cs->c_type, cs->c_sclass, + 0, cs->c_naux, &main_aux); + filestring = coff_getfilename (&main_aux, objfile); + } + else + filestring = cs->c_name; + + start_stabs (); + start_symtab (filestring, (char *) NULL, (CORE_ADDR) 0); + record_debugformat (debugfmt); + last_csect_name = 0; + + /* reset file start and end addresses. A compilation unit with no text + (only data) should have zero file boundaries. */ + file_start_addr = file_end_addr = 0; + break; + + case C_FUN: + fcn_stab_saved = *cs; + break; + + case C_FCN: + if (DEPRECATED_STREQ (cs->c_name, ".bf")) + { + CORE_ADDR off = ANOFFSET (objfile->section_offsets, + SECT_OFF_TEXT (objfile)); + bfd_coff_swap_aux_in (abfd, raw_auxptr, cs->c_type, cs->c_sclass, + 0, cs->c_naux, &main_aux); + + within_function = 1; + + new = push_context (0, fcn_start_addr + off); + + new->name = define_symbol + (fcn_cs_saved.c_value + off, + fcn_stab_saved.c_name, 0, 0, objfile); + if (new->name != NULL) + SYMBOL_SECTION (new->name) = SECT_OFF_TEXT (objfile); + } + else if (DEPRECATED_STREQ (cs->c_name, ".ef")) + { + + bfd_coff_swap_aux_in (abfd, raw_auxptr, cs->c_type, cs->c_sclass, + 0, cs->c_naux, &main_aux); + + /* The value of .ef is the address of epilogue code; + not useful for gdb. */ + /* { main_aux.x_sym.x_misc.x_lnsz.x_lnno + contains number of lines to '}' */ + + if (context_stack_depth <= 0) + { /* We attempted to pop an empty context stack */ + ef_complaint (cs->c_symnum); + within_function = 0; + break; + } + new = pop_context (); + /* Stack must be empty now. */ + if (context_stack_depth > 0 || new == NULL) + { + ef_complaint (cs->c_symnum); + within_function = 0; + break; + } + + finish_block (new->name, &local_symbols, new->old_blocks, + new->start_addr, + (fcn_cs_saved.c_value + + fcn_aux_saved.x_sym.x_misc.x_fsize + + ANOFFSET (objfile->section_offsets, + SECT_OFF_TEXT (objfile))), + objfile); + within_function = 0; + } + break; + + case C_BSTAT: + /* Begin static block. */ + { + struct internal_syment symbol; + + read_symbol (&symbol, cs->c_value); + static_block_base = symbol.n_value; + static_block_section = + secnum_to_section (symbol.n_scnum, objfile); + } + break; + + case C_ESTAT: + /* End of static block. */ + static_block_base = 0; + static_block_section = -1; + break; + + case C_ARG: + case C_REGPARM: + case C_REG: + case C_TPDEF: + case C_STRTAG: + case C_UNTAG: + case C_ENTAG: + { + complaint (&symfile_complaints, "Unrecognized storage class %d.", + cs->c_sclass); + } + break; + + case C_LABEL: + case C_NULL: + /* Ignore these. */ + break; + + case C_HIDEXT: + case C_STAT: + break; + + case C_BINCL: + /* beginning of include file */ + /* In xlc output, C_BINCL/C_EINCL pair doesn't show up in sorted + order. Thus, when wee see them, we might not know enough info + to process them. Thus, we'll be saving them into a table + (inclTable) and postpone their processing. */ + + record_include_begin (cs); + break; + + case C_EINCL: + /* End of include file. */ + /* See the comment after case C_BINCL. */ + record_include_end (cs); + break; + + case C_BLOCK: + if (DEPRECATED_STREQ (cs->c_name, ".bb")) + { + depth++; + new = push_context (depth, + (cs->c_value + + ANOFFSET (objfile->section_offsets, + SECT_OFF_TEXT (objfile)))); + } + else if (DEPRECATED_STREQ (cs->c_name, ".eb")) + { + if (context_stack_depth <= 0) + { /* We attempted to pop an empty context stack */ + eb_complaint (cs->c_symnum); + break; + } + new = pop_context (); + if (depth-- != new->depth) + { + eb_complaint (cs->c_symnum); + break; + } + if (local_symbols && context_stack_depth > 0) + { + /* Make a block for the local symbols within. */ + finish_block (new->name, &local_symbols, new->old_blocks, + new->start_addr, + (cs->c_value + + ANOFFSET (objfile->section_offsets, + SECT_OFF_TEXT (objfile))), + objfile); + } + local_symbols = new->locals; + } + break; + + default: + process_xcoff_symbol (cs, objfile); + break; + } + } + + if (last_source_file) + { + struct symtab *s; + + complete_symtab (filestring, file_start_addr); + cur_src_end_addr = file_end_addr; + s = end_symtab (file_end_addr, objfile, SECT_OFF_TEXT (objfile)); + /* When reading symbols for the last C_FILE of the objfile, try + to make sure that we set pst->symtab to the symtab for the + file, not to the _globals_ symtab. I'm not sure whether this + actually works right or when/if it comes up. */ + if (pst->symtab == NULL) + pst->symtab = s; + end_stabs (); + } +} + +#define SYMBOL_DUP(SYMBOL1, SYMBOL2) \ + (SYMBOL2) = (struct symbol *) \ + obstack_alloc (&objfile->objfile_obstack, sizeof (struct symbol)); \ + *(SYMBOL2) = *(SYMBOL1); + + +#define SYMNAME_ALLOC(NAME, ALLOCED) \ + (ALLOCED) ? (NAME) : obsavestring ((NAME), strlen (NAME), &objfile->objfile_obstack); + + +static struct type *func_symbol_type; +static struct type *var_symbol_type; + +/* process one xcoff symbol. */ + +static struct symbol * +process_xcoff_symbol (struct coff_symbol *cs, struct objfile *objfile) +{ + struct symbol onesymbol; + struct symbol *sym = &onesymbol; + struct symbol *sym2 = NULL; + char *name, *pp; + + int sec; + CORE_ADDR off; + + if (cs->c_secnum < 0) + { + /* The value is a register number, offset within a frame, etc., + and does not get relocated. */ + off = 0; + sec = -1; + } + else + { + sec = secnum_to_section (cs->c_secnum, objfile); + off = ANOFFSET (objfile->section_offsets, sec); + } + + name = cs->c_name; + if (name[0] == '.') + ++name; + + memset (sym, '\0', sizeof (struct symbol)); + + /* default assumptions */ + SYMBOL_VALUE_ADDRESS (sym) = cs->c_value + off; + SYMBOL_DOMAIN (sym) = VAR_DOMAIN; + SYMBOL_SECTION (sym) = secnum_to_section (cs->c_secnum, objfile); + + if (ISFCN (cs->c_type)) + { + /* At this point, we don't know the type of the function. This + will be patched with the type from its stab entry later on in + patch_block_stabs (), unless the file was compiled without -g. */ + + DEPRECATED_SYMBOL_NAME (sym) = SYMNAME_ALLOC (name, symname_alloced); + SYMBOL_TYPE (sym) = func_symbol_type; + + SYMBOL_CLASS (sym) = LOC_BLOCK; + SYMBOL_DUP (sym, sym2); + + if (cs->c_sclass == C_EXT) + add_symbol_to_list (sym2, &global_symbols); + else if (cs->c_sclass == C_HIDEXT || cs->c_sclass == C_STAT) + add_symbol_to_list (sym2, &file_symbols); + } + else + { + /* In case we can't figure out the type, provide default. */ + SYMBOL_TYPE (sym) = var_symbol_type; + + switch (cs->c_sclass) + { +#if 0 + /* The values of functions and global symbols are now resolved + via the global_sym_chain in stabsread.c. */ + case C_FUN: + if (fcn_cs_saved.c_sclass == C_EXT) + add_stab_to_list (name, &global_stabs); + else + add_stab_to_list (name, &file_stabs); + break; + + case C_GSYM: + add_stab_to_list (name, &global_stabs); + break; +#endif + + case C_BCOMM: + common_block_start (cs->c_name, objfile); + break; + + case C_ECOMM: + common_block_end (objfile); + break; + + default: + complaint (&symfile_complaints, "Unexpected storage class: %d", + cs->c_sclass); + /* FALLTHROUGH */ + + case C_DECL: + case C_PSYM: + case C_RPSYM: + case C_ECOML: + case C_LSYM: + case C_RSYM: + case C_GSYM: + + { + sym = define_symbol (cs->c_value + off, cs->c_name, 0, 0, objfile); + if (sym != NULL) + { + SYMBOL_SECTION (sym) = sec; + } + return sym; + } + + case C_STSYM: + + /* For xlc (not GCC), the 'V' symbol descriptor is used for + all statics and we need to distinguish file-scope versus + function-scope using within_function. We do this by + changing the string we pass to define_symbol to use 'S' + where we need to, which is not necessarily super-clean, + but seems workable enough. */ + + if (*name == ':' || (pp = (char *) strchr (name, ':')) == NULL) + return NULL; + + ++pp; + if (*pp == 'V' && !within_function) + *pp = 'S'; + sym = define_symbol ((cs->c_value + + ANOFFSET (objfile->section_offsets, + static_block_section)), + cs->c_name, 0, 0, objfile); + if (sym != NULL) + { + SYMBOL_VALUE_ADDRESS (sym) += static_block_base; + SYMBOL_SECTION (sym) = static_block_section; + } + return sym; + + } + } + return sym2; +} + +/* Extract the file name from the aux entry of a C_FILE symbol. + Result is in static storage and is only good for temporary use. */ + +static char * +coff_getfilename (union internal_auxent *aux_entry, struct objfile *objfile) +{ + static char buffer[BUFSIZ]; + + if (aux_entry->x_file.x_n.x_zeroes == 0) + strcpy (buffer, + ((struct coff_symfile_info *) objfile->sym_private)->strtbl + + aux_entry->x_file.x_n.x_offset); + else + { + strncpy (buffer, aux_entry->x_file.x_fname, FILNMLEN); + buffer[FILNMLEN] = '\0'; + } + return (buffer); +} + +/* Set *SYMBOL to symbol number symno in symtbl. */ +static void +read_symbol (struct internal_syment *symbol, int symno) +{ + int nsyms = + ((struct coff_symfile_info *) this_symtab_psymtab->objfile->sym_private) + ->symtbl_num_syms; + char *stbl = + ((struct coff_symfile_info *) this_symtab_psymtab->objfile->sym_private) + ->symtbl; + if (symno < 0 || symno >= nsyms) + { + complaint (&symfile_complaints, "Invalid symbol offset"); + symbol->n_value = 0; + symbol->n_scnum = -1; + return; + } + bfd_coff_swap_sym_in (this_symtab_psymtab->objfile->obfd, + stbl + (symno * local_symesz), + symbol); +} + +/* Get value corresponding to symbol number symno in symtbl. */ + +static CORE_ADDR +read_symbol_nvalue (int symno) +{ + struct internal_syment symbol[1]; + + read_symbol (symbol, symno); + return symbol->n_value; +} + + +/* Find the address of the function corresponding to symno, where + symno is the symbol pointed to by the linetable. */ + +static int +read_symbol_lineno (int symno) +{ + struct objfile *objfile = this_symtab_psymtab->objfile; + int xcoff64 = bfd_xcoff_is_xcoff64 (objfile->obfd); + + struct coff_symfile_info *info = + (struct coff_symfile_info *)objfile->sym_private; + int nsyms = info->symtbl_num_syms; + char *stbl = info->symtbl; + char *strtbl = info->strtbl; + + struct internal_syment symbol[1]; + union internal_auxent main_aux[1]; + + if (symno < 0) + { + bf_notfound_complaint (); + return 0; + } + + /* Note that just searching for a short distance (e.g. 50 symbols) + is not enough, at least in the following case. + + .extern foo + [many .stabx entries] + [a few functions, referring to foo] + .globl foo + .bf + + What happens here is that the assembler moves the .stabx entries + to right before the ".bf" for foo, but the symbol for "foo" is before + all the stabx entries. See PR gdb/2222. */ + + /* Maintaining a table of .bf entries might be preferable to this search. + If I understand things correctly it would need to be done only for + the duration of a single psymtab to symtab conversion. */ + while (symno < nsyms) + { + bfd_coff_swap_sym_in (symfile_bfd, + stbl + (symno * local_symesz), symbol); + if (symbol->n_sclass == C_FCN) + { + char *name = xcoff64 ? strtbl + symbol->n_offset : symbol->n_name; + if (DEPRECATED_STREQ (name, ".bf")) + goto gotit; + } + symno += symbol->n_numaux + 1; + } + + bf_notfound_complaint (); + return 0; + +gotit: + /* take aux entry and return its lineno */ + symno++; + bfd_coff_swap_aux_in (objfile->obfd, stbl + symno * local_symesz, + symbol->n_type, symbol->n_sclass, + 0, symbol->n_numaux, main_aux); + + return main_aux->x_sym.x_misc.x_lnsz.x_lnno; +} + +/* Support for line number handling */ + +/* This function is called for every section; it finds the outer limits + * of the line table (minimum and maximum file offset) so that the + * mainline code can read the whole thing for efficiency. + */ +static void +find_linenos (struct bfd *abfd, struct bfd_section *asect, void *vpinfo) +{ + struct coff_symfile_info *info; + int size, count; + file_ptr offset, maxoff; + + count = asect->lineno_count; + + if (!DEPRECATED_STREQ (asect->name, ".text") || count == 0) + return; + + size = count * coff_data (abfd)->local_linesz; + info = (struct coff_symfile_info *) vpinfo; + offset = asect->line_filepos; + maxoff = offset + size; + + if (offset < info->min_lineno_offset || info->min_lineno_offset == 0) + info->min_lineno_offset = offset; + + if (maxoff > info->max_lineno_offset) + info->max_lineno_offset = maxoff; +} + +static void xcoff_psymtab_to_symtab_1 (struct partial_symtab *); + +static void +xcoff_psymtab_to_symtab_1 (struct partial_symtab *pst) +{ + struct cleanup *old_chain; + int i; + + if (!pst) + return; + + if (pst->readin) + { + fprintf_unfiltered + (gdb_stderr, "Psymtab for %s already read in. Shouldn't happen.\n", + pst->filename); + return; + } + + /* Read in all partial symtabs on which this one is dependent */ + for (i = 0; i < pst->number_of_dependencies; i++) + if (!pst->dependencies[i]->readin) + { + /* Inform about additional files that need to be read in. */ + if (info_verbose) + { + fputs_filtered (" ", gdb_stdout); + wrap_here (""); + fputs_filtered ("and ", gdb_stdout); + wrap_here (""); + printf_filtered ("%s...", pst->dependencies[i]->filename); + wrap_here (""); /* Flush output */ + gdb_flush (gdb_stdout); + } + xcoff_psymtab_to_symtab_1 (pst->dependencies[i]); + } + + if (((struct symloc *) pst->read_symtab_private)->numsyms != 0) + { + /* Init stuff necessary for reading in symbols. */ + stabsread_init (); + buildsym_init (); + old_chain = make_cleanup (really_free_pendings, 0); + + read_xcoff_symtab (pst); + + do_cleanups (old_chain); + } + + pst->readin = 1; +} + +static void xcoff_psymtab_to_symtab (struct partial_symtab *); + +/* Read in all of the symbols for a given psymtab for real. + Be verbose about it if the user wants that. */ + +static void +xcoff_psymtab_to_symtab (struct partial_symtab *pst) +{ + bfd *sym_bfd; + + if (!pst) + return; + + if (pst->readin) + { + fprintf_unfiltered + (gdb_stderr, "Psymtab for %s already read in. Shouldn't happen.\n", + pst->filename); + return; + } + + if (((struct symloc *) pst->read_symtab_private)->numsyms != 0 + || pst->number_of_dependencies) + { + /* Print the message now, before reading the string table, + to avoid disconcerting pauses. */ + if (info_verbose) + { + printf_filtered ("Reading in symbols for %s...", pst->filename); + gdb_flush (gdb_stdout); + } + + sym_bfd = pst->objfile->obfd; + + next_symbol_text_func = xcoff_next_symbol_text; + + xcoff_psymtab_to_symtab_1 (pst); + + /* Match with global symbols. This only needs to be done once, + after all of the symtabs and dependencies have been read in. */ + scan_file_globals (pst->objfile); + + /* Finish up the debug error message. */ + if (info_verbose) + printf_filtered ("done.\n"); + } +} + +static void +xcoff_new_init (struct objfile *objfile) +{ + stabsread_new_init (); + buildsym_new_init (); +} + +/* Do initialization in preparation for reading symbols from OBJFILE. + + We will only be called if this is an XCOFF or XCOFF-like file. + BFD handles figuring out the format of the file, and code in symfile.c + uses BFD's determination to vector to us. */ + +static void +xcoff_symfile_init (struct objfile *objfile) +{ + /* Allocate struct to keep track of the symfile */ + objfile->sym_private = xmmalloc (objfile->md, + sizeof (struct coff_symfile_info)); + + /* XCOFF objects may be reordered, so set OBJF_REORDERED. If we + find this causes a significant slowdown in gdb then we could + set it in the debug symbol readers only when necessary. */ + objfile->flags |= OBJF_REORDERED; + + init_entry_point_info (objfile); +} + +/* Perform any local cleanups required when we are done with a particular + objfile. I.E, we are in the process of discarding all symbol information + for an objfile, freeing up all memory held for it, and unlinking the + objfile struct from the global list of known objfiles. */ + +static void +xcoff_symfile_finish (struct objfile *objfile) +{ + if (objfile->sym_private != NULL) + { + xmfree (objfile->md, objfile->sym_private); + } + + /* Start with a fresh include table for the next objfile. */ + if (inclTable) + { + xfree (inclTable); + inclTable = NULL; + } + inclIndx = inclLength = inclDepth = 0; +} + + +static void +init_stringtab (bfd *abfd, file_ptr offset, struct objfile *objfile) +{ + long length; + int val; + unsigned char lengthbuf[4]; + char *strtbl; + + ((struct coff_symfile_info *) objfile->sym_private)->strtbl = NULL; + + if (bfd_seek (abfd, offset, SEEK_SET) < 0) + error ("cannot seek to string table in %s: %s", + bfd_get_filename (abfd), bfd_errmsg (bfd_get_error ())); + + val = bfd_bread ((char *) lengthbuf, sizeof lengthbuf, abfd); + length = bfd_h_get_32 (abfd, lengthbuf); + + /* If no string table is needed, then the file may end immediately + after the symbols. Just return with `strtbl' set to NULL. */ + + if (val != sizeof lengthbuf || length < sizeof lengthbuf) + return; + + /* Allocate string table from objfile_obstack. We will need this table + as long as we have its symbol table around. */ + + strtbl = (char *) obstack_alloc (&objfile->objfile_obstack, length); + ((struct coff_symfile_info *) objfile->sym_private)->strtbl = strtbl; + + /* Copy length buffer, the first byte is usually zero and is + used for stabs with a name length of zero. */ + memcpy (strtbl, lengthbuf, sizeof lengthbuf); + if (length == sizeof lengthbuf) + return; + + val = bfd_bread (strtbl + sizeof lengthbuf, length - sizeof lengthbuf, abfd); + + if (val != length - sizeof lengthbuf) + error ("cannot read string table from %s: %s", + bfd_get_filename (abfd), bfd_errmsg (bfd_get_error ())); + if (strtbl[length - 1] != '\0') + error ("bad symbol file: string table does not end with null character"); + + return; +} + +/* If we have not yet seen a function for this psymtab, this is 0. If we + have seen one, it is the offset in the line numbers of the line numbers + for the psymtab. */ +static unsigned int first_fun_line_offset; + +static struct partial_symtab *xcoff_start_psymtab + (struct objfile *, char *, int, + struct partial_symbol **, struct partial_symbol **); + +/* Allocate and partially fill a partial symtab. It will be + completely filled at the end of the symbol list. + + SYMFILE_NAME is the name of the symbol-file we are reading from, and ADDR + is the address relative to which its symbols are (incremental) or 0 + (normal). */ + +static struct partial_symtab * +xcoff_start_psymtab (struct objfile *objfile, char *filename, int first_symnum, + struct partial_symbol **global_syms, + struct partial_symbol **static_syms) +{ + struct partial_symtab *result = + start_psymtab_common (objfile, objfile->section_offsets, + filename, + /* We fill in textlow later. */ + 0, + global_syms, static_syms); + + result->read_symtab_private = (char *) + obstack_alloc (&objfile->objfile_obstack, sizeof (struct symloc)); + ((struct symloc *) result->read_symtab_private)->first_symnum = first_symnum; + result->read_symtab = xcoff_psymtab_to_symtab; + + /* Deduce the source language from the filename for this psymtab. */ + psymtab_language = deduce_language_from_filename (filename); + + return result; +} + +static struct partial_symtab *xcoff_end_psymtab + (struct partial_symtab *, char **, int, int, + struct partial_symtab **, int, int); + +/* Close off the current usage of PST. + Returns PST, or NULL if the partial symtab was empty and thrown away. + + CAPPING_SYMBOL_NUMBER is the end of pst (exclusive). + + INCLUDE_LIST, NUM_INCLUDES, DEPENDENCY_LIST, and NUMBER_DEPENDENCIES + are the information for includes and dependencies. */ + +static struct partial_symtab * +xcoff_end_psymtab (struct partial_symtab *pst, char **include_list, + int num_includes, int capping_symbol_number, + struct partial_symtab **dependency_list, + int number_dependencies, int textlow_not_set) +{ + int i; + struct objfile *objfile = pst->objfile; + + if (capping_symbol_number != -1) + ((struct symloc *) pst->read_symtab_private)->numsyms = + capping_symbol_number + - ((struct symloc *) pst->read_symtab_private)->first_symnum; + ((struct symloc *) pst->read_symtab_private)->lineno_off = + first_fun_line_offset; + first_fun_line_offset = 0; + pst->n_global_syms = + objfile->global_psymbols.next - (objfile->global_psymbols.list + pst->globals_offset); + pst->n_static_syms = + objfile->static_psymbols.next - (objfile->static_psymbols.list + pst->statics_offset); + + pst->number_of_dependencies = number_dependencies; + if (number_dependencies) + { + pst->dependencies = (struct partial_symtab **) + obstack_alloc (&objfile->objfile_obstack, + number_dependencies * sizeof (struct partial_symtab *)); + memcpy (pst->dependencies, dependency_list, + number_dependencies * sizeof (struct partial_symtab *)); + } + else + pst->dependencies = 0; + + for (i = 0; i < num_includes; i++) + { + struct partial_symtab *subpst = + allocate_psymtab (include_list[i], objfile); + + subpst->section_offsets = pst->section_offsets; + subpst->read_symtab_private = + (char *) obstack_alloc (&objfile->objfile_obstack, + sizeof (struct symloc)); + ((struct symloc *) subpst->read_symtab_private)->first_symnum = 0; + ((struct symloc *) subpst->read_symtab_private)->numsyms = 0; + subpst->textlow = 0; + subpst->texthigh = 0; + + /* We could save slight bits of space by only making one of these, + shared by the entire set of include files. FIXME-someday. */ + subpst->dependencies = (struct partial_symtab **) + obstack_alloc (&objfile->objfile_obstack, + sizeof (struct partial_symtab *)); + subpst->dependencies[0] = pst; + subpst->number_of_dependencies = 1; + + subpst->globals_offset = + subpst->n_global_syms = + subpst->statics_offset = + subpst->n_static_syms = 0; + + subpst->readin = 0; + subpst->symtab = 0; + subpst->read_symtab = pst->read_symtab; + } + + sort_pst_symbols (pst); + + /* If there is already a psymtab or symtab for a file of this name, + remove it. (If there is a symtab, more drastic things also + happen.) This happens in VxWorks. */ + free_named_symtabs (pst->filename); + + if (num_includes == 0 + && number_dependencies == 0 + && pst->n_global_syms == 0 + && pst->n_static_syms == 0) + { + /* Throw away this psymtab, it's empty. We can't deallocate it, since + it is on the obstack, but we can forget to chain it on the list. */ + /* Empty psymtabs happen as a result of header files which don't have + any symbols in them. There can be a lot of them. */ + + discard_psymtab (pst); + + /* Indicate that psymtab was thrown away. */ + pst = (struct partial_symtab *) NULL; + } + return pst; +} + +static void swap_sym (struct internal_syment *, + union internal_auxent *, char **, char **, + unsigned int *, struct objfile *); + +/* Swap raw symbol at *RAW and put the name in *NAME, the symbol in + *SYMBOL, the first auxent in *AUX. Advance *RAW and *SYMNUMP over + the symbol and its auxents. */ + +static void +swap_sym (struct internal_syment *symbol, union internal_auxent *aux, + char **name, char **raw, unsigned int *symnump, + struct objfile *objfile) +{ + bfd_coff_swap_sym_in (objfile->obfd, *raw, symbol); + if (symbol->n_zeroes) + { + /* If it's exactly E_SYMNMLEN characters long it isn't + '\0'-terminated. */ + if (symbol->n_name[E_SYMNMLEN - 1] != '\0') + { + /* FIXME: wastes memory for symbols which we don't end up putting + into the minimal symbols. */ + char *p; + p = obstack_alloc (&objfile->objfile_obstack, E_SYMNMLEN + 1); + strncpy (p, symbol->n_name, E_SYMNMLEN); + p[E_SYMNMLEN] = '\0'; + *name = p; + } + else + /* Point to the unswapped name as that persists as long as the + objfile does. */ + *name = ((struct external_syment *) *raw)->e.e_name; + } + else if (symbol->n_sclass & 0x80) + { + *name = ((struct coff_symfile_info *) objfile->sym_private)->debugsec + + symbol->n_offset; + } + else + { + *name = ((struct coff_symfile_info *) objfile->sym_private)->strtbl + + symbol->n_offset; + } + ++*symnump; + *raw += coff_data (objfile->obfd)->local_symesz; + if (symbol->n_numaux > 0) + { + bfd_coff_swap_aux_in (objfile->obfd, *raw, symbol->n_type, + symbol->n_sclass, 0, symbol->n_numaux, aux); + + *symnump += symbol->n_numaux; + *raw += coff_data (objfile->obfd)->local_symesz * symbol->n_numaux; + } +} + +static void +function_outside_compilation_unit_complaint (const char *arg1) +{ + complaint (&symfile_complaints, + "function `%s' appears to be defined outside of all compilation units", + arg1); +} + +static void +scan_xcoff_symtab (struct objfile *objfile) +{ + CORE_ADDR toc_offset = 0; /* toc offset value in data section. */ + char *filestring = NULL; + + char *namestring; + int past_first_source_file = 0; + bfd *abfd; + asection *bfd_sect; + unsigned int nsyms; + + /* Current partial symtab */ + struct partial_symtab *pst; + + /* List of current psymtab's include files */ + char **psymtab_include_list; + int includes_allocated; + int includes_used; + + /* Index within current psymtab dependency list */ + struct partial_symtab **dependency_list; + int dependencies_used, dependencies_allocated; + + char *sraw_symbol; + struct internal_syment symbol; + union internal_auxent main_aux[5]; + unsigned int ssymnum; + + char *last_csect_name = NULL; /* last seen csect's name and value */ + CORE_ADDR last_csect_val = 0; + int last_csect_sec = 0; + int misc_func_recorded = 0; /* true if any misc. function */ + int textlow_not_set = 1; + + pst = (struct partial_symtab *) 0; + + includes_allocated = 30; + includes_used = 0; + psymtab_include_list = (char **) alloca (includes_allocated * + sizeof (char *)); + + dependencies_allocated = 30; + dependencies_used = 0; + dependency_list = + (struct partial_symtab **) alloca (dependencies_allocated * + sizeof (struct partial_symtab *)); + + last_source_file = NULL; + + abfd = objfile->obfd; + + sraw_symbol = ((struct coff_symfile_info *) objfile->sym_private)->symtbl; + nsyms = ((struct coff_symfile_info *) objfile->sym_private)->symtbl_num_syms; + ssymnum = 0; + while (ssymnum < nsyms) + { + int sclass; + + QUIT; + + bfd_coff_swap_sym_in (abfd, sraw_symbol, &symbol); + sclass = symbol.n_sclass; + + switch (sclass) + { + case C_EXT: + case C_HIDEXT: + { + /* The CSECT auxent--always the last auxent. */ + union internal_auxent csect_aux; + unsigned int symnum_before = ssymnum; + + swap_sym (&symbol, &main_aux[0], &namestring, &sraw_symbol, + &ssymnum, objfile); + if (symbol.n_numaux > 1) + { + bfd_coff_swap_aux_in + (objfile->obfd, + sraw_symbol - coff_data (abfd)->local_symesz, + symbol.n_type, + symbol.n_sclass, + symbol.n_numaux - 1, + symbol.n_numaux, + &csect_aux); + } + else + csect_aux = main_aux[0]; + + /* If symbol name starts with ".$" or "$", ignore it. */ + if (namestring[0] == '$' + || (namestring[0] == '.' && namestring[1] == '$')) + break; + + switch (csect_aux.x_csect.x_smtyp & 0x7) + { + case XTY_SD: + switch (csect_aux.x_csect.x_smclas) + { + case XMC_PR: + if (last_csect_name) + { + /* If no misc. function recorded in the last + seen csect, enter it as a function. This + will take care of functions like strcmp() + compiled by xlc. */ + + if (!misc_func_recorded) + { + RECORD_MINIMAL_SYMBOL + (last_csect_name, last_csect_val, + mst_text, last_csect_sec, + objfile); + } + + if (pst != NULL) + { + /* We have to allocate one psymtab for + each program csect, because their text + sections need not be adjacent. */ + xcoff_end_psymtab + (pst, psymtab_include_list, includes_used, + symnum_before, dependency_list, + dependencies_used, textlow_not_set); + includes_used = 0; + dependencies_used = 0; + /* Give all psymtabs for this source file the same + name. */ + pst = xcoff_start_psymtab + (objfile, + filestring, + symnum_before, + objfile->global_psymbols.next, + objfile->static_psymbols.next); + } + } + /* Activate the misc_func_recorded mechanism for + compiler- and linker-generated CSECTs like ".strcmp" + and "@FIX1". */ + if (namestring && (namestring[0] == '.' + || namestring[0] == '@')) + { + last_csect_name = namestring; + last_csect_val = symbol.n_value; + last_csect_sec = + secnum_to_section (symbol.n_scnum, objfile); + } + if (pst != NULL) + { + CORE_ADDR highval = + symbol.n_value + csect_aux.x_csect.x_scnlen.l; + if (highval > pst->texthigh) + pst->texthigh = highval; + if (pst->textlow == 0 || symbol.n_value < pst->textlow) + pst->textlow = symbol.n_value; + } + misc_func_recorded = 0; + break; + + case XMC_RW: + case XMC_TD: + /* Data variables are recorded in the minimal symbol + table, except for section symbols. */ + if (*namestring != '.') + prim_record_minimal_symbol_and_info + (namestring, symbol.n_value, + sclass == C_HIDEXT ? mst_file_data : mst_data, + NULL, secnum_to_section (symbol.n_scnum, objfile), + NULL, objfile); + break; + + case XMC_TC0: + if (toc_offset) + warning ("More than one XMC_TC0 symbol found."); + toc_offset = symbol.n_value; + + /* Make TOC offset relative to start address of section. */ + bfd_sect = secnum_to_bfd_section (symbol.n_scnum, objfile); + if (bfd_sect) + toc_offset -= bfd_section_vma (objfile->obfd, bfd_sect); + break; + + case XMC_TC: + /* These symbols tell us where the TOC entry for a + variable is, not the variable itself. */ + break; + + default: + break; + } + break; + + case XTY_LD: + switch (csect_aux.x_csect.x_smclas) + { + case XMC_PR: + /* A function entry point. */ + + if (first_fun_line_offset == 0 && symbol.n_numaux > 1) + first_fun_line_offset = + main_aux[0].x_sym.x_fcnary.x_fcn.x_lnnoptr; + RECORD_MINIMAL_SYMBOL + (namestring, symbol.n_value, + sclass == C_HIDEXT ? mst_file_text : mst_text, + secnum_to_section (symbol.n_scnum, objfile), + objfile); + break; + + case XMC_GL: + /* shared library function trampoline code entry + point. */ + + /* record trampoline code entries as + mst_solib_trampoline symbol. When we lookup mst + symbols, we will choose mst_text over + mst_solib_trampoline. */ + RECORD_MINIMAL_SYMBOL + (namestring, symbol.n_value, + mst_solib_trampoline, + secnum_to_section (symbol.n_scnum, objfile), + objfile); + break; + + case XMC_DS: + /* The symbols often have the same names as + debug symbols for functions, and confuse + lookup_symbol. */ + break; + + default: + + /* xlc puts each variable in a separate csect, + so we get an XTY_SD for each variable. But + gcc puts several variables in a csect, so + that each variable only gets an XTY_LD. We + still need to record them. This will + typically be XMC_RW; I suspect XMC_RO and + XMC_BS might be possible too. */ + if (*namestring != '.') + prim_record_minimal_symbol_and_info + (namestring, symbol.n_value, + sclass == C_HIDEXT ? mst_file_data : mst_data, + NULL, secnum_to_section (symbol.n_scnum, objfile), + NULL, objfile); + break; + } + break; + + case XTY_CM: + switch (csect_aux.x_csect.x_smclas) + { + case XMC_RW: + case XMC_BS: + /* Common variables are recorded in the minimal symbol + table, except for section symbols. */ + if (*namestring != '.') + prim_record_minimal_symbol_and_info + (namestring, symbol.n_value, + sclass == C_HIDEXT ? mst_file_bss : mst_bss, + NULL, secnum_to_section (symbol.n_scnum, objfile), + NULL, objfile); + break; + } + break; + + default: + break; + } + } + break; + case C_FILE: + { + unsigned int symnum_before; + + symnum_before = ssymnum; + swap_sym (&symbol, &main_aux[0], &namestring, &sraw_symbol, + &ssymnum, objfile); + + /* See if the last csect needs to be recorded. */ + + if (last_csect_name && !misc_func_recorded) + { + + /* If no misc. function recorded in the last seen csect, enter + it as a function. This will take care of functions like + strcmp() compiled by xlc. */ + + RECORD_MINIMAL_SYMBOL + (last_csect_name, last_csect_val, + mst_text, last_csect_sec, objfile); + } + + if (pst) + { + xcoff_end_psymtab (pst, psymtab_include_list, includes_used, + symnum_before, dependency_list, + dependencies_used, textlow_not_set); + includes_used = 0; + dependencies_used = 0; + } + first_fun_line_offset = 0; + + /* XCOFF, according to the AIX 3.2 documentation, puts the + filename in cs->c_name. But xlc 1.3.0.2 has decided to + do things the standard COFF way and put it in the auxent. + We use the auxent if the symbol is ".file" and an auxent + exists, otherwise use the symbol itself. */ + if (!strcmp (namestring, ".file") && symbol.n_numaux > 0) + { + filestring = coff_getfilename (&main_aux[0], objfile); + } + else + filestring = namestring; + + pst = xcoff_start_psymtab (objfile, + filestring, + symnum_before, + objfile->global_psymbols.next, + objfile->static_psymbols.next); + last_csect_name = NULL; + } + break; + + default: + { + complaint (&symfile_complaints, + "Storage class %d not recognized during scan", sclass); + } + /* FALLTHROUGH */ + + /* C_FCN is .bf and .ef symbols. I think it is sufficient + to handle only the C_FUN and C_EXT. */ + case C_FCN: + + case C_BSTAT: + case C_ESTAT: + case C_ARG: + case C_REGPARM: + case C_REG: + case C_TPDEF: + case C_STRTAG: + case C_UNTAG: + case C_ENTAG: + case C_LABEL: + case C_NULL: + + /* C_EINCL means we are switching back to the main file. But there + is no reason to care; the only thing we want to know about + includes is the names of all the included (.h) files. */ + case C_EINCL: + + case C_BLOCK: + + /* I don't think C_STAT is used in xcoff; C_HIDEXT appears to be + used instead. */ + case C_STAT: + + /* I don't think the name of the common block (as opposed to the + variables within it) is something which is user visible + currently. */ + case C_BCOMM: + case C_ECOMM: + + case C_PSYM: + case C_RPSYM: + + /* I think we can ignore C_LSYM; types on xcoff seem to use C_DECL + so C_LSYM would appear to be only for locals. */ + case C_LSYM: + + case C_AUTO: + case C_RSYM: + { + /* We probably could save a few instructions by assuming that + C_LSYM, C_PSYM, etc., never have auxents. */ + int naux1 = symbol.n_numaux + 1; + ssymnum += naux1; + sraw_symbol += bfd_coff_symesz (abfd) * naux1; + } + break; + + case C_BINCL: + { + /* Mark down an include file in the current psymtab */ + enum language tmp_language; + swap_sym (&symbol, &main_aux[0], &namestring, &sraw_symbol, + &ssymnum, objfile); + + tmp_language = deduce_language_from_filename (namestring); + + /* Only change the psymtab's language if we've learned + something useful (eg. tmp_language is not language_unknown). + In addition, to match what start_subfile does, never change + from C++ to C. */ + if (tmp_language != language_unknown + && (tmp_language != language_c + || psymtab_language != language_cplus)) + psymtab_language = tmp_language; + + /* In C++, one may expect the same filename to come round many + times, when code is coming alternately from the main file + and from inline functions in other files. So I check to see + if this is a file we've seen before -- either the main + source file, or a previously included file. + + This seems to be a lot of time to be spending on N_SOL, but + things like "break c-exp.y:435" need to work (I + suppose the psymtab_include_list could be hashed or put + in a binary tree, if profiling shows this is a major hog). */ + if (pst && DEPRECATED_STREQ (namestring, pst->filename)) + continue; + { + int i; + for (i = 0; i < includes_used; i++) + if (DEPRECATED_STREQ (namestring, psymtab_include_list[i])) + { + i = -1; + break; + } + if (i == -1) + continue; + } + psymtab_include_list[includes_used++] = namestring; + if (includes_used >= includes_allocated) + { + char **orig = psymtab_include_list; + + psymtab_include_list = (char **) + alloca ((includes_allocated *= 2) * + sizeof (char *)); + memcpy (psymtab_include_list, orig, + includes_used * sizeof (char *)); + } + continue; + } + case C_FUN: + /* The value of the C_FUN is not the address of the function (it + appears to be the address before linking), but as long as it + is smaller than the actual address, then find_pc_partial_function + will use the minimal symbols instead. I hope. */ + + case C_GSYM: + case C_ECOML: + case C_DECL: + case C_STSYM: + { + char *p; + swap_sym (&symbol, &main_aux[0], &namestring, &sraw_symbol, + &ssymnum, objfile); + + p = (char *) strchr (namestring, ':'); + if (!p) + continue; /* Not a debugging symbol. */ + + /* Main processing section for debugging symbols which + the initial read through the symbol tables needs to worry + about. If we reach this point, the symbol which we are + considering is definitely one we are interested in. + p must also contain the (valid) index into the namestring + which indicates the debugging type symbol. */ + + switch (p[1]) + { + case 'S': + symbol.n_value += ANOFFSET (objfile->section_offsets, SECT_OFF_DATA (objfile)); +#ifdef STATIC_TRANSFORM_NAME + namestring = STATIC_TRANSFORM_NAME (namestring); +#endif + add_psymbol_to_list (namestring, p - namestring, + VAR_DOMAIN, LOC_STATIC, + &objfile->static_psymbols, + 0, symbol.n_value, + psymtab_language, objfile); + continue; + + case 'G': + symbol.n_value += ANOFFSET (objfile->section_offsets, SECT_OFF_DATA (objfile)); + /* The addresses in these entries are reported to be + wrong. See the code that reads 'G's for symtabs. */ + add_psymbol_to_list (namestring, p - namestring, + VAR_DOMAIN, LOC_STATIC, + &objfile->global_psymbols, + 0, symbol.n_value, + psymtab_language, objfile); + continue; + + case 'T': + /* When a 'T' entry is defining an anonymous enum, it + may have a name which is the empty string, or a + single space. Since they're not really defining a + symbol, those shouldn't go in the partial symbol + table. We do pick up the elements of such enums at + 'check_enum:', below. */ + if (p >= namestring + 2 + || (p == namestring + 1 + && namestring[0] != ' ')) + { + add_psymbol_to_list (namestring, p - namestring, + STRUCT_DOMAIN, LOC_TYPEDEF, + &objfile->static_psymbols, + symbol.n_value, 0, + psymtab_language, objfile); + if (p[2] == 't') + { + /* Also a typedef with the same name. */ + add_psymbol_to_list (namestring, p - namestring, + VAR_DOMAIN, LOC_TYPEDEF, + &objfile->static_psymbols, + symbol.n_value, 0, + psymtab_language, objfile); + p += 1; + } + } + goto check_enum; + + case 't': + if (p != namestring) /* a name is there, not just :T... */ + { + add_psymbol_to_list (namestring, p - namestring, + VAR_DOMAIN, LOC_TYPEDEF, + &objfile->static_psymbols, + symbol.n_value, 0, + psymtab_language, objfile); + } + check_enum: + /* If this is an enumerated type, we need to + add all the enum constants to the partial symbol + table. This does not cover enums without names, e.g. + "enum {a, b} c;" in C, but fortunately those are + rare. There is no way for GDB to find those from the + enum type without spending too much time on it. Thus + to solve this problem, the compiler needs to put out the + enum in a nameless type. GCC2 does this. */ + + /* We are looking for something of the form + <name> ":" ("t" | "T") [<number> "="] "e" + {<constant> ":" <value> ","} ";". */ + + /* Skip over the colon and the 't' or 'T'. */ + p += 2; + /* This type may be given a number. Also, numbers can come + in pairs like (0,26). Skip over it. */ + while ((*p >= '0' && *p <= '9') + || *p == '(' || *p == ',' || *p == ')' + || *p == '=') + p++; + + if (*p++ == 'e') + { + /* The aix4 compiler emits extra crud before the members. */ + if (*p == '-') + { + /* Skip over the type (?). */ + while (*p != ':') + p++; + + /* Skip over the colon. */ + p++; + } + + /* We have found an enumerated type. */ + /* According to comments in read_enum_type + a comma could end it instead of a semicolon. + I don't know where that happens. + Accept either. */ + while (*p && *p != ';' && *p != ',') + { + char *q; + + /* Check for and handle cretinous dbx symbol name + continuation! */ + if (*p == '\\' || (*p == '?' && p[1] == '\0')) + p = next_symbol_text (objfile); + + /* Point to the character after the name + of the enum constant. */ + for (q = p; *q && *q != ':'; q++) + ; + /* Note that the value doesn't matter for + enum constants in psymtabs, just in symtabs. */ + add_psymbol_to_list (p, q - p, + VAR_DOMAIN, LOC_CONST, + &objfile->static_psymbols, 0, + 0, psymtab_language, objfile); + /* Point past the name. */ + p = q; + /* Skip over the value. */ + while (*p && *p != ',') + p++; + /* Advance past the comma. */ + if (*p) + p++; + } + } + continue; + + case 'c': + /* Constant, e.g. from "const" in Pascal. */ + add_psymbol_to_list (namestring, p - namestring, + VAR_DOMAIN, LOC_CONST, + &objfile->static_psymbols, symbol.n_value, + 0, psymtab_language, objfile); + continue; + + case 'f': + if (! pst) + { + int name_len = p - namestring; + char *name = xmalloc (name_len + 1); + memcpy (name, namestring, name_len); + name[name_len] = '\0'; + function_outside_compilation_unit_complaint (name); + xfree (name); + } + symbol.n_value += ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile)); + add_psymbol_to_list (namestring, p - namestring, + VAR_DOMAIN, LOC_BLOCK, + &objfile->static_psymbols, + 0, symbol.n_value, + psymtab_language, objfile); + continue; + + /* Global functions were ignored here, but now they + are put into the global psymtab like one would expect. + They're also in the minimal symbol table. */ + case 'F': + if (! pst) + { + int name_len = p - namestring; + char *name = xmalloc (name_len + 1); + memcpy (name, namestring, name_len); + name[name_len] = '\0'; + function_outside_compilation_unit_complaint (name); + xfree (name); + } + symbol.n_value += ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile)); + add_psymbol_to_list (namestring, p - namestring, + VAR_DOMAIN, LOC_BLOCK, + &objfile->global_psymbols, + 0, symbol.n_value, + psymtab_language, objfile); + continue; + + /* Two things show up here (hopefully); static symbols of + local scope (static used inside braces) or extensions + of structure symbols. We can ignore both. */ + case 'V': + case '(': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + case '#': /* for symbol identification (used in live ranges) */ + continue; + + case ':': + /* It is a C++ nested symbol. We don't need to record it + (I don't think); if we try to look up foo::bar::baz, + then symbols for the symtab containing foo should get + read in, I think. */ + /* Someone says sun cc puts out symbols like + /foo/baz/maclib::/usr/local/bin/maclib, + which would get here with a symbol type of ':'. */ + continue; + + default: + /* Unexpected symbol descriptor. The second and subsequent stabs + of a continued stab can show up here. The question is + whether they ever can mimic a normal stab--it would be + nice if not, since we certainly don't want to spend the + time searching to the end of every string looking for + a backslash. */ + + complaint (&symfile_complaints, + "unknown symbol descriptor `%c'", p[1]); + + /* Ignore it; perhaps it is an extension that we don't + know about. */ + continue; + } + } + } + } + + if (pst) + { + xcoff_end_psymtab (pst, psymtab_include_list, includes_used, + ssymnum, dependency_list, + dependencies_used, textlow_not_set); + } + + /* Record the toc offset value of this symbol table into objfile structure. + If no XMC_TC0 is found, toc_offset should be zero. Another place to obtain + this information would be file auxiliary header. */ + + ((struct coff_symfile_info *) objfile->sym_private)->toc_offset = toc_offset; +} + +/* Return the toc offset value for a given objfile. */ + +CORE_ADDR +get_toc_offset (struct objfile *objfile) +{ + if (objfile) + return ((struct coff_symfile_info *) objfile->sym_private)->toc_offset; + return 0; +} + +/* Scan and build partial symbols for a symbol file. + We have been initialized by a call to dbx_symfile_init, which + put all the relevant info into a "struct dbx_symfile_info", + hung off the objfile structure. + + SECTION_OFFSETS contains offsets relative to which the symbols in the + various sections are (depending where the sections were actually loaded). + MAINLINE is true if we are reading the main symbol + table (as opposed to a shared lib or dynamically loaded file). */ + +static void +xcoff_initial_scan (struct objfile *objfile, int mainline) +{ + bfd *abfd; + int val; + struct cleanup *back_to; + int num_symbols; /* # of symbols */ + file_ptr symtab_offset; /* symbol table and */ + file_ptr stringtab_offset; /* string table file offsets */ + struct coff_symfile_info *info; + char *name; + unsigned int size; + + info = (struct coff_symfile_info *) objfile->sym_private; + symfile_bfd = abfd = objfile->obfd; + name = objfile->name; + + num_symbols = bfd_get_symcount (abfd); /* # of symbols */ + symtab_offset = obj_sym_filepos (abfd); /* symbol table file offset */ + stringtab_offset = symtab_offset + + num_symbols * coff_data (abfd)->local_symesz; + + info->min_lineno_offset = 0; + info->max_lineno_offset = 0; + bfd_map_over_sections (abfd, find_linenos, info); + + if (num_symbols > 0) + { + /* Read the string table. */ + init_stringtab (abfd, stringtab_offset, objfile); + + /* Read the .debug section, if present. */ + { + struct bfd_section *secp; + bfd_size_type length; + char *debugsec = NULL; + + secp = bfd_get_section_by_name (abfd, ".debug"); + if (secp) + { + length = bfd_section_size (abfd, secp); + if (length) + { + debugsec = + (char *) obstack_alloc (&objfile->objfile_obstack, length); + + if (!bfd_get_section_contents (abfd, secp, debugsec, + (file_ptr) 0, length)) + { + error ("Error reading .debug section of `%s': %s", + name, bfd_errmsg (bfd_get_error ())); + } + } + } + ((struct coff_symfile_info *) objfile->sym_private)->debugsec = + debugsec; + } + } + + /* Read the symbols. We keep them in core because we will want to + access them randomly in read_symbol*. */ + val = bfd_seek (abfd, symtab_offset, SEEK_SET); + if (val < 0) + error ("Error reading symbols from %s: %s", + name, bfd_errmsg (bfd_get_error ())); + size = coff_data (abfd)->local_symesz * num_symbols; + ((struct coff_symfile_info *) objfile->sym_private)->symtbl = + obstack_alloc (&objfile->objfile_obstack, size); + ((struct coff_symfile_info *) objfile->sym_private)->symtbl_num_syms = + num_symbols; + + val = bfd_bread (((struct coff_symfile_info *) objfile->sym_private)->symtbl, + size, abfd); + if (val != size) + perror_with_name ("reading symbol table"); + + /* If we are reinitializing, or if we have never loaded syms yet, init */ + if (mainline + || (objfile->global_psymbols.size == 0 + && objfile->static_psymbols.size == 0)) + /* I'm not sure how how good num_symbols is; the rule of thumb in + init_psymbol_list was developed for a.out. On the one hand, + num_symbols includes auxents. On the other hand, it doesn't + include N_SLINE. */ + init_psymbol_list (objfile, num_symbols); + + free_pending_blocks (); + back_to = make_cleanup (really_free_pendings, 0); + + init_minimal_symbol_collection (); + make_cleanup_discard_minimal_symbols (); + + /* Now that the symbol table data of the executable file are all in core, + process them and define symbols accordingly. */ + + scan_xcoff_symtab (objfile); + + /* Install any minimal symbols that have been collected as the current + minimal symbols for this objfile. */ + + install_minimal_symbols (objfile); + + do_cleanups (back_to); +} + +static void +xcoff_symfile_offsets (struct objfile *objfile, struct section_addr_info *addrs) +{ + asection *sect = NULL; + int i; + + objfile->num_sections = bfd_count_sections (objfile->obfd); + objfile->section_offsets = (struct section_offsets *) + obstack_alloc (&objfile->objfile_obstack, + SIZEOF_N_SECTION_OFFSETS (objfile->num_sections)); + + /* Initialize the section indexes for future use. */ + sect = bfd_get_section_by_name (objfile->obfd, ".text"); + if (sect) + objfile->sect_index_text = sect->index; + + sect = bfd_get_section_by_name (objfile->obfd, ".data"); + if (sect) + objfile->sect_index_data = sect->index; + + sect = bfd_get_section_by_name (objfile->obfd, ".bss"); + if (sect) + objfile->sect_index_bss = sect->index; + + sect = bfd_get_section_by_name (objfile->obfd, ".rodata"); + if (sect) + objfile->sect_index_rodata = sect->index; + + for (i = 0; i < objfile->num_sections; ++i) + { + /* syms_from_objfile kindly subtracts from addr the + bfd_section_vma of the .text section. This strikes me as + wrong--whether the offset to be applied to symbol reading is + relative to the start address of the section depends on the + symbol format. In any event, this whole "addr" concept is + pretty broken (it doesn't handle any section but .text + sensibly), so just ignore the addr parameter and use 0. + rs6000-nat.c will set the correct section offsets via + objfile_relocate. */ + (objfile->section_offsets)->offsets[i] = 0; + } +} + +/* Register our ability to parse symbols for xcoff BFD files. */ + +static struct sym_fns xcoff_sym_fns = +{ + + /* It is possible that coff and xcoff should be merged as + they do have fundamental similarities (for example, the extra storage + classes used for stabs could presumably be recognized in any COFF file). + However, in addition to obvious things like all the csect hair, there are + some subtler differences between xcoffread.c and coffread.c, notably + the fact that coffread.c has no need to read in all the symbols, but + xcoffread.c reads all the symbols and does in fact randomly access them + (in C_BSTAT and line number processing). */ + + bfd_target_xcoff_flavour, + + xcoff_new_init, /* sym_new_init: init anything gbl to entire symtab */ + xcoff_symfile_init, /* sym_init: read initial info, setup for sym_read() */ + xcoff_initial_scan, /* sym_read: read a symbol file into symtab */ + xcoff_symfile_finish, /* sym_finish: finished with file, cleanup */ + xcoff_symfile_offsets, /* sym_offsets: xlate offsets ext->int form */ + NULL /* next: pointer to next struct sym_fns */ +}; + +void +_initialize_xcoffread (void) +{ + add_symtab_fns (&xcoff_sym_fns); + + func_symbol_type = init_type (TYPE_CODE_FUNC, 1, 0, + "<function, no debug info>", NULL); + TYPE_TARGET_TYPE (func_symbol_type) = builtin_type_int; + var_symbol_type = + init_type (TYPE_CODE_INT, TARGET_INT_BIT / HOST_CHAR_BIT, 0, + "<variable, no debug info>", NULL); +} diff --git a/contrib/gdb/gdb/xcoffsolib.c b/contrib/gdb/gdb/xcoffsolib.c new file mode 100644 index 0000000..99d2cc8 --- /dev/null +++ b/contrib/gdb/gdb/xcoffsolib.c @@ -0,0 +1,196 @@ +/* Shared library support for RS/6000 (xcoff) object files, for GDB. + Copyright 1991, 1992, 1995, 1996, 1999, 2000, 2001 + Free Software Foundation, Inc. + Contributed by IBM Corporation. + + 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 "defs.h" +#include "bfd.h" +#include "xcoffsolib.h" +#include "inferior.h" +#include "gdbcmd.h" +#include "symfile.h" +#include "frame.h" +#include "gdb_regex.h" + + +/* If ADDR lies in a shared library, return its name. + Note that returned name points to static data whose content is overwritten + by each call. */ + +char * +xcoff_solib_address (CORE_ADDR addr) +{ + static char *buffer = NULL; + struct vmap *vp = vmap; + + /* The first vmap entry is for the exec file. */ + + if (vp == NULL) + return NULL; + for (vp = vp->nxt; vp; vp = vp->nxt) + if (vp->tstart <= addr && addr < vp->tend) + { + xfree (buffer); + xasprintf (&buffer, "%s%s%s%s", + vp->name, + *vp->member ? "(" : "", + vp->member, + *vp->member ? ")" : ""); + return buffer; + } + return NULL; +} + +static void solib_info (char *, int); +static void sharedlibrary_command (char *pattern, int from_tty); + +static void +solib_info (char *args, int from_tty) +{ + struct vmap *vp = vmap; + + /* Check for new shared libraries loaded with load (). */ + if (! ptid_equal (inferior_ptid, null_ptid)) + xcoff_relocate_symtab (PIDGET (inferior_ptid)); + + if (vp == NULL || vp->nxt == NULL) + { + printf_unfiltered ("No shared libraries loaded at this time.\n"); + return; + } + + /* Skip over the first vmap, it is the main program, always loaded. */ + vp = vp->nxt; + + printf_unfiltered ("\ +Text Range Data Range Syms Shared Object Library\n"); + + for (; vp != NULL; vp = vp->nxt) + { + printf_unfiltered ("0x%s-0x%s 0x%s-0x%s %s %s%s%s%s\n", + paddr (vp->tstart),paddr (vp->tend), + paddr (vp->dstart), paddr (vp->dend), + vp->loaded ? "Yes" : "No ", + vp->name, + *vp->member ? "(" : "", + vp->member, + *vp->member ? ")" : ""); + } +} + +static void +sharedlibrary_command (char *pattern, int from_tty) +{ + dont_repeat (); + + /* Check for new shared libraries loaded with load (). */ + if (! ptid_equal (inferior_ptid, null_ptid)) + xcoff_relocate_symtab (PIDGET (inferior_ptid)); + + if (pattern) + { + char *re_err = re_comp (pattern); + + if (re_err) + error ("Invalid regexp: %s", re_err); + } + + /* Walk the list of currently loaded shared libraries, and read + symbols for any that match the pattern --- or any whose symbols + aren't already loaded, if no pattern was given. */ + { + int any_matches = 0; + int loaded_any_symbols = 0; + struct vmap *vp = vmap; + + if (!vp) + return; + + /* skip over the first vmap, it is the main program, always loaded. */ + for (vp = vp->nxt; vp; vp = vp->nxt) + if (! pattern + || re_exec (vp->name) + || (*vp->member && re_exec (vp->member))) + { + any_matches = 1; + + if (vp->loaded) + { + if (from_tty) + printf_unfiltered ("Symbols already loaded for %s\n", + vp->name); + } + else + { + if (vmap_add_symbols (vp)) + loaded_any_symbols = 1; + } + } + + if (from_tty && pattern && ! any_matches) + printf_unfiltered + ("No loaded shared libraries match the pattern `%s'.\n", pattern); + + if (loaded_any_symbols) + { + /* Getting new symbols may change our opinion about what is + frameless. */ + reinit_frame_cache (); + } + } +} + +/* LOCAL FUNCTION + + no_shared_libraries -- handle command to explicitly discard symbols + from shared libraries. + + DESCRIPTION + + Implements the command "nosharedlibrary", which discards symbols + that have been auto-loaded from shared libraries. Symbols from + shared libraries that were added by explicit request of the user + are not discarded. Also called from remote.c. */ + +void +no_shared_libraries (char *ignored, int from_tty) +{ + /* FIXME */ +} + +void +_initialize_xcoffsolib (void) +{ + add_com ("sharedlibrary", class_files, sharedlibrary_command, + "Load shared object library symbols for files matching REGEXP."); + add_info ("sharedlibrary", solib_info, + "Status of loaded shared object libraries"); + + add_show_from_set + (add_set_cmd ("auto-solib-add", class_support, var_boolean, + (char *) &auto_solib_add, + "Set autoloading of shared library symbols.\n\ +If \"on\", symbols from all shared object libraries will be loaded\n\ +automatically when the inferior begins execution, when the dynamic linker\n\ +informs gdb that a new library has been loaded, or when attaching to the\n\ +inferior. Otherwise, symbols must be loaded manually, using `sharedlibrary'.", + &setlist), + &showlist); +} diff --git a/contrib/gdb/gdb/xmodem.c b/contrib/gdb/gdb/xmodem.c new file mode 100644 index 0000000..7b8d77d --- /dev/null +++ b/contrib/gdb/gdb/xmodem.c @@ -0,0 +1,275 @@ +/* XMODEM support for GDB, the GNU debugger. + Copyright 1995, 2000, 2001 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 "defs.h" +#include "serial.h" +#include "target.h" +#include "xmodem.h" + +/* These definitions are for xmodem protocol. */ + +#define SOH 0x01 +#define STX 0x02 +#define ACK 0x06 +#define NAK 0x15 +#define EOT 0x04 +#define CANCEL 0x18 + +static int blknum; /* XMODEM block number */ +static int crcflag; /* Sez we are using CRC's instead of cksums */ + +static int +readchar (struct serial *desc, int timeout) +{ + int c; + + c = serial_readchar (desc, timeout); + + if (remote_debug > 0) + fputc_unfiltered (c, gdb_stdlog); + + if (c >= 0) + return c; + + if (c == SERIAL_TIMEOUT) + error ("Timeout reading from remote system."); + + perror_with_name ("xmodem.c:readchar()"); +} + +#define CRC16 0x1021 /* Generator polynomial (X^16 + X^12 + X^5 + 1) */ + +static unsigned short *crctab; + +/* Call this to init the fast CRC-16 calculation table. */ + +static void +crcinit (void) +{ + static int crctab_inited = 0; + int val; + + if (crctab_inited == 1) + return; + + crctab = xmalloc (256 * sizeof (short)); + + for (val = 0; val <= 255; val++) + { + int i; + unsigned int crc; + + crc = val << 8; + + for (i = 0; i < 8; ++i) + { + crc <<= 1; + + if (crc & 0x10000) + crc ^= CRC16; + } + + crctab[val] = crc; + } + + crctab_inited = 1; +} + +/* Calculate a CRC-16 for the LEN byte message pointed at by P. */ + +static unsigned short +docrc (unsigned char *p, int len) +{ + unsigned short crc = 0; + + while (len-- > 0) + crc = (crc << 8) ^ crctab[(crc >> 8) ^ *p++]; + + return crc; +} + +/* Start up the transmit process. Reset state variables. Wait for receiver to + send NAK or CRC request. */ + +int +xmodem_init_xfer (struct serial *desc) +{ + int c; + int i; + + blknum = 1; + crcflag = 0; + crcinit (); + + for (i = 1; i <= 10; i++) + { + c = readchar (desc, 6); + + switch (c) + { + case 'C': + crcflag = 1; + case NAK: + return 0; + default: + fprintf_unfiltered (gdb_stderr, "xmodem_init_xfer: Got unexpected character %c (0%o)\n", c, c); + continue; + case CANCEL: /* target aborted load */ + fprintf_unfiltered (gdb_stderr, "Got a CANCEL from the target.\n"); + continue; + } + } + error ("xmodem_init_xfer: Too many unexpected characters."); +} + +/* Take 128 bytes of data and make a packet out of it. + + * Each packet looks like this: + * +-----+-------+-------+------+-----+ + * | SOH | Seq1. | Seq2. | data | SUM | + * +-----+-------+-------+------+-----+ + * SOH = 0x01 + * Seq1 = The sequence number. + * Seq2 = The complement of the sequence number. + * Data = A 128 bytes of data. + * SUM = Add the contents of the 128 bytes and use the low-order + * 8 bits of the result. + * + * send_xmodem_packet fills in the XMODEM fields of PACKET and sends it to the + * remote system. PACKET must be XMODEM_PACKETSIZE bytes long. The data must + * start 3 bytes after the beginning of the packet to leave room for the + * XMODEM header. LEN is the length of the data portion of the packet (and + * must be <= 128 bytes). If it is < 128 bytes, ^Z padding will be added. + */ + +void +xmodem_send_packet (struct serial *desc, unsigned char *packet, int len, int hashmark) +{ + int i; + int retries; + int pktlen; + int datasize; + + /* build the packet header */ + + packet[1] = blknum; + packet[2] = ~blknum; + + blknum++; + + if (len <= XMODEM_DATASIZE) + { + packet[0] = SOH; + datasize = XMODEM_DATASIZE; + } + else if (len <= XMODEM_1KDATASIZE) + { + packet[0] = STX; + datasize = XMODEM_1KDATASIZE; + } + else + internal_error (__FILE__, __LINE__, "failed internal consistency check"); /* Packet way too large */ + + /* Add ^Z padding if packet < 128 (or 1024) bytes */ + + memset (packet + 3 + len, '\026', datasize - len); + + if (crcflag) + { + int crc; + + crc = docrc (packet + 3, datasize); + + packet[3 + datasize] = crc >> 8; + packet[3 + datasize + 1] = crc; + pktlen = datasize + 5; + } + else + { + int sum; + + sum = 0; + for (i = 3; i < datasize + 3; i++) + sum += packet[i]; + + packet[3 + datasize] = sum; /* add the checksum */ + pktlen = datasize + 4; + } + + for (retries = 3; retries >= 0; retries--) + { + int c; + + serial_write (desc, packet, pktlen); + + c = readchar (desc, 3); + switch (c) + { + case ACK: + return; + case NAK: + if (!hashmark) + continue; + putchar_unfiltered ('-'); + gdb_flush (gdb_stdout); + continue; + case CANCEL: + error ("xmodem_send_packet: Transfer aborted by receiver."); + default: + fprintf_unfiltered (gdb_stderr, "xmodem_send_packet: Got unexpected character %c (0%o)\n", c, c); + continue; + } + } + + serial_write (desc, "\004", 1); /* Send an EOT */ + + error ("xmodem_send_packet: Excessive retries."); +} + +/* Finish off the transfer. Send out the EOT, and wait for an ACK. */ + +void +xmodem_finish_xfer (struct serial *desc) +{ + int retries; + + for (retries = 10; retries >= 0; retries--) + { + int c; + + serial_write (desc, "\004", 1); /* Send an EOT */ + + c = readchar (desc, 3); + switch (c) + { + case ACK: + return; + case NAK: + continue; + case CANCEL: + error ("xmodem_finish_xfer: Transfer aborted by receiver."); + default: + fprintf_unfiltered (gdb_stderr, "xmodem_send_packet: Got unexpected character %c (0%o)\n", c, c); + continue; + } + } + + error ("xmodem_finish_xfer: Excessive retries."); +} diff --git a/contrib/gdb/gdb/xmodem.h b/contrib/gdb/gdb/xmodem.h new file mode 100644 index 0000000..83aa24f --- /dev/null +++ b/contrib/gdb/gdb/xmodem.h @@ -0,0 +1,32 @@ +/* XMODEM support for GDB, the GNU debugger. + Copyright 1995, 2000 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. */ + +struct serial; + +int xmodem_init_xfer (struct serial *desc); +void send_xmodem_packet (struct serial *desc, unsigned char *packet, int len, + int hashmark); +void xmodem_finish_xfer (struct serial *desc); + +#define XMODEM_DATASIZE 128 /* The data size is ALWAYS 128 */ +#define XMODEM_1KDATASIZE 1024 /* Unless it's 1024!!! */ +#define XMODEM_PACKETSIZE 133 /* data + packet headers and crc */ +#define XMODEM_1KPACKETSIZE 1024 + 5 /* data + packet headers and crc */ +#define XMODEM_DATAOFFSET 3 /* Offset to start of actual data */ diff --git a/contrib/gdb/include/COPYING b/contrib/gdb/include/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/contrib/gdb/include/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/contrib/gdb/include/ansidecl.h b/contrib/gdb/include/ansidecl.h new file mode 100644 index 0000000..d2c8776 --- /dev/null +++ b/contrib/gdb/include/ansidecl.h @@ -0,0 +1,315 @@ +/* ANSI and traditional C compatability macros + Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + +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. */ + +/* ANSI and traditional C compatibility macros + + ANSI C is assumed if __STDC__ is #defined. + + Macro ANSI C definition Traditional C definition + ----- ---- - ---------- ----------- - ---------- + ANSI_PROTOTYPES 1 not defined + PTR `void *' `char *' + PTRCONST `void *const' `char *' + LONG_DOUBLE `long double' `double' + const not defined `' + volatile not defined `' + signed not defined `' + VA_START(ap, var) va_start(ap, var) va_start(ap) + + Note that it is safe to write "void foo();" indicating a function + with no return value, in all K+R compilers we have been able to test. + + For declaring functions with prototypes, we also provide these: + + PARAMS ((prototype)) + -- for functions which take a fixed number of arguments. Use this + when declaring the function. When defining the function, write a + K+R style argument list. For example: + + char *strcpy PARAMS ((char *dest, char *source)); + ... + char * + strcpy (dest, source) + char *dest; + char *source; + { ... } + + + VPARAMS ((prototype, ...)) + -- for functions which take a variable number of arguments. Use + PARAMS to declare the function, VPARAMS to define it. For example: + + int printf PARAMS ((const char *format, ...)); + ... + int + printf VPARAMS ((const char *format, ...)) + { + ... + } + + For writing functions which take variable numbers of arguments, we + also provide the VA_OPEN, VA_CLOSE, and VA_FIXEDARG macros. These + hide the differences between K+R <varargs.h> and C89 <stdarg.h> more + thoroughly than the simple VA_START() macro mentioned above. + + VA_OPEN and VA_CLOSE are used *instead of* va_start and va_end. + Immediately after VA_OPEN, put a sequence of VA_FIXEDARG calls + corresponding to the list of fixed arguments. Then use va_arg + normally to get the variable arguments, or pass your va_list object + around. You do not declare the va_list yourself; VA_OPEN does it + for you. + + Here is a complete example: + + int + printf VPARAMS ((const char *format, ...)) + { + int result; + + VA_OPEN (ap, format); + VA_FIXEDARG (ap, const char *, format); + + result = vfprintf (stdout, format, ap); + VA_CLOSE (ap); + + return result; + } + + + You can declare variables either before or after the VA_OPEN, + VA_FIXEDARG sequence. Also, VA_OPEN and VA_CLOSE are the beginning + and end of a block. They must appear at the same nesting level, + and any variables declared after VA_OPEN go out of scope at + VA_CLOSE. Unfortunately, with a K+R compiler, that includes the + argument list. You can have multiple instances of VA_OPEN/VA_CLOSE + pairs in a single function in case you need to traverse the + argument list more than once. + + For ease of writing code which uses GCC extensions but needs to be + portable to other compilers, we provide the GCC_VERSION macro that + simplifies testing __GNUC__ and __GNUC_MINOR__ together, and various + wrappers around __attribute__. Also, __extension__ will be #defined + to nothing if it doesn't work. See below. + + This header also defines a lot of obsolete macros: + CONST, VOLATILE, SIGNED, PROTO, EXFUN, DEFUN, DEFUN_VOID, + AND, DOTS, NOARGS. Don't use them. */ + +#ifndef _ANSIDECL_H +#define _ANSIDECL_H 1 + +/* Every source file includes this file, + so they will all get the switch for lint. */ +/* LINTLIBRARY */ + +/* Using MACRO(x,y) in cpp #if conditionals does not work with some + older preprocessors. Thus we can't define something like this: + +#define HAVE_GCC_VERSION(MAJOR, MINOR) \ + (__GNUC__ > (MAJOR) || (__GNUC__ == (MAJOR) && __GNUC_MINOR__ >= (MINOR))) + +and then test "#if HAVE_GCC_VERSION(2,7)". + +So instead we use the macro below and test it against specific values. */ + +/* This macro simplifies testing whether we are using gcc, and if it + is of a particular minimum version. (Both major & minor numbers are + significant.) This macro will evaluate to 0 if we are not using + gcc at all. */ +#ifndef GCC_VERSION +#define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) +#endif /* GCC_VERSION */ + +#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(_WIN32) || (defined(__alpha) && defined(__cplusplus)) +/* All known AIX compilers implement these things (but don't always + define __STDC__). The RISC/OS MIPS compiler defines these things + in SVR4 mode, but does not define __STDC__. */ +/* eraxxon@alumni.rice.edu: The Compaq C++ compiler, unlike many other + C++ compilers, does not define __STDC__, though it acts as if this + was so. (Verified versions: 5.7, 6.2, 6.3, 6.5) */ + +#define ANSI_PROTOTYPES 1 +#define PTR void * +#define PTRCONST void *const +#define LONG_DOUBLE long double + +#define PARAMS(ARGS) ARGS +#define VPARAMS(ARGS) ARGS +#define VA_START(VA_LIST, VAR) va_start(VA_LIST, VAR) + +/* variadic function helper macros */ +/* "struct Qdmy" swallows the semicolon after VA_OPEN/VA_FIXEDARG's + use without inhibiting further decls and without declaring an + actual variable. */ +#define VA_OPEN(AP, VAR) { va_list AP; va_start(AP, VAR); { struct Qdmy +#define VA_CLOSE(AP) } va_end(AP); } +#define VA_FIXEDARG(AP, T, N) struct Qdmy + +#undef const +#undef volatile +#undef signed + +/* inline requires special treatment; it's in C99, and GCC >=2.7 supports + it too, but it's not in C89. */ +#undef inline +#if __STDC_VERSION__ > 199901L +/* it's a keyword */ +#else +# if GCC_VERSION >= 2007 +# define inline __inline__ /* __inline__ prevents -pedantic warnings */ +# else +# define inline /* nothing */ +# endif +#endif + +/* These are obsolete. Do not use. */ +#ifndef IN_GCC +#define CONST const +#define VOLATILE volatile +#define SIGNED signed + +#define PROTO(type, name, arglist) type name arglist +#define EXFUN(name, proto) name proto +#define DEFUN(name, arglist, args) name(args) +#define DEFUN_VOID(name) name(void) +#define AND , +#define DOTS , ... +#define NOARGS void +#endif /* ! IN_GCC */ + +#else /* Not ANSI C. */ + +#undef ANSI_PROTOTYPES +#define PTR char * +#define PTRCONST PTR +#define LONG_DOUBLE double + +#define PARAMS(args) () +#define VPARAMS(args) (va_alist) va_dcl +#define VA_START(va_list, var) va_start(va_list) + +#define VA_OPEN(AP, VAR) { va_list AP; va_start(AP); { struct Qdmy +#define VA_CLOSE(AP) } va_end(AP); } +#define VA_FIXEDARG(AP, TYPE, NAME) TYPE NAME = va_arg(AP, TYPE) + +/* some systems define these in header files for non-ansi mode */ +#undef const +#undef volatile +#undef signed +#undef inline +#define const +#define volatile +#define signed +#define inline + +#ifndef IN_GCC +#define CONST +#define VOLATILE +#define SIGNED + +#define PROTO(type, name, arglist) type name () +#define EXFUN(name, proto) name() +#define DEFUN(name, arglist, args) name arglist args; +#define DEFUN_VOID(name) name() +#define AND ; +#define DOTS +#define NOARGS +#endif /* ! IN_GCC */ + +#endif /* ANSI C. */ + +/* Define macros for some gcc attributes. This permits us to use the + macros freely, and know that they will come into play for the + version of gcc in which they are supported. */ + +#if (GCC_VERSION < 2007) +# define __attribute__(x) +#endif + +/* Attribute __malloc__ on functions was valid as of gcc 2.96. */ +#ifndef ATTRIBUTE_MALLOC +# if (GCC_VERSION >= 2096) +# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__)) +# else +# define ATTRIBUTE_MALLOC +# endif /* GNUC >= 2.96 */ +#endif /* ATTRIBUTE_MALLOC */ + +/* Attributes on labels were valid as of gcc 2.93. */ +#ifndef ATTRIBUTE_UNUSED_LABEL +# if (GCC_VERSION >= 2093) +# define ATTRIBUTE_UNUSED_LABEL ATTRIBUTE_UNUSED +# else +# define ATTRIBUTE_UNUSED_LABEL +# endif /* GNUC >= 2.93 */ +#endif /* ATTRIBUTE_UNUSED_LABEL */ + +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#endif /* ATTRIBUTE_UNUSED */ + +#ifndef ATTRIBUTE_NORETURN +#define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) +#endif /* ATTRIBUTE_NORETURN */ + +/* Attribute `nonnull' was valid as of gcc 3.3. */ +#ifndef ATTRIBUTE_NONNULL +# if (GCC_VERSION >= 3003) +# define ATTRIBUTE_NONNULL(m) __attribute__ ((__nonnull__ (m))) +# else +# define ATTRIBUTE_NONNULL(m) +# endif /* GNUC >= 3.3 */ +#endif /* ATTRIBUTE_NONNULL */ + +/* Use ATTRIBUTE_PRINTF when the format specifier must not be NULL. + This was the case for the `printf' format attribute by itself + before GCC 3.3, but as of 3.3 we need to add the `nonnull' + attribute to retain this behavior. */ +#ifndef ATTRIBUTE_PRINTF +#define ATTRIBUTE_PRINTF(m, n) __attribute__ ((__format__ (__printf__, m, n))) ATTRIBUTE_NONNULL(m) +#define ATTRIBUTE_PRINTF_1 ATTRIBUTE_PRINTF(1, 2) +#define ATTRIBUTE_PRINTF_2 ATTRIBUTE_PRINTF(2, 3) +#define ATTRIBUTE_PRINTF_3 ATTRIBUTE_PRINTF(3, 4) +#define ATTRIBUTE_PRINTF_4 ATTRIBUTE_PRINTF(4, 5) +#define ATTRIBUTE_PRINTF_5 ATTRIBUTE_PRINTF(5, 6) +#endif /* ATTRIBUTE_PRINTF */ + +/* Use ATTRIBUTE_NULL_PRINTF when the format specifier may be NULL. A + NULL format specifier was allowed as of gcc 3.3. */ +#ifndef ATTRIBUTE_NULL_PRINTF +# if (GCC_VERSION >= 3003) +# define ATTRIBUTE_NULL_PRINTF(m, n) __attribute__ ((__format__ (__printf__, m, n))) +# else +# define ATTRIBUTE_NULL_PRINTF(m, n) +# endif /* GNUC >= 3.3 */ +# define ATTRIBUTE_NULL_PRINTF_1 ATTRIBUTE_NULL_PRINTF(1, 2) +# define ATTRIBUTE_NULL_PRINTF_2 ATTRIBUTE_NULL_PRINTF(2, 3) +# define ATTRIBUTE_NULL_PRINTF_3 ATTRIBUTE_NULL_PRINTF(3, 4) +# define ATTRIBUTE_NULL_PRINTF_4 ATTRIBUTE_NULL_PRINTF(4, 5) +# define ATTRIBUTE_NULL_PRINTF_5 ATTRIBUTE_NULL_PRINTF(5, 6) +#endif /* ATTRIBUTE_NULL_PRINTF */ + +/* We use __extension__ in some places to suppress -pedantic warnings + about GCC extensions. This feature didn't work properly before + gcc 2.8. */ +#if GCC_VERSION < 2008 +#define __extension__ +#endif + +#endif /* ansidecl.h */ diff --git a/contrib/gdb/include/bfdlink.h b/contrib/gdb/include/bfdlink.h new file mode 100644 index 0000000..a989f64 --- /dev/null +++ b/contrib/gdb/include/bfdlink.h @@ -0,0 +1,686 @@ +/* bfdlink.h -- header file for BFD link routines + Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2002, 2003 + Free Software Foundation, Inc. + Written by Steve Chamberlain and Ian Lance Taylor, Cygnus Support. + + This file is part of BFD, the Binary File Descriptor library. + + 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. */ + +#ifndef BFDLINK_H +#define BFDLINK_H + +/* Which symbols to strip during a link. */ +enum bfd_link_strip +{ + strip_none, /* Don't strip any symbols. */ + strip_debugger, /* Strip debugging symbols. */ + strip_some, /* keep_hash is the list of symbols to keep. */ + strip_all /* Strip all symbols. */ +}; + +/* Which local symbols to discard during a link. This is irrelevant + if strip_all is used. */ +enum bfd_link_discard +{ + discard_sec_merge, /* Discard local temporary symbols in SEC_MERGE + sections. */ + discard_none, /* Don't discard any locals. */ + discard_l, /* Discard local temporary symbols. */ + discard_all /* Discard all locals. */ +}; + +/* Describes the type of hash table entry structure being used. + Different hash table structure have different fields and so + support different linking features. */ +enum bfd_link_hash_table_type + { + bfd_link_generic_hash_table, + bfd_link_elf_hash_table + }; + +/* These are the possible types of an entry in the BFD link hash + table. */ + +enum bfd_link_hash_type +{ + bfd_link_hash_new, /* Symbol is new. */ + bfd_link_hash_undefined, /* Symbol seen before, but undefined. */ + bfd_link_hash_undefweak, /* Symbol is weak and undefined. */ + bfd_link_hash_defined, /* Symbol is defined. */ + bfd_link_hash_defweak, /* Symbol is weak and defined. */ + bfd_link_hash_common, /* Symbol is common. */ + bfd_link_hash_indirect, /* Symbol is an indirect link. */ + bfd_link_hash_warning /* Like indirect, but warn if referenced. */ +}; + +enum bfd_link_common_skip_ar_aymbols +{ + bfd_link_common_skip_none, + bfd_link_common_skip_text, + bfd_link_common_skip_data, + bfd_link_common_skip_all +}; + +/* The linking routines use a hash table which uses this structure for + its elements. */ + +struct bfd_link_hash_entry +{ + /* Base hash table entry structure. */ + struct bfd_hash_entry root; + + /* Type of this entry. */ + enum bfd_link_hash_type type; + + /* Undefined and common symbols are kept in a linked list through + this field. This field is not in the union because that would + force us to remove entries from the list when we changed their + type, which would force the list to be doubly linked, which would + waste more memory. When an undefined or common symbol is + created, it should be added to this list, the head of which is in + the link hash table itself. As symbols are defined, they need + not be removed from the list; anything which reads the list must + doublecheck the symbol type. + + Weak symbols are not kept on this list. + + Defined and defweak symbols use this field as a reference marker. + If the field is not NULL, or this structure is the tail of the + undefined symbol list, the symbol has been referenced. If the + symbol is undefined and becomes defined, this field will + automatically be non-NULL since the symbol will have been on the + undefined symbol list. */ + struct bfd_link_hash_entry *und_next; + + /* A union of information depending upon the type. */ + union + { + /* Nothing is kept for bfd_hash_new. */ + /* bfd_link_hash_undefined, bfd_link_hash_undefweak. */ + struct + { + bfd *abfd; /* BFD symbol was found in. */ + } undef; + /* bfd_link_hash_defined, bfd_link_hash_defweak. */ + struct + { + bfd_vma value; /* Symbol value. */ + asection *section; /* Symbol section. */ + } def; + /* bfd_link_hash_indirect, bfd_link_hash_warning. */ + struct + { + struct bfd_link_hash_entry *link; /* Real symbol. */ + const char *warning; /* Warning (bfd_link_hash_warning only). */ + } i; + /* bfd_link_hash_common. */ + struct + { + /* The linker needs to know three things about common + symbols: the size, the alignment, and the section in + which the symbol should be placed. We store the size + here, and we allocate a small structure to hold the + section and the alignment. The alignment is stored as a + power of two. We don't store all the information + directly because we don't want to increase the size of + the union; this structure is a major space user in the + linker. */ + bfd_size_type size; /* Common symbol size. */ + struct bfd_link_hash_common_entry + { + unsigned int alignment_power; /* Alignment. */ + asection *section; /* Symbol section. */ + } *p; + } c; + } u; +}; + +/* This is the link hash table. It is a derived class of + bfd_hash_table. */ + +struct bfd_link_hash_table +{ + /* The hash table itself. */ + struct bfd_hash_table table; + /* The back end which created this hash table. This indicates the + type of the entries in the hash table, which is sometimes + important information when linking object files of different + types together. */ + const bfd_target *creator; + /* A linked list of undefined and common symbols, linked through the + next field in the bfd_link_hash_entry structure. */ + struct bfd_link_hash_entry *undefs; + /* Entries are added to the tail of the undefs list. */ + struct bfd_link_hash_entry *undefs_tail; + /* The type of the link hash table. */ + enum bfd_link_hash_table_type type; +}; + +/* Look up an entry in a link hash table. If FOLLOW is TRUE, this + follows bfd_link_hash_indirect and bfd_link_hash_warning links to + the real symbol. */ +extern struct bfd_link_hash_entry *bfd_link_hash_lookup + (struct bfd_link_hash_table *, const char *, bfd_boolean create, + bfd_boolean copy, bfd_boolean follow); + +/* Look up an entry in the main linker hash table if the symbol might + be wrapped. This should only be used for references to an + undefined symbol, not for definitions of a symbol. */ + +extern struct bfd_link_hash_entry *bfd_wrapped_link_hash_lookup + (bfd *, struct bfd_link_info *, const char *, bfd_boolean, + bfd_boolean, bfd_boolean); + +/* Traverse a link hash table. */ +extern void bfd_link_hash_traverse + (struct bfd_link_hash_table *, + bfd_boolean (*) (struct bfd_link_hash_entry *, void *), + void *); + +/* Add an entry to the undefs list. */ +extern void bfd_link_add_undef + (struct bfd_link_hash_table *, struct bfd_link_hash_entry *); + +struct bfd_sym_chain +{ + struct bfd_sym_chain *next; + const char *name; +}; + +/* How to handle unresolved symbols. + There are four possibilities which are enumerated below: */ +enum report_method +{ + /* This is the initial value when then link_info structure is created. + It allows the various stages of the linker to determine whether they + allowed to set the value. */ + RM_NOT_YET_SET = 0, + RM_IGNORE, + RM_GENERATE_WARNING, + RM_GENERATE_ERROR +}; + +/* This structure holds all the information needed to communicate + between BFD and the linker when doing a link. */ + +struct bfd_link_info +{ + /* TRUE if BFD should generate a relocatable object file. */ + unsigned int relocatable: 1; + + /* TRUE if BFD should generate relocation information in the final + executable. */ + unsigned int emitrelocations: 1; + + /* TRUE if BFD should generate a "task linked" object file, + similar to relocatable but also with globals converted to + statics. */ + unsigned int task_link: 1; + + /* TRUE if BFD should generate a shared object. */ + unsigned int shared: 1; + + /* TRUE if BFD should pre-bind symbols in a shared object. */ + unsigned int symbolic: 1; + + /* TRUE if BFD should export all symbols in the dynamic symbol table + of an executable, rather than only those used. */ + unsigned int export_dynamic: 1; + + /* TRUE if shared objects should be linked directly, not shared. */ + unsigned int static_link: 1; + + /* TRUE if the output file should be in a traditional format. This + is equivalent to the setting of the BFD_TRADITIONAL_FORMAT flag + on the output file, but may be checked when reading the input + files. */ + unsigned int traditional_format: 1; + + /* TRUE if we want to produced optimized output files. This might + need much more time and therefore must be explicitly selected. */ + unsigned int optimize: 1; + + /* TRUE if ok to have multiple definition. */ + unsigned int allow_multiple_definition: 1; + + /* TRUE if ok to have version with no definition. */ + unsigned int allow_undefined_version: 1; + + /* TRUE if symbols should be retained in memory, FALSE if they + should be freed and reread. */ + unsigned int keep_memory: 1; + + /* TRUE if every symbol should be reported back via the notice + callback. */ + unsigned int notice_all: 1; + + /* TRUE if executable should not contain copy relocs. + Setting this true may result in a non-sharable text segment. */ + unsigned int nocopyreloc: 1; + + /* TRUE if the new ELF dynamic tags are enabled. */ + unsigned int new_dtags: 1; + + /* TRUE if non-PLT relocs should be merged into one reloc section + and sorted so that relocs against the same symbol come together. */ + unsigned int combreloc: 1; + + /* TRUE if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment + should be created. */ + unsigned int eh_frame_hdr: 1; + + /* TRUE if global symbols in discarded sections should be stripped. */ + unsigned int strip_discarded: 1; + + /* TRUE if the final relax pass is needed. */ + unsigned int need_relax_finalize: 1; + + /* TRUE if generating a position independent executable. */ + unsigned int pie: 1; + + /* TRUE if generating an executable, position independent or not. */ + unsigned int executable : 1; + + /* TRUE if PT_GNU_STACK segment should be created with PF_R|PF_W|PF_X + flags. */ + unsigned int execstack: 1; + + /* TRUE if PT_GNU_STACK segment should be created with PF_R|PF_W + flags. */ + unsigned int noexecstack: 1; + + /* What to do with unresolved symbols in an object file. + When producing static binaries the default is GENERATE_ERROR. + When producing dynamic binaries the default is IGNORE. The + assumption with dynamic binaries is that the reference will be + resolved at load/execution time. */ + enum report_method unresolved_syms_in_objects; + + /* What to do with unresolved symbols in a shared library. + The same defaults apply. */ + enum report_method unresolved_syms_in_shared_libs; + + /* Which symbols to strip. */ + enum bfd_link_strip strip; + + /* Which local symbols to discard. */ + enum bfd_link_discard discard; + + /* Criteria for skipping symbols when detemining + whether to include an object from an archive. */ + enum bfd_link_common_skip_ar_aymbols common_skip_ar_aymbols; + + /* Function callbacks. */ + const struct bfd_link_callbacks *callbacks; + + /* Hash table handled by BFD. */ + struct bfd_link_hash_table *hash; + + /* Hash table of symbols to keep. This is NULL unless strip is + strip_some. */ + struct bfd_hash_table *keep_hash; + + /* Hash table of symbols to report back via the notice callback. If + this is NULL, and notice_all is FALSE, then no symbols are + reported back. */ + struct bfd_hash_table *notice_hash; + + /* Hash table of symbols which are being wrapped (the --wrap linker + option). If this is NULL, no symbols are being wrapped. */ + struct bfd_hash_table *wrap_hash; + + /* The list of input BFD's involved in the link. These are chained + together via the link_next field. */ + bfd *input_bfds; + + /* If a symbol should be created for each input BFD, this is section + where those symbols should be placed. It must be a section in + the output BFD. It may be NULL, in which case no such symbols + will be created. This is to support CREATE_OBJECT_SYMBOLS in the + linker command language. */ + asection *create_object_symbols_section; + + /* List of global symbol names that are starting points for marking + sections against garbage collection. */ + struct bfd_sym_chain *gc_sym_list; + + /* If a base output file is wanted, then this points to it */ + void *base_file; + + /* The function to call when the executable or shared object is + loaded. */ + const char *init_function; + + /* The function to call when the executable or shared object is + unloaded. */ + const char *fini_function; + + /* Non-zero if auto-import thunks for DATA items in pei386 DLLs + should be generated/linked against. Set to 1 if this feature + is explicitly requested by the user, -1 if enabled by default. */ + int pei386_auto_import; + + /* Non-zero if runtime relocs for DATA items with non-zero addends + in pei386 DLLs should be generated. Set to 1 if this feature + is explicitly requested by the user, -1 if enabled by default. */ + int pei386_runtime_pseudo_reloc; + + /* How many spare .dynamic DT_NULL entries should be added? */ + unsigned int spare_dynamic_tags; + + /* May be used to set DT_FLAGS for ELF. */ + bfd_vma flags; + + /* May be used to set DT_FLAGS_1 for ELF. */ + bfd_vma flags_1; +}; + +/* This structures holds a set of callback functions. These are + called by the BFD linker routines. The first argument to each + callback function is the bfd_link_info structure being used. Each + function returns a boolean value. If the function returns FALSE, + then the BFD function which called it will return with a failure + indication. */ + +struct bfd_link_callbacks +{ + /* A function which is called when an object is added from an + archive. ABFD is the archive element being added. NAME is the + name of the symbol which caused the archive element to be pulled + in. */ + bfd_boolean (*add_archive_element) + (struct bfd_link_info *, bfd *abfd, const char *name); + /* A function which is called when a symbol is found with multiple + definitions. NAME is the symbol which is defined multiple times. + OBFD is the old BFD, OSEC is the old section, OVAL is the old + value, NBFD is the new BFD, NSEC is the new section, and NVAL is + the new value. OBFD may be NULL. OSEC and NSEC may be + bfd_com_section or bfd_ind_section. */ + bfd_boolean (*multiple_definition) + (struct bfd_link_info *, const char *name, + bfd *obfd, asection *osec, bfd_vma oval, + bfd *nbfd, asection *nsec, bfd_vma nval); + /* A function which is called when a common symbol is defined + multiple times. NAME is the symbol appearing multiple times. + OBFD is the BFD of the existing symbol; it may be NULL if this is + not known. OTYPE is the type of the existing symbol, which may + be bfd_link_hash_defined, bfd_link_hash_defweak, + bfd_link_hash_common, or bfd_link_hash_indirect. If OTYPE is + bfd_link_hash_common, OSIZE is the size of the existing symbol. + NBFD is the BFD of the new symbol. NTYPE is the type of the new + symbol, one of bfd_link_hash_defined, bfd_link_hash_common, or + bfd_link_hash_indirect. If NTYPE is bfd_link_hash_common, NSIZE + is the size of the new symbol. */ + bfd_boolean (*multiple_common) + (struct bfd_link_info *, const char *name, + bfd *obfd, enum bfd_link_hash_type otype, bfd_vma osize, + bfd *nbfd, enum bfd_link_hash_type ntype, bfd_vma nsize); + /* A function which is called to add a symbol to a set. ENTRY is + the link hash table entry for the set itself (e.g., + __CTOR_LIST__). RELOC is the relocation to use for an entry in + the set when generating a relocatable file, and is also used to + get the size of the entry when generating an executable file. + ABFD, SEC and VALUE identify the value to add to the set. */ + bfd_boolean (*add_to_set) + (struct bfd_link_info *, struct bfd_link_hash_entry *entry, + bfd_reloc_code_real_type reloc, bfd *abfd, asection *sec, bfd_vma value); + /* A function which is called when the name of a g++ constructor or + destructor is found. This is only called by some object file + formats. CONSTRUCTOR is TRUE for a constructor, FALSE for a + destructor. This will use BFD_RELOC_CTOR when generating a + relocatable file. NAME is the name of the symbol found. ABFD, + SECTION and VALUE are the value of the symbol. */ + bfd_boolean (*constructor) + (struct bfd_link_info *, bfd_boolean constructor, const char *name, + bfd *abfd, asection *sec, bfd_vma value); + /* A function which is called to issue a linker warning. For + example, this is called when there is a reference to a warning + symbol. WARNING is the warning to be issued. SYMBOL is the name + of the symbol which triggered the warning; it may be NULL if + there is none. ABFD, SECTION and ADDRESS identify the location + which trigerred the warning; either ABFD or SECTION or both may + be NULL if the location is not known. */ + bfd_boolean (*warning) + (struct bfd_link_info *, const char *warning, const char *symbol, + bfd *abfd, asection *section, bfd_vma address); + /* A function which is called when a relocation is attempted against + an undefined symbol. NAME is the symbol which is undefined. + ABFD, SECTION and ADDRESS identify the location from which the + reference is made. FATAL indicates whether an undefined symbol is + a fatal error or not. In some cases SECTION may be NULL. */ + bfd_boolean (*undefined_symbol) + (struct bfd_link_info *, const char *name, bfd *abfd, + asection *section, bfd_vma address, bfd_boolean fatal); + /* A function which is called when a reloc overflow occurs. NAME is + the name of the symbol or section the reloc is against, + RELOC_NAME is the name of the relocation, and ADDEND is any + addend that is used. ABFD, SECTION and ADDRESS identify the + location at which the overflow occurs; if this is the result of a + bfd_section_reloc_link_order or bfd_symbol_reloc_link_order, then + ABFD will be NULL. */ + bfd_boolean (*reloc_overflow) + (struct bfd_link_info *, const char *name, const char *reloc_name, + bfd_vma addend, bfd *abfd, asection *section, bfd_vma address); + /* A function which is called when a dangerous reloc is performed. + The canonical example is an a29k IHCONST reloc which does not + follow an IHIHALF reloc. MESSAGE is an appropriate message. + ABFD, SECTION and ADDRESS identify the location at which the + problem occurred; if this is the result of a + bfd_section_reloc_link_order or bfd_symbol_reloc_link_order, then + ABFD will be NULL. */ + bfd_boolean (*reloc_dangerous) + (struct bfd_link_info *, const char *message, + bfd *abfd, asection *section, bfd_vma address); + /* A function which is called when a reloc is found to be attached + to a symbol which is not being written out. NAME is the name of + the symbol. ABFD, SECTION and ADDRESS identify the location of + the reloc; if this is the result of a + bfd_section_reloc_link_order or bfd_symbol_reloc_link_order, then + ABFD will be NULL. */ + bfd_boolean (*unattached_reloc) + (struct bfd_link_info *, const char *name, + bfd *abfd, asection *section, bfd_vma address); + /* A function which is called when a symbol in notice_hash is + defined or referenced. NAME is the symbol. ABFD, SECTION and + ADDRESS are the value of the symbol. If SECTION is + bfd_und_section, this is a reference. */ + bfd_boolean (*notice) + (struct bfd_link_info *, const char *name, + bfd *abfd, asection *section, bfd_vma address); + /* A function which is called for reporting a linker error. ID is the + error identifier. The remaining input is the same as einfo () in + ld. */ + bfd_boolean (*error_handler) + (int id, const char *fmt, ...); + +/* Identifiers of linker error messages used by error_handler. */ +#define LD_DEFINITION_IN_DISCARDED_SECTION 1 +}; + +/* The linker builds link_order structures which tell the code how to + include input data in the output file. */ + +/* These are the types of link_order structures. */ + +enum bfd_link_order_type +{ + bfd_undefined_link_order, /* Undefined. */ + bfd_indirect_link_order, /* Built from a section. */ + bfd_data_link_order, /* Set to explicit data. */ + bfd_section_reloc_link_order, /* Relocate against a section. */ + bfd_symbol_reloc_link_order /* Relocate against a symbol. */ +}; + +/* This is the link_order structure itself. These form a chain + attached to the section whose contents they are describing. */ + +struct bfd_link_order +{ + /* Next link_order in chain. */ + struct bfd_link_order *next; + /* Type of link_order. */ + enum bfd_link_order_type type; + /* Offset within output section. */ + bfd_vma offset; + /* Size within output section. */ + bfd_size_type size; + /* Type specific information. */ + union + { + struct + { + /* Section to include. If this is used, then + section->output_section must be the section the + link_order is attached to, section->output_offset must + equal the link_order offset field, and section->_raw_size + must equal the link_order size field. Maybe these + restrictions should be relaxed someday. */ + asection *section; + } indirect; + struct + { + /* Size of contents, or zero when contents size == size + within output section. + A non-zero value allows filling of the output section + with an arbitrary repeated pattern. */ + unsigned int size; + /* Data to put into file. */ + bfd_byte *contents; + } data; + struct + { + /* Description of reloc to generate. Used for + bfd_section_reloc_link_order and + bfd_symbol_reloc_link_order. */ + struct bfd_link_order_reloc *p; + } reloc; + } u; +}; + +/* A linker order of type bfd_section_reloc_link_order or + bfd_symbol_reloc_link_order means to create a reloc against a + section or symbol, respectively. This is used to implement -Ur to + generate relocs for the constructor tables. The + bfd_link_order_reloc structure describes the reloc that BFD should + create. It is similar to a arelent, but I didn't use arelent + because the linker does not know anything about most symbols, and + any asymbol structure it creates will be partially meaningless. + This information could logically be in the bfd_link_order struct, + but I didn't want to waste the space since these types of relocs + are relatively rare. */ + +struct bfd_link_order_reloc +{ + /* Reloc type. */ + bfd_reloc_code_real_type reloc; + + union + { + /* For type bfd_section_reloc_link_order, this is the section + the reloc should be against. This must be a section in the + output BFD, not any of the input BFDs. */ + asection *section; + /* For type bfd_symbol_reloc_link_order, this is the name of the + symbol the reloc should be against. */ + const char *name; + } u; + + /* Addend to use. The object file should contain zero. The BFD + backend is responsible for filling in the contents of the object + file correctly. For some object file formats (e.g., COFF) the + addend must be stored into in the object file, and for some + (e.g., SPARC a.out) it is kept in the reloc. */ + bfd_vma addend; +}; + +/* Allocate a new link_order for a section. */ +extern struct bfd_link_order *bfd_new_link_order (bfd *, asection *); + +/* These structures are used to describe version information for the + ELF linker. These structures could be manipulated entirely inside + BFD, but it would be a pain. Instead, the regular linker sets up + these structures, and then passes them into BFD. */ + +/* Glob pattern for a version. */ + +struct bfd_elf_version_expr +{ + /* Next glob pattern for this version. */ + struct bfd_elf_version_expr *next; + /* Glob pattern. */ + const char *pattern; + /* NULL for a glob pattern, otherwise a straight symbol. */ + const char *symbol; + /* Defined by ".symver". */ + unsigned int symver : 1; + /* Defined by version script. */ + unsigned int script : 1; + /* Pattern type. */ +#define BFD_ELF_VERSION_C_TYPE 1 +#define BFD_ELF_VERSION_CXX_TYPE 2 +#define BFD_ELF_VERSION_JAVA_TYPE 4 + unsigned int mask : 3; +}; + +struct bfd_elf_version_expr_head +{ + /* List of all patterns, both wildcards and non-wildcards. */ + struct bfd_elf_version_expr *list; + /* Hash table for non-wildcards. */ + void *htab; + /* Remaining patterns. */ + struct bfd_elf_version_expr *remaining; + /* What kind of pattern types are present in list (bitmask). */ + unsigned int mask; +}; + +/* Version dependencies. */ + +struct bfd_elf_version_deps +{ + /* Next dependency for this version. */ + struct bfd_elf_version_deps *next; + /* The version which this version depends upon. */ + struct bfd_elf_version_tree *version_needed; +}; + +/* A node in the version tree. */ + +struct bfd_elf_version_tree +{ + /* Next version. */ + struct bfd_elf_version_tree *next; + /* Name of this version. */ + const char *name; + /* Version number. */ + unsigned int vernum; + /* Regular expressions for global symbols in this version. */ + struct bfd_elf_version_expr_head globals; + /* Regular expressions for local symbols in this version. */ + struct bfd_elf_version_expr_head locals; + /* List of versions which this version depends upon. */ + struct bfd_elf_version_deps *deps; + /* Index of the version name. This is used within BFD. */ + unsigned int name_indx; + /* Whether this version tree was used. This is used within BFD. */ + int used; + /* Matching hook. */ + struct bfd_elf_version_expr *(*match) + (struct bfd_elf_version_expr_head *head, + struct bfd_elf_version_expr *prev, const char *sym); +}; + +#endif diff --git a/contrib/gdb/include/bout.h b/contrib/gdb/include/bout.h new file mode 100644 index 0000000..a69e280 --- /dev/null +++ b/contrib/gdb/include/bout.h @@ -0,0 +1,191 @@ +/* This file is a modified version of 'a.out.h'. It is to be used in all + GNU tools modified to support the i80960 (or tools that operate on + object files created by such tools). + + Copyright 2001 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. */ + +/* All i80960 development is done in a CROSS-DEVELOPMENT environment. I.e., + object code is generated on, and executed under the direction of a symbolic + debugger running on, a host system. We do not want to be subject to the + vagaries of which host it is or whether it supports COFF or a.out format, + or anything else. We DO want to: + + o always generate the same format object files, regardless of host. + + o have an 'a.out' header that we can modify for our own purposes + (the 80960 is typically an embedded processor and may require + enhanced linker support that the normal a.out.h header can't + accommodate). + + As for byte-ordering, the following rules apply: + + o Text and data that is actually downloaded to the target is always + in i80960 (little-endian) order. + + o All other numbers (in the header, symbols, relocation directives) + are in host byte-order: object files CANNOT be lifted from a + little-end host and used on a big-endian (or vice versa) without + modification. + ==> THIS IS NO LONGER TRUE USING BFD. WE CAN GENERATE ANY BYTE ORDER + FOR THE HEADER, AND READ ANY BYTE ORDER. PREFERENCE WOULD BE TO + USE LITTLE-ENDIAN BYTE ORDER THROUGHOUT, REGARDLESS OF HOST. <== + + o The downloader ('comm960') takes care to generate a pseudo-header + with correct (i80960) byte-ordering before shipping text and data + off to the NINDY monitor in the target systems. Symbols and + relocation info are never sent to the target. */ + +#define BMAGIC 0415 +/* We don't accept the following (see N_BADMAG macro). + They're just here so GNU code will compile. */ +#define OMAGIC 0407 /* old impure format */ +#define NMAGIC 0410 /* read-only text */ +#define ZMAGIC 0413 /* demand load format */ + +/* FILE HEADER + All 'lengths' are given as a number of bytes. + All 'alignments' are for relinkable files only; an alignment of + 'n' indicates the corresponding segment must begin at an + address that is a multiple of (2**n). */ +struct external_exec + { + /* Standard stuff */ + unsigned char e_info[4]; /* Identifies this as a b.out file */ + unsigned char e_text[4]; /* Length of text */ + unsigned char e_data[4]; /* Length of data */ + unsigned char e_bss[4]; /* Length of uninitialized data area */ + unsigned char e_syms[4]; /* Length of symbol table */ + unsigned char e_entry[4]; /* Runtime start address */ + unsigned char e_trsize[4]; /* Length of text relocation info */ + unsigned char e_drsize[4]; /* Length of data relocation info */ + + /* Added for i960 */ + unsigned char e_tload[4]; /* Text runtime load address */ + unsigned char e_dload[4]; /* Data runtime load address */ + unsigned char e_talign[1]; /* Alignment of text segment */ + unsigned char e_dalign[1]; /* Alignment of data segment */ + unsigned char e_balign[1]; /* Alignment of bss segment */ + unsigned char e_relaxable[1];/* Assembled with enough info to allow linker to relax */ + }; + +#define EXEC_BYTES_SIZE (sizeof (struct external_exec)) + +/* These macros use the a_xxx field names, since they operate on the exec + structure after it's been byte-swapped and realigned on the host machine. */ +#define N_BADMAG(x) (((x).a_info)!=BMAGIC) +#define N_TXTOFF(x) EXEC_BYTES_SIZE +#define N_DATOFF(x) ( N_TXTOFF(x) + (x).a_text ) +#define N_TROFF(x) ( N_DATOFF(x) + (x).a_data ) +#define N_TRELOFF N_TROFF +#define N_DROFF(x) ( N_TROFF(x) + (x).a_trsize ) +#define N_DRELOFF N_DROFF +#define N_SYMOFF(x) ( N_DROFF(x) + (x).a_drsize ) +#define N_STROFF(x) ( N_SYMOFF(x) + (x).a_syms ) +#define N_DATADDR(x) ( (x).a_dload ) + +/* Address of text segment in memory after it is loaded. */ +#if !defined (N_TXTADDR) +#define N_TXTADDR(x) 0 +#endif + +/* A single entry in the symbol table. */ +struct nlist + { + union + { + char* n_name; + struct nlist * n_next; + long n_strx; /* Index into string table */ + } n_un; + + unsigned char n_type; /* See below */ + char n_other; /* Used in i80960 support -- see below */ + short n_desc; + unsigned long n_value; + }; + + +/* Legal values of n_type. */ +#define N_UNDF 0 /* Undefined symbol */ +#define N_ABS 2 /* Absolute symbol */ +#define N_TEXT 4 /* Text symbol */ +#define N_DATA 6 /* Data symbol */ +#define N_BSS 8 /* BSS symbol */ +#define N_FN 31 /* Filename symbol */ + +#define N_EXT 1 /* External symbol (OR'd in with one of above) */ +#define N_TYPE 036 /* Mask for all the type bits */ +#define N_STAB 0340 /* Mask for all bits used for SDB entries */ + +/* MEANING OF 'n_other' + + If non-zero, the 'n_other' fields indicates either a leaf procedure or + a system procedure, as follows: + + 1 <= n_other <= 32 : + The symbol is the entry point to a system procedure. + 'n_value' is the address of the entry, as for any other + procedure. The system procedure number (which can be used in + a 'calls' instruction) is (n_other-1). These entries come from + '.sysproc' directives. + + n_other == N_CALLNAME + the symbol is the 'call' entry point to a leaf procedure. + The *next* symbol in the symbol table must be the corresponding + 'bal' entry point to the procedure (see following). These + entries come from '.leafproc' directives in which two different + symbols are specified (the first one is represented here). + + + n_other == N_BALNAME + the symbol is the 'bal' entry point to a leaf procedure. + These entries result from '.leafproc' directives in which only + one symbol is specified, or in which the same symbol is + specified twice. + + Note that an N_CALLNAME entry *must* have a corresponding N_BALNAME entry, + but not every N_BALNAME entry must have an N_CALLNAME entry. */ +#define N_CALLNAME ((char)-1) +#define N_BALNAME ((char)-2) +#define IS_CALLNAME(x) (N_CALLNAME == (x)) +#define IS_BALNAME(x) (N_BALNAME == (x)) +#define IS_OTHER(x) ((x)>0 && (x) <=32) + +#define b_out_relocation_info relocation_info +struct relocation_info + { + int r_address; /* File address of item to be relocated. */ + unsigned +#define r_index r_symbolnum + r_symbolnum:24, /* Index of symbol on which relocation is based, + if r_extern is set. Otherwise set to + either N_TEXT, N_DATA, or N_BSS to + indicate section on which relocation is + based. */ + r_pcrel:1, /* 1 => relocate PC-relative; else absolute + On i960, pc-relative implies 24-bit + address, absolute implies 32-bit. */ + r_length:2, /* Number of bytes to relocate: + 0 => 1 byte + 1 => 2 bytes -- used for 13 bit pcrel + 2 => 4 bytes. */ + r_extern:1, + r_bsr:1, /* Something for the GNU NS32K assembler. */ + r_disp:1, /* Something for the GNU NS32K assembler. */ + r_callj:1, /* 1 if relocation target is an i960 'callj'. */ + r_relaxable:1; /* 1 if enough info is left to relax the data. */ +}; diff --git a/contrib/gdb/include/demangle.h b/contrib/gdb/include/demangle.h new file mode 100644 index 0000000..6e995e4 --- /dev/null +++ b/contrib/gdb/include/demangle.h @@ -0,0 +1,533 @@ +/* Defs for interface to demanglers. + Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002, + 2003, 2004 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, 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. */ + + +#if !defined (DEMANGLE_H) +#define DEMANGLE_H + +#include "libiberty.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Options passed to cplus_demangle (in 2nd parameter). */ + +#define DMGL_NO_OPTS 0 /* For readability... */ +#define DMGL_PARAMS (1 << 0) /* Include function args */ +#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ +#define DMGL_JAVA (1 << 2) /* Demangle as Java rather than C++. */ +#define DMGL_VERBOSE (1 << 3) /* Include implementation details. */ +#define DMGL_TYPES (1 << 4) /* Also try to demangle type encodings. */ + +#define DMGL_AUTO (1 << 8) +#define DMGL_GNU (1 << 9) +#define DMGL_LUCID (1 << 10) +#define DMGL_ARM (1 << 11) +#define DMGL_HP (1 << 12) /* For the HP aCC compiler; + same as ARM except for + template arguments, etc. */ +#define DMGL_EDG (1 << 13) +#define DMGL_GNU_V3 (1 << 14) +#define DMGL_GNAT (1 << 15) + +/* If none of these are set, use 'current_demangling_style' as the default. */ +#define DMGL_STYLE_MASK (DMGL_AUTO|DMGL_GNU|DMGL_LUCID|DMGL_ARM|DMGL_HP|DMGL_EDG|DMGL_GNU_V3|DMGL_JAVA|DMGL_GNAT) + +/* Enumeration of possible demangling styles. + + Lucid and ARM styles are still kept logically distinct, even though + they now both behave identically. The resulting style is actual the + union of both. I.E. either style recognizes both "__pt__" and "__rf__" + for operator "->", even though the first is lucid style and the second + is ARM style. (FIXME?) */ + +extern enum demangling_styles +{ + no_demangling = -1, + unknown_demangling = 0, + auto_demangling = DMGL_AUTO, + gnu_demangling = DMGL_GNU, + lucid_demangling = DMGL_LUCID, + arm_demangling = DMGL_ARM, + hp_demangling = DMGL_HP, + edg_demangling = DMGL_EDG, + gnu_v3_demangling = DMGL_GNU_V3, + java_demangling = DMGL_JAVA, + gnat_demangling = DMGL_GNAT +} current_demangling_style; + +/* Define string names for the various demangling styles. */ + +#define NO_DEMANGLING_STYLE_STRING "none" +#define AUTO_DEMANGLING_STYLE_STRING "auto" +#define GNU_DEMANGLING_STYLE_STRING "gnu" +#define LUCID_DEMANGLING_STYLE_STRING "lucid" +#define ARM_DEMANGLING_STYLE_STRING "arm" +#define HP_DEMANGLING_STYLE_STRING "hp" +#define EDG_DEMANGLING_STYLE_STRING "edg" +#define GNU_V3_DEMANGLING_STYLE_STRING "gnu-v3" +#define JAVA_DEMANGLING_STYLE_STRING "java" +#define GNAT_DEMANGLING_STYLE_STRING "gnat" + +/* Some macros to test what demangling style is active. */ + +#define CURRENT_DEMANGLING_STYLE current_demangling_style +#define AUTO_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_AUTO) +#define GNU_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNU) +#define LUCID_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_LUCID) +#define ARM_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_ARM) +#define HP_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_HP) +#define EDG_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_EDG) +#define GNU_V3_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNU_V3) +#define JAVA_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_JAVA) +#define GNAT_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNAT) + +/* Provide information about the available demangle styles. This code is + pulled from gdb into libiberty because it is useful to binutils also. */ + +extern const struct demangler_engine +{ + const char *const demangling_style_name; + const enum demangling_styles demangling_style; + const char *const demangling_style_doc; +} libiberty_demanglers[]; + +extern char * +cplus_demangle PARAMS ((const char *mangled, int options)); + +extern int +cplus_demangle_opname PARAMS ((const char *opname, char *result, int options)); + +extern const char * +cplus_mangle_opname PARAMS ((const char *opname, int options)); + +/* Note: This sets global state. FIXME if you care about multi-threading. */ + +extern void +set_cplus_marker_for_demangling PARAMS ((int ch)); + +extern enum demangling_styles +cplus_demangle_set_style PARAMS ((enum demangling_styles style)); + +extern enum demangling_styles +cplus_demangle_name_to_style PARAMS ((const char *name)); + +/* V3 ABI demangling entry points, defined in cp-demangle.c. */ +extern char* +cplus_demangle_v3 PARAMS ((const char* mangled, int options)); + +extern char* +java_demangle_v3 PARAMS ((const char* mangled)); + + +enum gnu_v3_ctor_kinds { + gnu_v3_complete_object_ctor = 1, + gnu_v3_base_object_ctor, + gnu_v3_complete_object_allocating_ctor +}; + +/* Return non-zero iff NAME is the mangled form of a constructor name + in the G++ V3 ABI demangling style. Specifically, return an `enum + gnu_v3_ctor_kinds' value indicating what kind of constructor + it is. */ +extern enum gnu_v3_ctor_kinds + is_gnu_v3_mangled_ctor PARAMS ((const char *name)); + + +enum gnu_v3_dtor_kinds { + gnu_v3_deleting_dtor = 1, + gnu_v3_complete_object_dtor, + gnu_v3_base_object_dtor +}; + +/* Return non-zero iff NAME is the mangled form of a destructor name + in the G++ V3 ABI demangling style. Specifically, return an `enum + gnu_v3_dtor_kinds' value, indicating what kind of destructor + it is. */ +extern enum gnu_v3_dtor_kinds + is_gnu_v3_mangled_dtor PARAMS ((const char *name)); + +/* The V3 demangler works in two passes. The first pass builds a tree + representation of the mangled name, and the second pass turns the + tree representation into a demangled string. Here we define an + interface to permit a caller to build their own tree + representation, which they can pass to the demangler to get a + demangled string. This can be used to canonicalize user input into + something which the demangler might output. It could also be used + by other demanglers in the future. */ + +/* These are the component types which may be found in the tree. Many + component types have one or two subtrees, referred to as left and + right (a component type with only one subtree puts it in the left + subtree). */ + +enum demangle_component_type +{ + /* A name, with a length and a pointer to a string. */ + DEMANGLE_COMPONENT_NAME, + /* A qualified name. The left subtree is a class or namespace or + some such thing, and the right subtree is a name qualified by + that class. */ + DEMANGLE_COMPONENT_QUAL_NAME, + /* A local name. The left subtree describes a function, and the + right subtree is a name which is local to that function. */ + DEMANGLE_COMPONENT_LOCAL_NAME, + /* A typed name. The left subtree is a name, and the right subtree + describes that name as a function. */ + DEMANGLE_COMPONENT_TYPED_NAME, + /* A template. The left subtree is a template name, and the right + subtree is a template argument list. */ + DEMANGLE_COMPONENT_TEMPLATE, + /* A template parameter. This holds a number, which is the template + parameter index. */ + DEMANGLE_COMPONENT_TEMPLATE_PARAM, + /* A constructor. This holds a name and the kind of + constructor. */ + DEMANGLE_COMPONENT_CTOR, + /* A destructor. This holds a name and the kind of destructor. */ + DEMANGLE_COMPONENT_DTOR, + /* A vtable. This has one subtree, the type for which this is a + vtable. */ + DEMANGLE_COMPONENT_VTABLE, + /* A VTT structure. This has one subtree, the type for which this + is a VTT. */ + DEMANGLE_COMPONENT_VTT, + /* A construction vtable. The left subtree is the type for which + this is a vtable, and the right subtree is the derived type for + which this vtable is built. */ + DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE, + /* A typeinfo structure. This has one subtree, the type for which + this is the tpeinfo structure. */ + DEMANGLE_COMPONENT_TYPEINFO, + /* A typeinfo name. This has one subtree, the type for which this + is the typeinfo name. */ + DEMANGLE_COMPONENT_TYPEINFO_NAME, + /* A typeinfo function. This has one subtree, the type for which + this is the tpyeinfo function. */ + DEMANGLE_COMPONENT_TYPEINFO_FN, + /* A thunk. This has one subtree, the name for which this is a + thunk. */ + DEMANGLE_COMPONENT_THUNK, + /* A virtual thunk. This has one subtree, the name for which this + is a virtual thunk. */ + DEMANGLE_COMPONENT_VIRTUAL_THUNK, + /* A covariant thunk. This has one subtree, the name for which this + is a covariant thunk. */ + DEMANGLE_COMPONENT_COVARIANT_THUNK, + /* A Java class. This has one subtree, the type. */ + DEMANGLE_COMPONENT_JAVA_CLASS, + /* A guard variable. This has one subtree, the name for which this + is a guard variable. */ + DEMANGLE_COMPONENT_GUARD, + /* A reference temporary. This has one subtree, the name for which + this is a temporary. */ + DEMANGLE_COMPONENT_REFTEMP, + /* A standard substitution. This holds the name of the + substitution. */ + DEMANGLE_COMPONENT_SUB_STD, + /* The restrict qualifier. The one subtree is the type which is + being qualified. */ + DEMANGLE_COMPONENT_RESTRICT, + /* The volatile qualifier. The one subtree is the type which is + being qualified. */ + DEMANGLE_COMPONENT_VOLATILE, + /* The const qualifier. The one subtree is the type which is being + qualified. */ + DEMANGLE_COMPONENT_CONST, + /* The restrict qualifier modifying a member function. The one + subtree is the type which is being qualified. */ + DEMANGLE_COMPONENT_RESTRICT_THIS, + /* The volatile qualifier modifying a member function. The one + subtree is the type which is being qualified. */ + DEMANGLE_COMPONENT_VOLATILE_THIS, + /* The const qualifier modifying a member function. The one subtree + is the type which is being qualified. */ + DEMANGLE_COMPONENT_CONST_THIS, + /* A vendor qualifier. The left subtree is the type which is being + qualified, and the right subtree is the name of the + qualifier. */ + DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL, + /* A pointer. The one subtree is the type which is being pointed + to. */ + DEMANGLE_COMPONENT_POINTER, + /* A reference. The one subtree is the type which is being + referenced. */ + DEMANGLE_COMPONENT_REFERENCE, + /* A complex type. The one subtree is the base type. */ + DEMANGLE_COMPONENT_COMPLEX, + /* An imaginary type. The one subtree is the base type. */ + DEMANGLE_COMPONENT_IMAGINARY, + /* A builtin type. This holds the builtin type information. */ + DEMANGLE_COMPONENT_BUILTIN_TYPE, + /* A vendor's builtin type. This holds the name of the type. */ + DEMANGLE_COMPONENT_VENDOR_TYPE, + /* A function type. The left subtree is the return type. The right + subtree is a list of ARGLIST nodes. Either or both may be + NULL. */ + DEMANGLE_COMPONENT_FUNCTION_TYPE, + /* An array type. The left subtree is the dimension, which may be + NULL, or a string (represented as DEMANGLE_COMPONENT_NAME), or an + expression. The right subtree is the element type. */ + DEMANGLE_COMPONENT_ARRAY_TYPE, + /* A pointer to member type. The left subtree is the class type, + and the right subtree is the member type. CV-qualifiers appear + on the latter. */ + DEMANGLE_COMPONENT_PTRMEM_TYPE, + /* An argument list. The left subtree is the current argument, and + the right subtree is either NULL or another ARGLIST node. */ + DEMANGLE_COMPONENT_ARGLIST, + /* A template argument list. The left subtree is the current + template argument, and the right subtree is either NULL or + another TEMPLATE_ARGLIST node. */ + DEMANGLE_COMPONENT_TEMPLATE_ARGLIST, + /* An operator. This holds information about a standard + operator. */ + DEMANGLE_COMPONENT_OPERATOR, + /* An extended operator. This holds the number of arguments, and + the name of the extended operator. */ + DEMANGLE_COMPONENT_EXTENDED_OPERATOR, + /* A typecast, represented as a unary operator. The one subtree is + the type to which the argument should be cast. */ + DEMANGLE_COMPONENT_CAST, + /* A unary expression. The left subtree is the operator, and the + right subtree is the single argument. */ + DEMANGLE_COMPONENT_UNARY, + /* A binary expression. The left subtree is the operator, and the + right subtree is a BINARY_ARGS. */ + DEMANGLE_COMPONENT_BINARY, + /* Arguments to a binary expression. The left subtree is the first + argument, and the right subtree is the second argument. */ + DEMANGLE_COMPONENT_BINARY_ARGS, + /* A trinary expression. The left subtree is the operator, and the + right subtree is a TRINARY_ARG1. */ + DEMANGLE_COMPONENT_TRINARY, + /* Arguments to a trinary expression. The left subtree is the first + argument, and the right subtree is a TRINARY_ARG2. */ + DEMANGLE_COMPONENT_TRINARY_ARG1, + /* More arguments to a trinary expression. The left subtree is the + second argument, and the right subtree is the third argument. */ + DEMANGLE_COMPONENT_TRINARY_ARG2, + /* A literal. The left subtree is the type, and the right subtree + is the value, represented as a DEMANGLE_COMPONENT_NAME. */ + DEMANGLE_COMPONENT_LITERAL, + /* A negative literal. Like LITERAL, but the value is negated. + This is a minor hack: the NAME used for LITERAL points directly + to the mangled string, but since negative numbers are mangled + using 'n' instead of '-', we want a way to indicate a negative + number which involves neither modifying the mangled string nor + allocating a new copy of the literal in memory. */ + DEMANGLE_COMPONENT_LITERAL_NEG +}; + +/* Types which are only used internally. */ + +struct demangle_operator_info; +struct demangle_builtin_type_info; + +/* A node in the tree representation is an instance of a struct + demangle_component. Note that the field names of the struct are + not well protected against macros defined by the file including + this one. We can fix this if it ever becomes a problem. */ + +struct demangle_component +{ + /* The type of this component. */ + enum demangle_component_type type; + + union + { + /* For DEMANGLE_COMPONENT_NAME. */ + struct + { + /* A pointer to the name (which need not NULL terminated) and + its length. */ + const char *s; + int len; + } s_name; + + /* For DEMANGLE_COMPONENT_OPERATOR. */ + struct + { + /* Operator. */ + const struct demangle_operator_info *op; + } s_operator; + + /* For DEMANGLE_COMPONENT_EXTENDED_OPERATOR. */ + struct + { + /* Number of arguments. */ + int args; + /* Name. */ + struct demangle_component *name; + } s_extended_operator; + + /* For DEMANGLE_COMPONENT_CTOR. */ + struct + { + /* Kind of constructor. */ + enum gnu_v3_ctor_kinds kind; + /* Name. */ + struct demangle_component *name; + } s_ctor; + + /* For DEMANGLE_COMPONENT_DTOR. */ + struct + { + /* Kind of destructor. */ + enum gnu_v3_dtor_kinds kind; + /* Name. */ + struct demangle_component *name; + } s_dtor; + + /* For DEMANGLE_COMPONENT_BUILTIN_TYPE. */ + struct + { + /* Builtin type. */ + const struct demangle_builtin_type_info *type; + } s_builtin; + + /* For DEMANGLE_COMPONENT_SUB_STD. */ + struct + { + /* Standard substitution string. */ + const char* string; + /* Length of string. */ + int len; + } s_string; + + /* For DEMANGLE_COMPONENT_TEMPLATE_PARAM. */ + struct + { + /* Template parameter index. */ + long number; + } s_number; + + /* For other types. */ + struct + { + /* Left (or only) subtree. */ + struct demangle_component *left; + /* Right subtree. */ + struct demangle_component *right; + } s_binary; + + } u; +}; + +/* People building mangled trees are expected to allocate instances of + struct demangle_component themselves. They can then call one of + the following functions to fill them in. */ + +/* Fill in most component types with a left subtree and a right + subtree. Returns non-zero on success, zero on failure, such as an + unrecognized or inappropriate component type. */ + +extern int +cplus_demangle_fill_component PARAMS ((struct demangle_component *fill, + enum demangle_component_type, + struct demangle_component *left, + struct demangle_component *right)); + +/* Fill in a DEMANGLE_COMPONENT_NAME. Returns non-zero on success, + zero for bad arguments. */ + +extern int +cplus_demangle_fill_name PARAMS ((struct demangle_component *fill, + const char *, int)); + +/* Fill in a DEMANGLE_COMPONENT_BUILTIN_TYPE, using the name of the + builtin type (e.g., "int", etc.). Returns non-zero on success, + zero if the type is not recognized. */ + +extern int +cplus_demangle_fill_builtin_type PARAMS ((struct demangle_component *fill, + const char *typename)); + +/* Fill in a DEMANGLE_COMPONENT_OPERATOR, using the name of the + operator and the number of arguments which it takes (the latter is + used to disambiguate operators which can be both binary and unary, + such as '-'). Returns non-zero on success, zero if the operator is + not recognized. */ + +extern int +cplus_demangle_fill_operator PARAMS ((struct demangle_component *fill, + const char *opname, int args)); + +/* Fill in a DEMANGLE_COMPONENT_EXTENDED_OPERATOR, providing the + number of arguments and the name. Returns non-zero on success, + zero for bad arguments. */ + +extern int +cplus_demangle_fill_extended_operator PARAMS ((struct demangle_component *fill, + int numargs, + struct demangle_component *nm)); + +/* Fill in a DEMANGLE_COMPONENT_CTOR. Returns non-zero on success, + zero for bad arguments. */ + +extern int +cplus_demangle_fill_ctor PARAMS ((struct demangle_component *fill, + enum gnu_v3_ctor_kinds kind, + struct demangle_component *name)); + +/* Fill in a DEMANGLE_COMPONENT_DTOR. Returns non-zero on success, + zero for bad arguments. */ + +extern int +cplus_demangle_fill_dtor PARAMS ((struct demangle_component *fill, + enum gnu_v3_dtor_kinds kind, + struct demangle_component *name)); + +/* This function translates a mangled name into a struct + demangle_component tree. The first argument is the mangled name. + The second argument is DMGL_* options. This returns a pointer to a + tree on success, or NULL on failure. On success, the third + argument is set to a block of memory allocated by malloc. This + block should be passed to free when the tree is no longer + needed. */ + +extern struct demangle_component * +cplus_demangle_v3_components PARAMS ((const char *mangled, + int options, + void **mem)); + +/* This function takes a struct demangle_component tree and returns + the corresponding demangled string. The first argument is DMGL_* + options. The second is the tree to demangle. The third is a guess + at the length of the demangled string, used to initially allocate + the return buffer. The fourth is a pointer to a size_t. On + success, this function returns a buffer allocated by malloc(), and + sets the size_t pointed to by the fourth argument to the size of + the allocated buffer (not the length of the returned string). On + failure, this function returns NULL, and sets the size_t pointed to + by the fourth argument to 0 for an invalid tree, or to 1 for a + memory allocation error. */ + +extern char * +cplus_demangle_print PARAMS ((int options, + const struct demangle_component *tree, + int estimated_length, + size_t *p_allocated_size)); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* DEMANGLE_H */ diff --git a/contrib/gdb/include/dis-asm.h b/contrib/gdb/include/dis-asm.h new file mode 100644 index 0000000..3670c51 --- /dev/null +++ b/contrib/gdb/include/dis-asm.h @@ -0,0 +1,317 @@ +/* Interface between the opcode library and its callers. + + Copyright 2001, 2002, 2003 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, 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 Cygnus Support, 1993. + + The opcode library (libopcodes.a) provides instruction decoders for + a large variety of instruction sets, callable with an identical + interface, for making instruction-processing programs more independent + of the instruction set being processed. */ + +#ifndef DIS_ASM_H +#define DIS_ASM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> +#include "bfd.h" + +typedef int (*fprintf_ftype) (void *, const char*, ...); + +enum dis_insn_type { + dis_noninsn, /* Not a valid instruction */ + dis_nonbranch, /* Not a branch instruction */ + dis_branch, /* Unconditional branch */ + dis_condbranch, /* Conditional branch */ + dis_jsr, /* Jump to subroutine */ + dis_condjsr, /* Conditional jump to subroutine */ + dis_dref, /* Data reference instruction */ + dis_dref2 /* Two data references in instruction */ +}; + +/* This struct is passed into the instruction decoding routine, + and is passed back out into each callback. The various fields are used + for conveying information from your main routine into your callbacks, + for passing information into the instruction decoders (such as the + addresses of the callback functions), or for passing information + back from the instruction decoders to their callers. + + It must be initialized before it is first passed; this can be done + by hand, or using one of the initialization macros below. */ + +typedef struct disassemble_info { + fprintf_ftype fprintf_func; + void *stream; + void *application_data; + + /* Target description. We could replace this with a pointer to the bfd, + but that would require one. There currently isn't any such requirement + so to avoid introducing one we record these explicitly. */ + /* The bfd_flavour. This can be bfd_target_unknown_flavour. */ + enum bfd_flavour flavour; + /* The bfd_arch value. */ + enum bfd_architecture arch; + /* The bfd_mach value. */ + unsigned long mach; + /* Endianness (for bi-endian cpus). Mono-endian cpus can ignore this. */ + enum bfd_endian endian; + /* An arch/mach-specific bitmask of selected instruction subsets, mainly + for processors with run-time-switchable instruction sets. The default, + zero, means that there is no constraint. CGEN-based opcodes ports + may use ISA_foo masks. */ + unsigned long insn_sets; + + /* Some targets need information about the current section to accurately + display insns. If this is NULL, the target disassembler function + will have to make its best guess. */ + asection *section; + + /* An array of pointers to symbols either at the location being disassembled + or at the start of the function being disassembled. The array is sorted + so that the first symbol is intended to be the one used. The others are + present for any misc. purposes. This is not set reliably, but if it is + not NULL, it is correct. */ + asymbol **symbols; + /* Number of symbols in array. */ + int num_symbols; + + /* For use by the disassembler. + The top 16 bits are reserved for public use (and are documented here). + The bottom 16 bits are for the internal use of the disassembler. */ + unsigned long flags; +#define INSN_HAS_RELOC 0x80000000 + void *private_data; + + /* Function used to get bytes to disassemble. MEMADDR is the + address of the stuff to be disassembled, MYADDR is the address to + put the bytes in, and LENGTH is the number of bytes to read. + INFO is a pointer to this struct. + Returns an errno value or 0 for success. */ + int (*read_memory_func) + (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length, + struct disassemble_info *info); + + /* Function which should be called if we get an error that we can't + recover from. STATUS is the errno value from read_memory_func and + MEMADDR is the address that we were trying to read. INFO is a + pointer to this struct. */ + void (*memory_error_func) + (int status, bfd_vma memaddr, struct disassemble_info *info); + + /* Function called to print ADDR. */ + void (*print_address_func) + (bfd_vma addr, struct disassemble_info *info); + + /* Function called to determine if there is a symbol at the given ADDR. + If there is, the function returns 1, otherwise it returns 0. + This is used by ports which support an overlay manager where + the overlay number is held in the top part of an address. In + some circumstances we want to include the overlay number in the + address, (normally because there is a symbol associated with + that address), but sometimes we want to mask out the overlay bits. */ + int (* symbol_at_address_func) + (bfd_vma addr, struct disassemble_info * info); + + /* Function called to check if a SYMBOL is can be displayed to the user. + This is used by some ports that want to hide special symbols when + displaying debugging outout. */ + bfd_boolean (* symbol_is_valid) + (asymbol *, struct disassemble_info * info); + + /* These are for buffer_read_memory. */ + bfd_byte *buffer; + bfd_vma buffer_vma; + unsigned int buffer_length; + + /* This variable may be set by the instruction decoder. It suggests + the number of bytes objdump should display on a single line. If + the instruction decoder sets this, it should always set it to + the same value in order to get reasonable looking output. */ + int bytes_per_line; + + /* The next two variables control the way objdump displays the raw data. */ + /* For example, if bytes_per_line is 8 and bytes_per_chunk is 4, the */ + /* output will look like this: + 00: 00000000 00000000 + with the chunks displayed according to "display_endian". */ + int bytes_per_chunk; + enum bfd_endian display_endian; + + /* Number of octets per incremented target address + Normally one, but some DSPs have byte sizes of 16 or 32 bits. */ + unsigned int octets_per_byte; + + /* Results from instruction decoders. Not all decoders yet support + this information. This info is set each time an instruction is + decoded, and is only valid for the last such instruction. + + To determine whether this decoder supports this information, set + insn_info_valid to 0, decode an instruction, then check it. */ + + char insn_info_valid; /* Branch info has been set. */ + char branch_delay_insns; /* How many sequential insn's will run before + a branch takes effect. (0 = normal) */ + char data_size; /* Size of data reference in insn, in bytes */ + enum dis_insn_type insn_type; /* Type of instruction */ + bfd_vma target; /* Target address of branch or dref, if known; + zero if unknown. */ + bfd_vma target2; /* Second target address for dref2 */ + + /* Command line options specific to the target disassembler. */ + char * disassembler_options; + +} disassemble_info; + + +/* Standard disassemblers. Disassemble one instruction at the given + target address. Return number of octets processed. */ +typedef int (*disassembler_ftype) (bfd_vma, disassemble_info *); + +extern int print_insn_big_mips (bfd_vma, disassemble_info *); +extern int print_insn_little_mips (bfd_vma, disassemble_info *); +extern int print_insn_i386 (bfd_vma, disassemble_info *); +extern int print_insn_i386_att (bfd_vma, disassemble_info *); +extern int print_insn_i386_intel (bfd_vma, disassemble_info *); +extern int print_insn_ia64 (bfd_vma, disassemble_info *); +extern int print_insn_i370 (bfd_vma, disassemble_info *); +extern int print_insn_m68hc11 (bfd_vma, disassemble_info *); +extern int print_insn_m68hc12 (bfd_vma, disassemble_info *); +extern int print_insn_m68k (bfd_vma, disassemble_info *); +extern int print_insn_z8001 (bfd_vma, disassemble_info *); +extern int print_insn_z8002 (bfd_vma, disassemble_info *); +extern int print_insn_h8300 (bfd_vma, disassemble_info *); +extern int print_insn_h8300h (bfd_vma, disassemble_info *); +extern int print_insn_h8300s (bfd_vma, disassemble_info *); +extern int print_insn_h8500 (bfd_vma, disassemble_info *); +extern int print_insn_alpha (bfd_vma, disassemble_info *); +extern int print_insn_big_arm (bfd_vma, disassemble_info *); +extern int print_insn_little_arm (bfd_vma, disassemble_info *); +extern int print_insn_sparc (bfd_vma, disassemble_info *); +extern int print_insn_big_a29k (bfd_vma, disassemble_info *); +extern int print_insn_little_a29k (bfd_vma, disassemble_info *); +extern int print_insn_avr (bfd_vma, disassemble_info *); +extern int print_insn_d10v (bfd_vma, disassemble_info *); +extern int print_insn_d30v (bfd_vma, disassemble_info *); +extern int print_insn_dlx (bfd_vma, disassemble_info *); +extern int print_insn_fr30 (bfd_vma, disassemble_info *); +extern int print_insn_hppa (bfd_vma, disassemble_info *); +extern int print_insn_i860 (bfd_vma, disassemble_info *); +extern int print_insn_i960 (bfd_vma, disassemble_info *); +extern int print_insn_ip2k (bfd_vma, disassemble_info *); +extern int print_insn_m32r (bfd_vma, disassemble_info *); +extern int print_insn_m88k (bfd_vma, disassemble_info *); +extern int print_insn_mcore (bfd_vma, disassemble_info *); +extern int print_insn_mmix (bfd_vma, disassemble_info *); +extern int print_insn_mn10200 (bfd_vma, disassemble_info *); +extern int print_insn_mn10300 (bfd_vma, disassemble_info *); +extern int print_insn_msp430 (bfd_vma, disassemble_info *); +extern int print_insn_ns32k (bfd_vma, disassemble_info *); +extern int print_insn_openrisc (bfd_vma, disassemble_info *); +extern int print_insn_big_or32 (bfd_vma, disassemble_info *); +extern int print_insn_little_or32 (bfd_vma, disassemble_info *); +extern int print_insn_pdp11 (bfd_vma, disassemble_info *); +extern int print_insn_pj (bfd_vma, disassemble_info *); +extern int print_insn_big_powerpc (bfd_vma, disassemble_info *); +extern int print_insn_little_powerpc (bfd_vma, disassemble_info *); +extern int print_insn_rs6000 (bfd_vma, disassemble_info *); +extern int print_insn_s390 (bfd_vma, disassemble_info *); +extern int print_insn_sh (bfd_vma, disassemble_info *); +extern int print_insn_tic30 (bfd_vma, disassemble_info *); +extern int print_insn_tic4x (bfd_vma, disassemble_info *); +extern int print_insn_tic54x (bfd_vma, disassemble_info *); +extern int print_insn_tic80 (bfd_vma, disassemble_info *); +extern int print_insn_v850 (bfd_vma, disassemble_info *); +extern int print_insn_vax (bfd_vma, disassemble_info *); +extern int print_insn_w65 (bfd_vma, disassemble_info *); +extern int print_insn_xstormy16 (bfd_vma, disassemble_info *); +extern int print_insn_xtensa (bfd_vma, disassemble_info *); +extern int print_insn_sh64 (bfd_vma, disassemble_info *); +extern int print_insn_sh64x_media (bfd_vma, disassemble_info *); +extern int print_insn_frv (bfd_vma, disassemble_info *); +extern int print_insn_iq2000 (bfd_vma, disassemble_info *); + +extern disassembler_ftype arc_get_disassembler (void *); +extern disassembler_ftype cris_get_disassembler (bfd *); + +extern void print_mips_disassembler_options (FILE *); +extern void print_ppc_disassembler_options (FILE *); +extern void print_arm_disassembler_options (FILE *); +extern void parse_arm_disassembler_option (char *); +extern int get_arm_regname_num_options (void); +extern int set_arm_regname_option (int); +extern int get_arm_regnames (int, const char **, const char **, const char ***); +extern bfd_boolean arm_symbol_is_valid (asymbol *, struct disassemble_info *); + +/* Fetch the disassembler for a given BFD, if that support is available. */ +extern disassembler_ftype disassembler (bfd *); + +/* Amend the disassemble_info structure as necessary for the target architecture. + Should only be called after initialising the info->arch field. */ +extern void disassemble_init_for_target (struct disassemble_info * info); + +/* Document any target specific options available from the disassembler. */ +extern void disassembler_usage (FILE *); + + +/* This block of definitions is for particular callers who read instructions + into a buffer before calling the instruction decoder. */ + +/* Here is a function which callers may wish to use for read_memory_func. + It gets bytes from a buffer. */ +extern int buffer_read_memory + (bfd_vma, bfd_byte *, unsigned int, struct disassemble_info *); + +/* This function goes with buffer_read_memory. + It prints a message using info->fprintf_func and info->stream. */ +extern void perror_memory (int, bfd_vma, struct disassemble_info *); + + +/* Just print the address in hex. This is included for completeness even + though both GDB and objdump provide their own (to print symbolic + addresses). */ +extern void generic_print_address + (bfd_vma, struct disassemble_info *); + +/* Always true. */ +extern int generic_symbol_at_address + (bfd_vma, struct disassemble_info *); + +/* Also always true. */ +extern bfd_boolean generic_symbol_is_valid + (asymbol *, struct disassemble_info *); + +/* Method to initialize a disassemble_info struct. This should be + called by all applications creating such a struct. */ +extern void init_disassemble_info (struct disassemble_info *info, void *stream, + fprintf_ftype fprintf_func); + +/* For compatibility with existing code. */ +#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \ + init_disassemble_info (&(INFO), (STREAM), (fprintf_ftype) (FPRINTF_FUNC)) +#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \ + init_disassemble_info (&(INFO), (STREAM), (fprintf_ftype) (FPRINTF_FUNC)) + + +#ifdef __cplusplus +} +#endif + +#endif /* ! defined (DIS_ASM_H) */ diff --git a/contrib/gdb/include/floatformat.h b/contrib/gdb/include/floatformat.h new file mode 100644 index 0000000..a8244ad --- /dev/null +++ b/contrib/gdb/include/floatformat.h @@ -0,0 +1,133 @@ +/* IEEE floating point support declarations, for GDB, the GNU Debugger. + Copyright 1991, 1994, 1995, 1997, 2000, 2003 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. */ + +#if !defined (FLOATFORMAT_H) +#define FLOATFORMAT_H 1 + +#include "ansidecl.h" + +/* A floatformat consists of a sign bit, an exponent and a mantissa. Once the + bytes are concatenated according to the byteorder flag, then each of those + fields is contiguous. We number the bits with 0 being the most significant + (i.e. BITS_BIG_ENDIAN type numbering), and specify which bits each field + contains with the *_start and *_len fields. */ + +/* What is the order of the bytes. */ + +enum floatformat_byteorders { + + /* Standard little endian byte order. + EX: 1.2345678e10 => 00 00 80 c5 e0 fe 06 42 */ + + floatformat_little, + + /* Standard big endian byte order. + EX: 1.2345678e10 => 42 06 fe e0 c5 80 00 00 */ + + floatformat_big, + + /* Little endian byte order but big endian word order. + EX: 1.2345678e10 => e0 fe 06 42 00 00 80 c5 */ + + floatformat_littlebyte_bigword + +}; + +enum floatformat_intbit { floatformat_intbit_yes, floatformat_intbit_no }; + +struct floatformat +{ + enum floatformat_byteorders byteorder; + unsigned int totalsize; /* Total size of number in bits */ + + /* Sign bit is always one bit long. 1 means negative, 0 means positive. */ + unsigned int sign_start; + + unsigned int exp_start; + unsigned int exp_len; + /* Bias added to a "true" exponent to form the biased exponent. It + is intentionally signed as, otherwize, -exp_bias can turn into a + very large number (e.g., given the exp_bias of 0x3fff and a 64 + bit long, the equation (long)(1 - exp_bias) evaluates to + 4294950914) instead of -16382). */ + int exp_bias; + /* Exponent value which indicates NaN. This is the actual value stored in + the float, not adjusted by the exp_bias. This usually consists of all + one bits. */ + unsigned int exp_nan; + + unsigned int man_start; + unsigned int man_len; + + /* Is the integer bit explicit or implicit? */ + enum floatformat_intbit intbit; + + /* Internal name for debugging. */ + const char *name; + + /* Validator method. */ + int (*is_valid) PARAMS ((const struct floatformat *fmt, const char *from)); +}; + +/* floatformats for IEEE single and double, big and little endian. */ + +extern const struct floatformat floatformat_ieee_single_big; +extern const struct floatformat floatformat_ieee_single_little; +extern const struct floatformat floatformat_ieee_double_big; +extern const struct floatformat floatformat_ieee_double_little; + +/* floatformat for ARM IEEE double, little endian bytes and big endian words */ + +extern const struct floatformat floatformat_ieee_double_littlebyte_bigword; + +/* floatformats for various extendeds. */ + +extern const struct floatformat floatformat_i387_ext; +extern const struct floatformat floatformat_m68881_ext; +extern const struct floatformat floatformat_i960_ext; +extern const struct floatformat floatformat_m88110_ext; +extern const struct floatformat floatformat_m88110_harris_ext; +extern const struct floatformat floatformat_arm_ext_big; +extern const struct floatformat floatformat_arm_ext_littlebyte_bigword; +/* IA-64 Floating Point register spilt into memory. */ +extern const struct floatformat floatformat_ia64_spill_big; +extern const struct floatformat floatformat_ia64_spill_little; +extern const struct floatformat floatformat_ia64_quad_big; +extern const struct floatformat floatformat_ia64_quad_little; + +/* Convert from FMT to a double. + FROM is the address of the extended float. + Store the double in *TO. */ + +extern void +floatformat_to_double PARAMS ((const struct floatformat *, const char *, double *)); + +/* The converse: convert the double *FROM to FMT + and store where TO points. */ + +extern void +floatformat_from_double PARAMS ((const struct floatformat *, + const double *, char *)); + +/* Return non-zero iff the data at FROM is a valid number in format FMT. */ + +extern int +floatformat_is_valid PARAMS ((const struct floatformat *fmt, const char *from)); + +#endif /* defined (FLOATFORMAT_H) */ diff --git a/contrib/gdb/include/fopen-bin.h b/contrib/gdb/include/fopen-bin.h new file mode 100644 index 0000000..b868f63 --- /dev/null +++ b/contrib/gdb/include/fopen-bin.h @@ -0,0 +1,27 @@ +/* Macros for the 'type' part of an fopen, freopen or fdopen. + + <Read|Write>[Update]<Binary file|text file> + + This version is for "binary" systems, where text and binary files are + different. An example is Mess-Dose. Many Unix systems could also + cope with a "b" in the string, indicating binary files, but some reject this + (and thereby don't conform to ANSI C, but what else is new?). + + This file is designed for inclusion by host-dependent .h files. No + user application should include it directly, since that would make + the application unable to be configured for both "same" and "binary" + variant systems. */ + +#define FOPEN_RB "rb" +#define FOPEN_WB "wb" +#define FOPEN_AB "ab" +#define FOPEN_RUB "r+b" +#define FOPEN_WUB "w+b" +#define FOPEN_AUB "a+b" + +#define FOPEN_RT "r" +#define FOPEN_WT "w" +#define FOPEN_AT "a" +#define FOPEN_RUT "r+" +#define FOPEN_WUT "w+" +#define FOPEN_AUT "a+" diff --git a/contrib/gdb/include/fopen-same.h b/contrib/gdb/include/fopen-same.h new file mode 100644 index 0000000..0f37529 --- /dev/null +++ b/contrib/gdb/include/fopen-same.h @@ -0,0 +1,27 @@ +/* Macros for the 'type' part of an fopen, freopen or fdopen. + + <Read|Write>[Update]<Binary file|text file> + + This version is for "same" systems, where text and binary files are + the same. An example is Unix. Many Unix systems could also add a + "b" to the string, indicating binary files, but some reject this + (and thereby don't conform to ANSI C, but what else is new?). + + This file is designed for inclusion by host-dependent .h files. No + user application should include it directly, since that would make + the application unable to be configured for both "same" and "binary" + variant systems. */ + +#define FOPEN_RB "r" +#define FOPEN_WB "w" +#define FOPEN_AB "a" +#define FOPEN_RUB "r+" +#define FOPEN_WUB "w+" +#define FOPEN_AUB "a+" + +#define FOPEN_RT "r" +#define FOPEN_WT "w" +#define FOPEN_AT "a" +#define FOPEN_RUT "r+" +#define FOPEN_WUT "w+" +#define FOPEN_AUT "a+" diff --git a/contrib/gdb/include/gdbm.h b/contrib/gdb/include/gdbm.h new file mode 100644 index 0000000..3ebc26d --- /dev/null +++ b/contrib/gdb/include/gdbm.h @@ -0,0 +1,91 @@ +/* GNU DBM - DataBase Manager include file + Copyright 1989, 1991 Free Software Foundation, Inc. + Written by Philip A. Nelson. + +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. */ + +/* You may contact the author by: + e-mail: phil@wwu.edu + us-mail: Philip A. Nelson + Computer Science Department + Western Washington University + Bellingham, WA 98226 + phone: (206) 676-3035 + +*************************************************************************/ + +/* Parameters to gdbm_open for READERS, WRITERS, and WRITERS who + can create the database. */ +#define GDBM_READER 0 +#define GDBM_WRITER 1 +#define GDBM_WRCREAT 2 +#define GDBM_NEWDB 3 + +/* Parameters to gdbm_store for simple insertion or replacement. */ +#define GDBM_INSERT 0 +#define GDBM_REPLACE 1 + + +/* The data and key structure. This structure is defined for compatibility. */ +typedef struct { + char *dptr; + int dsize; + } datum; + + +/* The file information header. This is good enough for most applications. */ +typedef struct {int dummy[10];} *GDBM_FILE; + + +/* These are the routines! */ + +extern GDBM_FILE gdbm_open (); + +extern void gdbm_close (); + +extern datum gdbm_fetch (); + +extern int gdbm_store (); + +extern int gdbm_delete (); + +extern datum gdbm_firstkey (); + +extern datum gdbm_nextkey (); + +extern int gdbm_reorganize (); + + +/* gdbm sends back the following error codes in the variable gdbm_errno. */ +typedef enum { NO_ERROR, + MALLOC_ERROR, + BLOCK_SIZE_ERROR, + FILE_OPEN_ERROR, + FILE_WRITE_ERROR, + FILE_SEEK_ERROR, + FILE_READ_ERROR, + BAD_MAGIC_NUMBER, + EMPTY_DATABASE, + CANT_BE_READER, + CANT_BE_WRITER, + READER_CANT_RECOVER, + READER_CANT_DELETE, + READER_CANT_STORE, + READER_CANT_REORGANIZE, + UNKNOWN_UPDATE, + ITEM_NOT_FOUND, + REORGANIZE_FAILED, + CANNOT_REPLACE} + gdbm_error; diff --git a/contrib/gdb/include/getopt.h b/contrib/gdb/include/getopt.h new file mode 100644 index 0000000..a99a229 --- /dev/null +++ b/contrib/gdb/include/getopt.h @@ -0,0 +1,144 @@ +/* Declarations for getopt. + Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 2000, + 2002 Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + 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, 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. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ +/* HAVE_DECL_* is a three-state macro: undefined, 0 or 1. If it is + undefined, we haven't run the autoconf check so provide the + declaration without arguments. If it is 0, we checked and failed + to find the declaration so provide a fully prototyped one. If it + is 1, we found it so don't provide any declaration at all. */ +#if !HAVE_DECL_GETOPT +#if defined (__GNU_LIBRARY__) || defined (HAVE_DECL_GETOPT) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in unistd.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else +#ifndef __cplusplus +extern int getopt (); +#endif /* __cplusplus */ +#endif +#endif /* !HAVE_DECL_GETOPT */ + +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* getopt.h */ diff --git a/contrib/gdb/include/hp-symtab.h b/contrib/gdb/include/hp-symtab.h new file mode 100644 index 0000000..6267d55 --- /dev/null +++ b/contrib/gdb/include/hp-symtab.h @@ -0,0 +1,1866 @@ +/* Definitions and structures for reading debug symbols from the + native HP C compiler. + + Written by the Center for Software Science at the University of Utah + and by Cygnus Support. + + Copyright 1994, 1995, 1998, 1999, 2003 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. */ + +#ifndef HP_SYMTAB_INCLUDED +#define HP_SYMTAB_INCLUDED + +/* General information: + + This header file defines and describes only the data structures + necessary to read debug symbols produced by the HP C compiler, + HP ANSI C++ compiler, and HP FORTRAN 90 compiler using the + SOM object file format. + (For a full description of the debug format, ftp hpux-symtab.h from + jaguar.cs.utah.edu:/dist). + + Additional notes (Rich Title) + This file is a reverse-engineered version of a file called + "symtab.h" which exists internal to HP's Computer Languages Organization + in /CLO/Components/DDE/obj/som/symtab.h. Because HP's version of + the file is copyrighted and not distributed, it is necessary for + GDB to use the reverse-engineered version that follows. + Work was done by Cygnus to reverse-engineer the C subset of symtab.h. + The WDB project has extended this to also contain the C++ + symbol definitions, the F90 symbol definitions, + and the DOC (debugging-optimized-code) symbol definitions. + In some cases (the C++ symbol definitions) + I have added internal documentation here that + goes beyond what is supplied in HP's symtab.h. If we someday + unify these files again, the extra comments should be merged back + into HP's symtab.h. + + ------------------------------------------------------------------- + + Debug symbols are contained entirely within an unloadable space called + $DEBUG$. $DEBUG$ contains several subspaces which group related + debug symbols. + + $GNTT$ contains information for global variables, types and contants. + + $LNTT$ contains information for procedures (including nesting), scoping + information, local variables, types, and constants. + + $SLT$ contains source line information so that code addresses may be + mapped to source lines. + + $VT$ contains various strings and constants for named objects (variables, + typedefs, functions, etc). Strings are stored as null-terminated character + lists. Constants always begin on word boundaries. The first byte of + the VT must be zero (a null string). + + $XT$ is not currently used by GDB. + + Many structures within the subspaces point to other structures within + the same subspace, or to structures within a different subspace. These + pointers are represented as a structure index from the beginning of + the appropriate subspace. */ + +/* Used to describe where a constant is stored. */ +enum location_type +{ + LOCATION_IMMEDIATE, + LOCATION_PTR, + LOCATION_VT, +}; + +/* Languages supported by this debug format. Within the data structures + this type is limited to 4 bits for a maximum of 16 languages. */ +enum hp_language +{ + HP_LANGUAGE_UNKNOWN, + HP_LANGUAGE_C, + HP_LANGUAGE_FORTRAN, + HP_LANGUAGE_F77 = HP_LANGUAGE_FORTRAN, + HP_LANGUAGE_PASCAL, + HP_LANGUAGE_MODCAL, + HP_LANGUAGE_COBOL, + HP_LANGUAGE_BASIC, + HP_LANGUAGE_ADA, + HP_LANGUAGE_CPLUSPLUS, + HP_LANGUAGE_DMPASCAL +}; + + +/* Basic data types available in this debug format. Within the data + structures this type is limited to 5 bits for a maximum of 32 basic + data types. */ +enum hp_type +{ + HP_TYPE_UNDEFINED, /* 0 */ + HP_TYPE_BOOLEAN, /* 1 */ + HP_TYPE_CHAR, /* 2 */ + HP_TYPE_INT, /* 3 */ + HP_TYPE_UNSIGNED_INT, /* 4 */ + HP_TYPE_REAL, /* 5 */ + HP_TYPE_COMPLEX, /* 6 */ + HP_TYPE_STRING200, /* 7 */ + HP_TYPE_LONGSTRING200, /* 8 */ + HP_TYPE_TEXT, /* 9 */ + HP_TYPE_FLABEL, /* 10 */ + HP_TYPE_FTN_STRING_SPEC, /* 11 */ + HP_TYPE_MOD_STRING_SPEC, /* 12 */ + HP_TYPE_PACKED_DECIMAL, /* 13 */ + HP_TYPE_REAL_3000, /* 14 */ + HP_TYPE_MOD_STRING_3000, /* 15 */ + HP_TYPE_ANYPOINTER, /* 16 */ + HP_TYPE_GLOBAL_ANYPOINTER, /* 17 */ + HP_TYPE_LOCAL_ANYPOINTER, /* 18 */ + HP_TYPE_COMPLEXS3000, /* 19 */ + HP_TYPE_FTN_STRING_S300_COMPAT, /* 20 */ + HP_TYPE_FTN_STRING_VAX_COMPAT, /* 21 */ + HP_TYPE_BOOLEAN_S300_COMPAT, /* 22 */ + HP_TYPE_BOOLEAN_VAX_COMPAT, /* 23 */ + HP_TYPE_WIDE_CHAR, /* 24 */ + HP_TYPE_LONG, /* 25 */ + HP_TYPE_UNSIGNED_LONG, /* 26 */ + HP_TYPE_DOUBLE, /* 27 */ + HP_TYPE_TEMPLATE_ARG, /* 28 */ + HP_TYPE_VOID /* 29 */ +}; + +/* An immediate name and type table entry. + + extension and immediate will always be one. + global will always be zero. + hp_type is the basic type this entry describes. + bitlength is the length in bits for the basic type. */ +struct dnttp_immediate +{ + unsigned int extension: 1; + unsigned int immediate: 1; + unsigned int global: 1; + unsigned int type: 5; + unsigned int bitlength: 24; +}; + +/* A nonimmediate name and type table entry. + + extension will always be one. + immediate will always be zero. + if global is zero, this entry points into the LNTT + if global is one, this entry points into the GNTT + index is the index within the GNTT or LNTT for this entry. */ +struct dnttp_nonimmediate +{ + unsigned int extension: 1; + unsigned int immediate: 1; + unsigned int global: 1; + unsigned int index: 29; +}; + +/* A pointer to an entry in the GNTT and LNTT tables. It has two + forms depending on the type being described. + + The immediate form is used for simple entries and is one + word. + + The nonimmediate form is used for complex entries and contains + an index into the LNTT or GNTT which describes the entire type. + + If a dnttpointer is -1, then it is a NIL entry. */ + +#define DNTTNIL (-1) +typedef union dnttpointer +{ + struct dnttp_immediate dntti; + struct dnttp_nonimmediate dnttp; + int word; +} dnttpointer; + +/* An index into the source line table. As with dnttpointers, a sltpointer + of -1 indicates a NIL entry. */ +#define SLTNIL (-1) +typedef int sltpointer; + +/* Index into DOC (= "Debugging Optimized Code") line table. */ +#define LTNIL (-1) +typedef int ltpointer; + +/* Index into context table. */ +#define CTXTNIL (-1) +typedef int ctxtpointer; + +/* Unsigned byte offset into the VT. */ +typedef unsigned int vtpointer; + +/* A DNTT entry (used within the GNTT and LNTT). + + DNTT entries are variable sized objects, but are always a multiple + of 3 words (we call each group of 3 words a "block"). + + The first bit in each block is an extension bit. This bit is zero + for the first block of a DNTT entry. If the entry requires more + than one block, then this bit is set to one in all blocks after + the first one. */ + +/* Each DNTT entry describes a particular debug symbol (beginning of + a source file, a function, variables, structures, etc. + + The type of the DNTT entry is stored in the "kind" field within the + DNTT entry itself. */ + +enum dntt_entry_type +{ + DNTT_TYPE_NIL = -1, + DNTT_TYPE_SRCFILE, + DNTT_TYPE_MODULE, + DNTT_TYPE_FUNCTION, + DNTT_TYPE_ENTRY, + DNTT_TYPE_BEGIN, + DNTT_TYPE_END, + DNTT_TYPE_IMPORT, + DNTT_TYPE_LABEL, + DNTT_TYPE_FPARAM, + DNTT_TYPE_SVAR, + DNTT_TYPE_DVAR, + DNTT_TYPE_HOLE1, + DNTT_TYPE_CONST, + DNTT_TYPE_TYPEDEF, + DNTT_TYPE_TAGDEF, + DNTT_TYPE_POINTER, + DNTT_TYPE_ENUM, + DNTT_TYPE_MEMENUM, + DNTT_TYPE_SET, + DNTT_TYPE_SUBRANGE, + DNTT_TYPE_ARRAY, + DNTT_TYPE_STRUCT, + DNTT_TYPE_UNION, + DNTT_TYPE_FIELD, + DNTT_TYPE_VARIANT, + DNTT_TYPE_FILE, + DNTT_TYPE_FUNCTYPE, + DNTT_TYPE_WITH, + DNTT_TYPE_COMMON, + DNTT_TYPE_COBSTRUCT, + DNTT_TYPE_XREF, + DNTT_TYPE_SA, + DNTT_TYPE_MACRO, + DNTT_TYPE_BLOCKDATA, + DNTT_TYPE_CLASS_SCOPE, + DNTT_TYPE_REFERENCE, + DNTT_TYPE_PTRMEM, + DNTT_TYPE_PTRMEMFUNC, + DNTT_TYPE_CLASS, + DNTT_TYPE_GENFIELD, + DNTT_TYPE_VFUNC, + DNTT_TYPE_MEMACCESS, + DNTT_TYPE_INHERITANCE, + DNTT_TYPE_FRIEND_CLASS, + DNTT_TYPE_FRIEND_FUNC, + DNTT_TYPE_MODIFIER, + DNTT_TYPE_OBJECT_ID, + DNTT_TYPE_MEMFUNC, + DNTT_TYPE_TEMPLATE, + DNTT_TYPE_TEMPLATE_ARG, + DNTT_TYPE_FUNC_TEMPLATE, + DNTT_TYPE_LINK, + DNTT_TYPE_DYN_ARRAY_DESC, + DNTT_TYPE_DESC_SUBRANGE, + DNTT_TYPE_BEGIN_EXT, + DNTT_TYPE_INLN, + DNTT_TYPE_INLN_LIST, + DNTT_TYPE_ALIAS, + DNTT_TYPE_DOC_FUNCTION, + DNTT_TYPE_DOC_MEMFUNC, + DNTT_TYPE_MAX +}; + +/* DNTT_TYPE_SRCFILE: + + One DNTT_TYPE_SRCFILE symbol is output for the start of each source + file and at the begin and end of an included file. A DNTT_TYPE_SRCFILE + entry is also output before each DNTT_TYPE_FUNC symbol so that debuggers + can determine what file a function was defined in. + + LANGUAGE describes the source file's language. + + NAME points to an VT entry providing the source file's name. + + Note the name used for DNTT_TYPE_SRCFILE entries are exactly as seen + by the compiler (ie they may be relative or absolute). C include files + via <> inclusion must use absolute paths. + + ADDRESS points to an SLT entry from which line number and code locations + may be determined. */ + +struct dntt_type_srcfile +{ + unsigned int extension: 1; + unsigned int kind: 10; /* DNTT_TYPE_SRCFILE */ + unsigned int language: 4; + unsigned int unused: 17; + vtpointer name; + sltpointer address; +}; + +/* DNTT_TYPE_MODULE: + + A DNTT_TYPE_MODULE symbol is emitted for the start of a pascal + module or C source file. A module indicates a compilation unit + for name-scoping purposes; in that regard there should be + a 1-1 correspondence between GDB "symtab"'s and MODULE symbol records. + + Each DNTT_TYPE_MODULE must have an associated DNTT_TYPE_END symbol. + + NAME points to a VT entry providing the module's name. Note C + source files are considered nameless modules. + + ALIAS point to a VT entry providing a secondary name. + + ADDRESS points to an SLT entry from which line number and code locations + may be determined. */ + +struct dntt_type_module +{ + unsigned int extension: 1; + unsigned int kind: 10; /* DNTT_TYPE_MODULE */ + unsigned int unused: 21; + vtpointer name; + vtpointer alias; + dnttpointer unused2; + sltpointer address; +}; + +/* DNTT_TYPE_FUNCTION, + DNTT_TYPE_ENTRY, + DNTT_TYPE_BLOCKDATA, + DNTT_TYPE_MEMFUNC: + + A DNTT_TYPE_FUNCTION symbol is emitted for each function definition; + a DNTT_TYPE_ENTRY symbols is used for secondary entry points. Both + symbols used the dntt_type_function structure. + A DNTT_TYPE_BLOCKDATA symbol is emitted ...? + A DNTT_TYPE_MEMFUNC symbol is emitted for inlined member functions (C++). + + Each of DNTT_TYPE_FUNCTION must have a matching DNTT_TYPE_END. + + GLOBAL is nonzero if the function has global scope. + + LANGUAGE describes the function's source language. + + OPT_LEVEL describes the optimization level the function was compiled + with. + + VARARGS is nonzero if the function uses varargs. + + NAME points to a VT entry providing the function's name. + + ALIAS points to a VT entry providing a secondary name for the function. + + FIRSTPARAM points to a LNTT entry which describes the parameter list. + + ADDRESS points to an SLT entry from which line number and code locations + may be determined. + + ENTRYADDR is the memory address corresponding the function's entry point + + RETVAL points to a LNTT entry describing the function's return value. + + LOWADDR is the lowest memory address associated with this function. + + HIADDR is the highest memory address associated with this function. */ + +struct dntt_type_function +{ + unsigned int extension: 1; + unsigned int kind: 10; /* DNTT_TYPE_FUNCTION, + DNTT_TYPE_ENTRY, + DNTT_TYPE_BLOCKDATA + or DNTT_TYPE_MEMFUNC */ + unsigned int global: 1; + unsigned int language: 4; + unsigned int nest_level: 5; + unsigned int opt_level: 2; + unsigned int varargs: 1; + unsigned int lang_info: 4; + unsigned int inlined: 1; + unsigned int localalloc: 1; + unsigned int expansion: 1; + unsigned int unused: 1; + vtpointer name; + vtpointer alias; + dnttpointer firstparam; + sltpointer address; + CORE_ADDR entryaddr; + dnttpointer retval; + CORE_ADDR lowaddr; + CORE_ADDR hiaddr; +}; + +/* DNTT_TYPE_BEGIN: + + A DNTT_TYPE_BEGIN symbol is emitted to begin a new nested scope. + Every DNTT_TYPE_BEGIN symbol must have a matching DNTT_TYPE_END symbol. + + CLASSFLAG is nonzero if this is the beginning of a c++ class definition. + + ADDRESS points to an SLT entry from which line number and code locations + may be determined. */ + +struct dntt_type_begin +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int classflag: 1; + unsigned int unused: 20; + sltpointer address; +}; + +/* DNTT_TYPE_END: + + A DNTT_TYPE_END symbol is emitted when closing a scope started by + a DNTT_TYPE_MODULE, DNTT_TYPE_FUNCTION, DNTT_TYPE_WITH, + DNTT_TYPE_COMMON, DNTT_TYPE_BEGIN, and DNTT_TYPE_CLASS_SCOPE symbols. + + ENDKIND describes what type of scope the DNTT_TYPE_END is closing + (one of the above 6 kinds). + + CLASSFLAG is nonzero if this is the end of a c++ class definition. + + ADDRESS points to an SLT entry from which line number and code locations + may be determined. + + BEGINSCOPE points to the LNTT entry which opened the scope. */ + +struct dntt_type_end +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int endkind: 10; + unsigned int classflag: 1; + unsigned int unused: 10; + sltpointer address; + dnttpointer beginscope; +}; + +/* DNTT_TYPE_IMPORT is unused by GDB. */ +/* DNTT_TYPE_LABEL is unused by GDB. */ + +/* DNTT_TYPE_FPARAM: + + A DNTT_TYPE_FPARAM symbol is emitted for a function argument. When + chained together the symbols represent an argument list for a function. + + REGPARAM is nonzero if this parameter was passed in a register. + + INDIRECT is nonzero if this parameter is a pointer to the parameter + (pass by reference or pass by value for large items). + + LONGADDR is nonzero if the parameter is a 64bit pointer. + + NAME is a pointer into the VT for the parameter's name. + + LOCATION describes where the parameter is stored. Depending on the + parameter type LOCATION could be a register number, or an offset + from the stack pointer. + + TYPE points to a NTT entry describing the type of this parameter. + + NEXTPARAM points to the LNTT entry describing the next parameter. */ + +struct dntt_type_fparam +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int regparam: 1; + unsigned int indirect: 1; + unsigned int longaddr: 1; + unsigned int copyparam: 1; + unsigned int dflt: 1; + unsigned int doc_ranges: 1; + unsigned int misc_kind: 1; + unsigned int unused: 14; + vtpointer name; + CORE_ADDR location; + dnttpointer type; + dnttpointer nextparam; + int misc; +}; + +/* DNTT_TYPE_SVAR: + + A DNTT_TYPE_SVAR is emitted to describe a variable in static storage. + + GLOBAL is nonzero if the variable has global scope. + + INDIRECT is nonzero if the variable is a pointer to an object. + + LONGADDR is nonzero if the variable is in long pointer space. + + STATICMEM is nonzero if the variable is a member of a class. + + A_UNION is nonzero if the variable is an anonymous union member. + + NAME is a pointer into the VT for the variable's name. + + LOCATION provides the memory address for the variable. + + TYPE is a pointer into either the GNTT or LNTT which describes + the type of this variable. */ + +struct dntt_type_svar +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int global: 1; + unsigned int indirect: 1; + unsigned int longaddr: 1; + unsigned int staticmem: 1; + unsigned int a_union: 1; + unsigned int unused1: 1; + unsigned int thread_specific: 1; + unsigned int unused2: 14; + vtpointer name; + CORE_ADDR location; + dnttpointer type; + unsigned int offset; + unsigned int displacement; +}; + +/* DNTT_TYPE_DVAR: + + A DNTT_TYPE_DVAR is emitted to describe automatic variables and variables + held in registers. + + GLOBAL is nonzero if the variable has global scope. + + INDIRECT is nonzero if the variable is a pointer to an object. + + REGVAR is nonzero if the variable is in a register. + + A_UNION is nonzero if the variable is an anonymous union member. + + NAME is a pointer into the VT for the variable's name. + + LOCATION provides the memory address or register number for the variable. + + TYPE is a pointer into either the GNTT or LNTT which describes + the type of this variable. */ + +struct dntt_type_dvar +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int global: 1; + unsigned int indirect: 1; + unsigned int regvar: 1; + unsigned int a_union: 1; + unsigned int unused: 17; + vtpointer name; + int location; + dnttpointer type; + unsigned int offset; +}; + +/* DNTT_TYPE_CONST: + + A DNTT_TYPE_CONST symbol is emitted for program constants. + + GLOBAL is nonzero if the constant has global scope. + + INDIRECT is nonzero if the constant is a pointer to an object. + + LOCATION_TYPE describes where to find the constant's value + (in the VT, memory, or embedded in an instruction). + + CLASSMEM is nonzero if the constant is a member of a class. + + NAME is a pointer into the VT for the constant's name. + + LOCATION provides the memory address, register number or pointer + into the VT for the constant's value. + + TYPE is a pointer into either the GNTT or LNTT which describes + the type of this variable. */ + +struct dntt_type_const +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int global: 1; + unsigned int indirect: 1; + unsigned int location_type: 3; + unsigned int classmem: 1; + unsigned int unused: 15; + vtpointer name; + CORE_ADDR location; + dnttpointer type; + unsigned int offset; + unsigned int displacement; +}; + +/* DNTT_TYPE_TYPEDEF and DNTT_TYPE_TAGDEF: + + The same structure is used to describe typedefs and tagdefs. + + DNTT_TYPE_TYPEDEFS are associated with C "typedefs". + + DNTT_TYPE_TAGDEFs are associated with C "struct", "union", and "enum" + tags, which may have the same name as a typedef in the same scope. + Also they are associated with C++ "class" tags, which implicitly have + the same name as the class type. + + GLOBAL is nonzero if the typedef/tagdef has global scope. + + TYPEINFO is used to determine if full type information is available + for a tag. (usually 1, but can be zero for opaque types in C). + + NAME is a pointer into the VT for the constant's name. + + TYPE points to the underlying type for the typedef/tagdef in the + GNTT or LNTT. */ + +struct dntt_type_type +{ + unsigned int extension: 1; + unsigned int kind: 10; /* DNTT_TYPE_TYPEDEF or + DNTT_TYPE_TAGDEF. */ + unsigned int global: 1; + unsigned int typeinfo: 1; + unsigned int unused: 19; + vtpointer name; + dnttpointer type; /* Underlying type, which for TAGDEF's may be + DNTT_TYPE_STRUCT, DNTT_TYPE_UNION, + DNTT_TYPE_ENUM, or DNTT_TYPE_CLASS. + For TYPEDEF's other underlying types + are also possible. */ +}; + +/* DNTT_TYPE_POINTER: + + Used to describe a pointer to an underlying type. + + POINTSTO is a pointer into the GNTT or LNTT for the type which this + pointer points to. + + BITLENGTH is the length of the pointer (not the underlying type). */ + +struct dntt_type_pointer +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int unused: 21; + dnttpointer pointsto; + unsigned int bitlength; +}; + + +/* DNTT_TYPE_ENUM: + + Used to describe enumerated types. + + FIRSTMEM is a pointer to a DNTT_TYPE_MEMENUM in the GNTT/LNTT which + describes the first member (and contains a pointer to the chain of + members). + + BITLENGTH is the number of bits used to hold the values of the enum's + members. */ + +struct dntt_type_enum +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int unused: 21; + dnttpointer firstmem; + unsigned int bitlength; +}; + +/* DNTT_TYPE_MEMENUM + + Used to describe members of an enumerated type. + + CLASSMEM is nonzero if this member is part of a class. + + NAME points into the VT for the name of this member. + + VALUE is the value of this enumeration member. + + NEXTMEM points to the next DNTT_TYPE_MEMENUM in the chain. */ + +struct dntt_type_memenum +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int classmem: 1; + unsigned int unused: 20; + vtpointer name; + unsigned int value; + dnttpointer nextmem; +}; + +/* DNTT_TYPE_SET + + Used to describe PASCAL "set" type. + + DECLARATION describes the bitpacking of the set. + + SUBTYPE points to a DNTT entry describing the type of the members. + + BITLENGTH is the size of the set. */ + +struct dntt_type_set +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int declaration: 2; + unsigned int unused: 19; + dnttpointer subtype; + unsigned int bitlength; +}; + +/* DNTT_TYPE_SUBRANGE + + Used to describe subrange type. + + DYN_LOW describes the lower bound of the subrange: + + 00 for a constant lower bound (found in LOWBOUND). + + 01 for a dynamic lower bound with the lower bound found in the + memory address pointed to by LOWBOUND. + + 10 for a dynamic lower bound described by an variable found in the + DNTT/LNTT (LOWBOUND would be a pointer into the DNTT/LNTT). + + DYN_HIGH is similar to DYN_LOW, except it describes the upper bound. + + SUBTYPE points to the type of the subrange. + + BITLENGTH is the length in bits needed to describe the subrange's + values. */ + +struct dntt_type_subrange +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int dyn_low: 2; + unsigned int dyn_high: 2; + unsigned int unused: 17; + int lowbound; + int highbound; + dnttpointer subtype; + unsigned int bitlength; +}; + +/* DNTT_TYPE_ARRAY + + Used to describe an array type. + + DECLARATION describes the bit packing used in the array. + + ARRAYISBYTES is nonzero if the field in arraylength describes the + length in bytes rather than in bits. A value of zero is used to + describe an array with size 2**32. + + ELEMISBYTES is nonzero if the length if each element in the array + is describes in bytes rather than bits. A value of zero is used + to an element with size 2**32. + + ELEMORDER is nonzero if the elements are indexed in increasing order. + + JUSTIFIED if the elements are left justified to index zero. + + ARRAYLENGTH is the length of the array. + + INDEXTYPE is a DNTT pointer to the type used to index the array. + + ELEMTYPE is a DNTT pointer to the type for the array elements. + + ELEMLENGTH is the length of each element in the array (including + any padding). + + Multi-dimensional arrays are represented by ELEMTYPE pointing to + another DNTT_TYPE_ARRAY. */ + +struct dntt_type_array +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int declaration: 2; + unsigned int dyn_low: 2; + unsigned int dyn_high: 2; + unsigned int arrayisbytes: 1; + unsigned int elemisbytes: 1; + unsigned int elemorder: 1; + unsigned int justified: 1; + unsigned int unused: 11; + unsigned int arraylength; + dnttpointer indextype; + dnttpointer elemtype; + unsigned int elemlength; +}; + +/* DNTT_TYPE_STRUCT + + DNTT_TYPE_STRUCT is used to describe a C structure. + + DECLARATION describes the bitpacking used. + + FIRSTFIELD is a DNTT pointer to the first field of the structure + (each field contains a pointer to the next field, walk the list + to access all fields of the structure). + + VARTAGFIELD and VARLIST are used for Pascal variant records. + + BITLENGTH is the size of the structure in bits. */ + +struct dntt_type_struct +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int declaration: 2; + unsigned int unused: 19; + dnttpointer firstfield; + dnttpointer vartagfield; + dnttpointer varlist; + unsigned int bitlength; +}; + +/* DNTT_TYPE_UNION + + DNTT_TYPE_UNION is used to describe a C union. + + FIRSTFIELD is a DNTT pointer to the beginning of the field chain. + + BITLENGTH is the size of the union in bits. */ + +struct dntt_type_union +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int unused: 21; + dnttpointer firstfield; + unsigned int bitlength; +}; + +/* DNTT_TYPE_FIELD + + DNTT_TYPE_FIELD describes one field in a structure or union + or C++ class. + + VISIBILITY is used to describe the visibility of the field + (for c++. public = 0, protected = 1, private = 2). + + A_UNION is nonzero if this field is a member of an anonymous union. + + STATICMEM is nonzero if this field is a static member of a template. + + NAME is a pointer into the VT for the name of the field. + + BITOFFSET gives the offset of this field in bits from the beginning + of the structure or union this field is a member of. + + TYPE is a DNTT pointer to the type describing this field. + + BITLENGTH is the size of the entry in bits. + + NEXTFIELD is a DNTT pointer to the next field in the chain. */ + +struct dntt_type_field +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int visibility: 2; + unsigned int a_union: 1; + unsigned int staticmem: 1; + unsigned int unused: 17; + vtpointer name; + unsigned int bitoffset; + dnttpointer type; + unsigned int bitlength; + dnttpointer nextfield; +}; + +/* DNTT_TYPE_VARIANT is unused by GDB. */ +/* DNTT_TYPE_FILE is unused by GDB. */ + +/* DNTT_TYPE_FUNCTYPE + + I think this is used to describe a function type (e.g., would + be emitted as part of a function-pointer description). + + VARARGS is nonzero if this function uses varargs. + + FIRSTPARAM is a DNTT pointer to the first entry in the parameter + chain. + + RETVAL is a DNTT pointer to the type of the return value. */ + +struct dntt_type_functype +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int varargs: 1; + unsigned int info: 4; + unsigned int unused: 16; + unsigned int bitlength; + dnttpointer firstparam; + dnttpointer retval; +}; + +/* DNTT_TYPE_WITH is emitted by C++ to indicate "with" scoping semantics. + (Probably also emitted by PASCAL to support "with"...). + + C++ example: Say "memfunc" is a method of class "c", and say + "m" is a data member of class "c". Then from within "memfunc", + it is legal to reference "m" directly (e.g. you don't have to + say "this->m". The symbol table indicates + this by emitting a DNTT_TYPE_WITH symbol within the function "memfunc", + pointing to the type symbol for class "c". + + In GDB, this symbol record is unnecessary, + because GDB's symbol lookup algorithm + infers the "with" semantics when it sees a "this" argument to the member + function. So GDB can safely ignore the DNTT_TYPE_WITH record. + + A DNTT_TYPE_WITH has a matching DNTT_TYPE_END symbol. */ + +struct dntt_type_with +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* always DNTT_TYPE_WITH */ + unsigned int addrtype: 2; /* 0 => STATTYPE */ + /* 1 => DYNTYPE */ + /* 2 => REGTYPE */ + unsigned int indirect: 1; /* 1 => pointer to object */ + unsigned int longaddr: 1; /* 1 => in long pointer space */ + unsigned int nestlevel: 6; /* # of nesting levels back */ + unsigned int doc_ranges: 1; /* 1 => location is range list */ + unsigned int unused: 10; + long location; /* where stored (allocated) */ + sltpointer address; + dnttpointer type; /* type of with expression */ + vtpointer name; /* name of with expression */ + unsigned long offset; /* byte offset from location */ +}; + +/* DNTT_TYPE_COMMON is unsupported by GDB. */ +/* A DNTT_TYPE_COMMON symbol must have a matching DNTT_TYPE_END symbol */ + +/* DNTT_TYPE_COBSTRUCT is unsupported by GDB. */ +/* DNTT_TYPE_XREF is unsupported by GDB. */ +/* DNTT_TYPE_SA is unsupported by GDB. */ +/* DNTT_TYPE_MACRO is unsupported by GDB */ + +/* DNTT_TYPE_BLOCKDATA has the same structure as DNTT_TYPE_FUNCTION */ + +/* The following are the C++ specific SOM records */ + +/* The purpose of the DNTT_TYPE_CLASS_SCOPE is to bracket C++ methods + and indicate the method name belongs in the "class scope" rather + than in the module they are being defined in. For example: + + class c { + ... + void memfunc(); // member function + }; + + void c::memfunc() // definition of class c's "memfunc" + { + ... + } + + main() + { + ... + } + + In the above, the name "memfunc" is not directly visible from "main". + I.e., you have to say "break c::memfunc". + If it were a normal function (not a method), it would be visible + via the simple "break memfunc". Since "memfunc" otherwise looks + like a normal FUNCTION in the symbol table, the bracketing + CLASS_SCOPE is what is used to indicate it is really a method. + + + A DNTT_TYPE_CLASS_SCOPE symbol must have a matching DNTT_TYPE_END symbol. */ + +struct dntt_type_class_scope +{ + unsigned int extension: 1; /* Always zero. */ + unsigned int kind: 10; /* Always DNTT_TYPE_CLASS_SCOPE. */ + unsigned int unused: 21; + sltpointer address ; /* Pointer to SLT entry. */ + dnttpointer type ; /* Pointer to class type DNTT. */ +}; + +/* C++ reference parameter. + The structure of this record is the same as DNTT_TYPE_POINTER - + refer to struct dntt_type_pointer. */ + +/* The next two describe C++ pointer-to-data-member type, and + pointer-to-member-function type, respectively. + DNTT_TYPE_PTRMEM and DNTT_TYPE_PTRMEMFUNC have the same structure. */ + +struct dntt_type_ptrmem +{ + unsigned int extension: 1; /* Always zero. */ + unsigned int kind: 10; /* Always DNTT_TYPE_PTRMEM. */ + unsigned int unused: 21; + dnttpointer pointsto ; /* Pointer to class DNTT. */ + dnttpointer memtype ; /* Type of member. */ +}; + +struct dntt_type_ptrmemfunc +{ + unsigned int extension: 1; /* Always zero. */ + unsigned int kind: 10; /* Always DNTT_TYPE_PTRMEMFUNC. */ + unsigned int unused: 21; + dnttpointer pointsto ; /* Pointer to class DNTT. */ + dnttpointer memtype ; /* Type of member. */ +}; + +/* The DNTT_TYPE_CLASS symbol is emitted to describe a class type. + "memberlist" points to a chained list of FIELD or GENFIELD records + indicating the class members. "parentlist" points to a chained list + of INHERITANCE records indicating classes from which we inherit + fields. */ + +struct dntt_type_class +{ + unsigned int extension: 1; /* Always zero. */ + unsigned int kind: 10; /* Always DNTT_TYPE_CLASS. */ + unsigned int abstract: 1; /* Is this an abstract class? */ + unsigned int class_decl: 2; /* 0=class,1=union,2=struct. */ + unsigned int expansion: 1; /* 1=template expansion. */ + unsigned int unused: 17; + dnttpointer memberlist ; /* Ptr to chain of [GEN]FIELDs. */ + unsigned long vtbl_loc ; /* Offset in obj of ptr to vtbl. */ + dnttpointer parentlist ; /* Ptr to K_INHERITANCE list. */ + unsigned long bitlength ; /* Total at this level. */ + dnttpointer identlist ; /* Ptr to chain of class ident's. */ + dnttpointer friendlist ; /* Ptr to K_FRIEND list. */ + dnttpointer templateptr ; /* Ptr to template. */ + dnttpointer nextexp ; /* Ptr to next expansion. */ +}; + +/* Class members are indicated via either the FIELD record (for + data members, same as for C struct fields), or by the GENFIELD record + (for member functions). */ + +struct dntt_type_genfield +{ + unsigned int extension: 1; /* Always zero. */ + unsigned int kind: 10; /* Always DNTT_TYPE_GENFIELD. */ + unsigned int visibility: 2; /* Pub = 0, prot = 1, priv = 2. */ + unsigned int a_union: 1; /* 1 => anonymous union member. */ + unsigned int unused: 18; + dnttpointer field ; /* Pointer to field or qualifier. */ + dnttpointer nextfield ; /* Pointer to next field. */ +}; + +/* C++ virtual functions. */ + +struct dntt_type_vfunc +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* always DNTT_TYPE_VFUNC */ + unsigned int pure: 1; /* pure virtual function ? */ + unsigned int unused: 20; + dnttpointer funcptr ; /* points to FUNCTION symbol */ + unsigned long vtbl_offset ; /* offset into vtbl for virtual */ +}; + +/* Not precisely sure what this is intended for - DDE ignores it. */ + +struct dntt_type_memaccess +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* always DNTT_TYPE_MEMACCESS */ + unsigned int unused: 21; + dnttpointer classptr ; /* pointer to base class */ + dnttpointer field ; /* pointer field */ +}; + +/* The DNTT_TYPE_INHERITANCE record describes derived classes. + In particular, the "parentlist" field of the CLASS record points + to a list of INHERITANCE records for classes from which we + inherit members. */ + +struct dntt_type_inheritance +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* always DNTT_TYPE_INHERITANCE */ + unsigned int Virtual: 1; /* virtual base class ? */ + unsigned int visibility: 2; /* pub = 0, prot = 1, priv = 2 */ + unsigned int unused: 18; + dnttpointer classname ; /* first parent class, if any */ + unsigned long offset ; /* offset to start of base class */ + dnttpointer next ; /* pointer to next K_INHERITANCE */ + unsigned long future[2] ; /* padding to 3-word block end */ +}; + +/* C++ "friend" classes ... */ + +struct dntt_type_friend_class +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* always DNTT_TYPE_FRIEND_CLASS */ + unsigned int unused: 21; + dnttpointer classptr ; /* pointer to class DNTT */ + dnttpointer next ; /* next DNTT_FRIEND */ +}; + +struct dntt_type_friend_func +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* always DNTT_TYPE_FRIEND_FUNC */ + unsigned int unused: 21; + dnttpointer funcptr ; /* pointer to function */ + dnttpointer classptr ; /* pointer to class DNTT */ + dnttpointer next ; /* next DNTT_FRIEND */ + unsigned long future[2] ; /* padding to 3-word block end */ +}; + +/* DDE appears to ignore the DNTT_TYPE_MODIFIER record. + It could perhaps be used to give better "ptype" output in GDB; + otherwise it is probably safe for GDB to ignore it also. */ + +struct dntt_type_modifier +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* always DNTT_TYPE_MODIFIER */ + unsigned int m_const: 1; /* const */ + unsigned int m_static: 1; /* static */ + unsigned int m_void: 1; /* void */ + unsigned int m_volatile: 1; /* volatile */ + unsigned int m_duplicate: 1; /* duplicate */ + unsigned int unused: 16; + dnttpointer type ; /* subtype */ + unsigned long future ; /* padding to 3-word block end */ +}; + +/* I'm not sure what this was intended for - DDE ignores it. */ + +struct dntt_type_object_id +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* always DNTT_TYPE_OBJECT_ID */ + unsigned int indirect: 1; /* Is object_ident addr of addr? */ + unsigned int unused: 20; + unsigned long object_ident ; /* object identifier */ + unsigned long offset ; /* offset to start of base class */ + dnttpointer next ; /* pointer to next K_OBJECT_ID */ + unsigned long segoffset ; /* for linker fixup */ + unsigned long future ; /* padding to 3-word block end */ +}; + +/* No separate dntt_type_memfunc; same as dntt_type_func */ + +/* Symbol records to support templates. These only get used + in DDE's "describe" output (like GDB's "ptype"). */ + +/* The TEMPLATE record is the header for a template-class. + Like the CLASS record, a TEMPLATE record has a memberlist that + points to a list of template members. It also has an arglist + pointing to a list of TEMPLATE_ARG records. */ + +struct dntt_type_template +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* always DNTT_TYPE_TEMPLATE */ + unsigned int abstract: 1; /* is this an abstract class? */ + unsigned int class_decl: 2; /* 0=class,1=union,2=struct */ + unsigned int unused: 18; + dnttpointer memberlist ; /* ptr to chain of K_[GEN]FIELDs */ + long unused2 ; /* offset in obj of ptr to vtbl */ + dnttpointer parentlist ; /* ptr to K_INHERITANCE list */ + unsigned long bitlength ; /* total at this level */ + dnttpointer identlist ; /* ptr to chain of class ident's */ + dnttpointer friendlist ; /* ptr to K_FRIEND list */ + dnttpointer arglist ; /* ptr to argument list */ + dnttpointer expansions ; /* ptr to expansion list */ +}; + +/* Template-class arguments are a list of TEMPL_ARG records + chained together. The "name" field is the name of the formal. + E.g.: + + template <class T> class q { ... }; + + Then "T" is the name of the formal argument. */ + +struct dntt_type_templ_arg +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* always DNTT_TYPE_TEMPL_ARG */ + unsigned int usagetype: 1; /* 0 type-name 1 expression */ + unsigned int unused: 20; + vtpointer name ; /* name of argument */ + dnttpointer type ; /* for non type arguments */ + dnttpointer nextarg ; /* Next argument if any */ + long future[2] ; /* padding to 3-word block end */ +}; + +/* FUNC_TEMPLATE records are sort of like FUNCTION, but are emitted + for template member functions. E.g., + + template <class T> class q + { + ... + void f(); + ... + }; + + Within the list of FIELDs/GENFIELDs defining the member list + of the template "q", "f" would appear as a FUNC_TEMPLATE. + We'll also see instances of FUNCTION "f" records for each + instantiation of the template. */ + +struct dntt_type_func_template +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* always DNTT_TYPE_FUNC_TEMPLATE */ + unsigned int public: 1; /* 1 => globally visible */ + unsigned int language: 4; /* type of language */ + unsigned int level: 5; /* nesting level (top level = 0)*/ + unsigned int optimize: 2; /* level of optimization */ + unsigned int varargs: 1; /* ellipses. Pascal/800 later */ + unsigned int info: 4; /* lang-specific stuff; F_xxxx */ + unsigned int inlined: 1; + unsigned int localloc: 1; /* 0 at top, 1 at end of block */ + unsigned int unused: 2; + vtpointer name ; /* name of function */ + vtpointer alias ; /* alternate name, if any */ + dnttpointer firstparam ; /* first FPARAM, if any */ + dnttpointer retval ; /* return type, if any */ + dnttpointer arglist ; /* ptr to argument list */ +}; + +/* LINK is apparently intended to link together function template + definitions with their instantiations. However, it is not clear + why this would be needed, except to provide the information on + a "ptype" command. And as far as I can tell, aCC does not + generate this record. */ + +struct dntt_type_link +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* always DNTT_TYPE_LINK */ + unsigned int linkKind: 4; /* always LINK_UNKNOWN */ + unsigned int unused: 17; + long future1 ; /* expansion */ + dnttpointer ptr1 ; /* link from template */ + dnttpointer ptr2 ; /* to expansion */ + long future[2] ; /* padding to 3-word block end */ +}; + +/* end of C++ specific SOM's. */ + +/* DNTT_TYPE_DYN_ARRAY_DESC is unused by GDB */ +/* DNTT_TYPE_DESC_SUBRANGE is unused by GDB */ +/* DNTT_TYPE_BEGIN_EXT is unused by GDB */ +/* DNTT_TYPE_INLN is unused by GDB */ +/* DNTT_TYPE_INLN_LIST is unused by GDB */ +/* DNTT_TYPE_ALIAS is unused by GDB */ + +struct dntt_type_doc_function +{ + unsigned int extension: 1; /* always zero */ + unsigned int kind: 10; /* K_DOC_FUNCTION or */ + /* K_DOC_MEMFUNC */ + unsigned int global: 1; /* 1 => globally visible */ + unsigned int language: 4; /* type of language */ + unsigned int level: 5; /* nesting level (top level = 0)*/ + unsigned int optimize: 2; /* level of optimization */ + unsigned int varargs: 1; /* ellipses. Pascal/800 later */ + unsigned int info: 4; /* lang-specific stuff; F_xxxx */ + unsigned int inlined: 1; + unsigned int localloc: 1; /* 0 at top, 1 at end of block */ + unsigned int expansion: 1; /* 1 = function expansion */ + unsigned int doc_clone: 1; + vtpointer name; /* name of function */ + vtpointer alias; /* alternate name, if any */ + dnttpointer firstparam; /* first FPARAM, if any */ + sltpointer address; /* code and text locations */ + CORE_ADDR entryaddr; /* address of entry point */ + dnttpointer retval; /* return type, if any */ + CORE_ADDR lowaddr; /* lowest address of function */ + CORE_ADDR hiaddr; /* highest address of function */ + dnttpointer inline_list; /* pointer to first inline */ + ltpointer lt_offset; /* start of frag/cp line table */ + ctxtpointer ctxt_offset; /* start of context table for this routine */ +}; + +/* DNTT_TYPE_DOC_MEMFUNC is unused by GDB */ + +/* DNTT_TYPE_GENERIC and DNTT_TYPE_BLOCK are convience structures + so we can examine a DNTT entry in a generic fashion. */ +struct dntt_type_generic +{ + unsigned int word[9]; +}; + +struct dntt_type_block +{ + unsigned int extension: 1; + unsigned int kind: 10; + unsigned int unused: 21; + unsigned int word[2]; +}; + +/* One entry in a DNTT (either the LNTT or GNTT). + This is a union of the above 60 or so structure definitions. */ + +union dnttentry +{ + struct dntt_type_srcfile dsfile; + struct dntt_type_module dmodule; + struct dntt_type_function dfunc; + struct dntt_type_function dentry; + struct dntt_type_begin dbegin; + struct dntt_type_end dend; + struct dntt_type_fparam dfparam; + struct dntt_type_svar dsvar; + struct dntt_type_dvar ddvar; + struct dntt_type_const dconst; + struct dntt_type_type dtype; + struct dntt_type_type dtag; + struct dntt_type_pointer dptr; + struct dntt_type_enum denum; + struct dntt_type_memenum dmember; + struct dntt_type_set dset; + struct dntt_type_subrange dsubr; + struct dntt_type_array darray; + struct dntt_type_struct dstruct; + struct dntt_type_union dunion; + struct dntt_type_field dfield; + struct dntt_type_functype dfunctype; + struct dntt_type_with dwith; + struct dntt_type_function dblockdata; + struct dntt_type_class_scope dclass_scope; + struct dntt_type_pointer dreference; + struct dntt_type_ptrmem dptrmem; + struct dntt_type_ptrmemfunc dptrmemfunc; + struct dntt_type_class dclass; + struct dntt_type_genfield dgenfield; + struct dntt_type_vfunc dvfunc; + struct dntt_type_memaccess dmemaccess; + struct dntt_type_inheritance dinheritance; + struct dntt_type_friend_class dfriend_class; + struct dntt_type_friend_func dfriend_func; + struct dntt_type_modifier dmodifier; + struct dntt_type_object_id dobject_id; + struct dntt_type_template dtemplate; + struct dntt_type_templ_arg dtempl_arg; + struct dntt_type_func_template dfunc_template; + struct dntt_type_link dlink; + struct dntt_type_doc_function ddocfunc; + struct dntt_type_generic dgeneric; + struct dntt_type_block dblock; +}; + +/* Source line entry types. */ +enum slttype +{ + SLT_NORMAL, + SLT_SRCFILE, + SLT_MODULE, + SLT_FUNCTION, + SLT_ENTRY, + SLT_BEGIN, + SLT_END, + SLT_WITH, + SLT_EXIT, + SLT_ASSIST, + SLT_MARKER, + SLT_CLASS_SCOPE, + SLT_INLN, + SLT_NORMAL_OFFSET, +}; + +/* A normal source line entry. Simply provides a mapping of a source + line number to a code address. + + SLTDESC will always be SLT_NORMAL or SLT_EXIT. */ + +struct slt_normal +{ + unsigned int sltdesc: 4; + unsigned int line: 28; + CORE_ADDR address; +}; + +struct slt_normal_off +{ + unsigned int sltdesc: 4; + unsigned int offset: 6; + unsigned int line: 22; + CORE_ADDR address; +}; + +/* A special source line entry. Provides a mapping of a declaration + to a line number. These entries point back into the DNTT which + references them. */ + +struct slt_special +{ + unsigned int sltdesc: 4; + unsigned int line: 28; + dnttpointer backptr; +}; + +/* Used to describe nesting. + + For nested languages, an slt_assist entry must follow each SLT_FUNC + entry in the SLT. The address field will point forward to the + first slt_normal entry within the function's scope. */ + +struct slt_assist +{ + unsigned int sltdesc: 4; + unsigned int unused: 28; + sltpointer address; +}; + +struct slt_generic +{ + unsigned int word[2]; +}; + +union sltentry +{ + struct slt_normal snorm; + struct slt_normal_off snormoff; + struct slt_special sspec; + struct slt_assist sasst; + struct slt_generic sgeneric; +}; + +/* $LINES$ declarations + This is the line table used for optimized code, which is only present + in the new $PROGRAM_INFO$ debug space. */ + +#define DST_LN_ESCAPE_FLAG1 15 +#define DST_LN_ESCAPE_FLAG2 14 +#define DST_LN_CTX_SPEC1 13 +#define DST_LN_CTX_SPEC2 12 + +/* Escape function codes: */ + +typedef enum +{ + dst_ln_pad, /* pad byte */ + dst_ln_escape_1, /* reserved */ + dst_ln_dpc1_dln1, /* 1 byte line delta, 1 byte pc delta */ + dst_ln_dpc2_dln2, /* 2 bytes line delta, 2 bytes pc delta */ + dst_ln_pc4_ln4, /* 4 bytes ABSOLUTE line number, 4 bytes ABSOLUTE pc */ + dst_ln_dpc0_dln1, /* 1 byte line delta, pc delta = 0 */ + dst_ln_ln_off_1, /* statement escape, stmt # = 1 (2nd stmt on line) */ + dst_ln_ln_off, /* statement escape, stmt # = next byte */ + dst_ln_entry, /* entry escape, next byte is entry number */ + dst_ln_exit, /* exit escape */ + dst_ln_stmt_end, /* gap escape, 4 bytes pc delta */ + dst_ln_stmt_cp, /* current stmt is a critical point */ + dst_ln_escape_12, /* reserved */ + dst_ln_escape_13, /* this is an exception site record */ + dst_ln_nxt_byte, /* next byte contains the real escape code */ + dst_ln_end, /* end escape, final entry follows */ + dst_ln_escape1_END_OF_ENUM +} +dst_ln_escape1_t; + +typedef enum +{ + dst_ln_ctx_1, /* next byte describes context switch with 5-bit */ + /* index into the image table and 3-bit run length. */ + /* If run length is 0, end with another cxt specifier or ctx_end */ + dst_ln_ctx_2, /* next 2 bytes switch context: 13 bit index, 3 bit run length */ + dst_ln_ctx_4, /* next 4 bytes switch context: 29 bit index, 3 bit run length */ + dst_ln_ctx_end, /* end current context */ + dst_ln_col_run_1, /* next byte is column position of start of next statement, */ + /* following byte is length of statement */ + dst_ln_col_run_2, /* next 2 bytes is column position of start of next statement, */ + /* following 2 bytes is length of statement */ + dst_ln_init_base1, /* next 4 bytes are absolute PC, followed by 1 byte of line number */ + dst_ln_init_base2, /* next 4 bytes are absolute PC, followed by 2 bytes of line number */ + dst_ln_init_base3, /* next 4 bytes are absolute PC, followed by 3 bytes of line number */ + dst_ln_escape2_END_OF_ENUM +} +dst_ln_escape2_t; + +typedef union +{ + struct + { + unsigned int pc_delta : 4; /* 4 bit pc delta */ + int ln_delta : 4; /* 4 bit line number delta */ + } + delta; + + struct + { + unsigned int esc_flag : 4; /* alias for pc_delta */ + unsigned int esc_code : 4; /* escape function code (dst_ln_escape1_t, or ...2_t */ + } + esc; + + struct + { + unsigned int esc_flag : 4; /* dst_ln_ctx_spec1, or dst_ln_ctx_spec2 */ + unsigned int run_length : 2; + unsigned int ctx_index : 2; /* ...spec2 contains index; ...spec1, index - 4 */ + } + ctx_spec; + + char sdata; /* signed data byte */ + unsigned char udata; /* unsigned data byte */ +} +dst_ln_entry_t, + * dst_ln_entry_ptr_t; + +/* Warning: although the above union occupies only 1 byte the compiler treats + it as having size 2 (the minimum size of a struct). Therefore a sequence of + dst_ln_entry_t's cannot be described as an array, and walking through such a + sequence requires convoluted code such as + ln_ptr = (dst_ln_entry_ptr_t) (char*) ln_ptr + 1 + We regret the inconvenience. */ + +/* Structure for interpreting the byte following a dst_ln_ctx1 entry. */ +typedef struct +{ + unsigned int ctx1_index : 5; /* 5 bit index into context table */ + unsigned int ctx1_run_length : 3; /* 3 bit run length */ +} dst_ln_ctx1_t, + *dst_ln_ctx1_ptr_t; + +/* Structure for interpreting the bytes following a dst_ln_ctx2 entry. */ +typedef struct +{ + unsigned int ctx2_index : 13; /* 13 bit index into context table */ + unsigned int ctx2_run_length : 3; /* 3 bit run length */ +} dst_ln_ctx2_t, + *dst_ln_ctx2_ptr_t; + +/* Structure for interpreting the bytes following a dst_ln_ctx4 entry. */ +typedef struct +{ + unsigned int ctx4_index : 29; /* 29 bit index into context table */ + unsigned int ctx4_run_length : 3; /* 3 bit run length */ +} dst_ln_ctx4_t, + *dst_ln_ctx4_ptr_t; + + +/* PXDB definitions. + + PXDB is a post-processor which takes the executable file + and massages the debug information so that the debugger may + start up and run more efficiently. Some of the tasks + performed by PXDB are: + + o Remove duplicate global type and variable information + from the GNTT, + + o Append the GNTT onto the end of the LNTT and place both + back in the LNTT section, + + o Build quick look-up tables (description follows) for + files, procedures, modules, and paragraphs (for Cobol), + placing these in the GNTT section, + + o Reconstruct the header appearing in the header section + to access this information. + + The "quick look-up" tables are in the $GNTT$ sub-space, in + the following order: + + Procedures -sorted by address + Source files -sorted by address (of the + generated code from routines) + Modules -sorted by address + Classes -<unsorted?> + Address Alias -sorted by index <?> + Object IDs -sorted by object identifier + + Most quick entries have (0-based) indices into the LNTT tables to + the full entries for the item it describes. + + The post-PXDB header is in the $HEADER$ sub-space. Alas, it + occurs in different forms, depending on the optimization level + in the compilation step and whether PXDB was run or not. The + worst part is the forms aren't self-describing, so we'll have + to grovel in the bits to figure out what kind we're looking at + (see hp_get_header in hp-psymtab-read.c). */ + +/* PXDB versions. */ + +#define PXDB_VERSION_CPLUSPLUS 1 +#define PXDB_VERSION_7_4 2 +#define PXDB_VERSION_CPP_30 3 +#define PXDB_VERSION_DDE_3_2A 4 +#define PXDB_VERSION_DDE_3_2 5 +#define PXDB_VERSION_DDE_4_0 6 + +#define PXDB_VERSION_2_1 1 + +/* Header version for the case that there is no DOC info + but the executable has been processed by pxdb (the easy + case, from "cc -g"). */ + +typedef struct PXDB_struct +{ + int pd_entries; /* # of entries in function look-up table */ + int fd_entries; /* # of entries in file look-up table */ + int md_entries; /* # of entries in module look-up table */ + unsigned int pxdbed : 1; /* 1 => file has been preprocessed */ + unsigned int bighdr : 1; /* 1 => this header contains 'time' word */ + unsigned int sa_header : 1;/* 1 => created by SA version of pxdb */ + /* used for version check in xdb */ + unsigned int inlined: 1; /* one or more functions have been inlined */ + unsigned int spare:12; + short version; /* pxdb header version */ + int globals; /* index into the DNTT where GNTT begins */ + unsigned int time; /* modify time of file before being pxdbed */ + int pg_entries; /* # of entries in label look-up table */ + int functions; /* actual number of functions */ + int files; /* actual number of files */ + int cd_entries; /* # of entries in class look-up table */ + int aa_entries; /* # of entries in addr alias look-up table */ + int oi_entries; /* # of entries in object id look-up table */ +} PXDB_header, *PXDB_header_ptr; + +/* Header version for the case that there is no DOC info and the + executable has NOT been processed by pxdb. */ + +typedef struct XDB_header_struct +{ + long gntt_length; + long lntt_length; + long slt_length; + long vt_length; + long xt_length; +} XDB_header; + +/* Header version for the case that there is DOC info and the + executable has been processed by pxdb. */ + +typedef struct DOC_info_PXDB_header_struct +{ + unsigned int xdb_header: 1; /* bit set if this is post-3.1 xdb */ + unsigned int doc_header: 1; /* bit set if this is doc-style header */ + unsigned int version: 8; /* version of pxdb see defines + PXDB_VERSION_* in this file. */ + unsigned int reserved_for_flags: 16;/* for future use; -- must be + set to zero. */ + unsigned int has_aux_pd_table: 1; /* $GNTT$ has aux PD table */ + unsigned int has_expr_table: 1; /* space has $EXPR$ */ + unsigned int has_range_table: 1; /* space has $RANGE$ */ + unsigned int has_context_table: 1; /* space has $SRC_CTXT$ */ + unsigned int has_lines_table: 1; /* space contains a $LINES$ + subspace for line tables. */ + unsigned int has_lt_offset_map: 1; /* space contains an lt_offset + subspace for line table mapping. */ + /* The following fields are the same as those in the PXDB_header in $DEBUG$ */ + int pd_entries; /* # of entries in function look-up table */ + int fd_entries; /* # of entries in file look-up table */ + int md_entries; /* # of entries in module look-up table */ + unsigned int pxdbed : 1; /* 1 => file has been preprocessed */ + unsigned int bighdr : 1; /* 1 => this header contains 'time' word */ + unsigned int sa_header : 1;/* 1 => created by SA version of pxdb */ + /* used for version check in xdb */ + unsigned int inlined: 1; /* one or more functions have been inlined */ + unsigned int spare : 28; + int globals; /* index into the DNTT where GNTT begins */ + unsigned int time; /* modify time of file before being pxdbed */ + int pg_entries; /* # of entries in label look-up table */ + int functions; /* actual number of functions */ + int files; /* actual number of files */ + int cd_entries; /* # of entries in class look-up table */ + int aa_entries; /* # of entries in addr alias look-up table */ + int oi_entries; /* # of entries in object id look-up table */ +} DOC_info_PXDB_header; + +/* Header version for the case that there is DOC info and the + executable has NOT been processed by pxdb. */ + +typedef struct DOC_info_header_struct +{ + unsigned int xdb_header: 1; /* bit set if this is post-3.1 xdb */ + unsigned int doc_header: 1; /* bit set if this is doc-style header*/ + unsigned int version: 8; /* version of debug/header + format. For 10.0 the value + will be 1. For "Davis" the value is 2. */ + unsigned int reserved_for_flags: 18; /* for future use; -- must be set to zero. */ + unsigned int has_range_table: 1; /* space contains a $RANGE$ subspace for variable ranges. */ + unsigned int has_context_table: 1; /* space contains a $CTXT$ subspace for context/inline table. */ + unsigned int has_lines_table: 1; /* space contains a $LINES$ subspace for line tables. */ + unsigned int has_lt_offset_map: 1; /* space contains an lt_offset subspace for line table mapping. */ + + long gntt_length; /* same as old header */ + long lntt_length; /* same as old header */ + long slt_length; /* same as old header */ + long vt_length; /* same as old header */ + long xt_length; /* same as old header */ + long ctxt_length; /* present only if version >= 2 */ + long range_length; /* present only if version >= 2 */ + long expr_length; /* present only if version >= 2 */ + +} DOC_info_header; + +typedef union GenericDebugHeader_union +{ + PXDB_header no_doc; + DOC_info_PXDB_header doc; + XDB_header no_pxdb_no_doc; + DOC_info_header no_pxdb_doc; +} GenericDebugHeader; + + +/* Procedure Descriptor: + An element of the procedure quick look-up table. */ + +typedef struct quick_procedure +{ + long isym; /* 0-based index of first symbol + for procedure in $LNTT$, + i.e. the procedure itself. */ + CORE_ADDR adrStart; /* memory adr of start of proc */ + CORE_ADDR adrEnd; /* memory adr of end of proc */ + char *sbAlias; /* alias name of procedure */ + char *sbProc; /* real name of procedure */ + CORE_ADDR adrBp; /* address of entry breakpoint */ + CORE_ADDR adrExitBp; /* address of exit breakpoint */ + int icd; /* member of this class (index) */ + unsigned int ipd; /* index of template for this */ + /* function (index) */ + unsigned int unused: 5; + unsigned int no_lt_offset: 1;/* no entry in lt_offset table */ + unsigned int fTemplate: 1; /* function template */ + unsigned int fExpansion: 1; /* function expansion */ + unsigned int linked : 1; /* linked with other expansions */ + unsigned int duplicate: 1; /* clone of another procedure */ + unsigned int overloaded:1; /* overloaded function */ + unsigned int member: 1; /* class member function */ + unsigned int constructor:1; /* constructor function */ + unsigned int destructor:1; /* destructor function */ + unsigned int Static: 1; /* static function */ + unsigned int Virtual: 1; /* virtual function */ + unsigned int constant: 1; /* constant function */ + unsigned int pure: 1; /* pure (virtual) function */ + unsigned int language: 4; /* procedure's language */ + unsigned int inlined: 1; /* function has been inlined */ + unsigned int Operator: 1; /* operator function */ + unsigned int stub: 1; /* bodyless function */ + unsigned int optimize: 2; /* optimization level */ + unsigned int level: 5; /* nesting level (top=0) */ +} quick_procedure_entry, *quick_procedure_entry_ptr; + +/* Source File Descriptor: + An element of the source file quick look-up table. */ + +typedef struct quick_source +{ + long isym; /* 0-based index in $LNTT$ of + first symbol for this file. */ + CORE_ADDR adrStart; /* mem adr of start of file's code */ + CORE_ADDR adrEnd; /* mem adr of end of file's code */ + char *sbFile; /* name of source file */ + unsigned int fHasDecl: 1; /* do we have a .d file? */ + unsigned int fWarned: 1; /* have warned about age problems? */ + unsigned int fSrcfile: 1; /* 0 => include 1=> source */ + unsigned short ilnMac; /* lines in file (0 if don't know) */ + int ipd; /* 0-based index of first procedure + in this file, in the quick + look-up table of procedures. */ + unsigned int *rgLn; /* line pointer array, if any */ +} quick_file_entry, *quick_file_entry_ptr; + +/* Module Descriptor: + An element of the module quick reference table. */ + +typedef struct quick_module +{ + long isym; /* 0-based index of first + symbol for module. */ + CORE_ADDR adrStart; /* adr of start of mod. */ + CORE_ADDR adrEnd; /* adr of end of mod. */ + char *sbAlias; /* alias name of module */ + char *sbMod; /* real name of module */ + unsigned int imports: 1; /* module have any imports? */ + unsigned int vars_in_front: 1; /* module globals in front? */ + unsigned int vars_in_gaps: 1; /* module globals in gaps? */ + unsigned int language: 4; /* type of language */ + unsigned int unused : 25; + unsigned int unused2; /* space for future stuff */ +} quick_module_entry, *quick_module_entry_ptr; + +/* Auxiliary Procedure Descriptor: + An element of the auxiliary procedure quick look-up table. */ + +typedef struct quick_aux_procedure +{ + long isym_inln; /* start on inline list for proc */ + long spare; +} quick_aux_procedure_entry, *quick_aux_procedure_entry_ptr; + +/* Paragraph Descriptor: + An element of the paragraph quick look-up table. */ + +typedef struct quick_paragraph +{ + long isym; /* first symbol for label (index) */ + CORE_ADDR adrStart; /* memory adr of start of label */ + CORE_ADDR adrEnd; /* memory adr of end of label */ + char *sbLab; /* name of label */ + unsigned int inst; /* Used in xdb to store inst @ bp */ + unsigned int sect: 1; /* true = section, false = parag. */ + unsigned int unused: 31; /* future use */ +} quick_paragraph_entry, *quick_paragraph_entry_ptr; + +/* Class Descriptor: + An element of the class quick look-up table. */ + +typedef struct quick_class +{ + char *sbClass; /* name of class */ + long isym; /* class symbol (tag) */ + unsigned int type : 2; /* 0=class, 1=union, 2=struct */ + unsigned int fTemplate : 1;/* class template */ + unsigned int expansion : 1;/* template expansion */ + unsigned int unused :28; + sltpointer lowscope; /* beginning of defined scope */ + sltpointer hiscope; /* end of defined scope */ +} quick_class_entry, *quick_class_entry_ptr; + +/* Address Alias Entry + An element of the address alias quick look-up table. */ + +typedef struct quick_alias +{ + CORE_ADDR low; + CORE_ADDR high; + int index; + unsigned int unused : 31; + unsigned int alternate : 1; /* alternate unnamed aliases? */ +} quick_alias_entry, *quick_alias_entry_ptr; + +/* Object Identification Entry + An element of the object identification quick look-up table. */ + +typedef struct quick_obj_ID +{ + CORE_ADDR obj_ident; /* class identifier */ + long isym; /* class symbol */ + long offset; /* offset to object start */ +} quick_obj_ID_entry, *quick_obj_ID_entry_ptr; + +#endif /* HP_SYMTAB_INCLUDED */ diff --git a/contrib/gdb/include/ieee.h b/contrib/gdb/include/ieee.h new file mode 100644 index 0000000..5abc32b --- /dev/null +++ b/contrib/gdb/include/ieee.h @@ -0,0 +1,165 @@ +/* IEEE Standard 695-1980 "Universal Format for Object Modules" header file + + Copyright 2001 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, 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. + + Contributed by Cygnus Support. */ + +#define N_W_VARIABLES 8 +#define Module_Beginning 0xe0 + +typedef struct ieee_module + { + char *processor; + char *module_name; + } +ieee_module_begin_type; + +#define Address_Descriptor 0xec +typedef struct ieee_address + { + bfd_vma number_of_bits_mau; + bfd_vma number_of_maus_in_address; + + unsigned char byte_order; +#define IEEE_LITTLE 0xcc +#define IEEE_BIG 0xcd + } +ieee_address_descriptor_type; + +typedef union ieee_w_variable + { + file_ptr offset[N_W_VARIABLES]; + + struct + { + file_ptr extension_record; + file_ptr environmental_record; + file_ptr section_part; + file_ptr external_part; + file_ptr debug_information_part; + file_ptr data_part; + file_ptr trailer_part; + file_ptr me_record; + } + r; + } +ieee_w_variable_type; + +typedef enum ieee_record + { + ieee_number_start_enum = 0x00, + ieee_number_end_enum=0x7f, + ieee_number_repeat_start_enum = 0x80, + ieee_number_repeat_end_enum = 0x88, + ieee_number_repeat_4_enum = 0x84, + ieee_number_repeat_3_enum = 0x83, + ieee_number_repeat_2_enum = 0x82, + ieee_number_repeat_1_enum = 0x81, + ieee_module_beginning_enum = 0xe0, + ieee_module_end_enum = 0xe1, + ieee_extension_length_1_enum = 0xde, + ieee_extension_length_2_enum = 0xdf, + ieee_section_type_enum = 0xe6, + ieee_section_alignment_enum = 0xe7, + ieee_external_symbol_enum = 0xe8, + ieee_comma = 0x90, + ieee_external_reference_enum = 0xe9, + ieee_set_current_section_enum = 0xe5, + ieee_address_descriptor_enum = 0xec, + ieee_load_constant_bytes_enum = 0xed, + ieee_load_with_relocation_enum = 0xe4, + + ieee_variable_A_enum = 0xc1, + ieee_variable_B_enum = 0xc2, + ieee_variable_C_enum = 0xc3, + ieee_variable_D_enum = 0xc4, + ieee_variable_E_enum = 0xc5, + ieee_variable_F_enum = 0xc6, + ieee_variable_G_enum = 0xc7, + ieee_variable_H_enum = 0xc8, + ieee_variable_I_enum = 0xc9, + ieee_variable_J_enum = 0xca, + ieee_variable_K_enum = 0xcb, + ieee_variable_L_enum = 0xcc, + ieee_variable_M_enum = 0xcd, + ieee_variable_N_enum = 0xce, + ieee_variable_O_enum = 0xcf, + ieee_variable_P_enum = 0xd0, + ieee_variable_Q_enum = 0xd1, + ieee_variable_R_enum = 0xd2, + ieee_variable_S_enum = 0xd3, + ieee_variable_T_enum = 0xd4, + ieee_variable_U_enum = 0xd5, + ieee_variable_V_enum = 0xd6, + ieee_variable_W_enum = 0xd7, + ieee_variable_X_enum = 0xd8, + ieee_variable_Y_enum = 0xd9, + ieee_variable_Z_enum = 0xda, + ieee_function_plus_enum = 0xa5, + ieee_function_minus_enum = 0xa6, + ieee_function_signed_open_b_enum = 0xba, + ieee_function_signed_close_b_enum = 0xbb, + + ieee_function_unsigned_open_b_enum = 0xbc, + ieee_function_unsigned_close_b_enum = 0xbd, + + ieee_function_either_open_b_enum = 0xbe, + ieee_function_either_close_b_enum = 0xbf, + ieee_record_seperator_enum = 0xdb, + + ieee_e2_first_byte_enum = 0xe2, + ieee_section_size_enum = 0xe2d3, + ieee_physical_region_size_enum = 0xe2c1, + ieee_region_base_address_enum = 0xe2c2, + ieee_mau_size_enum = 0xe2c6, + ieee_m_value_enum = 0xe2cd, + ieee_section_base_address_enum = 0xe2cc, + ieee_asn_record_enum = 0xe2ce, + ieee_section_offset_enum = 0xe2d2, + ieee_value_starting_address_enum = 0xe2c7, + ieee_assign_value_to_variable_enum = 0xe2d7, + ieee_set_current_pc_enum = 0xe2d0, + ieee_value_record_enum = 0xe2c9, + ieee_nn_record = 0xf0, + ieee_at_record_enum = 0xf1, + ieee_ty_record_enum = 0xf2, + ieee_attribute_record_enum = 0xf1c9, + ieee_atn_record_enum = 0xf1ce, + ieee_external_reference_info_record_enum = 0xf1d8, + ieee_weak_external_reference_enum= 0xf4, + ieee_repeat_data_enum = 0xf7, + ieee_bb_record_enum = 0xf8, + ieee_be_record_enum = 0xf9 + } +ieee_record_enum_type; + +typedef struct ieee_section + { + unsigned int section_index; + unsigned int section_type; + char * section_name; + unsigned int parent_section_index; + unsigned int sibling_section_index; + unsigned int context_index; + } +ieee_section_type; + +#define IEEE_REFERENCE_BASE 11 +#define IEEE_PUBLIC_BASE 32 +#define IEEE_SECTION_NUMBER_BASE 1 + diff --git a/contrib/gdb/include/libiberty.h b/contrib/gdb/include/libiberty.h new file mode 100644 index 0000000..761b2cf --- /dev/null +++ b/contrib/gdb/include/libiberty.h @@ -0,0 +1,335 @@ +/* Function declarations for libiberty. + + Copyright 2001, 2002 Free Software Foundation, Inc. + + Note - certain prototypes declared in this header file are for + functions whoes implementation copyright does not belong to the + FSF. Those prototypes are present in this file for reference + purposes only and their presence in this file should not construed + as an indication of ownership by the FSF of the implementation of + those functions in any way or form whatsoever. + + 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, 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 Cygnus Support, 1994. + + The libiberty library provides a number of functions which are + missing on some operating systems. We do not declare those here, + to avoid conflicts with the system header files on operating + systems that do support those functions. In this file we only + declare those functions which are specific to libiberty. */ + +#ifndef LIBIBERTY_H +#define LIBIBERTY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ansidecl.h" + +#ifdef ANSI_PROTOTYPES +/* Get a definition for size_t. */ +#include <stddef.h> +/* Get a definition for va_list. */ +#include <stdarg.h> +#endif + +/* Build an argument vector from a string. Allocates memory using + malloc. Use freeargv to free the vector. */ + +extern char **buildargv PARAMS ((const char *)) ATTRIBUTE_MALLOC; + +/* Free a vector returned by buildargv. */ + +extern void freeargv PARAMS ((char **)); + +/* Duplicate an argument vector. Allocates memory using malloc. Use + freeargv to free the vector. */ + +extern char **dupargv PARAMS ((char **)) ATTRIBUTE_MALLOC; + + +/* Return the last component of a path name. Note that we can't use a + prototype here because the parameter is declared inconsistently + across different systems, sometimes as "char *" and sometimes as + "const char *" */ + +/* HAVE_DECL_* is a three-state macro: undefined, 0 or 1. If it is + undefined, we haven't run the autoconf check so provide the + declaration without arguments. If it is 0, we checked and failed + to find the declaration so provide a fully prototyped one. If it + is 1, we found it so don't provide any declaration at all. */ +#if !HAVE_DECL_BASENAME +#if defined (__GNU_LIBRARY__ ) || defined (__linux__) || defined (__FreeBSD__) || defined (__OpenBSD__) || defined(__NetBSD__) || defined (__CYGWIN__) || defined (__CYGWIN32__) || defined (HAVE_DECL_BASENAME) +extern char *basename PARAMS ((const char *)); +#else +extern char *basename (); +#endif +#endif + +/* A well-defined basename () that is always compiled in. */ + +extern const char *lbasename PARAMS ((const char *)); + +/* A well-defined realpath () that is always compiled in. */ + +extern char *lrealpath PARAMS ((const char *)); + +/* Concatenate an arbitrary number of strings. You must pass NULL as + the last argument of this function, to terminate the list of + strings. Allocates memory using xmalloc. */ + +extern char *concat PARAMS ((const char *, ...)) ATTRIBUTE_MALLOC; + +/* Concatenate an arbitrary number of strings. You must pass NULL as + the last argument of this function, to terminate the list of + strings. Allocates memory using xmalloc. The first argument is + not one of the strings to be concatenated, but if not NULL is a + pointer to be freed after the new string is created, similar to the + way xrealloc works. */ + +extern char *reconcat PARAMS ((char *, const char *, ...)) ATTRIBUTE_MALLOC; + +/* Determine the length of concatenating an arbitrary number of + strings. You must pass NULL as the last argument of this function, + to terminate the list of strings. */ + +extern unsigned long concat_length PARAMS ((const char *, ...)); + +/* Concatenate an arbitrary number of strings into a SUPPLIED area of + memory. You must pass NULL as the last argument of this function, + to terminate the list of strings. The supplied memory is assumed + to be large enough. */ + +extern char *concat_copy PARAMS ((char *, const char *, ...)); + +/* Concatenate an arbitrary number of strings into a GLOBAL area of + memory. You must pass NULL as the last argument of this function, + to terminate the list of strings. The supplied memory is assumed + to be large enough. */ + +extern char *concat_copy2 PARAMS ((const char *, ...)); + +/* This is the global area used by concat_copy2. */ + +extern char *libiberty_concat_ptr; + +/* Concatenate an arbitrary number of strings. You must pass NULL as + the last argument of this function, to terminate the list of + strings. Allocates memory using alloca. The arguments are + evaluated twice! */ +#define ACONCAT(ACONCAT_PARAMS) \ + (libiberty_concat_ptr = alloca (concat_length ACONCAT_PARAMS + 1), \ + concat_copy2 ACONCAT_PARAMS) + +/* Check whether two file descriptors refer to the same file. */ + +extern int fdmatch PARAMS ((int fd1, int fd2)); + +/* Get the working directory. The result is cached, so don't call + chdir() between calls to getpwd(). */ + +extern char * getpwd PARAMS ((void)); + +/* Get the amount of time the process has run, in microseconds. */ + +extern long get_run_time PARAMS ((void)); + +/* Generate a relocated path to some installation directory. Allocates + return value using malloc. */ + +extern char *make_relative_prefix PARAMS ((const char *, const char *, + const char *)); + +/* Choose a temporary directory to use for scratch files. */ + +extern char *choose_temp_base PARAMS ((void)) ATTRIBUTE_MALLOC; + +/* Return a temporary file name or NULL if unable to create one. */ + +extern char *make_temp_file PARAMS ((const char *)) ATTRIBUTE_MALLOC; + +/* Allocate memory filled with spaces. Allocates using malloc. */ + +extern const char *spaces PARAMS ((int count)); + +/* Return the maximum error number for which strerror will return a + string. */ + +extern int errno_max PARAMS ((void)); + +/* Return the name of an errno value (e.g., strerrno (EINVAL) returns + "EINVAL"). */ + +extern const char *strerrno PARAMS ((int)); + +/* Given the name of an errno value, return the value. */ + +extern int strtoerrno PARAMS ((const char *)); + +/* ANSI's strerror(), but more robust. */ + +extern char *xstrerror PARAMS ((int)); + +/* Return the maximum signal number for which strsignal will return a + string. */ + +extern int signo_max PARAMS ((void)); + +/* Return a signal message string for a signal number + (e.g., strsignal (SIGHUP) returns something like "Hangup"). */ +/* This is commented out as it can conflict with one in system headers. + We still document its existence though. */ + +/*extern const char *strsignal PARAMS ((int));*/ + +/* Return the name of a signal number (e.g., strsigno (SIGHUP) returns + "SIGHUP"). */ + +extern const char *strsigno PARAMS ((int)); + +/* Given the name of a signal, return its number. */ + +extern int strtosigno PARAMS ((const char *)); + +/* Register a function to be run by xexit. Returns 0 on success. */ + +extern int xatexit PARAMS ((void (*fn) (void))); + +/* Exit, calling all the functions registered with xatexit. */ + +extern void xexit PARAMS ((int status)) ATTRIBUTE_NORETURN; + +/* Set the program name used by xmalloc. */ + +extern void xmalloc_set_program_name PARAMS ((const char *)); + +/* Report an allocation failure. */ +extern void xmalloc_failed PARAMS ((size_t)) ATTRIBUTE_NORETURN; + +/* Allocate memory without fail. If malloc fails, this will print a + message to stderr (using the name set by xmalloc_set_program_name, + if any) and then call xexit. */ + +extern PTR xmalloc PARAMS ((size_t)) ATTRIBUTE_MALLOC; + +/* Reallocate memory without fail. This works like xmalloc. Note, + realloc type functions are not suitable for attribute malloc since + they may return the same address across multiple calls. */ + +extern PTR xrealloc PARAMS ((PTR, size_t)); + +/* Allocate memory without fail and set it to zero. This works like + xmalloc. */ + +extern PTR xcalloc PARAMS ((size_t, size_t)) ATTRIBUTE_MALLOC; + +/* Copy a string into a memory buffer without fail. */ + +extern char *xstrdup PARAMS ((const char *)) ATTRIBUTE_MALLOC; + +/* Copy an existing memory buffer to a new memory buffer without fail. */ + +extern PTR xmemdup PARAMS ((const PTR, size_t, size_t)) ATTRIBUTE_MALLOC; + +/* Physical memory routines. Return values are in BYTES. */ +extern double physmem_total PARAMS ((void)); +extern double physmem_available PARAMS ((void)); + +/* hex character manipulation routines */ + +#define _hex_array_size 256 +#define _hex_bad 99 +extern const unsigned char _hex_value[_hex_array_size]; +extern void hex_init PARAMS ((void)); +#define hex_p(c) (hex_value (c) != _hex_bad) +/* If you change this, note well: Some code relies on side effects in + the argument being performed exactly once. */ +#define hex_value(c) ((unsigned int) _hex_value[(unsigned char) (c)]) + +/* Definitions used by the pexecute routine. */ + +#define PEXECUTE_FIRST 1 +#define PEXECUTE_LAST 2 +#define PEXECUTE_ONE (PEXECUTE_FIRST + PEXECUTE_LAST) +#define PEXECUTE_SEARCH 4 +#define PEXECUTE_VERBOSE 8 + +/* Execute a program. */ + +extern int pexecute PARAMS ((const char *, char * const *, const char *, + const char *, char **, char **, int)); + +/* Wait for pexecute to finish. */ + +extern int pwait PARAMS ((int, int *, int)); + +#if !HAVE_DECL_ASPRINTF +/* Like sprintf but provides a pointer to malloc'd storage, which must + be freed by the caller. */ + +extern int asprintf PARAMS ((char **, const char *, ...)) ATTRIBUTE_PRINTF_2; +#endif + +#if !HAVE_DECL_VASPRINTF +/* Like vsprintf but provides a pointer to malloc'd storage, which + must be freed by the caller. */ + +extern int vasprintf PARAMS ((char **, const char *, va_list)) + ATTRIBUTE_PRINTF(2,0); +#endif + +#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) + +/* Drastically simplified alloca configurator. If we're using GCC, + we use __builtin_alloca; otherwise we use the C alloca. The C + alloca is always available. You can override GCC by defining + USE_C_ALLOCA yourself. The canonical autoconf macro C_ALLOCA is + also set/unset as it is often used to indicate whether code needs + to call alloca(0). */ +extern PTR C_alloca PARAMS ((size_t)) ATTRIBUTE_MALLOC; +#undef alloca +#if GCC_VERSION >= 2000 && !defined USE_C_ALLOCA +# define alloca(x) __builtin_alloca(x) +# undef C_ALLOCA +# define ASTRDUP(X) \ + (__extension__ ({ const char *const libiberty_optr = (X); \ + const unsigned long libiberty_len = strlen (libiberty_optr) + 1; \ + char *const libiberty_nptr = alloca (libiberty_len); \ + (char *) memcpy (libiberty_nptr, libiberty_optr, libiberty_len); })) +#else +# define alloca(x) C_alloca(x) +# undef USE_C_ALLOCA +# define USE_C_ALLOCA 1 +# undef C_ALLOCA +# define C_ALLOCA 1 +extern const char *libiberty_optr; +extern char *libiberty_nptr; +extern unsigned long libiberty_len; +# define ASTRDUP(X) \ + (libiberty_optr = (X), \ + libiberty_len = strlen (libiberty_optr) + 1, \ + libiberty_nptr = alloca (libiberty_len), \ + (char *) memcpy (libiberty_nptr, libiberty_optr, libiberty_len)) +#endif + +#ifdef __cplusplus +} +#endif + + +#endif /* ! defined (LIBIBERTY_H) */ diff --git a/contrib/gdb/include/oasys.h b/contrib/gdb/include/oasys.h new file mode 100644 index 0000000..c8f737a --- /dev/null +++ b/contrib/gdb/include/oasys.h @@ -0,0 +1,192 @@ +/* Oasys object format header file for BFD. + + Copyright 2001 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, 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. + + Contributed by Cygnus Support. */ + +#define OASYS_MAX_SEC_COUNT 16 +/* **** */ + +typedef struct oasys_archive_header + { + unsigned int version; + char create_date[12]; + char revision_date[12]; + unsigned int mod_count; + file_ptr mod_tbl_offset; + unsigned int sym_tbl_size; + unsigned int sym_count; + file_ptr sym_tbl_offset; + unsigned int xref_count; + file_ptr xref_lst_offset; + } +oasys_archive_header_type; + +typedef struct oasys_extarchive_header + { + bfd_byte version[4]; + bfd_byte create_date[12]; + bfd_byte revision_date[12]; + bfd_byte mod_count[4]; + bfd_byte mod_tbl_offset[4]; + bfd_byte sym_tbl_size[4]; + bfd_byte sym_count[4]; + bfd_byte sym_tbl_offset[4]; + bfd_byte xref_count[4]; + bfd_byte xref_lst_offset[4]; + } +oasys_extarchive_header_type; + +typedef struct oasys_module_table + { + int mod_number; + char mod_date[12]; + unsigned int mod_size; + unsigned int dep_count; + unsigned int depee_count; + file_ptr file_offset; + unsigned int sect_count; + char *module_name; + unsigned int module_name_size; + } +oasys_module_table_type; + +typedef struct oasys_extmodule_table_a + { + bfd_byte mod_number[4]; + bfd_byte mod_date[12]; + bfd_byte mod_size[4]; + bfd_byte dep_count[4]; + bfd_byte depee_count[4]; + bfd_byte sect_count[4]; + bfd_byte file_offset[4]; + bfd_byte mod_name[32]; + } +oasys_extmodule_table_type_a_type; + +typedef struct oasys_extmodule_table_b + { + bfd_byte mod_number[4]; + bfd_byte mod_date[12]; + bfd_byte mod_size[4]; + bfd_byte dep_count[4]; + bfd_byte depee_count[4]; + bfd_byte sect_count[4]; + bfd_byte file_offset[4]; + bfd_byte mod_name_length[4]; + } +oasys_extmodule_table_type_b_type; + +typedef enum oasys_record + { + oasys_record_is_end_enum = 0, + oasys_record_is_data_enum = 1, + oasys_record_is_symbol_enum = 2, + oasys_record_is_header_enum = 3, + oasys_record_is_named_section_enum = 4, + oasys_record_is_com_enum = 5, + oasys_record_is_debug_enum = 6, + oasys_record_is_section_enum = 7, + oasys_record_is_debug_file_enum = 8, + oasys_record_is_module_enum = 9, + oasys_record_is_local_enum = 10 + } +oasys_record_enum_type; + +typedef struct oasys_record_header + { + unsigned char length; + unsigned char check_sum; + unsigned char type; + unsigned char fill; + } +oasys_record_header_type; + +typedef struct oasys_data_record + { + oasys_record_header_type header; + unsigned char relb; + bfd_byte addr[4]; + /* maximum total size of data record is 255 bytes */ + bfd_byte data[246]; + } +oasys_data_record_type; + +typedef struct oasys_header_record + { + oasys_record_header_type header; + unsigned char version_number; + unsigned char rev_number; + char module_name[26-6]; + char description[64-26]; + } +oasys_header_record_type; + +#define OASYS_VERSION_NUMBER 0 +#define OASYS_REV_NUMBER 0 + +typedef struct oasys_symbol_record + { + oasys_record_header_type header; + unsigned char relb; + bfd_byte value[4]; + bfd_byte refno[2]; + char name[64]; + } +oasys_symbol_record_type; + +#define RELOCATION_PCREL_BIT 0x80 +#define RELOCATION_32BIT_BIT 0x40 +#define RELOCATION_TYPE_BITS 0x30 +#define RELOCATION_TYPE_ABS 0x00 +#define RELOCATION_TYPE_REL 0x10 +#define RELOCATION_TYPE_UND 0x20 +#define RELOCATION_TYPE_COM 0x30 +#define RELOCATION_SECT_BITS 0x0f + +typedef struct oasys_section_record + { + oasys_record_header_type header; + unsigned char relb; + bfd_byte value[4]; + bfd_byte vma[4]; + bfd_byte fill[3]; + } +oasys_section_record_type; + +typedef struct oasys_end_record + { + oasys_record_header_type header; + unsigned char relb; + bfd_byte entry[4]; + bfd_byte fill[2]; + bfd_byte zero; + } +oasys_end_record_type; + +typedef union oasys_record_union + { + oasys_record_header_type header; + oasys_data_record_type data; + oasys_section_record_type section; + oasys_symbol_record_type symbol; + oasys_header_record_type first; + oasys_end_record_type end; + bfd_byte pad[256]; + } +oasys_record_union_type; diff --git a/contrib/gdb/include/obstack.h b/contrib/gdb/include/obstack.h new file mode 100644 index 0000000..5496ff2 --- /dev/null +++ b/contrib/gdb/include/obstack.h @@ -0,0 +1,611 @@ +/* obstack.h - object stack macros + Copyright 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, + 1999, 2000 + Free Software Foundation, Inc. + + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + 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, 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. */ + +/* Summary: + +All the apparent functions defined here are macros. The idea +is that you would use these pre-tested macros to solve a +very specific set of problems, and they would run fast. +Caution: no side-effects in arguments please!! They may be +evaluated MANY times!! + +These macros operate a stack of objects. Each object starts life +small, and may grow to maturity. (Consider building a word syllable +by syllable.) An object can move while it is growing. Once it has +been "finished" it never changes address again. So the "top of the +stack" is typically an immature growing object, while the rest of the +stack is of mature, fixed size and fixed address objects. + +These routines grab large chunks of memory, using a function you +supply, called `obstack_chunk_alloc'. On occasion, they free chunks, +by calling `obstack_chunk_free'. You must define them and declare +them before using any obstack macros. + +Each independent stack is represented by a `struct obstack'. +Each of the obstack macros expects a pointer to such a structure +as the first argument. + +One motivation for this package is the problem of growing char strings +in symbol tables. Unless you are "fascist pig with a read-only mind" +--Gosper's immortal quote from HAKMEM item 154, out of context--you +would not like to put any arbitrary upper limit on the length of your +symbols. + +In practice this often means you will build many short symbols and a +few long symbols. At the time you are reading a symbol you don't know +how long it is. One traditional method is to read a symbol into a +buffer, realloc()ating the buffer every time you try to read a symbol +that is longer than the buffer. This is beaut, but you still will +want to copy the symbol from the buffer to a more permanent +symbol-table entry say about half the time. + +With obstacks, you can work differently. Use one obstack for all symbol +names. As you read a symbol, grow the name in the obstack gradually. +When the name is complete, finalize it. Then, if the symbol exists already, +free the newly read name. + +The way we do this is to take a large chunk, allocating memory from +low addresses. When you want to build a symbol in the chunk you just +add chars above the current "high water mark" in the chunk. When you +have finished adding chars, because you got to the end of the symbol, +you know how long the chars are, and you can create a new object. +Mostly the chars will not burst over the highest address of the chunk, +because you would typically expect a chunk to be (say) 100 times as +long as an average object. + +In case that isn't clear, when we have enough chars to make up +the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed) +so we just point to it where it lies. No moving of chars is +needed and this is the second win: potentially long strings need +never be explicitly shuffled. Once an object is formed, it does not +change its address during its lifetime. + +When the chars burst over a chunk boundary, we allocate a larger +chunk, and then copy the partly formed object from the end of the old +chunk to the beginning of the new larger chunk. We then carry on +accreting characters to the end of the object as we normally would. + +A special macro is provided to add a single char at a time to a +growing object. This allows the use of register variables, which +break the ordinary 'growth' macro. + +Summary: + We allocate large chunks. + We carve out one object at a time from the current chunk. + Once carved, an object never moves. + We are free to append data of any size to the currently + growing object. + Exactly one object is growing in an obstack at any one time. + You can run one obstack per control block. + You may have as many control blocks as you dare. + Because of the way we do it, you can `unwind' an obstack + back to a previous state. (You may remove objects much + as you would with a stack.) +*/ + + +/* Don't do the contents of this file more than once. */ + +#ifndef _OBSTACK_H +#define _OBSTACK_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* We use subtraction of (char *) 0 instead of casting to int + because on word-addressable machines a simple cast to int + may ignore the byte-within-word field of the pointer. */ + +#ifndef __PTR_TO_INT +# define __PTR_TO_INT(P) ((P) - (char *) 0) +#endif + +#ifndef __INT_TO_PTR +# define __INT_TO_PTR(P) ((P) + (char *) 0) +#endif + +/* We need the type of the resulting object. If __PTRDIFF_TYPE__ is + defined, as with GNU C, use that; that way we don't pollute the + namespace with <stddef.h>'s symbols. Otherwise, if <stddef.h> is + available, include it and use ptrdiff_t. In traditional C, long is + the best that we can do. */ + +#ifdef __PTRDIFF_TYPE__ +# define PTR_INT_TYPE __PTRDIFF_TYPE__ +#else +# ifdef HAVE_STDDEF_H +# include <stddef.h> +# define PTR_INT_TYPE ptrdiff_t +# else +# define PTR_INT_TYPE long +# endif +#endif + +#if defined _LIBC || defined HAVE_STRING_H +# include <string.h> +# if defined __STDC__ && __STDC__ +# define _obstack_memcpy(To, From, N) memcpy ((To), (From), (N)) +# else +# define _obstack_memcpy(To, From, N) memcpy ((To), (char *)(From), (N)) +# endif +#else +# ifdef memcpy +# define _obstack_memcpy(To, From, N) memcpy ((To), (char *)(From), (N)) +# else +# define _obstack_memcpy(To, From, N) bcopy ((char *)(From), (To), (N)) +# endif +#endif + +struct _obstack_chunk /* Lives at front of each chunk. */ +{ + char *limit; /* 1 past end of this chunk */ + struct _obstack_chunk *prev; /* address of prior chunk or NULL */ + char contents[4]; /* objects begin here */ +}; + +struct obstack /* control current object in current chunk */ +{ + long chunk_size; /* preferred size to allocate chunks in */ + struct _obstack_chunk *chunk; /* address of current struct obstack_chunk */ + char *object_base; /* address of object we are building */ + char *next_free; /* where to add next char to current object */ + char *chunk_limit; /* address of char after current chunk */ + PTR_INT_TYPE temp; /* Temporary for some macros. */ + int alignment_mask; /* Mask of alignment for each object. */ +#if defined __STDC__ && __STDC__ + /* These prototypes vary based on `use_extra_arg', and we use + casts to the prototypeless function type in all assignments, + but having prototypes here quiets -Wstrict-prototypes. */ + struct _obstack_chunk *(*chunkfun) (void *, long); + void (*freefun) (void *, struct _obstack_chunk *); + void *extra_arg; /* first arg for chunk alloc/dealloc funcs */ +#else + struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk. */ + void (*freefun) (); /* User's function to free a chunk. */ + char *extra_arg; /* first arg for chunk alloc/dealloc funcs */ +#endif + unsigned use_extra_arg:1; /* chunk alloc/dealloc funcs take extra arg */ + unsigned maybe_empty_object:1;/* There is a possibility that the current + chunk contains a zero-length object. This + prevents freeing the chunk if we allocate + a bigger chunk to replace it. */ + unsigned alloc_failed:1; /* No longer used, as we now call the failed + handler on error, but retained for binary + compatibility. */ +}; + +/* Declare the external functions we use; they are in obstack.c. */ + +#if defined __STDC__ && __STDC__ +extern void _obstack_newchunk (struct obstack *, int); +extern void _obstack_free (struct obstack *, void *); +extern int _obstack_begin (struct obstack *, int, int, + void *(*) (long), void (*) (void *)); +extern int _obstack_begin_1 (struct obstack *, int, int, + void *(*) (void *, long), + void (*) (void *, void *), void *); +extern int _obstack_memory_used (struct obstack *); +#else +extern void _obstack_newchunk (); +extern void _obstack_free (); +extern int _obstack_begin (); +extern int _obstack_begin_1 (); +extern int _obstack_memory_used (); +#endif + +#if defined __STDC__ && __STDC__ + +/* Do the function-declarations after the structs + but before defining the macros. */ + +void obstack_init (struct obstack *obstack); + +void * obstack_alloc (struct obstack *obstack, int size); + +void * obstack_copy (struct obstack *obstack, void *address, int size); +void * obstack_copy0 (struct obstack *obstack, void *address, int size); + +void obstack_free (struct obstack *obstack, void *block); + +void obstack_blank (struct obstack *obstack, int size); + +void obstack_grow (struct obstack *obstack, void *data, int size); +void obstack_grow0 (struct obstack *obstack, void *data, int size); + +void obstack_1grow (struct obstack *obstack, int data_char); +void obstack_ptr_grow (struct obstack *obstack, void *data); +void obstack_int_grow (struct obstack *obstack, int data); + +void * obstack_finish (struct obstack *obstack); + +int obstack_object_size (struct obstack *obstack); + +int obstack_room (struct obstack *obstack); +void obstack_make_room (struct obstack *obstack, int size); +void obstack_1grow_fast (struct obstack *obstack, int data_char); +void obstack_ptr_grow_fast (struct obstack *obstack, void *data); +void obstack_int_grow_fast (struct obstack *obstack, int data); +void obstack_blank_fast (struct obstack *obstack, int size); + +void * obstack_base (struct obstack *obstack); +void * obstack_next_free (struct obstack *obstack); +int obstack_alignment_mask (struct obstack *obstack); +int obstack_chunk_size (struct obstack *obstack); +int obstack_memory_used (struct obstack *obstack); + +#endif /* __STDC__ */ + +/* Non-ANSI C cannot really support alternative functions for these macros, + so we do not declare them. */ + +/* Error handler called when `obstack_chunk_alloc' failed to allocate + more memory. This can be set to a user defined function. The + default action is to print a message and abort. */ +#if defined __STDC__ && __STDC__ +extern void (*obstack_alloc_failed_handler) (void); +#else +extern void (*obstack_alloc_failed_handler) (); +#endif + +/* Exit value used when `print_and_abort' is used. */ +extern int obstack_exit_failure; + +/* Pointer to beginning of object being allocated or to be allocated next. + Note that this might not be the final address of the object + because a new chunk might be needed to hold the final size. */ + +#define obstack_base(h) ((h)->object_base) + +/* Size for allocating ordinary chunks. */ + +#define obstack_chunk_size(h) ((h)->chunk_size) + +/* Pointer to next byte not yet allocated in current chunk. */ + +#define obstack_next_free(h) ((h)->next_free) + +/* Mask specifying low bits that should be clear in address of an object. */ + +#define obstack_alignment_mask(h) ((h)->alignment_mask) + +/* To prevent prototype warnings provide complete argument list in + standard C version. */ +#if defined __STDC__ && __STDC__ + +# define obstack_init(h) \ + _obstack_begin ((h), 0, 0, \ + (void *(*) (long)) obstack_chunk_alloc, (void (*) (void *)) obstack_chunk_free) + +# define obstack_begin(h, size) \ + _obstack_begin ((h), (size), 0, \ + (void *(*) (long)) obstack_chunk_alloc, (void (*) (void *)) obstack_chunk_free) + +# define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \ + _obstack_begin ((h), (size), (alignment), \ + (void *(*) (long)) (chunkfun), (void (*) (void *)) (freefun)) + +# define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \ + _obstack_begin_1 ((h), (size), (alignment), \ + (void *(*) (void *, long)) (chunkfun), \ + (void (*) (void *, void *)) (freefun), (arg)) + +# define obstack_chunkfun(h, newchunkfun) \ + ((h) -> chunkfun = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun)) + +# define obstack_freefun(h, newfreefun) \ + ((h) -> freefun = (void (*)(void *, struct _obstack_chunk *)) (newfreefun)) + +#else + +# define obstack_init(h) \ + _obstack_begin ((h), 0, 0, \ + (void *(*) ()) obstack_chunk_alloc, (void (*) ()) obstack_chunk_free) + +# define obstack_begin(h, size) \ + _obstack_begin ((h), (size), 0, \ + (void *(*) ()) obstack_chunk_alloc, (void (*) ()) obstack_chunk_free) + +# define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \ + _obstack_begin ((h), (size), (alignment), \ + (void *(*) ()) (chunkfun), (void (*) ()) (freefun)) + +# define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \ + _obstack_begin_1 ((h), (size), (alignment), \ + (void *(*) ()) (chunkfun), (void (*) ()) (freefun), (arg)) + +# define obstack_chunkfun(h, newchunkfun) \ + ((h) -> chunkfun = (struct _obstack_chunk *(*)()) (newchunkfun)) + +# define obstack_freefun(h, newfreefun) \ + ((h) -> freefun = (void (*)()) (newfreefun)) + +#endif + +#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = (achar)) + +#define obstack_blank_fast(h,n) ((h)->next_free += (n)) + +#define obstack_memory_used(h) _obstack_memory_used (h) + +#if defined __GNUC__ && defined __STDC__ && __STDC__ +/* NextStep 2.0 cc is really gcc 1.93 but it defines __GNUC__ = 2 and + does not implement __extension__. But that compiler doesn't define + __GNUC_MINOR__. */ +# if __GNUC__ < 2 || (__NeXT__ && !__GNUC_MINOR__) +# define __extension__ +# endif + +/* For GNU C, if not -traditional, + we can define these macros to compute all args only once + without using a global variable. + Also, we can avoid using the `temp' slot, to make faster code. */ + +# define obstack_object_size(OBSTACK) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + (unsigned) (__o->next_free - __o->object_base); }) + +# define obstack_room(OBSTACK) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + (unsigned) (__o->chunk_limit - __o->next_free); }) + +# define obstack_make_room(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + if (__o->chunk_limit - __o->next_free < __len) \ + _obstack_newchunk (__o, __len); \ + (void) 0; }) + +# define obstack_empty_p(OBSTACK) \ + __extension__ \ + ({ struct obstack *__o = (OBSTACK); \ + (__o->chunk->prev == 0 && __o->next_free - __o->chunk->contents == 0); }) + +# define obstack_grow(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + if (__o->next_free + __len > __o->chunk_limit) \ + _obstack_newchunk (__o, __len); \ + _obstack_memcpy (__o->next_free, (where), __len); \ + __o->next_free += __len; \ + (void) 0; }) + +# define obstack_grow0(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + if (__o->next_free + __len + 1 > __o->chunk_limit) \ + _obstack_newchunk (__o, __len + 1); \ + _obstack_memcpy (__o->next_free, (where), __len); \ + __o->next_free += __len; \ + *(__o->next_free)++ = 0; \ + (void) 0; }) + +# define obstack_1grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + if (__o->next_free + 1 > __o->chunk_limit) \ + _obstack_newchunk (__o, 1); \ + obstack_1grow_fast (__o, datum); \ + (void) 0; }) + +/* These assume that the obstack alignment is good enough for pointers or ints, + and that the data added so far to the current object + shares that much alignment. */ + +# define obstack_ptr_grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + if (__o->next_free + sizeof (void *) > __o->chunk_limit) \ + _obstack_newchunk (__o, sizeof (void *)); \ + obstack_ptr_grow_fast (__o, datum); }) + +# define obstack_int_grow(OBSTACK,datum) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + if (__o->next_free + sizeof (int) > __o->chunk_limit) \ + _obstack_newchunk (__o, sizeof (int)); \ + obstack_int_grow_fast (__o, datum); }) + +# define obstack_ptr_grow_fast(OBSTACK,aptr) \ +__extension__ \ +({ struct obstack *__o1 = (OBSTACK); \ + *(const void **) __o1->next_free = (aptr); \ + __o1->next_free += sizeof (const void *); \ + (void) 0; }) + +# define obstack_int_grow_fast(OBSTACK,aint) \ +__extension__ \ +({ struct obstack *__o1 = (OBSTACK); \ + *(int *) __o1->next_free = (aint); \ + __o1->next_free += sizeof (int); \ + (void) 0; }) + +# define obstack_blank(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + int __len = (length); \ + if (__o->chunk_limit - __o->next_free < __len) \ + _obstack_newchunk (__o, __len); \ + obstack_blank_fast (__o, __len); \ + (void) 0; }) + +# define obstack_alloc(OBSTACK,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_blank (__h, (length)); \ + obstack_finish (__h); }) + +# define obstack_copy(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow (__h, (where), (length)); \ + obstack_finish (__h); }) + +# define obstack_copy0(OBSTACK,where,length) \ +__extension__ \ +({ struct obstack *__h = (OBSTACK); \ + obstack_grow0 (__h, (where), (length)); \ + obstack_finish (__h); }) + +/* The local variable is named __o1 to avoid a name conflict + when obstack_blank is called. */ +# define obstack_finish(OBSTACK) \ +__extension__ \ +({ struct obstack *__o1 = (OBSTACK); \ + void *value; \ + value = (void *) __o1->object_base; \ + if (__o1->next_free == value) \ + __o1->maybe_empty_object = 1; \ + __o1->next_free \ + = __INT_TO_PTR ((__PTR_TO_INT (__o1->next_free)+__o1->alignment_mask)\ + & ~ (__o1->alignment_mask)); \ + if (__o1->next_free - (char *)__o1->chunk \ + > __o1->chunk_limit - (char *)__o1->chunk) \ + __o1->next_free = __o1->chunk_limit; \ + __o1->object_base = __o1->next_free; \ + value; }) + +# define obstack_free(OBSTACK, OBJ) \ +__extension__ \ +({ struct obstack *__o = (OBSTACK); \ + void *__obj = (OBJ); \ + if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit) \ + __o->next_free = __o->object_base = __obj; \ + else (obstack_free) (__o, __obj); }) + +#else /* not __GNUC__ or not __STDC__ */ + +# define obstack_object_size(h) \ + (unsigned) ((h)->next_free - (h)->object_base) + +# define obstack_room(h) \ + (unsigned) ((h)->chunk_limit - (h)->next_free) + +# define obstack_empty_p(h) \ + ((h)->chunk->prev == 0 && (h)->next_free - (h)->chunk->contents == 0) + +/* Note that the call to _obstack_newchunk is enclosed in (..., 0) + so that we can avoid having void expressions + in the arms of the conditional expression. + Casting the third operand to void was tried before, + but some compilers won't accept it. */ + +# define obstack_make_room(h,length) \ +( (h)->temp = (length), \ + (((h)->next_free + (h)->temp > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp), 0) : 0)) + +# define obstack_grow(h,where,length) \ +( (h)->temp = (length), \ + (((h)->next_free + (h)->temp > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \ + _obstack_memcpy ((h)->next_free, (where), (h)->temp), \ + (h)->next_free += (h)->temp) + +# define obstack_grow0(h,where,length) \ +( (h)->temp = (length), \ + (((h)->next_free + (h)->temp + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), (h)->temp + 1), 0) : 0), \ + _obstack_memcpy ((h)->next_free, (where), (h)->temp), \ + (h)->next_free += (h)->temp, \ + *((h)->next_free)++ = 0) + +# define obstack_1grow(h,datum) \ +( (((h)->next_free + 1 > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), 1), 0) : 0), \ + obstack_1grow_fast (h, datum)) + +# define obstack_ptr_grow(h,datum) \ +( (((h)->next_free + sizeof (char *) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \ + obstack_ptr_grow_fast (h, datum)) + +# define obstack_int_grow(h,datum) \ +( (((h)->next_free + sizeof (int) > (h)->chunk_limit) \ + ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \ + obstack_int_grow_fast (h, datum)) + +# define obstack_ptr_grow_fast(h,aptr) \ + (((const void **) ((h)->next_free += sizeof (void *)))[-1] = (aptr)) + +# define obstack_int_grow_fast(h,aint) \ + (((int *) ((h)->next_free += sizeof (int)))[-1] = (aptr)) + +# define obstack_blank(h,length) \ +( (h)->temp = (length), \ + (((h)->chunk_limit - (h)->next_free < (h)->temp) \ + ? (_obstack_newchunk ((h), (h)->temp), 0) : 0), \ + obstack_blank_fast (h, (h)->temp)) + +# define obstack_alloc(h,length) \ + (obstack_blank ((h), (length)), obstack_finish ((h))) + +# define obstack_copy(h,where,length) \ + (obstack_grow ((h), (where), (length)), obstack_finish ((h))) + +# define obstack_copy0(h,where,length) \ + (obstack_grow0 ((h), (where), (length)), obstack_finish ((h))) + +# define obstack_finish(h) \ +( ((h)->next_free == (h)->object_base \ + ? (((h)->maybe_empty_object = 1), 0) \ + : 0), \ + (h)->temp = __PTR_TO_INT ((h)->object_base), \ + (h)->next_free \ + = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) \ + & ~ ((h)->alignment_mask)), \ + (((h)->next_free - (char *) (h)->chunk \ + > (h)->chunk_limit - (char *) (h)->chunk) \ + ? ((h)->next_free = (h)->chunk_limit) : 0), \ + (h)->object_base = (h)->next_free, \ + __INT_TO_PTR ((h)->temp)) + +# if defined __STDC__ && __STDC__ +# define obstack_free(h,obj) \ +( (h)->temp = (char *) (obj) - (char *) (h)->chunk, \ + (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\ + ? (int) ((h)->next_free = (h)->object_base \ + = (h)->temp + (char *) (h)->chunk) \ + : (((obstack_free) ((h), (h)->temp + (char *) (h)->chunk), 0), 0))) +# else +# define obstack_free(h,obj) \ +( (h)->temp = (char *) (obj) - (char *) (h)->chunk, \ + (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\ + ? (int) ((h)->next_free = (h)->object_base \ + = (h)->temp + (char *) (h)->chunk) \ + : (_obstack_free ((h), (h)->temp + (char *) (h)->chunk), 0))) +# endif + +#endif /* not __GNUC__ or not __STDC__ */ + +#ifdef __cplusplus +} /* C++ */ +#endif + +#endif /* obstack.h */ diff --git a/contrib/gdb/include/os9k.h b/contrib/gdb/include/os9k.h new file mode 100644 index 0000000..596f56d --- /dev/null +++ b/contrib/gdb/include/os9k.h @@ -0,0 +1,181 @@ +/* os9k.h - OS-9000 i386 module header definitions + Copyright 2000 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#if !defined(_MODULE_H) +#define _MODULE_H + +#define _MPF386 + +/* Size of common header less parity field. */ +#define N_M_PARITY (sizeof(mh_com)-sizeof(unisgned short)) +#define OLD_M_PARITY 46 +#define M_PARITY N_M_PARITY + +#ifdef _MPF68K +#define MODSYNC 0x4afc /* Module header sync code for 680x0 processors. */ +#endif + +#ifdef _MPF386 +#define MODSYNC 0x4afc /* Module header sync code for 80386 processors. */ +#endif + +#define MODREV 1 /* Module format revision 1. */ +#define CRCCON 0x800063 /* CRC polynomial constant. */ + +/* Module access permission values. */ +#define MP_OWNER_READ 0x0001 +#define MP_OWNER_WRITE 0x0002 +#define MP_OWNER_EXEC 0x0004 +#define MP_GROUP_READ 0x0010 +#define MP_GROUP_WRITE 0x0020 +#define MP_GROUP_EXEC 0x0040 +#define MP_WORLD_READ 0x0100 +#define MP_WORLD_WRITE 0x0200 +#define MP_WORLD_EXEC 0x0400 +#define MP_WORLD_ACCESS 0x0777 +#define MP_OWNER_MASK 0x000f +#define MP_GROUP_MASK 0x00f0 +#define MP_WORLD_MASK 0x0f00 +#define MP_SYSTM_MASK 0xf000 + +/* Module Type/Language values. */ +#define MT_ANY 0 +#define MT_PROGRAM 0x0001 +#define MT_SUBROUT 0x0002 +#define MT_MULTI 0x0003 +#define MT_DATA 0x0004 +#define MT_TRAPLIB 0x000b +#define MT_SYSTEM 0x000c +#define MT_FILEMAN 0x000d +#define MT_DEVDRVR 0x000e +#define MT_DEVDESC 0x000f +#define MT_MASK 0xff00 + +#define ML_ANY 0 +#define ML_OBJECT 1 +#define ML_ICODE 2 +#define ML_PCODE 3 +#define ML_CCODE 4 +#define ML_CBLCODE 5 +#define ML_FRTNCODE 6 +#define ML_MASK 0x00ff + +#define mktypelang(type, lang) (((type) << 8) | (lang)) + +/* Module Attribute values. */ +#define MA_REENT 0x80 +#define MA_GHOST 0x40 +#define MA_SUPER 0x20 +#define MA_MASK 0xff00 +#define MR_MASK 0x00ff + +#define mkattrevs(attr, revs) (((attr) << 8) | (revs)) + +#define m_user m_owner.grp_usr.usr +#define m_group m_owner.grp_usr.grp +#define m_group_user m_owner.group_user + +/* Macro definitions for accessing module header fields. */ +#define MODNAME(mod) ((u_char*)((u_char*)mod + ((Mh_com)mod)->m_name)) +#if 0 +/* Appears not to be used, and the u_int32 typedef is gone (because it + conflicted with a Mach header. */ +#define MODSIZE(mod) ((u_int32)((Mh_com)mod)->m_size) +#endif /* 0 */ +#define MHCOM_BYTES_SIZE 80 +#define N_BADMAG(a) (((a).a_info) != MODSYNC) + +typedef struct mh_com +{ + /* Sync bytes ($4afc). */ + unsigned char m_sync[2]; + unsigned char m_sysrev[2]; /* System revision check value. */ + unsigned char m_size[4]; /* Module size. */ + unsigned char m_owner[4]; /* Group/user id. */ + unsigned char m_name[4]; /* Offset to module name. */ + unsigned char m_access[2]; /* Access permissions. */ + unsigned char m_tylan[2]; /* Type/lang. */ + unsigned char m_attrev[2]; /* Rev/attr. */ + unsigned char m_edit[2]; /* Edition. */ + unsigned char m_needs[4]; /* Module hardware requirements flags. (reserved). */ + unsigned char m_usage[4]; /* Comment string offset. */ + unsigned char m_symbol[4]; /* Symbol table offset. */ + unsigned char m_exec[4]; /* Offset to execution entry point. */ + unsigned char m_excpt[4]; /* Offset to exception entry point. */ + unsigned char m_data[4]; /* Data storage requirement. */ + unsigned char m_stack[4]; /* Stack size. */ + unsigned char m_idata[4]; /* Offset to initialized data. */ + unsigned char m_idref[4]; /* Offset to data reference lists. */ + unsigned char m_init[4]; /* Initialization routine offset. */ + unsigned char m_term[4]; /* Termination routine offset. */ + unsigned char m_ident[2]; /* Ident code for ident program. */ + char m_spare[8]; /* Reserved bytes. */ + unsigned char m_parity[2]; /* Header parity. */ +} mh_com,*Mh_com; + +/* Executable memory module. */ +typedef mh_com *Mh_exec,mh_exec; + +/* Data memory module. */ +typedef mh_com *Mh_data,mh_data; + +/* File manager memory module. */ +typedef mh_com *Mh_fman,mh_fman; + +/* Device driver module. */ +typedef mh_com *Mh_drvr,mh_drvr; + +/* Trap handler module. */ +typedef mh_com mh_trap, *Mh_trap; + +/* Device descriptor module. */ +typedef mh_com *Mh_dev,mh_dev; + +/* Configuration module. */ +typedef mh_com *Mh_config, mh_config; + +#if 0 + +#if !defined(_MODDIR_H) +/* Go get _os_fmod (and others). */ +#include <moddir.h> +#endif + +error_code _os_crc (void *, u_int32, int *); +error_code _os_datmod (char *, u_int32, u_int16 *, u_int16 *, u_int32, void **, mh_data **); +error_code _os_get_moddir (void *, u_int32 *); +error_code _os_initdata (mh_com *, void *); +error_code _os_link (char **, mh_com **, void **, u_int16 *, u_int16 *); +error_code _os_linkm (mh_com *, void **, u_int16 *, u_int16 *); +error_code _os_load (char *, mh_com **, void **, u_int32, u_int16 *, u_int16 *, u_int32); +error_code _os_mkmodule (char *, u_int32, u_int16 *, u_int16 *, u_int32, void **, mh_com **, u_int32); +error_code _os_modaddr (void *, mh_com **); +error_code _os_setcrc (mh_com *); +error_code _os_slink (u_int32, char *, void **, void **, mh_com **); +error_code _os_slinkm (u_int32, mh_com *, void **, void **); +error_code _os_unlink (mh_com *); +error_code _os_unload (char *, u_int32); +error_code _os_tlink (u_int32, char *, void **, mh_trap **, void *, u_int32); +error_code _os_tlinkm (u_int32, mh_com *, void **, void *, u_int32); +error_code _os_iodel (mh_com *); +error_code _os_vmodul (mh_com *, mh_com *, u_int32); +#endif /* 0 */ + +#endif diff --git a/contrib/gdb/include/progress.h b/contrib/gdb/include/progress.h new file mode 100644 index 0000000..23b0960 --- /dev/null +++ b/contrib/gdb/include/progress.h @@ -0,0 +1,37 @@ +/* Default definitions for progress macros. + Copyright 1994 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 default definitions below are intended to be replaced by real + definitions, if building the tools for an interactive programming + environment. */ + +#ifndef _PROGRESS_H +#define _PROGRESS_H + +#ifndef START_PROGRESS +#define START_PROGRESS(STR,N) +#endif + +#ifndef PROGRESS +#define PROGRESS(X) +#endif + +#ifndef END_PROGRESS +#define END_PROGRESS(STR) +#endif + +#endif /* _PROGRESS_H */ diff --git a/contrib/gdb/move-if-change b/contrib/gdb/move-if-change new file mode 100755 index 0000000..ee1b348 --- /dev/null +++ b/contrib/gdb/move-if-change @@ -0,0 +1,32 @@ +#!/bin/sh + +# Copyright (C) 1996 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +if +test -r $2 +then +if +cmp $1 $2 > /dev/null +then +echo $2 is unchanged +rm -f $1 +else +mv -f $1 $2 +fi +else +mv -f $1 $2 +fi |