diff options
author | Paul Brook <paul@codesourcery.com> | 2005-10-20 03:36:28 +0000 |
---|---|---|
committer | Paul Brook <paul@codesourcery.com> | 2005-10-20 03:36:28 +0000 |
commit | b17b0a21521433274f6a8e526142943cea969da6 (patch) | |
tree | 721fa271e0f1c32ac7bf94b607533f27b5171e18 | |
parent | 3ce39ae2f1a1cc2c1af09beeb9409ded021e8dd8 (diff) | |
download | gdb-b17b0a21521433274f6a8e526142943cea969da6.tar.gz |
2005-03-30 Daniel Jacobowitz <dan@codesourcery.com>
* gdb/Makefile.in (arm-linux-nat.o): Update dependencies.
* gdb/arm-linux-nat.c: Include "gdb_assert.h".
(PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS): Define.
(arm_linux_has_wmmx_registers): New flag.
(GET_THREAD_ID): Remove stray punctuation.
(IWMMXT_REGS_SIZE): Define.
(fetch_wmmx_regs, store_wmmx_regs): New functions.
(fetch_inferior_registers, store_inferior_registers): Call them.
(arm_linux_available_registers): New function.
* gdb/inftarg.c (child_xfer_partial): Handle
TARGET_OBJECT_AVAILABLE_REGISTERS.
* gdb/config/arm/nm-linux.h (arm_linux_available_registers): Add
prototype.
(NATIVE_XFER_AVAILABLE_REGISTERS): Define.
* gdb/gdbserver/linux-arm-low.c (arm_fill_wmmxregset)
(arm_store_wmmxregset): Remove stray text.
(arm_available_registers): Remove debugging output. Use hex.
* gdb/gdbserver/regcache.c (num_registers): Make global.
* gdb/gdbserver/server.c (handle_p_packet, handle_P_packet): Check
the value of regnum.
2005-03-28 Paul Brook <paul@codesourcery.com>
Daniel Jacobowitz <dan@codesourcery.com>
* gdb/Makefile.in: arm-tdep.o depends on $(observer_h).
* gdb/arm-linux-nat.c (fetch_fpregister, fetch_fpregs,
store_fpregister, store_fpregs): Rename...
(fetch_fpa_register, fetch_fpa_regs, store_fpa_register,
store_fpa_regs): ... to this.
* gdb/arm-linux-tdep.c (arm_linux_init_abi): Provide ABI specific
overrides.
* gdb/arm-tdep.c: Include opserver.h.
(struct gdbarch_tdep_info): Add.
(arm_push_dummy_call): Handle doubleword alignment.
(arm_register_type): Handle iWMMXt and VFP regs.
(arm_register_byte): Ditto.
(arm_pseudo_register_read): New function.
(arm_pseudo_register_write): New function.
(arm_dwarf_reg_to_regnum): New function.
(arm_register_sim_regno): Handle iWMMXT regs.
(arm_register_remote_regno): New function.
(arm_extract_return_value): Use new macro names.
(arm_register_name): Handle iWMMXt and VFP registers.
(arm_sim_available_registers): New function.
(arm_update_architecture): New function.
(arm_gdbarch_init): Handle optional register sets. Register new
hooks. Fix misleading error message.
(_initialize_arm_tdep): Call observer_attach_inferior_created.
* gdb/arm-tdep.h (ARM_NUM_FP_ARG_REGS, ARM_LAST_FP_ARG_REGNUM,
FP_REGISTER_SIZE, NUM_FREGS): Rename ...
(ARM_NUM_FPA_ARG_REGS, ARM_LAST_FPA_ARG_REGNUM, FPA_REGISTER_SIZE,
NUM_FPA_REGS): ... to this.
(NUM_IWMMXT_COP0REGS, NUM_IWMMXT_COP1REGS, NUM_IWMMXT_REGS,
IWMMXT_COP0_REGSIZE, IWMMXT_COP1_REGSIZE, NUM_VFP_XREGS,
VFP_XREG_SIZE, NUM_VFP_SREGS, VFP_SREG_SIZE, NUM_VFP_PSEUDOS): Define.
(struct gdbarch_tdep): Add target_has_iwmmxt_regs,
target_has_vfp_regs, first_iwmmxt_regnum, target_iwmmxt_regnum,
first_vfp_regnum, first_vfp_pseudo and target_vfp_regnum.
* gdb/gdbarch.c (struct gdbarch): Add remote_num_g_packet_regs,
sim_available_registers and register_remote_regno.
(startup_gdbarch, verify_gdbarch): Ditto.
(gdbarch_dump): Dump new fields.
(gdbarch_remote_num_g_packet_regs_p, gdbarch_remote_num_g_packet_regs,
set_gdbarch_remote_num_g_packet_regs,
gdbarch_sim_available_registers_p, gdbarch_sim_available_registers,
set_gdbarch_sim_available_registers, gdbarch_register_remote_regno_p,
gdbarch_register_remote_regno, set_gdbarch_register_remote_regno):
New functions.
* gdb/gdbarch.h: Add prototypes.
* gdb/gdbarch.sh: Add new fields.
(deprecated_current_gdbarch_select_hack): Call flush_cached_frames.
* gdb/remote-sim.h (gdbsim_create_inferior): Call
observer_notify_inferior_created.
(gdbsim_xfer_partial): New function.
(init_gdbsim_ops): Use it.
* gdb/remote.c (struct remote_state): Add num_g_regs.
(init_remote_state): Only consider hard regs. Allow target to provide
register mapping.
(packet_reg_from_regnum): Don't check pseudo regs.
(packet_reg_from_pnum): Ditto.
(remote_protocol_qPart_availableRegisters): Add.
(set_remote_protocol_qPart_availableRegisters_packet_cmd,
show_remote_protocol_qPart_availableRegisters_packet_cmd): New
functions.
(fetch_register_using_p, store_register_using_P): Handle arbitrary
register mappings.
(fetch_registers_using_g): New function.
(remote_fetch_registers): Use it.
(store_registers_using_G): New function.
(remote_store_registers): Use it.
(remote_xfer_partial): Handle TARGET_OBJECT_AVAILABLE_REGISTERS.
(show_remote_cmd): Display availableRegisters.
(_initialize_remote): Register qPart_availableRegisters.
* gdb/target.h (enum target_object): Add
TARGET_OBJECT_AVAILABLE_REGISTERS.
* gdb/gdbserver/configure.srv (arm*-*-linux*): set
srv_linux_regsets=yes.
* gdb/gdbserver/linux-arm-low.c (PTRACE_GETWMMXREGS,
PTRACE_SETWMMXREGS): Define.
(arm_fill_gregset, arm_store_gregset, arm_fill_wmmxregset,
arm_store_wmmxregset, arm_available_registers): New functions.
(target_regsets): Add.
(the_low_target): Use arm_available_registers.
* gdb/gdbserver/linux-low.c (linux_available_registers): New function.
(linux_target_op): Use it.
* gdb/gdbserver/linux-low.h (gdbserver/linux-low.h): Add
available_registers.
(use_regsets_p): Declare.
* gdb/gdbserver/regcache.c (g_register_bytes): Add.
(regcache_invalidate, registers_to_string, registers_from_string):
Use it.
(set_register_cache): Set g_register_bytes.
(supply_register_as_string): New function.
* gdb/gdbserver/regcache.h (supply_register_as_string): Add prototype.
* gdb/gdbserver/server.c (handle_query): Handle
qPart:availableRegisters.
(handle_p_packet, handle_P_packet): New functions.
(main): Handle 'p' and 'P' packets.
* gdb/gdbserver/target.h (struct target_ops): Add available_registers.
* gdb/regformats/reg-arm.dat: Add last_g_reg and iWMMXT regs.
* gdb/regformats/regdat.sh: Handle last_g_reg.
* gdb/regformats/regdef.h (set_register_cache): Update prototype.
-rw-r--r-- | ChangeLog.csl | 126 | ||||
-rw-r--r-- | gdb/Makefile.in | 2 | ||||
-rw-r--r-- | gdb/arm-linux-nat.c | 185 | ||||
-rw-r--r-- | gdb/arm-tdep.c | 464 | ||||
-rw-r--r-- | gdb/arm-tdep.h | 52 | ||||
-rw-r--r-- | gdb/config/arm/nm-linux.h | 16 | ||||
-rw-r--r-- | gdb/defs.h | 4 | ||||
-rw-r--r-- | gdb/gdbarch.c | 98 | ||||
-rw-r--r-- | gdb/gdbarch.h | 21 | ||||
-rwxr-xr-x | gdb/gdbarch.sh | 6 | ||||
-rw-r--r-- | gdb/gdbserver/configure.srv | 1 | ||||
-rw-r--r-- | gdb/gdbserver/linux-arm-low.c | 97 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.c | 12 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.h | 6 | ||||
-rw-r--r-- | gdb/gdbserver/regcache.c | 32 | ||||
-rw-r--r-- | gdb/gdbserver/regcache.h | 2 | ||||
-rw-r--r-- | gdb/gdbserver/server.c | 64 | ||||
-rw-r--r-- | gdb/gdbserver/target.h | 3 | ||||
-rw-r--r-- | gdb/inftarg.c | 7 | ||||
-rw-r--r-- | gdb/regformats/reg-arm.dat | 33 | ||||
-rwxr-xr-x | gdb/regformats/regdat.sh | 7 | ||||
-rw-r--r-- | gdb/regformats/regdef.h | 5 | ||||
-rw-r--r-- | gdb/remote-sim.c | 72 | ||||
-rw-r--r-- | gdb/remote.c | 316 | ||||
-rw-r--r-- | gdb/target.h | 5 |
25 files changed, 1480 insertions, 156 deletions
diff --git a/ChangeLog.csl b/ChangeLog.csl index c00e1b511ef..b38fb78b004 100644 --- a/ChangeLog.csl +++ b/ChangeLog.csl @@ -1,3 +1,129 @@ +2005-03-30 Daniel Jacobowitz <dan@codesourcery.com> + + * gdb/Makefile.in (arm-linux-nat.o): Update dependencies. + * gdb/arm-linux-nat.c: Include "gdb_assert.h". + (PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS): Define. + (arm_linux_has_wmmx_registers): New flag. + (GET_THREAD_ID): Remove stray punctuation. + (IWMMXT_REGS_SIZE): Define. + (fetch_wmmx_regs, store_wmmx_regs): New functions. + (fetch_inferior_registers, store_inferior_registers): Call them. + (arm_linux_available_registers): New function. + * gdb/inftarg.c (child_xfer_partial): Handle + TARGET_OBJECT_AVAILABLE_REGISTERS. + * gdb/config/arm/nm-linux.h (arm_linux_available_registers): Add + prototype. + (NATIVE_XFER_AVAILABLE_REGISTERS): Define. + + * gdb/gdbserver/linux-arm-low.c (arm_fill_wmmxregset) + (arm_store_wmmxregset): Remove stray text. + (arm_available_registers): Remove debugging output. Use hex. + * gdb/gdbserver/regcache.c (num_registers): Make global. + * gdb/gdbserver/server.c (handle_p_packet, handle_P_packet): Check + the value of regnum. + +2005-03-28 Paul Brook <paul@codesourcery.com> + Daniel Jacobowitz <dan@codesourcery.com> + + * gdb/Makefile.in: arm-tdep.o depends on $(observer_h). + * gdb/arm-linux-nat.c (fetch_fpregister, fetch_fpregs, + store_fpregister, store_fpregs): Rename... + (fetch_fpa_register, fetch_fpa_regs, store_fpa_register, + store_fpa_regs): ... to this. + * gdb/arm-linux-tdep.c (arm_linux_init_abi): Provide ABI specific + overrides. + * gdb/arm-tdep.c: Include opserver.h. + (struct gdbarch_tdep_info): Add. + (arm_push_dummy_call): Handle doubleword alignment. + (arm_register_type): Handle iWMMXt and VFP regs. + (arm_register_byte): Ditto. + (arm_pseudo_register_read): New function. + (arm_pseudo_register_write): New function. + (arm_dwarf_reg_to_regnum): New function. + (arm_register_sim_regno): Handle iWMMXT regs. + (arm_register_remote_regno): New function. + (arm_extract_return_value): Use new macro names. + (arm_register_name): Handle iWMMXt and VFP registers. + (arm_sim_available_registers): New function. + (arm_update_architecture): New function. + (arm_gdbarch_init): Handle optional register sets. Register new + hooks. Fix misleading error message. + (_initialize_arm_tdep): Call observer_attach_inferior_created. + * gdb/arm-tdep.h (ARM_NUM_FP_ARG_REGS, ARM_LAST_FP_ARG_REGNUM, + FP_REGISTER_SIZE, NUM_FREGS): Rename ... + (ARM_NUM_FPA_ARG_REGS, ARM_LAST_FPA_ARG_REGNUM, FPA_REGISTER_SIZE, + NUM_FPA_REGS): ... to this. + (NUM_IWMMXT_COP0REGS, NUM_IWMMXT_COP1REGS, NUM_IWMMXT_REGS, + IWMMXT_COP0_REGSIZE, IWMMXT_COP1_REGSIZE, NUM_VFP_XREGS, + VFP_XREG_SIZE, NUM_VFP_SREGS, VFP_SREG_SIZE, NUM_VFP_PSEUDOS): Define. + (struct gdbarch_tdep): Add target_has_iwmmxt_regs, + target_has_vfp_regs, first_iwmmxt_regnum, target_iwmmxt_regnum, + first_vfp_regnum, first_vfp_pseudo and target_vfp_regnum. + * gdb/gdbarch.c (struct gdbarch): Add remote_num_g_packet_regs, + sim_available_registers and register_remote_regno. + (startup_gdbarch, verify_gdbarch): Ditto. + (gdbarch_dump): Dump new fields. + (gdbarch_remote_num_g_packet_regs_p, gdbarch_remote_num_g_packet_regs, + set_gdbarch_remote_num_g_packet_regs, + gdbarch_sim_available_registers_p, gdbarch_sim_available_registers, + set_gdbarch_sim_available_registers, gdbarch_register_remote_regno_p, + gdbarch_register_remote_regno, set_gdbarch_register_remote_regno): + New functions. + * gdb/gdbarch.h: Add prototypes. + * gdb/gdbarch.sh: Add new fields. + (deprecated_current_gdbarch_select_hack): Call flush_cached_frames. + * gdb/remote-sim.h (gdbsim_create_inferior): Call + observer_notify_inferior_created. + (gdbsim_xfer_partial): New function. + (init_gdbsim_ops): Use it. + * gdb/remote.c (struct remote_state): Add num_g_regs. + (init_remote_state): Only consider hard regs. Allow target to provide + register mapping. + (packet_reg_from_regnum): Don't check pseudo regs. + (packet_reg_from_pnum): Ditto. + (remote_protocol_qPart_availableRegisters): Add. + (set_remote_protocol_qPart_availableRegisters_packet_cmd, + show_remote_protocol_qPart_availableRegisters_packet_cmd): New + functions. + (fetch_register_using_p, store_register_using_P): Handle arbitrary + register mappings. + (fetch_registers_using_g): New function. + (remote_fetch_registers): Use it. + (store_registers_using_G): New function. + (remote_store_registers): Use it. + (remote_xfer_partial): Handle TARGET_OBJECT_AVAILABLE_REGISTERS. + (show_remote_cmd): Display availableRegisters. + (_initialize_remote): Register qPart_availableRegisters. + * gdb/target.h (enum target_object): Add + TARGET_OBJECT_AVAILABLE_REGISTERS. + * gdb/gdbserver/configure.srv (arm*-*-linux*): set + srv_linux_regsets=yes. + * gdb/gdbserver/linux-arm-low.c (PTRACE_GETWMMXREGS, + PTRACE_SETWMMXREGS): Define. + (arm_fill_gregset, arm_store_gregset, arm_fill_wmmxregset, + arm_store_wmmxregset, arm_available_registers): New functions. + (target_regsets): Add. + (the_low_target): Use arm_available_registers. + * gdb/gdbserver/linux-low.c (linux_available_registers): New function. + (linux_target_op): Use it. + * gdb/gdbserver/linux-low.h (gdbserver/linux-low.h): Add + available_registers. + (use_regsets_p): Declare. + * gdb/gdbserver/regcache.c (g_register_bytes): Add. + (regcache_invalidate, registers_to_string, registers_from_string): + Use it. + (set_register_cache): Set g_register_bytes. + (supply_register_as_string): New function. + * gdb/gdbserver/regcache.h (supply_register_as_string): Add prototype. + * gdb/gdbserver/server.c (handle_query): Handle + qPart:availableRegisters. + (handle_p_packet, handle_P_packet): New functions. + (main): Handle 'p' and 'P' packets. + * gdb/gdbserver/target.h (struct target_ops): Add available_registers. + * gdb/regformats/reg-arm.dat: Add last_g_reg and iWMMXT regs. + * gdb/regformats/regdat.sh: Handle last_g_reg. + * gdb/regformats/regdef.h (set_register_cache): Update prototype. + 2005-04-29 Paul Brook <paul@codesourcery.com> * gdb/remote-sim.c (SIGTRAP): Provide default defnition. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 02bfcef36c1..9130b564edc 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1749,7 +1749,7 @@ arm-tdep.o: arm-tdep.c $(defs_h) $(frame_h) $(inferior_h) $(gdbcmd_h) \ $(frame_unwind_h) $(frame_base_h) $(trad_frame_h) $(arm_tdep_h) \ $(gdb_sim_arm_h) $(elf_bfd_h) $(coff_internal_h) $(elf_arm_h) \ $(gdb_assert_h) $(bfd_in2_h) $(libcoff_h) $(objfiles_h) \ - $(dwarf2_frame_h) + $(dwarf2_frame_h) $(observer_h) auxv.o: auxv.c $(defs_h) $(target_h) $(gdbtypes_h) $(command_h) \ $(inferior_h) $(valprint_h) $(gdb_assert_h) $(auxv_h) \ $(elf_common_h) diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c index c29c783074d..480a10d9c8f 100644 --- a/gdb/arm-linux-nat.c +++ b/gdb/arm-linux-nat.c @@ -26,6 +26,7 @@ #include "regcache.h" #include "target.h" #include "linux-nat.h" +#include "gdb_assert.h" #include "arm-tdep.h" @@ -44,6 +45,13 @@ #define PTRACE_GET_THREAD_AREA 22 #endif +#ifndef PTRACE_GETWMMXREGS +#define PTRACE_GETWMMXREGS 18 +#define PTRACE_SETWMMXREGS 19 +#endif + +static int arm_linux_has_wmmx_registers = 1; + extern int arm_apcs_32; #define typeNone 0x00 @@ -234,7 +242,7 @@ store_nwfpe_register (int regno, FPA11 * fpa11) state of the process and store it into regcache. */ static void -fetch_fpregister (int regno) +fetch_fpa_register (int regno) { int ret, tid; FPA11 fp; @@ -283,7 +291,7 @@ fetch_fpregister (int regno) into regcache. */ static void -fetch_fpregs (void) +fetch_fpa_regs (void) { int ret, regno, tid; FPA11 fp; @@ -331,7 +339,7 @@ fetch_fpregs (void) process using the contents from regcache. */ static void -store_fpregister (int regno) +store_fpa_register (int regno) { int ret, tid; FPA11 fp; @@ -369,7 +377,7 @@ store_fpregister (int regno) the contents from regcache. */ static void -store_fpregs (void) +store_fpa_regs (void) { int ret, regno, tid; FPA11 fp; @@ -553,6 +561,97 @@ store_regs (void) } } +/* Fetch all WMMX registers of the process and store into + regcache. */ + +#define IWMMXT_REGS_SIZE (16 * 8 + 6 * 4) + +static void +fetch_wmmx_regs (void) +{ + char regbuf[IWMMXT_REGS_SIZE]; + int ret, regno, tid, first; + + /* Get the thread id for the ptrace call. */ + tid = GET_THREAD_ID (inferior_ptid); + + ret = ptrace (PTRACE_GETWMMXREGS, tid, 0, regbuf); + if (ret < 0) + { + warning (_("Unable to fetch WMMX registers.")); + return; + } + + first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum; + + for (regno = 0; regno < NUM_IWMMXT_COP0REGS; regno++) + regcache_raw_supply (current_regcache, first + regno, + ®buf[regno * 8]); + + first += NUM_IWMMXT_COP0REGS; + + for (regno = 0; regno < 2; regno++) + regcache_raw_supply (current_regcache, first + regno, NULL); + + for (regno = 2; regno < 4; regno++) + regcache_raw_supply (current_regcache, first + regno, + ®buf[16 * 8 + (regno - 2) * 4]); + + for (regno = 4; regno < 8; regno++) + regcache_raw_supply (current_regcache, first + regno, NULL); + + for (regno = 8; regno < 12; regno++) + regcache_raw_supply (current_regcache, first + regno, + ®buf[16 * 8 + 2 * 4 + (regno - 8) * 4]); + + for (regno = 12; regno < 16; regno++) + regcache_raw_supply (current_regcache, first + regno, NULL); +} + +static void +store_wmmx_regs (void) +{ + char regbuf[IWMMXT_REGS_SIZE]; + int ret, regno, tid, first; + + /* Get the thread id for the ptrace call. */ + tid = GET_THREAD_ID (inferior_ptid); + + ret = ptrace (PTRACE_GETWMMXREGS, tid, 0, regbuf); + if (ret < 0) + { + warning (_("Unable to fetch WMMX registers.")); + return; + } + + first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum; + + for (regno = 0; regno < NUM_IWMMXT_COP0REGS; regno++) + if (register_cached (first + regno)) + regcache_raw_collect (current_regcache, first + regno, + ®buf[regno * 8]); + + first += 18; + for (regno = 0; regno < 2; regno++) + if (register_cached (first + regno)) + regcache_raw_collect (current_regcache, first + regno, + ®buf[16 * 8 + regno * 4]); + + first += 6; + for (regno = 0; regno < 4; regno++) + if (register_cached (first + regno)) + regcache_raw_collect (current_regcache, first + regno, + ®buf[16 * 8 + 2 * 4 + regno * 4]); + + ret = ptrace (PTRACE_SETWMMXREGS, tid, 0, regbuf); + + if (ret < 0) + { + warning (_("Unable to store WMMX registers.")); + return; + } +} + /* Fetch registers from the child process. Fetch all registers if regno == -1, otherwise fetch all general registers or all floating point registers depending upon the value of regno. */ @@ -563,15 +662,22 @@ arm_linux_fetch_inferior_registers (int regno) if (-1 == regno) { fetch_regs (); - fetch_fpregs (); + fetch_fpa_regs (); + if (arm_linux_has_wmmx_registers) + fetch_wmmx_regs (); } else { - if (regno < ARM_F0_REGNUM || regno > ARM_FPS_REGNUM) + if (regno < ARM_F0_REGNUM || regno == ARM_PS_REGNUM) fetch_register (regno); - - if (regno >= ARM_F0_REGNUM && regno <= ARM_FPS_REGNUM) - fetch_fpregister (regno); + else if (regno >= ARM_F0_REGNUM && regno <= ARM_FPS_REGNUM) + fetch_fpa_register (regno); + else if (arm_linux_has_wmmx_registers) + { + int first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum; + if (regno >= first && regno < first + NUM_IWMMXT_REGS) + fetch_wmmx_regs (); + } } } @@ -585,15 +691,22 @@ arm_linux_store_inferior_registers (int regno) if (-1 == regno) { store_regs (); - store_fpregs (); + store_fpa_regs (); + if (arm_linux_has_wmmx_registers) + store_wmmx_regs (); } else { - if ((regno < ARM_F0_REGNUM) || (regno > ARM_FPS_REGNUM)) + if (regno < ARM_F0_REGNUM || regno == ARM_PS_REGNUM) store_register (regno); - - if ((regno >= ARM_F0_REGNUM) && (regno <= ARM_FPS_REGNUM)) - store_fpregister (regno); + else if ((regno >= ARM_F0_REGNUM) && (regno <= ARM_FPS_REGNUM)) + store_fpa_register (regno); + else if (arm_linux_has_wmmx_registers) + { + int first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum; + if (regno >= first && regno < first + NUM_IWMMXT_REGS) + store_wmmx_regs (); + } } } @@ -745,6 +858,50 @@ get_linux_version (unsigned int *vmajor, void _initialize_arm_linux_nat (void); +LONGEST +arm_linux_available_registers (struct target_ops *ops, + int /* enum target_object */ object, + const char *annex, + void *readbuf, + const void *writebuf, + ULONGEST offset, + LONGEST len) +{ + char *result = NULL; + int total_len; + + gdb_assert (object == TARGET_OBJECT_AVAILABLE_REGISTERS); + gdb_assert (readbuf && !writebuf); + + if (arm_linux_has_wmmx_registers) + { + int ret; + char regbuf[IWMMXT_REGS_SIZE]; + + ret = ptrace (PTRACE_GETWMMXREGS, GET_THREAD_ID (inferior_ptid), 0, + regbuf); + if (ret < 0) + /* Should we be checking the error code? */ + arm_linux_has_wmmx_registers = 0; + } + + if (arm_linux_has_wmmx_registers) + result = "iwmmxt"; + + if (result == NULL) + return 0; + + total_len = strlen (result); + if (total_len > offset) + { + int bytes_read = min (total_len - offset, len); + memcpy (readbuf, result + offset, bytes_read); + return bytes_read; + } + + return 0; +} + void _initialize_arm_linux_nat (void) { diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index ba713f21419..90dbbae4746 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -39,6 +39,7 @@ #include "trad-frame.h" #include "objfiles.h" #include "dwarf2-frame.h" +#include "observer.h" #include "arm-tdep.h" #include "gdb/sim-arm.h" @@ -51,6 +52,19 @@ static int arm_debug; +/* Extra information which ARM uses to select the appropriate GDB + architecture. */ + +struct gdbarch_tdep_info +{ + /* See the descriptions of these fields in struct gdbarch_tdep. */ + + int target_has_iwmmxt_regs; + int target_iwmmxt_regnum; + int target_has_vfp_regs; + int target_vfp_regnum; +}; + /* Each OS has a different mechanism for accessing the various registers stored in the sigcontext structure. @@ -1450,7 +1464,42 @@ arm_print_float_info (struct gdbarch *gdbarch, struct ui_file *file, static struct type * arm_register_type (struct gdbarch *gdbarch, int regnum) { - if (regnum >= ARM_F0_REGNUM && regnum < ARM_F0_REGNUM + NUM_FREGS) + int first; + + first = gdbarch_tdep (gdbarch)->first_iwmmxt_regnum; + if (first != -1) + { + if (regnum >= first && regnum < first + NUM_IWMMXT_COP0REGS) + return builtin_type_vec64i; + + first += NUM_IWMMXT_COP0REGS; + + if (regnum >= first && regnum < first + NUM_IWMMXT_COP1REGS) + return builtin_type_int32; + } + + first = gdbarch_tdep (gdbarch)->first_vfp_regnum; + if (first != -1) + { + if (regnum >= first && regnum < first + NUM_VFP_XREGS) + return builtin_type_int32; + + first += NUM_VFP_XREGS; + + if (regnum >= first && regnum < first + NUM_VFP_SREGS) + return builtin_type_float; + } + + first = gdbarch_tdep (gdbarch)->first_vfp_pseudo; + if (first != -1) + { + first += gdbarch_num_regs (gdbarch); + + if (regnum >= first && regnum < first + NUM_VFP_PSEUDOS) + return builtin_type_double; + } + + if (regnum >= ARM_F0_REGNUM && regnum < ARM_F0_REGNUM + NUM_FPA_REGS) { if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) return builtin_type_arm_ext_big; @@ -1467,15 +1516,162 @@ arm_register_type (struct gdbarch *gdbarch, int regnum) static int arm_register_byte (int regnum) { + int offset; + int first; + + offset = 0; + first = 0; 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); + offset += NUM_GREGS * INT_REGISTER_SIZE; + first += NUM_GREGS; + + if (regnum < first + NUM_FPA_REGS) + return offset + (regnum - first) * FPA_REGISTER_SIZE; + offset += NUM_FPA_REGS * FPA_REGISTER_SIZE; + first += NUM_FPA_REGS; + + if (regnum < first + NUM_SREGS) + return offset + (regnum - first) * STATUS_REGISTER_SIZE; + offset += NUM_SREGS * STATUS_REGISTER_SIZE; + + first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum; + if (first != -1) + { + if (regnum >= first && regnum < first + NUM_IWMMXT_COP0REGS) + return offset + (regnum - first) * IWMMXT_COP0_REGSIZE; + + offset += NUM_IWMMXT_COP0REGS * IWMMXT_COP0_REGSIZE; + first += NUM_IWMMXT_COP0REGS; + + if (regnum >= first && regnum < first + NUM_IWMMXT_COP1REGS) + return offset + (regnum - first) * IWMMXT_COP1_REGSIZE; + + offset += NUM_IWMMXT_COP1REGS * IWMMXT_COP1_REGSIZE; + } + + first = gdbarch_tdep (current_gdbarch)->first_vfp_regnum; + if (first != -1) + { + if (regnum >= first && regnum < first + NUM_VFP_XREGS) + return offset + (regnum - first) * VFP_XREG_SIZE; + + offset += NUM_VFP_XREGS * VFP_XREG_SIZE; + first += NUM_VFP_XREGS; + + if (regnum >= first && regnum < first + NUM_VFP_SREGS) + return offset + (regnum - first) * VFP_SREG_SIZE; + } + internal_error (__FILE__, __LINE__, _("Bad REGNUM %d"), regnum); +} + +static void +arm_pseudo_register_read (struct gdbarch *gdbarch, + struct regcache *regcache, + int regnum, void *buf) +{ + int first; + int low_regnum; + int high_regnum; + + first = gdbarch_tdep (current_gdbarch)->first_vfp_pseudo; + if (first != -1) + { + first += gdbarch_num_regs (gdbarch); + + if (regnum >= first && regnum < first + NUM_VFP_PSEUDOS) + { + regnum = gdbarch_tdep (current_gdbarch)->first_vfp_regnum + + NUM_VFP_XREGS + 2 * (regnum - first); + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE) + { + low_regnum = regnum; + high_regnum = regnum + 1; + } + else + { + low_regnum = regnum + 1; + high_regnum = regnum; + } + regcache_raw_read (regcache, low_regnum, buf); + regcache_raw_read (regcache, high_regnum, ((char *)buf) + 4); + return; + } + } + internal_error (__FILE__, __LINE__, _("Bad pseudo %d"), regnum); +} + +static void +arm_pseudo_register_write (struct gdbarch *gdbarch, + struct regcache *regcache, + int regnum, const void *buf) +{ + int first; + int low_regnum; + int high_regnum; + + first = gdbarch_tdep (current_gdbarch)->first_vfp_pseudo; + if (first != -1) + { + first += gdbarch_num_regs (gdbarch); + + if (regnum >= first && regnum < first + NUM_VFP_PSEUDOS) + { + regnum = gdbarch_tdep (current_gdbarch)->first_vfp_regnum + + NUM_VFP_XREGS + 2 * (regnum - first); + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE) + { + low_regnum = regnum; + high_regnum = regnum + 1; + } + else + { + low_regnum = regnum + 1; + high_regnum = regnum; + } + regcache_raw_write (regcache, low_regnum, buf); + regcache_raw_write (regcache, high_regnum + 1, ((char *)buf) + 4); + return; + } + } + internal_error (__FILE__, __LINE__, _("Bad pseudo %d"), regnum); +} + +/* Map DWARF register numbers onto internal GDB register numbers. */ +static int +arm_dwarf_reg_to_regnum (int reg) +{ + int first; + + if (reg >= 0 && reg <= 16) + return reg; /* Core integer regs. */ + if (reg >= 16 && reg <= 23) + return ARM_F0_REGNUM + reg - 16; /* Legacy FPA encoding. */ + if (reg >= 96 && reg <= 103) + return ARM_F0_REGNUM + reg - 96; /* FPA regs. */ + + /* VFP regs. */ + first = gdbarch_tdep (current_gdbarch)->first_vfp_regnum; + if (first != -1 && reg >= 64 && reg <= 95); + return first + NUM_VFP_XREGS + reg - 64;; + + /* iWMMXt regs. */ + first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum; + if (first != -1) + { + if (reg >= 112 && reg <= 127) /* wr0-wr15 */ + return first + reg - 112; + first += NUM_IWMMXT_COP0REGS; + if (reg >= 192 && reg <= 199) /* wc0-wc7 */ + return first + reg - 104; + first += 8; + if (reg >= 104 && reg <= 111) /*wcgr0-wcgr7 */ + return first + reg - 104; + } + + warning (_("Unmapped DWARF Register #%d encountered."), reg); + + return -1; } /* Map GDB internal REGNUM onto the Arm simulator register numbers. */ @@ -1485,13 +1681,21 @@ arm_register_sim_regno (int regnum) int reg = regnum; gdb_assert (reg >= 0 && reg < NUM_REGS); + if (gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum != -1) + { + int first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum; + + if (regnum >= first && regnum < first + NUM_IWMMXT_REGS) + return SIM_ARM_IWMMXT_COP0R0_REGNUM + (regnum - first); + } + if (reg < NUM_GREGS) return SIM_ARM_R0_REGNUM + reg; reg -= NUM_GREGS; - if (reg < NUM_FREGS) + if (reg < NUM_FPA_REGS) return SIM_ARM_FP0_REGNUM + reg; - reg -= NUM_FREGS; + reg -= NUM_FPA_REGS; if (reg < NUM_SREGS) return SIM_ARM_FPS_REGNUM + reg; @@ -1500,6 +1704,35 @@ arm_register_sim_regno (int regnum) internal_error (__FILE__, __LINE__, _("Bad REGNUM %d"), regnum); } +/* Map GDB internal REGNUM onto the current remote protocol + register numbers. */ + +static int +arm_register_remote_regno (int regnum) +{ + gdb_assert (regnum >= 0 && regnum < NUM_REGS); + + if (gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum != -1) + { + int first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum; + + if (regnum >= first && regnum < first + NUM_IWMMXT_REGS) + return gdbarch_tdep (current_gdbarch)->target_iwmmxt_regnum + + (regnum - first); + } + + if (gdbarch_tdep (current_gdbarch)->first_vfp_regnum != -1) + { + int first = gdbarch_tdep (current_gdbarch)->first_vfp_regnum; + + if (regnum >= first && regnum < first + NUM_VFP_XREGS + NUM_VFP_SREGS) + return gdbarch_tdep (current_gdbarch)->target_vfp_regnum + + (regnum - first); + } + + return 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 @@ -2103,7 +2336,7 @@ arm_extract_return_value (struct type *type, struct regcache *regs, /* 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]; + bfd_byte tmpbuf[FPA_REGISTER_SIZE]; regcache_cooked_read (regs, ARM_F0_REGNUM, tmpbuf); convert_from_extended (floatformat_from_type (type), tmpbuf, @@ -2571,9 +2804,59 @@ set_disassembly_style_sfunc (char *args, int from_tty, /* Return the ARM register name corresponding to register I. */ static const char * -arm_register_name (int i) +arm_register_name (int regnum) { - return arm_register_names[i]; + int first; + + first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum; + if (first != -1) + { + static const char *const iwmmxt_register_names[] = + { + "wr0", "wr1", "wr2", "wr3", "wr4", "wr5", "wr6", "wr7", + "wr8", "wr9", "wr10", "wr11", "wr12", "wr13", "wr14", "wr15", + "wcid", "wcon", "wcssf", "wcasf", "", "", "", "", + "wcgr0", "wcgr1", "wcgr2", "wcgr3", "", "", "", "" + }; + + if (regnum >= first && regnum < first + NUM_IWMMXT_REGS) + return iwmmxt_register_names[regnum - first]; + } + + first = gdbarch_tdep (current_gdbarch)->first_vfp_regnum; + if (first != -1) + { + static const char *const vfp_register_names[] = + { + "fpsid", "fpscr", "", "", "", "", "", "", + "fpexc", "", "", "", "", "", "", "", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", + "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", + "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31" + }; + + if (regnum >= first && regnum < first + NUM_VFP_XREGS + NUM_VFP_SREGS) + return vfp_register_names[regnum - first]; + } + + first = gdbarch_tdep (current_gdbarch)->first_vfp_pseudo; + if (first != -1) + { + static const char *const vfp_pseudo_names[] = + { + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", + "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", + }; + + first += gdbarch_num_regs (current_gdbarch); + + if (regnum >= first && regnum < first + NUM_VFP_PSEUDOS) + return vfp_pseudo_names[regnum - first]; + } + + gdb_assert (regnum < NUM_GREGS + NUM_FPA_REGS + NUM_SREGS); + return arm_register_names[regnum]; } static void @@ -2681,6 +2964,70 @@ arm_elf_osabi_sniffer (bfd *abfd) /* Anything else will be handled by the generic ELF sniffer. */ return osabi; } + +/* */ + +static char * +arm_sim_available_registers (struct gdbarch *gdbarch, + const struct target_ops *target) +{ + /* The built-in simulator supports iWMMXt. */ + return xstrdup ("iwmmxt"); +} + +/* Update the current architecture based on architecture features. */ + +static void +arm_update_architecture (struct target_ops *target, int from_tty) +{ + struct gdbarch_tdep_info tdep; + struct gdbarch_info info; + char buf[64]; + int bytes_read; + + memset (&tdep, 0, sizeof (tdep)); + + /* FIXME: Define TARGET_READ_FULL which allocates the buffer large + enough, modelled on auxv.c. */ + bytes_read = target_read_partial (target, TARGET_OBJECT_AVAILABLE_REGISTERS, + "", buf, 0, 64); + + /* "Parse" the response. */ + if (bytes_read == 6 && strncmp (buf, "iwmmxt", 6) == 0) + { + tdep.target_has_iwmmxt_regs = 1; + tdep.target_iwmmxt_regnum = -1; + } + else if (bytes_read > 6 && strncmp (buf, "iwmmxt:", 7) == 0) + { + char *end; + tdep.target_has_iwmmxt_regs = 1; + tdep.target_iwmmxt_regnum = strtol (buf + 7, &end, 16); + } + else + { + tdep.target_has_iwmmxt_regs = 0; + tdep.target_iwmmxt_regnum = -1; + } + + if (bytes_read > 3 && strncmp (buf, "vfp:", 4) == 0) + { + char *end; + tdep.target_has_vfp_regs = 1; + tdep.target_vfp_regnum = strtol (buf + 4, &end, 16); + } + else + { + tdep.target_has_vfp_regs = 0; + tdep.target_vfp_regnum = -1; + } + + /* Update the architecture. */ + gdbarch_info_init (&info); + info.tdep_info = &tdep; + if (!gdbarch_update_p (info)) + internal_error (__FILE__, __LINE__, "could not update architecture"); +} /* Initialize the current architecture based on INFO. If possible, @@ -2698,6 +3045,8 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) struct gdbarch_list *best_arch; enum arm_abi_kind arm_abi = arm_abi_global; enum arm_float_model fp_model = arm_fp_model; + int nregs; + int pseudos; /* If we have an object to base this architecture on, try to determine its ABI. */ @@ -2792,6 +3141,25 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) best_arch != NULL; best_arch = gdbarch_list_lookup_by_info (best_arch->next, &info)) { + /* If we have target-specific bits in INFO, then make sure + they match. */ + if (info.tdep_info) + { + if (gdbarch_tdep (best_arch->gdbarch)->target_has_iwmmxt_regs + != info.tdep_info->target_has_iwmmxt_regs) + continue; + if (gdbarch_tdep (best_arch->gdbarch)->target_iwmmxt_regnum + != info.tdep_info->target_iwmmxt_regnum) + continue; + + if (gdbarch_tdep (best_arch->gdbarch)->target_has_vfp_regs + != info.tdep_info->target_has_vfp_regs) + continue; + if (gdbarch_tdep (best_arch->gdbarch)->target_vfp_regnum + != info.tdep_info->target_vfp_regnum) + continue; + } + if (arm_abi != gdbarch_tdep (best_arch->gdbarch)->arm_abi) continue; @@ -2813,6 +3181,35 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) tdep->arm_abi = arm_abi; tdep->fp_model = fp_model; + if (info.tdep_info) + { + /* If specific target capabilities were requested, use them. */ + tdep->target_has_iwmmxt_regs = info.tdep_info->target_has_iwmmxt_regs; + tdep->target_iwmmxt_regnum = info.tdep_info->target_iwmmxt_regnum; + tdep->target_has_vfp_regs = info.tdep_info->target_has_vfp_regs; + tdep->target_vfp_regnum = info.tdep_info->target_vfp_regnum; + } + else if (arches) + { + /* Otherwise, inherit from the last ARM architecture, if any. */ + tdep->target_has_iwmmxt_regs + = gdbarch_tdep (arches->gdbarch)->target_has_iwmmxt_regs; + tdep->target_iwmmxt_regnum + = gdbarch_tdep (arches->gdbarch)->target_iwmmxt_regnum; + tdep->target_has_vfp_regs + = gdbarch_tdep (arches->gdbarch)->target_has_vfp_regs; + tdep->target_vfp_regnum + = gdbarch_tdep (arches->gdbarch)->target_vfp_regnum; + } + else + { + /* Set defaults. */ + tdep->target_has_iwmmxt_regs = 0; + tdep->target_iwmmxt_regnum = -1; + tdep->target_has_vfp_regs = 0; + tdep->target_vfp_regnum = -1; + } + /* Breakpoints. */ switch (info.byte_order) { @@ -2877,12 +3274,49 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) 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_num_regs (gdbarch, NUM_GREGS + NUM_FREGS + NUM_SREGS); set_gdbarch_register_type (gdbarch, arm_register_type); + nregs = NUM_GREGS + NUM_FPA_REGS + NUM_SREGS; + pseudos = 0; + if (tdep->target_has_iwmmxt_regs) + { + tdep->first_iwmmxt_regnum = nregs; + nregs += NUM_IWMMXT_REGS; + } + else + tdep->first_iwmmxt_regnum = -1; + + if (tdep->target_has_vfp_regs) + { + tdep->first_vfp_regnum = nregs; + nregs += NUM_VFP_XREGS + NUM_VFP_SREGS; + tdep->first_vfp_pseudo = pseudos; + pseudos += NUM_VFP_PSEUDOS; + } + else + { + tdep->first_vfp_regnum = -1; + tdep->first_vfp_pseudo = -1; + } + + set_gdbarch_num_regs (gdbarch, nregs); + set_gdbarch_num_pseudo_regs (gdbarch, pseudos); + set_gdbarch_pseudo_register_read (gdbarch, arm_pseudo_register_read); + set_gdbarch_pseudo_register_write (gdbarch, arm_pseudo_register_write); + + /* The FPA registers are included in the remote 'g' packet for + historic reasons. */ + set_gdbarch_remote_num_g_packet_regs (gdbarch, + NUM_GREGS + NUM_FPA_REGS + NUM_SREGS); + /* Internal <-> external register number maps. */ + set_gdbarch_dwarf_reg_to_regnum (gdbarch, arm_dwarf_reg_to_regnum); + set_gdbarch_dwarf2_reg_to_regnum (gdbarch, arm_dwarf_reg_to_regnum); set_gdbarch_register_sim_regno (gdbarch, arm_register_sim_regno); + set_gdbarch_sim_available_registers (gdbarch, arm_sim_available_registers); + set_gdbarch_register_remote_regno (gdbarch, arm_register_remote_regno); + /* Integer registers are 4 bytes. */ set_gdbarch_deprecated_register_size (gdbarch, 4); set_gdbarch_register_name (gdbarch, arm_register_name); @@ -3078,4 +3512,6 @@ vfp - VFP co-processor."), NULL, NULL, /* FIXME: i18n: "ARM debugging is %s. */ &setdebuglist, &showdebuglist); + + observer_attach_inferior_created (arm_update_architecture); } diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h index 5217ff6a3dd..6eed14cb667 100644 --- a/gdb/arm-tdep.h +++ b/gdb/arm-tdep.h @@ -41,17 +41,17 @@ enum gdb_regnum { THUMB_FP_REGNUM = 7, /* Frame register in Thumb code, if used. */ ARM_NUM_ARG_REGS = 4, ARM_LAST_ARG_REGNUM = ARM_A4_REGNUM, - ARM_NUM_FP_ARG_REGS = 4, - ARM_LAST_FP_ARG_REGNUM = ARM_F3_REGNUM + ARM_NUM_FPA_ARG_REGS = 4, + ARM_LAST_FPA_ARG_REGNUM = ARM_F3_REGNUM, }; /* Size of integer registers. */ #define INT_REGISTER_SIZE 4 -/* Say how long FP registers are. Used for documentation purposes and +/* Say how long FPA registers are. Used for documentation purposes and code readability in this header. IEEE extended doubles are 80 bits. DWORD aligned they use 96 bits. */ -#define FP_REGISTER_SIZE 12 +#define FPA_REGISTER_SIZE 12 /* Status registers are the same size as general purpose registers. Used for documentation purposes and code readability in this @@ -65,10 +65,31 @@ enum gdb_regnum { (and called PS for processor status) so the status bits can be cleared from the PC (register 15). For 32 bit ARM code, a copy of CPSR is placed in PS. */ -#define NUM_FREGS 8 /* Number of floating point registers. */ +#define NUM_FPA_REGS 8 /* Number of FPA floating point registers. */ #define NUM_SREGS 2 /* Number of status registers. */ #define NUM_GREGS 16 /* Number of general purpose registers. */ +/* Optional supported coprocessors. */ +enum +{ + NUM_IWMMXT_COP0REGS = 16, + NUM_IWMMXT_COP1REGS = 16, + NUM_IWMMXT_REGS = NUM_IWMMXT_COP0REGS + NUM_IWMMXT_COP1REGS, + IWMMXT_COP0_REGSIZE = 8, + IWMMXT_COP1_REGSIZE = 4 +}; + +enum +{ + /* Status registers. */ + NUM_VFP_XREGS = 16, + VFP_XREG_SIZE = 4, + /* Single precision registers. */ + NUM_VFP_SREGS = 32, + VFP_SREG_SIZE = 4, + /* Pseudo regs for access to double precision values. */ + NUM_VFP_PSEUDOS = 16 +}; /* Instruction condition field values. */ #define INST_EQ 0x0 @@ -125,6 +146,16 @@ enum arm_abi_kind /* Target-dependent structure in gdbarch. */ struct gdbarch_tdep { + /* These fields are architecture-specific properties of the target. + After connecting to a new target, we check the target's properties, + and switch to a new gdbarch if necessary. */ + + int target_has_iwmmxt_regs; /* Does the target have iWMMXt registers? */ + + int target_has_vfp_regs; /* Does the target have VFP registers? */ + + /* End of target properties. */ + /* The ABI for this architecture. It should never be set to ARM_ABI_AUTO. */ enum arm_abi_kind arm_abi; @@ -143,6 +174,17 @@ struct gdbarch_tdep If this is negative, longjmp support will be disabled. */ size_t jb_elt_size; /* And the size of each entry in the buf. */ + + int first_iwmmxt_regnum; /* The first iWMMXt register, or -1 if none. */ + + int target_iwmmxt_regnum; /* The target-supplied numbering for the + iWMMXt registers, or -1 if none. */ + int first_vfp_regnum; /* The first VFP register, or -1 if none. */ + int first_vfp_pseudo; /* The first VFP pseudoreg, or -1 if none. + This is an offset from NUM_REGS. */ + + int target_vfp_regnum; /* The target-supplied numbering for the + VFP registers, or -1 if none. */ }; #ifndef LOWEST_PC diff --git a/gdb/config/arm/nm-linux.h b/gdb/config/arm/nm-linux.h index 1d7bbaa8650..2ad3968f636 100644 --- a/gdb/config/arm/nm-linux.h +++ b/gdb/config/arm/nm-linux.h @@ -38,4 +38,20 @@ extern int kernel_u_size (void); /* Override copies of {fetch,store}_inferior_registers in infptrace.c. */ #define FETCH_INFERIOR_REGISTERS +/* This function is called like a to_xfer_partial hook, + but must be called with TARGET_OBJECT_AVAILABLE_REGISTERS. */ + +struct target_ops; + +extern LONGEST arm_linux_available_registers + (struct target_ops *ops, + int /* enum target_object */ object, + const char *annex, + void *readbuf, + const void *writebuf, + ULONGEST offset, + LONGEST len); + +#define NATIVE_XFER_AVAILABLE_REGISTERS arm_linux_available_registers + #endif /* NM_ARMLINUX_H */ diff --git a/gdb/defs.h b/gdb/defs.h index 2f0e2e62f69..197545a18ac 100644 --- a/gdb/defs.h +++ b/gdb/defs.h @@ -1156,6 +1156,10 @@ extern int use_windows; #define SLASH_STRING "/" #endif +#if defined(__WIN32__) && !defined(__CYGWIN__) +#define WINAPI +#endif + /* Provide default definitions of PIDGET, TIDGET, and MERGEPID. The name ``TIDGET'' is a historical accident. Many uses of TIDGET in the code actually refer to a lightweight process id, i.e, diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index 527fe177d39..e24987a775c 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -152,6 +152,7 @@ struct gdbarch gdbarch_pseudo_register_write_ftype *pseudo_register_write; int num_regs; int num_pseudo_regs; + int remote_num_g_packet_regs; int sp_regnum; int pc_regnum; int ps_regnum; @@ -177,6 +178,8 @@ struct gdbarch gdbarch_register_bytes_ok_ftype *register_bytes_ok; gdbarch_cannot_fetch_register_ftype *cannot_fetch_register; gdbarch_cannot_store_register_ftype *cannot_store_register; + gdbarch_sim_available_registers_ftype *sim_available_registers; + gdbarch_register_remote_regno_ftype *register_remote_regno; gdbarch_get_longjmp_target_ftype *get_longjmp_target; int believe_pcc_promotion; gdbarch_convert_register_p_ftype *convert_register_p; @@ -278,6 +281,7 @@ struct gdbarch startup_gdbarch = 0, /* pseudo_register_write */ 0, /* num_regs */ 0, /* num_pseudo_regs */ + 0, /* remote_num_g_packet_regs */ -1, /* sp_regnum */ -1, /* pc_regnum */ -1, /* ps_regnum */ @@ -303,6 +307,8 @@ struct gdbarch startup_gdbarch = 0, /* register_bytes_ok */ 0, /* cannot_fetch_register */ 0, /* cannot_store_register */ + 0, /* sim_available_registers */ + 0, /* register_remote_regno */ 0, /* get_longjmp_target */ 0, /* believe_pcc_promotion */ 0, /* convert_register_p */ @@ -536,6 +542,7 @@ verify_gdbarch (struct gdbarch *current_gdbarch) if (current_gdbarch->num_regs == -1) fprintf_unfiltered (log, "\n\tnum_regs"); /* Skip verify of num_pseudo_regs, invalid_p == 0 */ + /* Skip verify of remote_num_g_packet_regs, has predicate */ /* Skip verify of sp_regnum, invalid_p == 0 */ /* Skip verify of pc_regnum, invalid_p == 0 */ /* Skip verify of ps_regnum, invalid_p == 0 */ @@ -559,6 +566,8 @@ verify_gdbarch (struct gdbarch *current_gdbarch) /* Skip verify of register_bytes_ok, has predicate */ /* Skip verify of cannot_fetch_register, invalid_p == 0 */ /* Skip verify of cannot_store_register, invalid_p == 0 */ + /* Skip verify of sim_available_registers, has predicate */ + /* Skip verify of register_remote_regno, has predicate */ /* Skip verify of get_longjmp_target, has predicate */ /* Skip verify of convert_register_p, invalid_p == 0 */ /* Skip verify of pointer_to_address, invalid_p == 0 */ @@ -1444,6 +1453,12 @@ gdbarch_dump (struct gdbarch *current_gdbarch, struct ui_file *file) fprintf_unfiltered (file, "gdbarch_dump: register_reggroup_p = <0x%lx>\n", (long) current_gdbarch->register_reggroup_p); + fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_register_remote_regno_p() = %d\n", + gdbarch_register_remote_regno_p (current_gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: register_remote_regno = <0x%lx>\n", + (long) current_gdbarch->register_remote_regno); #ifdef REGISTER_SIM_REGNO fprintf_unfiltered (file, "gdbarch_dump: %s # %s\n", @@ -1475,6 +1490,12 @@ gdbarch_dump (struct gdbarch *current_gdbarch, struct ui_file *file) "gdbarch_dump: regset_from_core_section = <0x%lx>\n", (long) current_gdbarch->regset_from_core_section); fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_remote_num_g_packet_regs_p() = %d\n", + gdbarch_remote_num_g_packet_regs_p (current_gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: remote_num_g_packet_regs = %s\n", + paddr_d (current_gdbarch->remote_num_g_packet_regs)); + fprintf_unfiltered (file, "gdbarch_dump: remote_translate_xfer_address = <0x%lx>\n", (long) current_gdbarch->remote_translate_xfer_address); fprintf_unfiltered (file, @@ -1501,6 +1522,12 @@ gdbarch_dump (struct gdbarch *current_gdbarch, struct ui_file *file) "gdbarch_dump: short_bit = %s\n", paddr_d (current_gdbarch->short_bit)); fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_sim_available_registers_p() = %d\n", + gdbarch_sim_available_registers_p (current_gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: sim_available_registers = <0x%lx>\n", + (long) current_gdbarch->sim_available_registers); + fprintf_unfiltered (file, "gdbarch_dump: gdbarch_single_step_through_delay_p() = %d\n", gdbarch_single_step_through_delay_p (current_gdbarch)); fprintf_unfiltered (file, @@ -2071,6 +2098,29 @@ set_gdbarch_num_pseudo_regs (struct gdbarch *gdbarch, } int +gdbarch_remote_num_g_packet_regs_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->remote_num_g_packet_regs != 0; +} + +int +gdbarch_remote_num_g_packet_regs (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_remote_num_g_packet_regs called\n"); + return gdbarch->remote_num_g_packet_regs; +} + +void +set_gdbarch_remote_num_g_packet_regs (struct gdbarch *gdbarch, + int remote_num_g_packet_regs) +{ + gdbarch->remote_num_g_packet_regs = remote_num_g_packet_regs; +} + +int gdbarch_sp_regnum (struct gdbarch *gdbarch) { gdb_assert (gdbarch != NULL); @@ -2552,6 +2602,54 @@ set_gdbarch_cannot_store_register (struct gdbarch *gdbarch, } int +gdbarch_sim_available_registers_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->sim_available_registers != NULL; +} + +char * +gdbarch_sim_available_registers (struct gdbarch *gdbarch, const struct target_ops *target) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->sim_available_registers != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_sim_available_registers called\n"); + return gdbarch->sim_available_registers (gdbarch, target); +} + +void +set_gdbarch_sim_available_registers (struct gdbarch *gdbarch, + gdbarch_sim_available_registers_ftype sim_available_registers) +{ + gdbarch->sim_available_registers = sim_available_registers; +} + +int +gdbarch_register_remote_regno_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->register_remote_regno != NULL; +} + +int +gdbarch_register_remote_regno (struct gdbarch *gdbarch, int reg_nr) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->register_remote_regno != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_register_remote_regno called\n"); + return gdbarch->register_remote_regno (reg_nr); +} + +void +set_gdbarch_register_remote_regno (struct gdbarch *gdbarch, + gdbarch_register_remote_regno_ftype register_remote_regno) +{ + gdbarch->register_remote_regno = register_remote_regno; +} + +int gdbarch_get_longjmp_target_p (struct gdbarch *gdbarch) { gdb_assert (gdbarch != NULL); diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 434966f0b33..d1e8ef7f7f0 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -355,6 +355,11 @@ extern void set_gdbarch_num_pseudo_regs (struct gdbarch *gdbarch, int num_pseudo #define NUM_PSEUDO_REGS (gdbarch_num_pseudo_regs (current_gdbarch)) #endif +extern int gdbarch_remote_num_g_packet_regs_p (struct gdbarch *gdbarch); + +extern int gdbarch_remote_num_g_packet_regs (struct gdbarch *gdbarch); +extern void set_gdbarch_remote_num_g_packet_regs (struct gdbarch *gdbarch, int remote_num_g_packet_regs); + /* GDB's standard (or well known) register numbers. These can map onto a real register or a pseudo (computed) register or not be defined at all (-1). @@ -633,6 +638,22 @@ extern void set_gdbarch_cannot_store_register (struct gdbarch *gdbarch, gdbarch_ #define CANNOT_STORE_REGISTER(regnum) (gdbarch_cannot_store_register (current_gdbarch, regnum)) #endif +/* Describe the optional registers provided by the simulator target. */ + +extern int gdbarch_sim_available_registers_p (struct gdbarch *gdbarch); + +typedef char * (gdbarch_sim_available_registers_ftype) (struct gdbarch *gdbarch, const struct target_ops *target); +extern char * gdbarch_sim_available_registers (struct gdbarch *gdbarch, const struct target_ops *target); +extern void set_gdbarch_sim_available_registers (struct gdbarch *gdbarch, gdbarch_sim_available_registers_ftype *sim_available_registers); + +/* Describe the register numbering used by the remote protocol. */ + +extern int gdbarch_register_remote_regno_p (struct gdbarch *gdbarch); + +typedef int (gdbarch_register_remote_regno_ftype) (int reg_nr); +extern int gdbarch_register_remote_regno (struct gdbarch *gdbarch, int reg_nr); +extern void set_gdbarch_register_remote_regno (struct gdbarch *gdbarch, gdbarch_register_remote_regno_ftype *register_remote_regno); + /* setjmp/longjmp support. */ #if defined (GET_LONGJMP_TARGET) diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index fdbc70b2977..78804f37d86 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -433,6 +433,8 @@ v:=:int:num_regs:::0:-1 # combinations of other registers, or they may be computed by GDB. v:=:int:num_pseudo_regs:::0:0::0 +V::int:remote_num_g_packet_regs + # GDB's standard (or well known) register numbers. These can map onto # a real register or a pseudo (computed) register or not be defined at # all (-1). @@ -485,6 +487,10 @@ f:=:int:register_sim_regno:int reg_nr:reg_nr::legacy_register_sim_regno::0 F:=:int:register_bytes_ok:long nr_bytes:nr_bytes f:=:int:cannot_fetch_register:int regnum:regnum::cannot_register_not::0 f:=:int:cannot_store_register:int regnum:regnum::cannot_register_not::0 +# Describe the optional registers provided by the simulator target. +M::char *:sim_available_registers:const struct target_ops *target:target +# Describe the register numbering used by the remote protocol. +F::int:register_remote_regno:int reg_nr:reg_nr # setjmp/longjmp support. F:=:int:get_longjmp_target:CORE_ADDR *pc:pc # diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv index f0e4dd368b9..42e08996eaf 100644 --- a/gdb/gdbserver/configure.srv +++ b/gdb/gdbserver/configure.srv @@ -21,6 +21,7 @@ case "${target}" in arm*-*-linux*) srv_regobj=reg-arm.o srv_tgtobj="linux-low.o linux-arm-low.o" srv_linux_usrregs=yes + srv_linux_regsets=yes srv_linux_thread_db=yes ;; crisv32-*-linux*) srv_regobj=reg-crisv32.o diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c index 2cbccd9af52..9792e2cb472 100644 --- a/gdb/gdbserver/linux-arm-low.c +++ b/gdb/gdbserver/linux-arm-low.c @@ -28,6 +28,23 @@ #define PTRACE_GET_THREAD_AREA 22 #endif +#include <sys/ptrace.h> + +/* Correct for all GNU/Linux targets (for quite some time). */ +#define GDB_GREGSET_T elf_gregset_t +#define GDB_FPREGSET_T elf_fpregset_t + +#ifndef HAVE_ELF_FPREGSET_T +/* Make sure we have said types. Not all platforms bring in <linux/elf.h> + via <sys/procfs.h>. */ +#ifdef HAVE_LINUX_ELF_H +#include <linux/elf.h> +#endif +#endif + +#define PTRACE_GETWMMXREGS 18 +#define PTRACE_SETWMMXREGS 19 + #ifdef HAVE_SYS_REG_H #include <sys/reg.h> #endif @@ -89,6 +106,85 @@ arm_breakpoint_at (CORE_ADDR where) one. */ return 0; } + +static void +arm_fill_gregset (void *buf) +{ + int i; + + for (i = 0; i < arm_num_regs; i++) + if (arm_regmap[i] != -1) + collect_register (i, ((char *) buf) + arm_regmap[i]); +} + +static void +arm_store_gregset (const void *buf) +{ + int i; + char zerobuf[8]; + + memset (zerobuf, 0, 8); + for (i = 0; i < arm_num_regs; i++) + if (arm_regmap[i] != -1) + supply_register (i, ((char *) buf) + arm_regmap[i]); + else + supply_register (i, zerobuf); +} + +static void +arm_fill_wmmxregset (void *buf) +{ + int i; + + for (i = 0; i < 16; i++) + collect_register (arm_num_regs + i, ((char *) buf) + i * 8); + + for (i = 0; i < 2; i++) + collect_register (arm_num_regs + i + 16 + 2, ((char *) buf) + 16 * 8 + i * 4); + + for (i = 0; i < 4; i++) + collect_register (arm_num_regs + i + 16 + 8, ((char *) buf) + 16 * 8 + 8 + i * 4); +} + +static void +arm_store_wmmxregset (const void *buf) +{ + int i; + + for (i = 0; i < 16; i++) + supply_register (arm_num_regs + i, ((char *) buf) + i * 8); + + for (i = 0; i < 2; i++) + supply_register (arm_num_regs + i + 16 + 2, ((char *) buf) + 16 * 8 + i * 4); + + for (i = 0; i < 4; i++) + supply_register (arm_num_regs + i + 16 + 8, ((char *) buf) + 16 * 8 + 8 + i * 4); +} + +char * +arm_available_registers (void) +{ + char buf[64]; + + if (use_regsets_p && target_regsets[1].size > 0) + { + int wr0 = find_regno ("wr0"); + sprintf (buf, "iwmmxt:%x", wr0); + return strdup (buf); + } + + return NULL; +} + +struct regset_info target_regsets[] = { + { PTRACE_GETREGS, PTRACE_SETREGS, sizeof (elf_gregset_t), + GENERAL_REGS, + arm_fill_gregset, arm_store_gregset }, + { PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS, 16 * 8 + 6 * 4, + EXTENDED_REGS, + arm_fill_wmmxregset, arm_store_wmmxregset }, + { 0, 0, -1, -1, NULL, NULL } +}; /* We only place breakpoints in empty marker functions, and thread locking is outside of the function. So rather than importing software single-step, @@ -130,4 +226,5 @@ struct linux_target_ops the_low_target = { arm_reinsert_addr, 0, arm_breakpoint_at, + arm_available_registers }; diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 70fab4ffd1f..7de6fada5c4 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -68,7 +68,7 @@ struct pending_signals #define PTRACE_XFER_TYPE long #ifdef HAVE_LINUX_REGSETS -static int use_regsets_p = 1; +int use_regsets_p = 1; #endif int debug_threads = 0; @@ -1355,6 +1355,15 @@ linux_store_registers (int regno) #endif } +static char * +linux_available_registers (void) +{ + if (the_low_target.available_registers == NULL) + return NULL; + else + return (*the_low_target.available_registers) (); +} + /* Copy LEN bytes from inferior's memory starting at MEMADDR to debugger memory starting at MYADDR. */ @@ -1555,6 +1564,7 @@ static struct target_ops linux_target_ops = { linux_remove_watchpoint, linux_stopped_by_watchpoint, linux_stopped_data_address, + linux_available_registers, }; static void diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index 79b1bb71de0..80695a2014f 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -67,10 +67,16 @@ struct linux_target_ops /* Whether to left-pad registers for PEEKUSR/POKEUSR if they are smaller than an xfer unit. */ int left_pad_xfer; + + char *(*available_registers) (void); }; extern struct linux_target_ops the_low_target; +#ifdef HAVE_LINUX_REGSETS +extern int use_regsets_p; +#endif + #define get_process(inf) ((struct process_info *)(inf)) #define get_thread_process(thr) (get_process (inferior_target_data (thr))) #define get_process_thread(proc) ((struct thread_info *) \ diff --git a/gdb/gdbserver/regcache.c b/gdb/gdbserver/regcache.c index f1d0fe51f14..ee5591b2e5b 100644 --- a/gdb/gdbserver/regcache.c +++ b/gdb/gdbserver/regcache.c @@ -35,10 +35,10 @@ struct inferior_regcache_data unsigned char *registers; }; -static int register_bytes; +static int register_bytes, g_register_bytes; static struct reg *reg_defs; -static int num_registers; +int num_registers; const char **gdbserver_expedite_regs; @@ -91,7 +91,7 @@ regcache_invalidate () int registers_length (void) { - return 2 * register_bytes; + return 2 * g_register_bytes; } void * @@ -124,7 +124,7 @@ free_register_cache (void *regcache_p) } void -set_register_cache (struct reg *regs, int n) +set_register_cache (struct reg *regs, const char *last_g_reg, int n) { int offset, i; @@ -139,6 +139,14 @@ set_register_cache (struct reg *regs, int n) } register_bytes = offset / 8; + + if (last_g_reg == NULL) + g_register_bytes = register_bytes; + else + { + int n = find_regno (last_g_reg); + g_register_bytes = regs[n].offset + regs[n].size / 8; + } } void @@ -146,7 +154,7 @@ registers_to_string (char *buf) { unsigned char *registers = get_regcache (current_inferior, 1)->registers; - convert_int_to_ascii (registers, buf, register_bytes); + convert_int_to_ascii (registers, buf, g_register_bytes); } void @@ -155,11 +163,11 @@ registers_from_string (char *buf) int len = strlen (buf); unsigned char *registers = get_regcache (current_inferior, 1)->registers; - if (len != register_bytes * 2) + if (len != g_register_bytes * 2) { - warning ("Wrong sized register packet (expected %d bytes, got %d)", 2*register_bytes, len); - if (len > register_bytes * 2) - len = register_bytes * 2; + warning ("Wrong sized register packet (expected %d bytes, got %d)", 2*g_register_bytes, len); + if (len > g_register_bytes * 2) + len = g_register_bytes * 2; } convert_ascii_to_int (buf, registers, len / 2); } @@ -216,6 +224,12 @@ supply_register (int n, const void *buf) } void +supply_register_as_string (int n, const char *buf) +{ + convert_ascii_to_int (buf, register_data (n, 0), register_size (n)); +} + +void supply_register_by_name (const char *name, const void *buf) { supply_register (find_regno (name), buf); diff --git a/gdb/gdbserver/regcache.h b/gdb/gdbserver/regcache.h index 930bd9cbfe6..be4b328f813 100644 --- a/gdb/gdbserver/regcache.h +++ b/gdb/gdbserver/regcache.h @@ -61,6 +61,8 @@ extern const char **gdbserver_expedite_regs; void supply_register (int n, const void *buf); +void supply_register_as_string (int n, const char *buf); + void supply_register_by_name (const char *name, const void *buf); void collect_register (int n, void *buf); diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index dfefe2b52a9..316dcab3e8c 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -143,6 +143,26 @@ handle_query (char *own_buf) return; } + if (the_target->available_registers != NULL + && strncmp ("qPart:availableRegisters:read::", own_buf, 31) == 0) + { + char *data; + CORE_ADDR ofs; + unsigned int len; + decode_m_packet (&own_buf[31], &ofs, &len); /* "OFS,LEN" */ + if (len > sizeof data) + len = sizeof data; + /* FIXME: Handle OFS, LEN */ + data = (*the_target->available_registers) (); + if (data == NULL || *data == 0) + write_ok (own_buf); + else + convert_int_to_ascii (data, own_buf, strlen (data)); + if (data) + free (data); + return; + } + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; @@ -283,6 +303,41 @@ handle_v_requests (char *own_buf, char *status, int *signal) return; } +extern int num_registers; + +/* Handle a register fetch ('p') request. */ +void +handle_p_packet (char *own_buf) +{ + char *end = own_buf + 1; + int regnum = strtol (own_buf + 1, &end, 16); + + if (*end || regnum < 0 || regnum >= num_registers) + { + write_enn (own_buf); + return; + } + + collect_register_as_string (regnum, own_buf); + own_buf[2 * register_size (regnum)] = 0; +} + +void +handle_P_packet (char *own_buf) +{ + char *end = own_buf + 1; + int regnum = strtol (own_buf + 1, &end, 16); + + if (*end != '=' || regnum < 0 || regnum >= num_registers) + { + write_enn (own_buf); + return; + } + + supply_register_as_string (regnum, end + 1); + write_ok (own_buf); +} + void myresume (int step, int sig) { @@ -479,6 +534,15 @@ main (int argc, char *argv[]) registers_from_string (&own_buf[1]); write_ok (own_buf); break; + case 'p': + set_desired_inferior (1); + handle_p_packet (own_buf); + break; + case 'P': + set_desired_inferior (1); + handle_P_packet (own_buf); + write_ok (own_buf); + break; case 'm': decode_m_packet (&own_buf[1], &mem_addr, &len); if (read_inferior_memory (mem_addr, mem_buf, len) == 0) diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index e3a183a9b9c..dfff3f8eaba 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -156,6 +156,9 @@ struct target_ops CORE_ADDR (*stopped_data_address) (void); + /* Return a string describing the optional available registers, + or NULL if there are none. */ + char *(*available_registers) (void); }; extern struct target_ops *the_target; diff --git a/gdb/inftarg.c b/gdb/inftarg.c index 5d7750d65bd..836c77d2792 100644 --- a/gdb/inftarg.c +++ b/gdb/inftarg.c @@ -559,6 +559,13 @@ child_xfer_partial (struct target_ops *ops, enum target_object object, return NATIVE_XFER_AUXV (ops, object, annex, readbuf, writebuf, offset, len); + case TARGET_OBJECT_AVAILABLE_REGISTERS: +#ifndef NATIVE_XFER_AVAILABLE_REGISTERS +#define NATIVE_XFER_AVAILABLE_REGISTERS(OPS,OBJECT,ANNEX,WRITEBUF,READBUF,OFFSET,LEN) (-1) +#endif + return NATIVE_XFER_AVAILABLE_REGISTERS (ops, object, annex, readbuf, + writebuf, offset, len); + default: return -1; } diff --git a/gdb/regformats/reg-arm.dat b/gdb/regformats/reg-arm.dat index 5a600258239..9edba2fb455 100644 --- a/gdb/regformats/reg-arm.dat +++ b/gdb/regformats/reg-arm.dat @@ -1,5 +1,6 @@ name:arm expedite:r11,sp,pc +last_g_reg:cpsr 32:r0 32:r1 32:r2 @@ -26,3 +27,35 @@ expedite:r11,sp,pc 96:f7 32:fps 32:cpsr +64:wr0 +64:wr1 +64:wr2 +64:wr3 +64:wr4 +64:wr5 +64:wr6 +64:wr7 +64:wr8 +64:wr9 +64:wr10 +64:wr11 +64:wr12 +64:wr13 +64:wr14 +64:wr15 +32:wcid +32:wcon +32:wcssf +32:wcasf +32: +32: +32: +32: +32:wcgr0 +32:wcgr1 +32:wcgr2 +32:wcgr3 +32: +32: +32: +32: diff --git a/gdb/regformats/regdat.sh b/gdb/regformats/regdat.sh index 9035b3dc90d..d779f4d6954 100755 --- a/gdb/regformats/regdat.sh +++ b/gdb/regformats/regdat.sh @@ -129,6 +129,7 @@ offset=0 i=0 name=x expedite=x +last_g_reg=NULL exec < $1 while do_read do @@ -139,6 +140,9 @@ do elif test "${type}" = "expedite"; then expedite="${entry}" continue + elif test "${type}" = "last_g_reg"; then + last_g_reg="\"${entry}\"" + continue elif test "${name}" = x; then echo "$0: $1 does not specify \`\`name''." 1>&2 exit 1 @@ -152,13 +156,14 @@ done echo "};" echo echo "const char *expedite_regs_${name}[] = { \"`echo ${expedite} | sed 's/,/", "/g'`\", 0 };" +echo "const char *last_g_reg_${name} = $last_g_reg;" echo cat <<EOF void init_registers () { - set_register_cache (regs_${name}, + set_register_cache (regs_${name}, last_g_reg_${name}, sizeof (regs_${name}) / sizeof (regs_${name}[0])); gdbserver_expedite_regs = expedite_regs_${name}; } diff --git a/gdb/regformats/regdef.h b/gdb/regformats/regdef.h index c1f862cc356..8d5d3cb0f51 100644 --- a/gdb/regformats/regdef.h +++ b/gdb/regformats/regdef.h @@ -39,8 +39,9 @@ struct reg }; /* Set the current remote protocol and register cache according to the array - ``regs'', with ``n'' elements. */ + ``regs'', with ``n'' elements. If non-NULL, LAST_G_REG is the last + register to include in g/G packets. */ -void set_register_cache (struct reg *regs, int n); +void set_register_cache (struct reg *regs, const char *last_g_reg, int n); #endif /* REGDEF_H */ diff --git a/gdb/remote-sim.c b/gdb/remote-sim.c index d54bcfb8379..2d8ef7e513a 100644 --- a/gdb/remote-sim.c +++ b/gdb/remote-sim.c @@ -458,6 +458,12 @@ gdbsim_create_inferior (char *exec_file, char *args, char **env, int from_tty) argv = NULL; sim_create_inferior (gdbsim_desc, exec_bfd, argv, env); + /* This doesn't seem like quite the right place... it would + be nice to have an inferior_created in a central location, + and a separate "target connected" observer for the ARM + registers hook. */ + observer_notify_inferior_created (current_target, from_tty); + inferior_ptid = pid_to_ptid (42); insert_breakpoints (); /* Needed to get correct instruction in cache */ @@ -772,6 +778,69 @@ gdbsim_xfer_inferior_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len, return len; } +/* Implement xfer_partial for the simulator. */ +static LONGEST +gdbsim_xfer_partial (struct target_ops *ops, enum target_object object, + const char *annex, void *readbuf, const void *writebuf, + ULONGEST offset, LONGEST len) +{ + /* Handle memory using remote_xfer_memory. */ + if (object == TARGET_OBJECT_MEMORY) + { + int xfered; + errno = 0; + + if (writebuf != NULL) + { + void *buffer = xmalloc (len); + struct cleanup *cleanup = make_cleanup (xfree, buffer); + memcpy (buffer, writebuf, len); + xfered = gdbsim_xfer_inferior_memory (offset, buffer, len, 1, NULL, ops); + do_cleanups (cleanup); + } + else + xfered = gdbsim_xfer_inferior_memory (offset, readbuf, len, 0, NULL, ops); + + if (xfered > 0) + return xfered; + else if (xfered == 0 && errno == 0) + return 0; + else + return -1; + } + + /* Only handle reads. */ + if (writebuf != NULL || readbuf == NULL) + return -1; + + switch (object) + { + case TARGET_OBJECT_AVAILABLE_REGISTERS: + /* FIXME: Should this be a NULL terminated string or a binary blob + without trailing NUL? */ + if (gdbarch_sim_available_registers_p (current_gdbarch)) + { + char *result = gdbarch_sim_available_registers (current_gdbarch, ops); + int total_len = strlen (result); + int bytes_read; + + if (total_len > offset) + { + bytes_read = min (total_len - offset, len); + memcpy (readbuf, result + offset, bytes_read); + xfree (result); + return bytes_read; + } + + xfree (result); + return 0; + } + + default: + return -1; + } +} + static void gdbsim_files_info (struct target_ops *target) { @@ -862,7 +931,8 @@ init_gdbsim_ops (void) 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.deprecated_xfer_memory = gdbsim_xfer_inferior_memory; + /* gdbsim_ops.deprecated_xfer_memory = gdbsim_xfer_inferior_memory; */ + gdbsim_ops.to_xfer_partial = gdbsim_xfer_partial; gdbsim_ops.to_files_info = gdbsim_files_info; gdbsim_ops.to_insert_breakpoint = gdbsim_insert_breakpoint; gdbsim_ops.to_remove_breakpoint = gdbsim_remove_breakpoint; diff --git a/gdb/remote.c b/gdb/remote.c index 8113674f555..846e329fd63 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -210,9 +210,10 @@ struct remote_state { /* Description of the remote protocol registers. */ long sizeof_g_packet; + long num_g_regs; /* Description of the remote protocol registers indexed by REGNUM - (making an array of NUM_REGS + NUM_PSEUDO_REGS in size). */ + (making an array NUM_REGS in size). */ struct packet_reg *regs; /* This is the size (in chars) of the first response to the ``g'' @@ -243,24 +244,33 @@ init_remote_state (struct gdbarch *gdbarch) { int regnum; struct remote_state *rs = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct remote_state); + int num_g_regs; + + if (gdbarch_remote_num_g_packet_regs_p (gdbarch)) + num_g_regs = gdbarch_remote_num_g_packet_regs (gdbarch); + else + num_g_regs = NUM_REGS; rs->sizeof_g_packet = 0; /* Assume a 1:1 regnum<->pnum table. */ - rs->regs = GDBARCH_OBSTACK_CALLOC (gdbarch, NUM_REGS + NUM_PSEUDO_REGS, - struct packet_reg); - for (regnum = 0; regnum < NUM_REGS + NUM_PSEUDO_REGS; regnum++) + rs->regs = GDBARCH_OBSTACK_CALLOC (gdbarch, NUM_REGS, struct packet_reg); + for (regnum = 0; regnum < NUM_REGS; regnum++) { struct packet_reg *r = &rs->regs[regnum]; - r->pnum = regnum; + + if (gdbarch_register_remote_regno_p (gdbarch)) + r->pnum = gdbarch_register_remote_regno (gdbarch, regnum); + else + r->pnum = regnum; + r->regnum = regnum; r->offset = DEPRECATED_REGISTER_BYTE (regnum); - r->in_g_packet = (regnum < NUM_REGS); + r->in_g_packet = (regnum < num_g_regs); /* ...name = REGISTER_NAME (regnum); */ /* Compute packet size by accumulating the size of all registers. */ - if (regnum < NUM_REGS) - rs->sizeof_g_packet += register_size (current_gdbarch, regnum); + rs->sizeof_g_packet += register_size (current_gdbarch, regnum); } /* Default maximum number of characters in a packet body. Many @@ -290,7 +300,7 @@ init_remote_state (struct gdbarch *gdbarch) static struct packet_reg * packet_reg_from_regnum (struct remote_state *rs, long regnum) { - if (regnum < 0 && regnum >= NUM_REGS + NUM_PSEUDO_REGS) + if (regnum < 0 && regnum >= NUM_REGS) return NULL; else { @@ -304,7 +314,7 @@ static struct packet_reg * packet_reg_from_pnum (struct remote_state *rs, LONGEST pnum) { int i; - for (i = 0; i < NUM_REGS + NUM_PSEUDO_REGS; i++) + for (i = 0; i < NUM_REGS; i++) { struct packet_reg *r = &rs->regs[i]; if (r->pnum == pnum) @@ -978,6 +988,26 @@ show_remote_protocol_qGetTLSAddr_packet_cmd (struct ui_file *file, int from_tty, show_packet_config_cmd (&remote_protocol_qGetTLSAddr); } +/* FIXME: Kill these duplicated functions. */ +/* Should we try the 'qPart:availableRegisters' request? */ +static struct packet_config remote_protocol_qPart_availableRegisters; + +static void +set_remote_protocol_qPart_availableRegisters_packet_cmd (char *args, int from_tty, + struct cmd_list_element *c) +{ + update_packet_config (&remote_protocol_qPart_availableRegisters); +} + +static void +show_remote_protocol_qPart_availableRegisters_packet_cmd (struct ui_file *file, int from_tty, + struct cmd_list_element *c, + const char * value) +{ + deprecated_show_value_hack (file, from_tty, c, value); + show_packet_config_cmd (&remote_protocol_qPart_availableRegisters); +} + static struct packet_config remote_protocol_p; static void @@ -2109,6 +2139,7 @@ init_all_packet_configs (void) update_packet_config (&remote_protocol_binary_download); update_packet_config (&remote_protocol_qPart_auxv); update_packet_config (&remote_protocol_qGetTLSAddr); + update_packet_config (&remote_protocol_qPart_availableRegisters); } /* Symbol look-up. */ @@ -3213,60 +3244,69 @@ got_status: return inferior_ptid; } -/* Number of bytes of registers this stub implements. */ - -static int register_bytes_found; - -/* Read the remote registers into the block REGS. */ -/* Currently we just read all the registers, so we don't use regnum. */ +/* Fetch a single register using a 'p' packet. */ static int -fetch_register_using_p (int regnum) +fetch_register_using_p (struct packet_reg *reg) { struct remote_state *rs = get_remote_state (); char *buf = alloca (rs->remote_packet_size), *p; char regp[MAX_REGISTER_SIZE]; int i; + if (remote_protocol_p.support == PACKET_DISABLE) + return 0; + p = buf; *p++ = 'p'; - p += hexnumstr (p, regnum); + p += hexnumstr (p, reg->pnum); *p++ = '\0'; remote_send (buf, rs->remote_packet_size); - /* If the stub didn't recognize the packet, or if we got an error, - tell our caller. */ - if (buf[0] == '\0' || buf[0] == 'E') - return 0; + if (buf[0] == 0) + { + if (remote_protocol_p.support == PACKET_ENABLE) + error ("Protocol error: p packet enabled but not recognized by stub"); + else + { + /* The stub does not support the 'P' packet. Use 'G' + instead, and don't try using 'P' in the future (it + will just waste our time). */ + remote_protocol_p.support = PACKET_DISABLE; + return 0; + } + } + + remote_protocol_p.support = PACKET_ENABLE; - /* If this register is unfetchable, tell the regcache. */ if (buf[0] == 'x') { - regcache_raw_supply (current_regcache, regnum, NULL); - set_register_cached (regnum, -1); + regcache_raw_supply (current_regcache, reg->regnum, NULL); + set_register_cached (reg->regnum, -1); return 1; } - /* Otherwise, parse and supply the value. */ p = buf; i = 0; while (p[0] != 0) { if (p[1] == 0) - { - error (_("fetch_register_using_p: early buf termination")); - return 0; - } - + error("fetch_register_using_p: early buf termination"); regp[i++] = fromhex (p[0]) * 16 + fromhex (p[1]); p += 2; } - regcache_raw_supply (current_regcache, regnum, regp); + regcache_raw_supply (current_regcache, reg->regnum, regp); return 1; } +/* Number of bytes of registers this stub implements. */ + +static int register_bytes_found; + +/* Fetch the registers included in the target's 'g' packet. */ + static void -remote_fetch_registers (int regnum) +fetch_registers_using_g (void) { struct remote_state *rs = get_remote_state (); char *buf = alloca (rs->remote_packet_size); @@ -3276,41 +3316,6 @@ remote_fetch_registers (int regnum) set_thread (PIDGET (inferior_ptid), 1); - if (regnum >= 0) - { - struct packet_reg *reg = packet_reg_from_regnum (rs, regnum); - gdb_assert (reg != NULL); - if (!reg->in_g_packet) - internal_error (__FILE__, __LINE__, - _("Attempt to fetch a non G-packet register when this " - "remote.c does not support the p-packet.")); - } - switch (remote_protocol_p.support) - { - case PACKET_DISABLE: - break; - case PACKET_ENABLE: - if (fetch_register_using_p (regnum)) - return; - else - error (_("Protocol error: p packet not recognized by stub")); - case PACKET_SUPPORT_UNKNOWN: - if (fetch_register_using_p (regnum)) - { - /* The stub recognized the 'p' packet. Remember this. */ - remote_protocol_p.support = PACKET_ENABLE; - return; - } - else - { - /* The stub does not support the 'P' packet. Use 'G' - instead, and don't try using 'P' in the future (it - will just waste our time). */ - remote_protocol_p.support = PACKET_DISABLE; - break; - } - } - sprintf (buf, "g"); remote_send (buf, (rs->remote_packet_size)); @@ -3371,7 +3376,7 @@ remote_fetch_registers (int regnum) supply_them: { int i; - for (i = 0; i < NUM_REGS + NUM_PSEUDO_REGS; i++) + for (i = 0; i < NUM_REGS; i++) { struct packet_reg *r = &rs->regs[i]; if (r->in_g_packet) @@ -3398,6 +3403,38 @@ remote_fetch_registers (int regnum) } } +static void +remote_fetch_registers (int regnum) +{ + struct remote_state *rs = get_remote_state (); + int i; + + set_thread (PIDGET (inferior_ptid), 1); + + if (regnum >= 0) + { + struct packet_reg *reg = packet_reg_from_regnum (rs, regnum); + gdb_assert (reg != NULL); + + if (fetch_register_using_p (reg)) + return; + + if (!reg->in_g_packet) + error ("Protocol error: register %ld not supported by stub", reg->regnum); + + fetch_registers_using_g (); + return; + } + + fetch_registers_using_g (); + + for (i = 0; i < NUM_REGS; i++) + if (!rs->regs[i].in_g_packet) + if (!fetch_register_using_p (&rs->regs[i])) + error ("Protocol error: register %ld not supported by stub", + rs->regs[i].regnum); +} + /* Prepare to store registers. Since we may send them all (using a 'G' request), we have to read out the ones we don't want to change first. */ @@ -3428,74 +3465,58 @@ remote_prepare_to_store (void) packet was not recognized. */ static int -store_register_using_P (int regnum) +store_register_using_P (struct packet_reg *reg) { struct remote_state *rs = get_remote_state (); - struct packet_reg *reg = packet_reg_from_regnum (rs, regnum); /* Try storing a single register. */ char *buf = alloca (rs->remote_packet_size); char regp[MAX_REGISTER_SIZE]; char *p; + if (remote_protocol_P.support == PACKET_DISABLE) + return 0; + xsnprintf (buf, rs->remote_packet_size, "P%s=", phex_nz (reg->pnum, 0)); p = buf + strlen (buf); regcache_raw_collect (current_regcache, reg->regnum, regp); bin2hex (regp, p, register_size (current_gdbarch, reg->regnum)); remote_send (buf, rs->remote_packet_size); - return buf[0] != '\0'; + if (buf[0] == 0) + { + if (remote_protocol_P.support == PACKET_ENABLE) + error ("Protocol error: P packet enabled but not recognized by stub"); + else + { + /* The stub does not support the 'P' packet. Use 'G' + instead, and don't try using 'P' in the future (it + will just waste our time). */ + remote_protocol_P.support = PACKET_DISABLE; + return 0; + } + } + + return 1; } - /* Store register REGNUM, or all registers if REGNUM == -1, from the contents of the register cache buffer. FIXME: ignores errors. */ -static void -remote_store_registers (int regnum) +void +store_registers_using_G (void) { struct remote_state *rs = get_remote_state (); char *buf; char *regs; char *p; - set_thread (PIDGET (inferior_ptid), 1); - - if (regnum >= 0) - { - switch (remote_protocol_P.support) - { - case PACKET_DISABLE: - break; - case PACKET_ENABLE: - if (store_register_using_P (regnum)) - return; - else - error (_("Protocol error: P packet not recognized by stub")); - case PACKET_SUPPORT_UNKNOWN: - if (store_register_using_P (regnum)) - { - /* The stub recognized the 'P' packet. Remember this. */ - remote_protocol_P.support = PACKET_ENABLE; - return; - } - else - { - /* The stub does not support the 'P' packet. Use 'G' - instead, and don't try using 'P' in the future (it - will just waste our time). */ - remote_protocol_P.support = PACKET_DISABLE; - break; - } - } - } - /* Extract all the registers in the regcache copying them into a local buffer. */ { int i; regs = alloca (rs->sizeof_g_packet); memset (regs, 0, rs->sizeof_g_packet); - for (i = 0; i < NUM_REGS + NUM_PSEUDO_REGS; i++) + for (i = 0; i < NUM_REGS; i++) { struct packet_reg *r = &rs->regs[i]; if (r->in_g_packet) @@ -4456,6 +4477,41 @@ extended_remote_async_create_inferior (char *exec_file, char *args, /* Let the remote process run. */ proceed (-1, TARGET_SIGNAL_0, 0); } + +/* Store register REGNUM, or all registers if REGNUM == -1, from the contents + of the register cache buffer. FIXME: ignores errors. */ + +static void +remote_store_registers (int regnum) +{ + struct remote_state *rs = get_remote_state (); + int i; + + set_thread (PIDGET (inferior_ptid), 1); + + if (regnum >= 0) + { + struct packet_reg *reg = packet_reg_from_regnum (rs, regnum); + gdb_assert (reg != NULL); + + if (store_register_using_P (reg)) + return; + + if (!reg->in_g_packet) + error ("Protocol error: register %ld not supported by stub", reg->regnum); + + store_registers_using_G (); + return; + } + + store_registers_using_G (); + + for (i = 0; i < NUM_REGS; i++) + if (!rs->regs[i].in_g_packet) + if (!store_register_using_P (&rs->regs[i])) + error ("Protocol error: register %ld not supported by stub", + rs->regs[i].regnum); +} /* On some machines, e.g. 68k, we may use a different breakpoint @@ -5035,6 +5091,44 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object, } return -1; + /* FIXME: + - Currently hex encoded. Should this be just a string? + - One way or another remove the copy/paste. + */ + case TARGET_OBJECT_AVAILABLE_REGISTERS: + if (remote_protocol_qPart_availableRegisters.support != PACKET_DISABLE) + { + unsigned int total = 0; + while (len > 0) + { + LONGEST n = min ((rs->remote_packet_size - 2) / 2, len); + snprintf (buf2, rs->remote_packet_size, + "qPart:availableRegisters:read::%s,%s", + phex_nz (offset, sizeof offset), + phex_nz (n, sizeof n)); + i = putpkt (buf2); + if (i < 0) + return total > 0 ? total : i; + buf2[0] = '\0'; + getpkt (buf2, rs->remote_packet_size, 0); + if (packet_ok (buf2, &remote_protocol_qPart_availableRegisters) != PACKET_OK) + return total > 0 ? total : -1; + if (buf2[0] == 'O' && buf2[1] == 'K' && buf2[2] == '\0') + break; /* Got EOF indicator. */ + /* Got some data. */ + i = hex2bin (buf2, readbuf, len); + if (i > 0) + { + readbuf = (void *) ((char *) readbuf + i); + offset += i; + len -= i; + total += i; + } + } + return total; + } + return -1; + default: return -1; } @@ -5583,6 +5677,7 @@ show_remote_cmd (char *args, int from_tty) show_remote_protocol_binary_download_cmd (gdb_stdout, from_tty, NULL, NULL); show_remote_protocol_qPart_auxv_packet_cmd (gdb_stdout, from_tty, NULL, NULL); show_remote_protocol_qGetTLSAddr_packet_cmd (gdb_stdout, from_tty, NULL, NULL); + show_remote_protocol_qPart_availableRegisters_packet_cmd (gdb_stdout, from_tty, NULL, NULL); } static void @@ -5819,6 +5914,13 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, &remote_set_cmdlist, &remote_show_cmdlist, 0); + add_packet_config_cmd (&remote_protocol_qPart_availableRegisters, + "qPart_availableRegisters", "available-registers", + set_remote_protocol_qPart_availableRegisters_packet_cmd, + show_remote_protocol_qPart_availableRegisters_packet_cmd, + &remote_set_cmdlist, &remote_show_cmdlist, + 0); + /* Keep the old ``set remote Z-packet ...'' working. */ add_setshow_auto_boolean_cmd ("Z-packet", class_obscure, &remote_Z_packet_detect, _("\ diff --git a/gdb/target.h b/gdb/target.h index f1de230c463..fd565ac8634 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -229,7 +229,10 @@ enum target_object /* Transfer auxilliary vector. */ TARGET_OBJECT_AUXV, /* StackGhost cookie. See "sparc-tdep.c". */ - TARGET_OBJECT_WCOOKIE + TARGET_OBJECT_WCOOKIE, + /* A target-specific description of the available registers on + the target. */ + TARGET_OBJECT_AVAILABLE_REGISTERS /* Possible future objects: TARGET_OBJECT_FILE, TARGET_OBJECT_PROC, ... */ }; |