| /* Target dependent code for ARC processor family, for GDB, the GNU debugger. |
| |
| Copyright 2005, 2008, 2009 Free Software Foundation, Inc. |
| |
| Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) |
| |
| Authors: |
| Soam Vasani <soam.vasani@codito.com> |
| Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> |
| Richard Stuckey <richard.stuckey@arc.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 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| /******************************************************************************/ |
| /* */ |
| /* Outline: */ |
| /* This module provides support for the ARC processor family's target */ |
| /* dependencies which are specific to the arc-elf32 configuration of the */ |
| /* ARC gdb. */ |
| /* */ |
| /* */ |
| /* Functionality: */ |
| /* This module provides a number of operations: */ |
| /* */ |
| /* 1) a function which returns the name of a register, given its number */ |
| /* */ |
| /* 2) a function which determines whether a given register belongs to a */ |
| /* particular group (e.g. the group of registers which should be saved */ |
| /* and restored across a function call) */ |
| /* */ |
| /* 3) a function which prints out registers */ |
| /* */ |
| /* 4) functions which implement the gdb extended commands */ |
| /* */ |
| /* arc-watch-range <start> [<kind>] for setting a watchpoint range */ |
| /* arc-break-range <start> <length> for setting a breakpoint range */ |
| /* arc-fill-memory <start> <length> [<pattern>] for filling memory */ |
| /* */ |
| /* 5) functions for various operations (such as program loading) which */ |
| /* are common to the different arc-elf32 targets supported */ |
| /* */ |
| /* */ |
| /* Usage: */ |
| /* The module exports a function _initialize_arc_elf32_tdep: the call to */ |
| /* this function is generated by the gdb build mechanism, so this function*/ |
| /* should not be explicitly called. */ |
| /* */ |
| /* This module exports a function arc_elf32_initialize which creates the */ |
| /* user commands which use those command-implementing functions; it also */ |
| /* stores pointers to the other functions in a data structure so that */ |
| /* they may be called from outside this module. */ |
| /* */ |
| /* Some of the operations provided by this module are registered with gdb */ |
| /* during initialization; gdb then calls them via function pointers, */ |
| /* rather than by name (this allows gdb to handle multiple target */ |
| /* architectures): */ |
| /* */ |
| /* set_gdbarch_XXX (gdbarch, <function>); */ |
| /* */ |
| /* */ |
| /* Register Numbering Scheme: */ |
| /* The N target processor registers are assigned gdb numbers which form a */ |
| /* contiguous range starting at 0. The scheme used is: */ |
| /* */ |
| /* 0 .. n1 : core registers R0 .. R31 */ |
| /* n1+1 .. n2 : extension core registers R32 .. R59 (if any) */ |
| /* n2+1 : r60 (LP_COUNT) */ |
| /* n2+2 : r63 (PCL) */ |
| /* n2+3 : IDENTITY auxiliary register */ |
| /* n2+4 .. n3 : non-BCR auxiliary registers in address order */ |
| /* n3+1 .. N-1 : Build Configuration Registers in address order */ |
| /* */ |
| /* N.B. 1) core registers R61 and R62 are not included in the scheme, as */ |
| /* R61 is reserved, and R62 is not a real register; */ |
| /* */ |
| /* 2) the set of non-BCR auxiliary registers, and the set of BCRs, */ |
| /* are each ordered by increasing register address in the ARC */ |
| /* auxiliary register space; */ |
| /* */ |
| /* 3) the IDENTITY auxiliary register comes before all the other */ |
| /* auxiliary registers, even though it does not come first in the */ |
| /* address space: this is so that the debugger can always get the */ |
| /* contents of the register, and so determine the architectural */ |
| /* version of the target processor, regardless of what that */ |
| /* version may be (and hence what the processor's auxiliary */ |
| /* register set is) - this is vital when the debug target is a */ |
| /* remote target, as the position of the register contents in the */ |
| /* 'g' RSP response packet (or the number to be specified in the */ |
| /* 'p' query packet) depends upon the target processor version; */ |
| /* otherwise, we would have the problem that the debugger could */ |
| /* not determine the target processor version without already */ |
| /* knowing it! */ |
| /* */ |
| /* The numbers are assigned to the registers by the arc-registers module, */ |
| /* after the XML definitions of the auxiliary registers and any extension */ |
| /* core registers defined for the target processor have been read and */ |
| /* processed. */ |
| /* */ |
| /* */ |
| /* Auxiliary Registers Definition: */ |
| /* The ARC processor is configurable, and the set of auxiliary registers */ |
| /* that a target processor may possess depends upon the configuration. It */ |
| /* is therefore not possible to hard-code descriptions of this register */ |
| /* set into the debugger. Instead, the descriptions of the registers are */ |
| /* read from an XML file (or files). */ |
| /* */ |
| /* The arc-elf32-gdb debugger provides commands which allow the user to */ |
| /* instruct it to read an XML file and use the register definitions in */ |
| /* that file; these definitions either completely replace, or are added */ |
| /* to (depending upon which command is used), any existing set of */ |
| /* definitions the debugger has; this allows processor architecture */ |
| /* variants to be described by groups of files (e.g. a common main file */ |
| /* describing the base configuration, plus additional files describing */ |
| /* cache-related registers, or MMU-related registers, which are specific */ |
| /* to different variants). */ |
| /* */ |
| /* Note that this scheme may also be used to define the set of extension */ |
| /* core registers (if any) possessed by the target processor. */ |
| /* */ |
| /* If no register set is defined by use of the commands, the debugger */ |
| /* attempts to read the register definitions from a default file; it will */ |
| /* look for this file first in the user's current working directory, then */ |
| /* in the user's home directory. In order to provide maximum flexibility, */ |
| /* the debugger delays the attempt to read the default file until it is */ |
| /* necessary, e.g. when connection to a target is being attempted, or a */ |
| /* check is made that the executable file to be debugged has been built */ |
| /* for the same architectural version (e.g. ARC600, ARC700, etc.) as the */ |
| /* version of the target processor. */ |
| /* */ |
| /* The arc-elf32 specific code makes as few assumptions as possible about */ |
| /* the auxiliary register set: it will always try to get the number (i.e. */ |
| /* the hardware number, the offset in the auxiliary register space) of an */ |
| /* auxiliary register from the definition of that register, even in the */ |
| /* case that the register is defined in the base ARC architecture, and */ |
| /* hence should be present in all processor variants. */ |
| /* */ |
| /* In particular, no assumption is made about the PC; so, to guarantee */ |
| /* that the debugger has read a definition of the PC by the time that it */ |
| /* is needed (as the complete set of auxiliary register definitions may */ |
| /* be read from a number of files, there is no requirement for the PC's */ |
| /* definition to be in any given file), a "guard" is set upon the PC such */ |
| /* that any attempt to read/write the PC by gdb will result in a check */ |
| /* that the PC is defined: if the PC is defined, the guard is removed, */ |
| /* and the read/write operation performed - otherwise, an error message */ |
| /* is reported, and the operation aborted. */ |
| /* */ |
| /* Register Byte Order: */ |
| /* The target register contents are held in gdb's register cache (i.e. in */ |
| /* a regcache struct) in target byte order; however, when values are */ |
| /* read/written to/from the xISS target (i.e. the dynamically loaded xISS */ |
| /* simulator) or the ARCangel4 target those values must be in host byte */ |
| /* order. */ |
| /* */ |
| /* The ARC debugger is currently built only to run on an X86 Linux host, */ |
| /* so the assumption is made that the host is little-endian. */ |
| /* */ |
| /******************************************************************************/ |
| |
| /* system header files */ |
| #include <string.h> |
| #include <signal.h> |
| #include <byteswap.h> |
| |
| /* gdb header files */ |
| #include "defs.h" |
| #include "inferior.h" |
| #include "gdbcore.h" |
| #include "gdbcmd.h" |
| #include "regcache.h" |
| #include "exceptions.h" |
| #include "reggroups.h" |
| #include "observer.h" |
| #include "objfiles.h" |
| #include "arch-utils.h" |
| #include "gdb-events.h" |
| #include "gdb_assert.h" |
| |
| /* ARC header files */ |
| #include "config/arc/tm-embed.h" |
| #include "arc-tdep.h" |
| #include "arc-memory.h" |
| #include "arc-arguments.h" |
| #include "arc-elf32-tdep.h" |
| #include "arc-registers.h" |
| #include "arc-remote-fileio.h" |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* local types */ |
| /* -------------------------------------------------------------------------- */ |
| |
| typedef struct |
| { |
| struct gdbarch *gdbarch; |
| struct ui_file *file; |
| struct frame_info *frame; |
| } PrintData; |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* local data */ |
| /* -------------------------------------------------------------------------- */ |
| |
| #define INVALID_REGISTER_NUMBER (ARC_RegisterNumber) 0xFFFFFFFFU |
| |
| #define WATCH_MEMORY_COMMAND "arc-watch-range" |
| #define BREAK_MEMORY_COMMAND "arc-break-range" |
| #define FILL_MEMORY_COMMAND "arc-fill-memory" |
| |
| #define WATCH_MEMORY_COMMAND_USAGE "Usage: " WATCH_MEMORY_COMMAND " <START> <LENGTH> [ read | write | access ]\n" |
| #define BREAK_MEMORY_COMMAND_USAGE "Usage: " BREAK_MEMORY_COMMAND " <START> <LENGTH>\n" |
| #define FILL_MEMORY_COMMAND_USAGE "Usage: " FILL_MEMORY_COMMAND " <START> <LENGTH> [ <PATTERN> ]\n" |
| |
| |
| |
| /* ARC 700 brk_s instruction. */ |
| static const unsigned char le_breakpoint_instruction[] = { 0xff, 0x7f }; |
| static const unsigned char be_breakpoint_instruction[] = { 0x7f, 0xff }; |
| |
| |
| /* N.B. the array size is specified in the declaration so that the compiler |
| will warn of "excess elements in array initializer" if there is a |
| mismatch (but not of too few elements, unfortunately!). */ |
| static const char *register_names[ARC_MAX_CORE_REGS] = |
| { |
| "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", |
| |
| "fp", // r27 |
| "sp", // r28 |
| "ilink1", // r29 |
| "ilink2", // r30 |
| "blink", // r31 |
| |
| /* Extension core registers are 32 .. 59 inclusive. */ |
| "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", |
| "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", "r48", "r49", |
| "r50", "r51", "r52", "r53", "r54", "r55", "r56", "r57", "r58", "r59", |
| |
| "lp_count", |
| |
| /* 61 is reserved, 62 is not a real register. */ |
| "r61", |
| "r62", |
| |
| "pcl" |
| }; |
| |
| |
| /* For the Ctrl-C signal handler. */ |
| static void (*old_signal_handler) (int); |
| |
| /* This flag is used by the Ctrl-C interrupt mechanism: it is set by an |
| interrupt handler and tested by non-interrupt code, so must be declared |
| as volatile to avoid possible optimisation problems. */ |
| static volatile Boolean interrupt_processor; |
| |
| /* A pointer to the remote target's register store function. */ |
| static void (*remote_register_store)(struct regcache *regcache, int regno); |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* externally visible data */ |
| /* -------------------------------------------------------------------------- */ |
| |
| /* These are the h/w register numbers of the DEBUG, PC and STATUS32 registers. */ |
| ARC_RegisterNumber arc_debug_regnum; |
| ARC_RegisterNumber arc_pc_regnum; |
| ARC_RegisterNumber arc_status32_regnum; |
| |
| /* Whether a program has been loaded to the target. */ |
| Boolean arc_program_is_loaded; |
| |
| /* Whether a target is connected. */ |
| Boolean arc_target_is_connected; |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* local macros */ |
| /* -------------------------------------------------------------------------- */ |
| |
| #define PRINT(regnum) \ |
| default_print_registers_info (gdbarch, file, frame, regnum, all) |
| |
| #define PRINT_HW(hw_regnum) PRINT(arc_core_register_gdb_number(hw_regnum)) |
| |
| #define PRINT_BY_NAME(regname) \ |
| { \ |
| ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_name(regname); \ |
| \ |
| if (def) \ |
| PRINT(arc_aux_gdb_register_number(def)); \ |
| } while (0) |
| |
| |
| #define EXTRACT(argument, type, result) \ |
| { \ |
| struct expression *expr = parse_expression(argument); \ |
| struct value *val = evaluate_expression(expr); \ |
| struct cleanup *chain = make_cleanup(free_current_contents, &expr); \ |
| \ |
| result = *(type*) (value_contents (val)); \ |
| do_cleanups (chain); \ |
| } |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* local functions */ |
| /* -------------------------------------------------------------------------- */ |
| |
| /* This function creates the processor-specific information for the arc-elf32-gdb |
| variant of the the ARC gdb deubbger. */ |
| |
| static void |
| create_variant_info (struct gdbarch_tdep *tdep) |
| { |
| tdep->processor_variant_info = xmalloc(sizeof(ARC_VariantsInfo)); |
| tdep->processor_variant_info->processor_version = NO_ARCHITECTURE; |
| |
| arc_initialize_aux_reg_info(&tdep->processor_variant_info->registers); |
| } |
| |
| |
| /* This function is used to read an auxiliary register on the target. */ |
| |
| static Boolean |
| read_target_aux_register (ARC_RegisterNumber hw_regno, |
| ARC_RegisterContents *contents, |
| Boolean warn_on_failure) |
| { |
| struct regcache *regcache = get_current_regcache(); |
| ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_hw_number(hw_regno); |
| int gdb_regno = arc_aux_gdb_register_number(def); |
| |
| /* Read the register contents from the target to the register cache, |
| then collect the register value from the cache. */ |
| target_fetch_registers(regcache, gdb_regno); |
| regcache_raw_collect (regcache, gdb_regno, contents); |
| |
| /* The register cache holds the contents in target byte order, so convert to |
| host byte order if the target and host orders are different. */ |
| if (HOST_AND_TARGET_ENDIANNESS_DIFFER(current_gdbarch)) |
| *contents = __bswap_32(*contents); |
| |
| /* Unfortunately, we can not tell whether the read succeeded or failed. */ |
| return TRUE; |
| } |
| |
| |
| /* This function returns a pointer to a function which may be used to read an |
| auxiliary register on the target. The register contents returned by that |
| function are in host byte order. */ |
| |
| static ReadRegisterFunction |
| read_aux_register (struct target_ops *target) |
| { |
| /* If we have a function which can read an aux register on the target, |
| and return a success/failure result, use it instead. */ |
| if ((strcmp(target->to_shortname, "arcxiss") == 0) || |
| (strcmp(target->to_shortname, "arcjtag") == 0)) |
| { |
| TargetOperations *operations = (TargetOperations*) target->to_data; |
| return operations->read_auxiliary_register; |
| } |
| |
| return read_target_aux_register; |
| } |
| |
| |
| /* Convert the contents of the given register in the given cache so that it can |
| be written to the target (i.e. if there are any fields in the register which |
| are required by the ARC processor architectural definition to have particular |
| values on write, set those fields to those values). */ |
| |
| static void convert_register(int gdb_regno, |
| struct regcache *regcache) |
| { |
| ARC_RegisterContents contents; |
| |
| regcache_raw_collect (regcache, gdb_regno, &contents); |
| arc_convert_aux_contents_for_write (gdb_regno, &contents); |
| regcache_raw_supply (regcache, gdb_regno, &contents); |
| } |
| |
| |
| /* This function is called when a remote target calls its 'to_store_registers' |
| operation: we intercept that call, and convert the register contents as |
| required, before calling the real operation. */ |
| |
| static void |
| intercept_remote_register_store (struct regcache *regcache, int gdb_regno) |
| { |
| struct gdbarch *gdbarch = get_regcache_arch(regcache); |
| struct regcache *savedcache = regcache_xmalloc(gdbarch); |
| |
| ENTERARGS("gdb_regno: %d", gdb_regno); |
| |
| /* Save the register cache. */ |
| regcache_cpy(savedcache, regcache); |
| |
| /* Convert the value of the register(s) for writing. */ |
| if (gdb_regno >= 0) |
| convert_register(gdb_regno, regcache); |
| else |
| { |
| int num_regs = gdbarch_num_regs (gdbarch); |
| |
| for (gdb_regno = 0; gdb_regno < num_regs; gdb_regno++) |
| convert_register(gdb_regno, regcache); |
| } |
| |
| /* Now use the real remote target 'store registers' operation to store the |
| converted cache. */ |
| remote_register_store(regcache, gdb_regno); |
| |
| /* Restore the register cache. */ |
| regcache_cpy(regcache, savedcache); |
| regcache_xfree(savedcache); |
| } |
| |
| |
| /* This is a callback function which gets called by gdb whenever the current |
| object file changes. */ |
| |
| static void |
| new_object_file (struct objfile *objfile) |
| { |
| if (objfile) |
| ARCHITECTURE_CHECK(current_gdbarch, objfile->obfd); |
| } |
| |
| |
| /* This is a callback function which gets called by gdb just before connection |
| * to a target is attempted. */ |
| |
| static void |
| pre_target_connection (struct target_ops *target) |
| { |
| DEBUG("pre_target_connect : %s\n", target->to_shortname); |
| |
| /* We do not yet know the version of the target processor. */ |
| arc_architecture_is_unknown(); |
| |
| /* If we have not read yet any aux register definitions for this architecture, |
| * try to read the default file now. */ |
| if (!arc_aux_regs_defined(current_gdbarch)) |
| arc_read_default_aux_registers(current_gdbarch); |
| } |
| |
| |
| /* This is a callback function which gets called by gdb just after connection |
| to a target is completed. */ |
| |
| static void |
| post_target_connection (struct target_ops *target) |
| { |
| DEBUG("post_target_connect : %s\n", target->to_shortname); |
| |
| arc_target_is_connected = TRUE; |
| |
| arc_update_architecture(read_aux_register(target)); |
| |
| ARCHITECTURE_CHECK(current_gdbarch, |
| (current_objfile) ? current_objfile->obfd : NULL); |
| } |
| |
| |
| /* This is a callback function which gets called by gdb just after disconnection |
| from a target has been completed. */ |
| |
| static void |
| post_target_disconnection (struct target_ops *target) |
| { |
| DEBUG("post_target_disconnect : %s\n", target->to_shortname); |
| |
| arc_target_is_connected = FALSE; |
| } |
| |
| |
| /* This is a callback function which gets called by gdb after a target has been updated. */ |
| |
| static void |
| target_updated (struct target_ops *target) |
| { |
| DEBUG("target_updated : %s\n", target->to_shortname); |
| |
| if (strcmp(target->to_shortname, "remote") == 0) |
| { |
| DEBUG("remote target register store interception is in force\n"); |
| |
| /* We must intercept the remote target's register store operation: we |
| need to convert register contents before they are written to the |
| target (we can not do that in remote.c as that is generic gdb code). */ |
| if (target->to_store_registers != intercept_remote_register_store) |
| { |
| remote_register_store = target->to_store_registers; |
| target->to_store_registers = intercept_remote_register_store; |
| } |
| } |
| } |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* 1) local functions for handling Ctrl-C */ |
| /* -------------------------------------------------------------------------- */ |
| |
| /* The command line interface's stop routines. The interrupted_by_user function |
| is installed as a signal handler for SIGINT (it gets called when the user |
| types Ctrl-C). |
| |
| The first time a user requests a stop, we set the interrupt_processor flag. |
| If this does not work, and the user tries a second time, we ask the user if |
| he'd like to detach from the target. */ |
| |
| |
| static void |
| interrupted_twice (int signo); |
| |
| |
| /* This function is called when the user types Ctrl-C. */ |
| |
| static void |
| interrupted_by_user (int signo) |
| { |
| /* Change the signal handler for Ctrl-C to the second level handler so that |
| if we get the signal again whilst waiting for the program to halt, we do |
| something more drastic. */ |
| (void) signal (SIGINT, interrupted_twice); |
| |
| /* This flag is checked in each iteration of the loop that polls the target |
| processor to see whether it has halted (e.g. at a breakpoint); if the |
| flag is set, an attempt will be made to force the processor to halt. |
| |
| N.B. once the polling loop is running, this flag is set only by this |
| handler, and is read only by the polling loop - so there is no |
| mutual exclusion problem to be worried about here; this is a MUCH |
| cleaner and more reliable method than trying to have this handler |
| force the halt itself, e.g. by calling target_stop. */ |
| interrupt_processor = TRUE; |
| |
| DEBUG("Attempting to interrupt...\n"); |
| } |
| |
| |
| /* This function is called when the user types Ctrl-C twice. */ |
| |
| static void |
| interrupted_twice (int signo) |
| { |
| if (query(_("Interrupted while waiting for the program to halt.\n" |
| "Give up (and stop debugging it)?"))) |
| { |
| struct gdb_exception exception = {RETURN_QUIT, |
| GDB_NO_ERROR, |
| _("Interrupted by user")}; |
| |
| /* Put the old signal handler back. */ |
| (void) signal (signo, old_signal_handler); |
| |
| target_mourn_inferior(); |
| DEBUG("interrupted_twice: throwing exception\n"); |
| throw_exception (exception); |
| |
| /* Control does not return here! */ |
| } |
| |
| /* Change the signal handler for Ctrl-C back to the first level handler. */ |
| (void) signal (SIGINT, interrupted_by_user); |
| } |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* 2) functions for reading/writing registers */ |
| /* -------------------------------------------------------------------------- */ |
| |
| /* This function maps a gdb internal register number to the hardware number |
| (i.e. core register number or number in the auxiliary register space). */ |
| |
| static ARC_RegisterNumber |
| get_hw_regnum_mapping (int gdb_regno) |
| { |
| ARC_AuxRegisterDefinition *def; |
| |
| if (arc_is_core_register(gdb_regno)) |
| return arc_core_register_number(gdb_regno); |
| |
| def = arc_find_aux_register_by_gdb_number(gdb_regno); |
| |
| if (def) |
| return arc_aux_hw_register_number(def); |
| |
| /* Not found. */ |
| return INVALID_REGISTER_NUMBER; |
| } |
| |
| |
| /* This function fetches one register from the target and saves its contents in |
| the given register cache. The register is identified both by its gdb number |
| and its ARC hardware number. */ |
| |
| static void |
| debug_fetch_one_register (struct regcache * regcache, |
| ARC_RegisterNumber hw_regno, |
| int gdb_regno) |
| { |
| TargetOperations *operations = (TargetOperations*) current_target.to_data; |
| ARC_RegisterContents contents; |
| Boolean register_read = FALSE; |
| |
| ENTERARGS("gdb = %d, h/w = %d", gdb_regno, hw_regno); |
| |
| gdb_assert(gdb_regno >= 0); |
| |
| /* N.B. do not give a warning message if the register is write-only, as gdb |
| may be reading all registers, and it is best to quietly ignore the |
| ones that can not be read! */ |
| if (arc_is_core_register(gdb_regno)) |
| { |
| if (arc_core_register_access(hw_regno) != WRITE_ONLY) |
| register_read = operations->read_core_register(hw_regno, &contents, TRUE); |
| } |
| else |
| { |
| ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_hw_number(hw_regno); |
| |
| if (arc_aux_register_access(def) != WRITE_ONLY) |
| register_read = operations->read_auxiliary_register (hw_regno, &contents, TRUE); |
| } |
| |
| if (register_read) |
| { |
| DEBUG("read 0x%08X from target\n", contents); |
| |
| /* The read_<type>_register functions return the register contents in |
| host order, but the register cache holds them in target byte order, |
| so swap the bytes if necessary before supplying the contents to the |
| cache. */ |
| if (HOST_AND_TARGET_ENDIANNESS_DIFFER(get_regcache_arch(regcache))) |
| { |
| contents = __bswap_32(contents); |
| DEBUG("byte-swapped to 0x%08X\n", contents); |
| } |
| |
| regcache_raw_supply (regcache, (int) gdb_regno, &contents); |
| } |
| |
| LEAVEMSG; |
| } |
| |
| |
| /* This function is passed to the arc_all_aux_registers iterator. |
| It fetches one auxiliary register from the target. */ |
| |
| static void |
| debug_fetch_reg (ARC_AuxRegisterDefinition *def, void *data) |
| { |
| debug_fetch_one_register((struct regcache*) data, |
| arc_aux_hw_register_number (def), |
| arc_aux_gdb_register_number(def)); |
| } |
| |
| |
| /* This function gets a register from the given register cache and stores it |
| to the target. The register is identified both by its gdb number and its |
| ARC hardware number. */ |
| |
| static void |
| debug_store_one_register (struct regcache *regcache, |
| ARC_RegisterNumber hw_regno, |
| int gdb_regno) |
| { |
| TargetOperations *operations = (TargetOperations*) current_target.to_data; |
| ARC_RegisterContents contents; |
| |
| ENTERARGS("gdb = %d, h/w = %d", gdb_regno, hw_regno); |
| |
| gdb_assert(gdb_regno >= 0); |
| |
| regcache_raw_collect(regcache, gdb_regno, &contents); |
| |
| DEBUG("collected 0x%08X from cache\n", contents); |
| |
| /* The write_<type>_register functions take the register contents in |
| host order, but the register cache holds them in target byte order, |
| so swap the bytes if necessary after collecting the contents from the |
| functions. */ |
| if (HOST_AND_TARGET_ENDIANNESS_DIFFER(get_regcache_arch(regcache))) |
| { |
| contents = __bswap_32(contents); |
| DEBUG("byte-swapped to 0x%08X\n", contents); |
| } |
| |
| if (arc_is_core_register(gdb_regno)) |
| { |
| if (arc_core_register_access(hw_regno) == READ_ONLY) |
| arc_elf32_core_warning(REGISTER_IS_READ_ONLY, hw_regno); |
| else |
| (void) operations->write_core_register(hw_regno, contents, TRUE); |
| } |
| else |
| { |
| ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_hw_number(hw_regno); |
| |
| if (arc_aux_register_access(def) == READ_ONLY) |
| arc_elf32_aux_warning(REGISTER_IS_READ_ONLY, hw_regno); |
| else |
| (void) operations->write_auxiliary_register (hw_regno, contents, TRUE); |
| } |
| |
| LEAVEMSG; |
| } |
| |
| |
| /* This function is passed to the arc_all_aux_registers iterator. |
| It stores one auxiliary register to the target. */ |
| |
| static void |
| debug_store_reg (ARC_AuxRegisterDefinition *def, void *data) |
| { |
| debug_store_one_register ((struct regcache*) data, |
| arc_aux_hw_register_number (def), |
| arc_aux_gdb_register_number(def)); |
| } |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* 3) functions for reading/writing mmeory */ |
| /* -------------------------------------------------------------------------- */ |
| |
| static unsigned int |
| read_bytes (ARC_Address address, |
| ARC_Byte *data, /* May be not word-aligned. */ |
| unsigned int bytes) |
| { |
| TargetOperations *operations = (TargetOperations*) current_target.to_data; |
| |
| return arc_read_memory(operations, address, data, bytes); |
| } |
| |
| |
| static unsigned int |
| write_bytes (ARC_Address address, |
| ARC_Byte *data, /* May be not word-aligned. */ |
| unsigned int bytes) |
| { |
| TargetOperations *operations = (TargetOperations*) current_target.to_data; |
| |
| return arc_write_memory(operations, address, data, bytes); |
| } |
| |
| |
| static unsigned int |
| write_pattern (ARC_Address address, |
| ARC_Word pattern, |
| unsigned int bytes) |
| { |
| TargetOperations *operations = (TargetOperations*) current_target.to_data; |
| |
| return arc_write_pattern(operations, address, 0, bytes); |
| } |
| |
| |
| static unsigned int |
| write_zeros (ARC_Address address, |
| unsigned int bytes) |
| { |
| TargetOperations *operations = (TargetOperations*) current_target.to_data; |
| |
| return arc_write_pattern(operations, address, 0, bytes); |
| } |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* 4) local functions for processor control */ |
| /* -------------------------------------------------------------------------- */ |
| |
| /* This function is called when execution on the target has halted for some |
| reason (such as a breakpoint trigger). It determines whether the halt should |
| be reported to gdb, or execution resumed. |
| |
| Parameters: |
| status : a pointer to the target status information |
| debug : the contents of the target processor DEBUG register |
| read_core_register : a function which can read a target core register |
| |
| Result: TRUE if the halt is to be reported, FALSE if execution is to resume. */ |
| |
| static Boolean |
| report_processor_halt (struct target_waitstatus *status, |
| ARC_RegisterContents debug, |
| ReadRegisterFunction read_core_register) |
| { |
| /* Test BH bit of DEBUG register. */ |
| if (debug & DEBUG_BH) |
| { |
| DEBUG("s/w breakpoint instruction was executed\n"); |
| |
| /* If the breakpoint is on an intercepted function entrypoint. */ |
| switch (arc_check_interception_breakpoint(&status->value.integer)) |
| { |
| case INTERCEPTION_RESUME: |
| /* If the user has typed a Ctrl-C since target execution was |
| last started. */ |
| if (interrupt_processor) |
| { |
| /* The interception is complete, so honour the interrupt |
| request by making it appear that the target was stopped |
| by a SIGINT signal; the PC has been set to the return |
| address of the intercepted function, so it will look to |
| the user as though the program was interrupted at that |
| point. */ |
| status->kind = TARGET_WAITKIND_STOPPED; |
| status->value.sig = TARGET_SIGNAL_INT; |
| } |
| else |
| { |
| /* This is the only case in which we return FALSE. */ |
| return FALSE; |
| } |
| break; |
| |
| case INTERCEPTION_HALT: |
| /* Some other breakpoint has triggered. */ |
| status->kind = TARGET_WAITKIND_STOPPED; |
| status->value.sig = TARGET_SIGNAL_TRAP; |
| break; |
| |
| case INTERCEPTION_EXIT: |
| /* The program called the 'exit' routine (its exit status has |
| been read by the interception mechanism and returned to us in |
| status->value.integer). */ |
| status->kind = TARGET_WAITKIND_EXITED; |
| break; |
| |
| case INTERCEPTION_INTERRUPT: |
| DEBUG("*** interception was interrupted!\n"); |
| status->kind = TARGET_WAITKIND_STOPPED; |
| status->value.sig = TARGET_SIGNAL_INT; |
| break; |
| } |
| } |
| /* Test SH bit of DEBUG register. */ |
| else if (debug & DEBUG_SH) |
| { |
| ARC_RegisterContents exit_code = 0; |
| |
| /* If the DEBUG.SH ("self halt") bit is set, we stopped because of the |
| flag instruction, which is used by programs to exit. */ |
| status->kind = TARGET_WAITKIND_EXITED; |
| |
| /* Get the exit code of the program (held in R0). */ |
| if (read_core_register(0, &exit_code, TRUE)) |
| { |
| DEBUG("exit code = %d\n", exit_code); |
| } |
| else |
| warning(_("assuming exit code = 0")); |
| |
| status->value.integer = (int) exit_code; |
| } |
| else |
| { |
| /* We stopped for some other reason: if the user had tried to interrupt |
| with a Ctrl-C, return the event as a SIGINT, otherwise as a SIGTRAP |
| (and let gdb work out what happened). */ |
| status->kind = TARGET_WAITKIND_STOPPED; |
| status->value.sig = (interrupt_processor) ? TARGET_SIGNAL_INT |
| : TARGET_SIGNAL_TRAP; |
| } |
| |
| return TRUE; |
| } |
| |
| |
| /* Determine whether the given register is a member of the given group. |
| |
| Returns 0, 1, or -1: |
| 0 means the register is not in the group. |
| 1 means the register is in the group. |
| -1 means the tdep has nothing to say about this register and group. */ |
| |
| static int |
| register_reggroup_p (int regnum, struct reggroup *group) |
| { |
| gdb_assert(regnum >= 0); |
| |
| /* Save/restore: |
| 1. all standard core regs, except PCL (PCL is not writable) |
| 2. those extension core regs which are read/write |
| 3. aux regs LP_START .. LP_END (IDENTITY is not writable) |
| 4. aux regs PC_REGNUM .. STATUS32_L2 |
| 5. aux regs ERET .. EFA */ |
| |
| if (arc_is_core_register(regnum)) |
| { |
| ARC_RegisterNumber hw_num = arc_core_register_number(regnum); |
| |
| /* R61 and R62 are reserved; they are not in any reggroup. */ |
| if (hw_num == 61 || hw_num == 62) |
| return 0; |
| |
| if ((group == save_reggroup || group == restore_reggroup)) |
| { |
| if (IS_EXTENSION_CORE_REGISTER(hw_num)) |
| return (arc_core_register_access(hw_num) == READ_WRITE) ? 1 : 0; |
| |
| return (hw_num == ARC_PCL_REGNUM) ? 0 : 1; |
| } |
| |
| if (group == general_reggroup) |
| return 1; |
| } |
| else |
| { |
| #define REGISTER_NAME_IS(ident) (strcasecmp(name, ident) == 0) |
| |
| ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_gdb_number(regnum); |
| |
| if (def) |
| { |
| const char *name = arc_aux_register_name(def); |
| |
| if (arc_aux_is_unused(def)) |
| return 0; |
| |
| if ((group == save_reggroup || group == restore_reggroup)) |
| { |
| if (arc_aux_register_access(def) != READ_WRITE) |
| return 0; |
| } |
| |
| /* Which regs to save/restore? */ |
| if ((group == save_reggroup || group == restore_reggroup)) |
| { |
| return (REGISTER_NAME_IS("LP_START") || |
| REGISTER_NAME_IS("LP_END") || |
| REGISTER_NAME_IS("PC") || |
| REGISTER_NAME_IS("STATUS32") || |
| REGISTER_NAME_IS("STATUS32_L1") || |
| REGISTER_NAME_IS("STATUS32_L2") || |
| REGISTER_NAME_IS("ERET") || |
| REGISTER_NAME_IS("ERBTA") || |
| REGISTER_NAME_IS("ERSTATUS") || |
| REGISTER_NAME_IS("ECR") || |
| REGISTER_NAME_IS("EFA")) ? 1 : 0; |
| } |
| |
| if (group == general_reggroup) |
| return (REGISTER_NAME_IS("STATUS32")) ? 0 : 1; |
| |
| if (group == system_reggroup) |
| { |
| return (REGISTER_NAME_IS("SEMAPHORE") || |
| REGISTER_NAME_IS("STATUS32_L1") || |
| REGISTER_NAME_IS("STATUS32_L2") || |
| REGISTER_NAME_IS("AUX_IRQ_LV12") || |
| REGISTER_NAME_IS("AUX_IRQ_LEV") || |
| REGISTER_NAME_IS("AUX_IRQ_HINT") || |
| REGISTER_NAME_IS("ERET") || |
| REGISTER_NAME_IS("ERBTA") || |
| REGISTER_NAME_IS("ERSTATUS") || |
| REGISTER_NAME_IS("ECR") || |
| REGISTER_NAME_IS("EFA") || |
| REGISTER_NAME_IS("ICAUSE1") || |
| REGISTER_NAME_IS("ICAUSE2") || |
| REGISTER_NAME_IS("AUX_IENABLE") || |
| REGISTER_NAME_IS("AUX_ITRIGGER") || |
| REGISTER_NAME_IS("BTA_L1") || |
| REGISTER_NAME_IS("BTA_L2") || |
| REGISTER_NAME_IS("AUX_IRQ_PULSE_CANCEL") || |
| REGISTER_NAME_IS("AUX_IRQ_PENDING")) ? 1 : 0; |
| } |
| } |
| } |
| |
| /* Let the caller sort it out! */ |
| return -1; |
| } |
| |
| |
| /* This function is passed to the arc_all_aux_registers iterator. |
| It prints the value of one auxiliary register. */ |
| |
| static void |
| print_one_aux_register (ARC_AuxRegisterDefinition *def, void *data) |
| { |
| if (!arc_aux_is_unused(def)) |
| { |
| PrintData *p = (PrintData*) data; |
| int regnum = arc_aux_gdb_register_number(def); |
| |
| default_print_registers_info (p->gdbarch, p->file, p->frame, regnum, TRUE); |
| } |
| } |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* 5) local functions called from gdb */ |
| /* -------------------------------------------------------------------------- */ |
| |
| /* Mapping from binutils/gcc register number to gdb register number ("regnum"). |
| |
| N.B. registers such as ARC_FP_REGNUM, ARC_SP_REGNUM, etc., actually have |
| different gdb register numbers in the arc-elf32 and arc-linux-uclibc |
| configurations of the ARC gdb. */ |
| |
| static int |
| arc_elf32_binutils_reg_to_regnum (struct gdbarch *gdbarch, int reg) |
| { |
| return arc_core_register_gdb_number((ARC_RegisterNumber) reg); |
| } |
| |
| |
| /* Print the contents of one, some or all registers. */ |
| |
| static void |
| arc_elf32_print_registers_info (struct gdbarch *gdbarch, |
| struct ui_file *file, |
| struct frame_info *frame, |
| int regnum, |
| int all) |
| { |
| if (regnum >= 0) |
| PRINT(regnum); |
| else |
| /* If regnum < 0, print registers. */ |
| { |
| /* R32 .. R59 are the extension core registers, R61 and R62 are reserved. */ |
| |
| /* R0 .. R26 */ |
| for (regnum = 0; regnum <= 26; regnum++) |
| PRINT_HW ((ARC_RegisterNumber) regnum); |
| |
| PRINT_HW (ARC_FP_REGNUM ); // r27 |
| PRINT_HW (ARC_SP_REGNUM ); // r28 |
| PRINT_HW (ARC_ILINK1_REGNUM ); // r29 |
| PRINT_HW (ARC_ILINK2_REGNUM ); // r30 |
| PRINT_HW (ARC_BLINK_REGNUM ); // r31 |
| PRINT_HW (ARC_LP_COUNT_REGNUM); // r60 |
| PRINT_HW (ARC_PCL_REGNUM ); // r63 |
| |
| if (all) |
| { |
| PrintData data = {gdbarch, file, frame}; |
| |
| /* Print all of the aux registers. */ |
| arc_all_aux_registers(print_one_aux_register, &data); |
| } |
| else |
| { |
| /* Print just a selection of the aux registers. */ |
| PRINT_BY_NAME ("LP_START" ); |
| PRINT_BY_NAME ("LP_END" ); |
| PRINT_BY_NAME ("STATUS32" ); |
| PRINT_BY_NAME ("BTA" ); |
| PRINT_BY_NAME ("EFA" ); |
| PRINT_BY_NAME ("ERET" ); |
| PRINT_BY_NAME ("STATUS32_L1"); |
| PRINT_BY_NAME ("STATUS32_L2"); |
| PRINT_BY_NAME ("ERSTATUS" ); |
| PRINT_BY_NAME ("PC" ); |
| } |
| } |
| } |
| |
| |
| /* Return the name of the given register. */ |
| |
| static const char* |
| arc_elf32_register_name (struct gdbarch *gdbarch, int gdb_regno) |
| { |
| if (gdb_regno >= 0) |
| { |
| if (arc_is_core_register(gdb_regno)) |
| { |
| ARC_RegisterNumber hw_num = arc_core_register_number(gdb_regno); |
| |
| if (hw_num < ELEMENTS_IN_ARRAY(register_names)) |
| return register_names[hw_num]; |
| } |
| else |
| { |
| ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_gdb_number(gdb_regno); |
| |
| /* If it is an auxiliary register. */ |
| if (def) |
| return arc_aux_register_name(def); |
| } |
| } |
| |
| internal_error(__FILE__, __LINE__, _("invalid register number: %d"), gdb_regno); |
| } |
| |
| |
| /* Determine whether the given register is read-only. */ |
| |
| static int |
| arc_elf32_cannot_store_register (struct gdbarch *gdbarch, int gdb_regno) |
| { |
| ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_gdb_number(gdb_regno); |
| |
| /* No warning should be printed. arc_cannot_store_register being |
| called does not imply that someone is actually writing to regnum. */ |
| |
| /* If it is an auxiliary register. */ |
| if (def) |
| return (arc_aux_register_access(def) == READ_ONLY) ? 1 : 0; |
| |
| /* It is writable. */ |
| return 0; |
| } |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* 6) local functions implementing commands */ |
| /* -------------------------------------------------------------------------- */ |
| |
| /* This function handles seeting a hardware breakpoint or watchpoint across a |
| range of memory address (rather than at a single location). |
| |
| Parameters: |
| args : the user arguments to the command |
| from_tty : non-zero if the command was issued at the command-line |
| is_watchpoint : TRUE if a watchpoint to to be set, FALSE if a breakpoint |
| command : the name of the command |
| usage : the usage message for the command |
| */ |
| |
| static void |
| memory_range_command (char *args, |
| int from_tty, |
| Boolean is_watchpoint, |
| const char *command, |
| const char *usage) |
| { |
| char *length_arg; |
| unsigned int start; |
| int length; |
| enum target_hw_bp_type type; |
| |
| if (!args) |
| { |
| printf_filtered (_("%s"), usage); |
| return; |
| } |
| |
| length_arg = strchr(args, ' '); |
| |
| if (!length_arg) |
| { |
| printf_filtered (_("%s : no second argument\n%s"), command, usage); |
| return; |
| } |
| |
| /* Split up the input string. */ |
| length_arg[0] = (char) 0; |
| length_arg++; |
| while (*length_arg == ' ') length_arg++; |
| |
| if (is_watchpoint) |
| { |
| char *access_arg = strchr(length_arg, ' '); |
| |
| if (access_arg) |
| { |
| /* Split up the input string. */ |
| access_arg[0] = (char) 0; |
| access_arg++; |
| while (*access_arg == ' ') access_arg++; |
| |
| if (strcmp(access_arg, "read") == 0) |
| type = hw_read; |
| else if (strcmp(access_arg, "write") == 0) |
| type = hw_write; |
| else if (strcmp(access_arg, "access") == 0) |
| type = hw_access; |
| else |
| { |
| printf_filtered (_("%s: invalid type '%s'\n%s"), command, access_arg, usage); |
| return; |
| } |
| } |
| else |
| /* Access is write by default. */ |
| type = hw_write; |
| } |
| else |
| type = hw_execute; |
| |
| /* The address expression. */ |
| EXTRACT(args, unsigned int, start) |
| |
| /* The length expression. */ |
| EXTRACT(length_arg, int, length) |
| |
| if (length <= 0) |
| { |
| warning(_("%s: %s <= 0"), command, length_arg); |
| return; |
| } |
| |
| DEBUG("try to set %u breakpoint at 0x%08X length %d bytes\n", |
| type, start, length); |
| |
| watch_range_command(start, (unsigned int) length, type, from_tty); |
| |
| /* Although the call to insert_breakpoints would result in an error message |
| if the range breakpoint could not be set, the breakpoint would still be |
| entered into gdb's breakpoint table, and displayed by the 'info break' |
| command - that would be even more confusing to the user! */ |
| #if 0 |
| /* gdb manages breakpoints by deleting them from the target as soon as it |
| has halted, then re-inserting them again immediately before execution is |
| resumed (no, I don't know why either, unless it is to make generating a |
| disassembly display easier by removing all the s/w b/ps from the code) - |
| so in order to display what actionpoints are currently in use, we must |
| temporarily re-insert the breakpoints! */ |
| insert_breakpoints(); |
| arc_display_actionpoints(); |
| (void) remove_breakpoints(); |
| #endif |
| } |
| |
| |
| /* arc-break-range <start> <length> |
| Set hardware breakpoint at address START covering LENGTH bytes. */ |
| |
| static void |
| arc_elf32_break_memory_command (char *arg, int from_tty) |
| { |
| memory_range_command(arg, from_tty, FALSE, BREAK_MEMORY_COMMAND, BREAK_MEMORY_COMMAND_USAGE); |
| } |
| |
| |
| /* arc-watch-range <start> <length> [read|write|access] |
| Set hardware watchpoint at address START covering LENGTH bytes. */ |
| |
| static void |
| arc_elf32_watch_memory_command (char *arg, int from_tty) |
| { |
| memory_range_command(arg, from_tty, TRUE, WATCH_MEMORY_COMMAND, WATCH_MEMORY_COMMAND_USAGE); |
| } |
| |
| |
| /* arc-fill-memory <start> <length> [<pattern>] |
| Write repeated copies of PATTERN at address START covering LENGTH bytes. */ |
| |
| static void |
| arc_elf32_fill_memory_command (char *arg, int from_tty) |
| { |
| TargetOperations *operations = (TargetOperations*) current_target.to_data; |
| char *length_arg; |
| char *pattern_arg; |
| ARC_Address start; |
| ARC_Word pattern; |
| int length; |
| |
| gdb_assert(operations != NULL); |
| |
| if (!arg) |
| { |
| printf_filtered ("%s", _(FILL_MEMORY_COMMAND_USAGE)); |
| return; |
| } |
| |
| length_arg = strchr(arg, ' '); |
| |
| if (!length_arg) |
| { |
| printf_filtered (_(FILL_MEMORY_COMMAND " : no second argument\n" FILL_MEMORY_COMMAND_USAGE)); |
| return; |
| } |
| |
| /* Split up the input string. */ |
| length_arg[0] = (char) 0; |
| length_arg++; |
| while (*length_arg == ' ') length_arg++; |
| |
| pattern_arg = strchr(length_arg, ' '); |
| if (pattern_arg) |
| { |
| /* Split up the input string. */ |
| pattern_arg[0] = (char) 0; |
| pattern_arg++; |
| } |
| |
| /* The address expression. */ |
| EXTRACT(arg, ARC_Address, start) |
| |
| /* The length expression. */ |
| EXTRACT(length_arg, int, length) |
| |
| if (length <= 0) |
| { |
| warning(_(FILL_MEMORY_COMMAND ": %s <= 0"), length_arg); |
| return; |
| } |
| |
| if (pattern_arg) |
| { |
| /* The pattern expression. */ |
| EXTRACT(pattern_arg, ARC_Word, pattern) |
| } |
| else |
| pattern = 0; |
| |
| if (operations) |
| { |
| unsigned int written = write_pattern(start, pattern, (unsigned int) length); |
| |
| if (written != (unsigned int) length) |
| warning (_(FILL_MEMORY_COMMAND ": only %u bytes written to target memory"), written); |
| } |
| else |
| error(_(FILL_MEMORY_COMMAND " is not available for this target")); |
| } |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* externally visible functions */ |
| /* -------------------------------------------------------------------------- */ |
| |
| /* Perform arc-elf32-gdb specific initialization. */ |
| |
| struct gdbarch* |
| arc_elf32_initialize (struct gdbarch *gdbarch, |
| struct gdbarch_list *arches) |
| { |
| struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| |
| /* Set up a guard on the PC: if any attempt is made by gdb to read or write |
| the PC before an XML definition of the PC aux register has been read this |
| will cause an error message to be output. */ |
| arc_aux_pc_guard(gdbarch, TRUE); |
| |
| /* N.B. this function may be called twice: once when gdb is started, then again |
| when the 'target' command is issued; do not try to read the aux |
| registers definitions the first time (when 'arches' is NULL) as that |
| results in an error message (if the XML file is not found) which is |
| output too early in the start-up process (before gdb has identified |
| itself). */ |
| if (arches == NULL) |
| create_variant_info(tdep); |
| else |
| { |
| /* This is the arch that was created earlier. */ |
| struct gdbarch *gdbarch0 = arches[0].gdbarch; |
| struct gdbarch_tdep *tdep0 = gdbarch_tdep(gdbarch0); |
| |
| /* Have auxiliary registers been defined for that arch? */ |
| if (arc_aux_regs_defined(gdbarch0)) |
| { |
| /* Share the variant info. */ |
| tdep->processor_variant_info = tdep0->processor_variant_info; |
| } |
| else |
| create_variant_info(tdep); |
| } |
| |
| /* Fill in target-dependent info in ARC-private structure. */ |
| |
| tdep->is_sigtramp = NULL; |
| tdep->sigcontext_addr = NULL; |
| tdep->sc_reg_offset = NULL; |
| tdep->sc_num_regs = 0; |
| tdep->pc_regnum_in_sigcontext = 0; |
| |
| tdep->le_breakpoint_instruction = le_breakpoint_instruction; |
| tdep->be_breakpoint_instruction = be_breakpoint_instruction; |
| tdep->breakpoint_size = (unsigned int) sizeof(le_breakpoint_instruction); |
| |
| tdep->register_reggroup_p = register_reggroup_p; |
| tdep->lowest_pc = 0; |
| |
| /* Pass target-dependent info to gdb. */ |
| |
| DEBUG("setting PC %d\n", arc_aux_pc_number(gdbarch)); |
| DEBUG("setting #regs %d\n", ARC_NR_REGS); |
| DEBUG("setting #pseudo %d\n", ARC_NR_PSEUDO_REGS); |
| |
| /* ARC_NR_REGS and ARC_NR_PSEUDO_REGS are defined in the tm-embed.h configuration file. */ |
| set_gdbarch_pc_regnum (gdbarch, arc_aux_pc_number(gdbarch)); |
| set_gdbarch_num_regs (gdbarch, ARC_NR_REGS); |
| set_gdbarch_num_pseudo_regs (gdbarch, ARC_NR_PSEUDO_REGS); |
| set_gdbarch_print_registers_info (gdbarch, arc_elf32_print_registers_info); |
| set_gdbarch_register_name (gdbarch, arc_elf32_register_name); |
| set_gdbarch_cannot_store_register (gdbarch, arc_elf32_cannot_store_register); |
| set_gdbarch_dwarf2_reg_to_regnum (gdbarch, arc_elf32_binutils_reg_to_regnum); |
| |
| /* See ARC Bug #96650: disable the use of the 'P' and 'p' RSP packets, so |
| forcing gdb to use the 'G' and 'g' packets instead, in case the arc-elf32 |
| debug target is the xISS being used as a remote target. */ |
| { |
| static Boolean packets_disabled = FALSE; |
| |
| /* N.B. this is something of a kluge: if execution of the target program |
| is restarted (e.g. a 'start' command is followed by a 'run' |
| command) this function is called again - but if execute_command |
| is called again, an assertion fails way down in the regcache code |
| because the target appears to have a stack, but current_gdbarch |
| is set to NULL! So do this only once. */ |
| if (!packets_disabled) |
| { |
| execute_command("set remote set-register-packet off", 0); |
| execute_command("set remote fetch-register-packet off", 0); |
| |
| packets_disabled = TRUE; |
| } |
| } |
| |
| return gdbarch; |
| } |
| |
| |
| /* This is the module initialization function that is called from gdb. */ |
| |
| void |
| _initialize_arc_elf32_tdep (void) |
| { |
| (void) add_cmd (BREAK_MEMORY_COMMAND, |
| class_breakpoint, |
| arc_elf32_break_memory_command, |
| _("Set a breakpoint on a memory address range.\n" |
| BREAK_MEMORY_COMMAND_USAGE |
| "<START> and <LENGTH> can be any expressions that evaluate to integers.\n"), |
| &cmdlist); |
| |
| (void) add_cmd (WATCH_MEMORY_COMMAND, |
| class_breakpoint, |
| arc_elf32_watch_memory_command, |
| _("Set a watchpoint on a memory address range.\n" |
| WATCH_MEMORY_COMMAND_USAGE |
| "<START> and <LENGTH> can be any expressions that evaluate to integers.\n" |
| "If the watchpoint mode is omitted, it defaults to 'access'.\n"), |
| &cmdlist); |
| |
| (void) add_cmd (FILL_MEMORY_COMMAND, |
| class_obscure, |
| arc_elf32_fill_memory_command, |
| _("Fill a memory address range with a repeated pattern.\n" |
| FILL_MEMORY_COMMAND_USAGE |
| "<START>, <LENGTH> and <PATTERN> can be any expressions that evaluate to integers.\n" |
| "If <PATTERN> is omitted, it defaults to 0.\n"), |
| &cmdlist); |
| |
| (void) observer_attach_new_objfile (new_object_file); |
| (void) observer_attach_target_pre_connect (pre_target_connection); |
| (void) observer_attach_target_post_connect (post_target_connection); |
| (void) observer_attach_target_post_disconnect(post_target_disconnection); |
| (void) observer_attach_target_updated (target_updated); |
| } |
| |
| |
| /* Find the ARC hardware numbers of the DEBUG, POC and STATUS32 aux registers: this |
| is the minimal set of registers required for controlling program execution on a |
| h/w target. */ |
| |
| void arc_elf32_find_register_numbers (void) |
| { |
| arc_debug_regnum = arc_aux_find_register_number("DEBUG", ARC_HW_DEBUG_REGNUM); |
| arc_pc_regnum = arc_aux_find_register_number("PC", ARC_HW_PC_REGNUM); |
| arc_status32_regnum = arc_aux_find_register_number("STATUS32", ARC_HW_STATUS32_REGNUM); |
| } |
| |
| |
| /* Check that an XML definition of the PC aux register has been read: 'error' |
| is called if that is not the case. |
| |
| This function is simply a wrapper for a call to arc_aux_check_pc_defined; |
| there is a function of the same name in the arc-linux-tdep module (which does |
| nothing) so that it may be called from the arc-tdep module in either of the |
| builds of the ARC debugger (as the arc-registers module is present only in |
| the arc-elf32-gdb build). */ |
| |
| void |
| arc_check_pc_defined (struct gdbarch *gdbarch) |
| { |
| arc_aux_check_pc_defined(gdbarch); |
| } |
| |
| |
| /* Load the program to be debugged to the tarrget. |
| |
| Parameters: |
| filename : the executable file |
| from_tty : non-zero if the command was issued at the command-line |
| */ |
| |
| void |
| arc_elf32_load_program (char *filename, int from_tty) |
| { |
| TargetOperations *operations = (TargetOperations*) current_target.to_data; |
| asection *bss_section; |
| |
| ENTERARGS("%s", filename); |
| |
| if (exec_bfd == NULL) |
| error(_("Must use 'file' command before 'load' command")); |
| |
| arc_aux_check_pc_defined(NULL); |
| |
| /* Now that we know the program file as well as the target (since we can be |
| loading only if the target is connected), check that the program has been |
| built for the processor version that is in the target. */ |
| ARCHITECTURE_CHECK(current_gdbarch, exec_bfd); |
| |
| if (filename == NULL || *filename == '\0') |
| filename = bfd_get_filename(exec_bfd); |
| |
| /* Check that the file has been compiled for the endianness of the target. */ |
| { |
| ARC_RegisterNumber memsubsys = arc_aux_find_register_number("MEMSUBSYS", ARC_HW_MEMSUBSYS_REGNUM); |
| ARC_RegisterContents contents; |
| |
| if (read_aux_register(¤t_target)(memsubsys, &contents, TRUE)) |
| { |
| Boolean big_endian_target = ((contents & 4) != 0); |
| |
| DEBUG("MEMSUBSYS BCR: 0x%08X\n", contents); |
| |
| if (big_endian_target && bfd_little_endian(exec_bfd)) |
| warning(_("target is big-endian but file %s is little-endian"), filename); |
| |
| if (!big_endian_target && bfd_big_endian(exec_bfd)) |
| warning(_("target is little-endian but file %s is big-endian"), filename); |
| } |
| } |
| |
| /* In case anything was previously loaded. */ |
| arc_set_IO_interception(operations, INTERCEPTION_RESET); |
| |
| /* Let gdb do all the real work of loading. */ |
| generic_load(filename, from_tty); |
| |
| /* Zero the BSS section in memory, if it exists. */ |
| bss_section = bfd_get_section_by_name (exec_bfd, ".bss"); |
| |
| if (bss_section) |
| { |
| CORE_ADDR bss_addr = bfd_section_lma (exec_bfd, bss_section); |
| bfd_size_type bss_size = bfd_get_section_size (bss_section); |
| unsigned int bytes; |
| |
| printf_filtered(_("Zeroing section .bss, size 0x%0x lma 0x%0x\n"), |
| (unsigned int) bss_size, (unsigned int) bss_addr); |
| |
| bytes = write_zeros((ARC_Address) bss_addr, (unsigned int) bss_size); |
| |
| if (bytes != (unsigned int) bss_size) |
| warning(_("load: error zeroing BSS section - only %u bytes zeroed"), bytes); |
| } |
| else |
| { |
| DEBUG("%s: no BSS section\n", __FUNCTION__); |
| } |
| |
| |
| /* We now have a program ready for execution on the target; inform the |
| program arguments module that we have a newly-loaded program (so any |
| information that it had about any program loaded before is now invalid). */ |
| arc_program_is_loaded = TRUE; |
| arc_program_loaded(); |
| |
| /* But the program has not yet been executed. */ |
| current_target.to_has_execution = 0; |
| } |
| |
| |
| /* Create the inferior, ready for execution on the target to start. |
| |
| Parameters: |
| exec_file : the executable file from which the loaded program was read |
| args : the arguments to be passed to the program in argc/argv |
| env : the environment (name/value pairs) for program execution |
| target_ops : the target operations structure for the target |
| */ |
| |
| void |
| arc_elf32_create_inferior (char *exec_file, |
| char *args, |
| char **env, |
| struct target_ops *target_ops) |
| { |
| TargetOperations *operations = (TargetOperations*) target_ops->to_data; |
| Boolean set_no_args = TRUE; |
| char *all_args = NULL; |
| CORE_ADDR start_address; |
| |
| ENTERARGS("exec_file = \"%s\", args = \"%s\"", exec_file, args); |
| |
| /* If no exec file handed to us, get it from the exec-file command - |
| with a good, common error message if none is specified. */ |
| if (exec_file == NULL) |
| exec_file = get_exec_file (1); |
| |
| /* Include the exec file name as arg[0]. */ |
| if (exec_file != NULL || args != NULL) |
| { |
| size_t length = 10; /* Safety margin. */ |
| |
| if (exec_file != NULL) |
| length += strlen(exec_file) + 1; |
| |
| if (args != NULL) |
| length += strlen(args) + 1; |
| |
| all_args = xmalloc(length); |
| |
| all_args[0] = '\0'; |
| |
| if (exec_file != NULL) |
| (void) strcat(all_args, exec_file); |
| |
| if (args != NULL) |
| { |
| (void) strcat(all_args, " "); |
| (void) strcat(all_args, args); |
| } |
| } |
| |
| /* Check that we do know which register is the PC. */ |
| arc_aux_check_pc_defined(NULL); |
| |
| if (!arc_program_is_loaded) |
| error(_("No program loaded")); |
| |
| /* We don't really have a PID or anything, but GDB uses this value to check |
| if the program is running. */ |
| inferior_ptid.pid = 42; |
| |
| /* Must set the PC to the program start address. */ |
| start_address = bfd_get_start_address (exec_bfd); |
| |
| DEBUG("setting PC to 0x%x\n", (unsigned int) start_address); |
| |
| write_pc (start_address); |
| |
| /* This sets the target's to_has_execution flag to 1. */ |
| target_mark_running(target_ops); |
| |
| /* Do we have arguments to pass to the program? */ |
| if (all_args != NULL) |
| { |
| if (arc_setup_arguments(all_args)) |
| set_no_args = FALSE; |
| else |
| warning(_("can not set up arguments to program")); |
| |
| xfree(all_args); |
| } |
| |
| /* If there are no arguments to be passed to the program, or we failed to |
| set them up, at least try to set R0 and R1 to indicate that are no |
| arguments! */ |
| if (set_no_args) |
| { |
| /* N.B. we do not use the target_store_registers operation here, as that |
| does not give us an indication of success or failure. */ |
| if (!operations->write_core_register(0, 0, TRUE)) |
| warning(_("can not set parameter register R0 to 0 - main parameter 'argc' will be undefined")); |
| |
| if (!operations->write_core_register(1, 0, TRUE)) |
| warning(_("can not set parameter register R1 to 0 - main parameter 'argv' will be undefined")); |
| } |
| |
| /* Set I/O interception on, so that any I/O operations performed by the program |
| when it is executed will be trapped and handled by the debugger. */ |
| arc_set_IO_interception(operations, INTERCEPTION_ON); |
| } |
| |
| |
| /* There are two ways in which the target processor may be executed: |
| |
| 1) once started, the processor will run asynchronously until an explicit |
| attempt to stop it is made (e.g. real h/w); |
| |
| 2) the processor may be run synchronously until it has executed a certain |
| 'chunk' of instructions (e.g. the xISS). |
| |
| So, this function gets either 1) a 'run' operation, or 2) 'start' and 'stop' |
| operations - but not all three of them! |
| |
| If an attempt is made (by the user typing Ctrl-C) to interrupt the processor |
| whilst it is running, in case 1) we must wait until the 'run' operation has |
| completed before checking the 'interrupt_processor' flag; whereas in case 2), |
| we can attempt to force the processor to stop. */ |
| |
| void |
| arc_elf32_execute (struct target_waitstatus *status, |
| ProcessorControlFunction run_processor, |
| ProcessorControlFunction start_processor, |
| ProcessorControlFunction stop_processor) |
| { |
| TargetOperations *operations = (TargetOperations*) current_target.to_data; |
| |
| /* This flag will be set if the user types Ctrl-C. */ |
| interrupt_processor = FALSE; |
| |
| /* Set up a signal handler for Ctrl-C. */ |
| old_signal_handler = signal (SIGINT, interrupted_by_user); |
| |
| /* Wait until the processor has *really* halted. */ |
| while (TRUE) |
| { |
| /* Set to 0 in case we leave inner loop without reading DEBUG register. */ |
| ARC_RegisterContents debug = 0; |
| |
| /* Wait until the processor has *apparently* halted. */ |
| while (TRUE) |
| { |
| ARC_RegisterContents status32; |
| |
| /* Are we running the processor synchronously? */ |
| if (run_processor) |
| { |
| /* If the user has typed a Ctrl-C since the last chunk of |
| instructions were executed, exit from this inner loop. */ |
| if (interrupt_processor) |
| break; |
| |
| DEBUG("running processor...\n"); |
| |
| /* Otherwise, run the processor to execute another chunk. */ |
| run_processor(); |
| } |
| else |
| { |
| /* If the user has typed a Ctrl-C since target execution was |
| last started, try to force the processor to halt; it does not |
| matter if we do not succeed, as we will simply try again on |
| the next iteration of the loop. */ |
| if (interrupt_processor) |
| stop_processor(); |
| } |
| |
| /* Now try to read the STATUS32 register, and check whether its H |
| bit is set, indicating that the processor has really halted (as |
| opposed to having simply finished executing a chunk); again, it |
| does not matter if we do not succeed, as we will simply try again |
| on the next iteration of the loop. */ |
| if (operations->read_auxiliary_register(arc_status32_regnum, &status32, TRUE)) |
| { |
| #if 0 |
| ARC_RegisterContents PC; |
| |
| printf_filtered(_("STATUS32: %08X\n"), status32); |
| |
| if (operations->read_auxiliary_register(arc_pc_regnum, &PC, TRUE)) |
| printf_filtered(_("PC: %08X\n"), PC); |
| #endif |
| |
| if (status32 & STATUS32_HALT) |
| { |
| DEBUG("halted: STATUS32 = %08X\n", status32); |
| |
| /* Perform a polling wait for any delayed load to complete. |
| |
| N.B. this is necessary for real hardware, though the xISS |
| currently does not simulate pending loads, although |
| it will do so at some future date - however, it is |
| not an error to check the flag! */ |
| while (TRUE) |
| { |
| if (operations->read_auxiliary_register(arc_debug_regnum, &debug, TRUE)) |
| { |
| if (!(debug & DEBUG_LOAD_PENDING)) |
| break; |
| } |
| } |
| |
| break; |
| } |
| } |
| } |
| |
| /* The processor is now halted in a reliable state, but it might need to |
| be re-started... */ |
| if (report_processor_halt(status, debug, operations->read_core_register)) |
| break; |
| |
| DEBUG("*** resuming execution\n"); |
| |
| /* If we are running the processor asynchronously, we must explicitly |
| start it again - otherwise, we simply execute another chunk in the |
| next iteration of this loop. */ |
| if (start_processor) |
| start_processor(); |
| } |
| |
| /* Put the old signal handler back. */ |
| (void) signal (SIGINT, old_signal_handler); |
| |
| DEBUG("processor has halted\n"); |
| } |
| |
| |
| /* Close the connection to the target. */ |
| |
| void |
| arc_elf32_close (Boolean resume) |
| { |
| TargetOperations *operations = (TargetOperations*) current_target.to_data; |
| |
| /* Do this while the target is halted. */ |
| arc_restore_stack_top_address(); |
| |
| /* We will no longer intercept any I/O operations. */ |
| arc_set_IO_interception(operations, INTERCEPTION_OFF); |
| |
| /* Let the target continue. */ |
| if (resume) |
| target_resume (inferior_ptid, 0, 0); |
| |
| current_target.to_has_execution = 0; |
| |
| arc_architecture_is_unknown(); |
| } |
| |
| |
| /* Fetch one or all registers from the target. |
| |
| Parameters: |
| regcache : cache to write register to |
| gdb_regno: register number (-1 means 'all registers') |
| */ |
| |
| void |
| arc_elf32_fetch_registers (struct regcache *regcache, int gdb_regno) |
| { |
| ENTERARGS("%d", gdb_regno); |
| |
| /* All registers. */ |
| if (gdb_regno == -1) |
| { |
| int num_core_registers = (int) arc_core_register_count(get_regcache_arch(regcache)); |
| |
| /* Core registers. */ |
| for (gdb_regno = 0; gdb_regno < num_core_registers; gdb_regno++) |
| debug_fetch_one_register(regcache, (ARC_RegisterNumber) gdb_regno, gdb_regno); |
| |
| /* Auxiliary registers (incl. build configuration registers). */ |
| arc_all_aux_registers(debug_fetch_reg, regcache); |
| } |
| else |
| { |
| ARC_RegisterNumber hw_regno = get_hw_regnum_mapping (gdb_regno); |
| |
| if (hw_regno == INVALID_REGISTER_NUMBER) |
| error(_("Invalid register number: %d"), gdb_regno); |
| else |
| debug_fetch_one_register(regcache, hw_regno, gdb_regno); |
| } |
| |
| LEAVEMSG; |
| } |
| |
| |
| /* Store one or all registers to the target. |
| |
| Parameters : |
| regcache : cache to read register from |
| gdb_regno: register number (-1 means 'all registers') |
| */ |
| |
| void |
| arc_elf32_store_registers (struct regcache *regcache, int gdb_regno) |
| { |
| ENTERARGS("%d", gdb_regno); |
| |
| /* All registers. */ |
| if (gdb_regno == -1) |
| { |
| int num_core_registers = (int) arc_core_register_count(get_regcache_arch(regcache)); |
| |
| /* Core registers. */ |
| for (gdb_regno = 0; gdb_regno < num_core_registers; gdb_regno++) |
| debug_store_one_register(regcache, (ARC_RegisterNumber) gdb_regno, gdb_regno); |
| |
| /* Auxiliary registers (excl. build configuration registers, which are not writable). */ |
| arc_all_aux_registers(debug_store_reg, regcache); |
| } |
| else |
| { |
| ARC_RegisterNumber hw_regno = get_hw_regnum_mapping (gdb_regno); |
| |
| if (hw_regno == INVALID_REGISTER_NUMBER) |
| error(_("Invalid register number: %d"), gdb_regno); |
| else |
| debug_store_one_register(regcache, hw_regno, gdb_regno); |
| } |
| |
| LEAVEMSG; |
| } |
| |
| |
| /* Read or write data to/from target memory. |
| |
| if 'object' is TARGET_OBJECT_MEMORY then |
| if 'writebuf' is NULL |
| read 'len' bytes of data from target memory starting at address 'offset' to 'readbuf' |
| else |
| write 'len' bytes of data from 'writebuf' to target memory starting at address 'offset' |
| |
| Returns number of bytes of memory read/written. |
| |
| Returns -1 for all other operations (i.e. object != TARGET_OBJECT_MEMORY). */ |
| |
| LONGEST |
| arc_elf32_xfer_partial (struct target_ops *ops, // unused |
| enum target_object object, |
| const char *annex, // unused |
| gdb_byte *readbuf, |
| const gdb_byte *writebuf, |
| ULONGEST offset, |
| LONGEST len) |
| { |
| ENTERARGS("object %d offset 0x%x len %lld", (unsigned int) object, (unsigned int) offset, len); |
| |
| /* Handle memory access. */ |
| if (object == TARGET_OBJECT_MEMORY) |
| { |
| TargetOperations *operations = (TargetOperations*) current_target.to_data; |
| unsigned int xfered; |
| |
| /* No need to worry about the alignment of the address 'offset', or the number |
| of bytes to be transffered - the memory read/write operations handle that. */ |
| if (writebuf != NULL) |
| xfered = write_bytes((ARC_Address) offset, |
| (ARC_Byte*) writebuf, |
| (unsigned int) len); |
| else |
| xfered = read_bytes((ARC_Address) offset, |
| (ARC_Byte*) readbuf, |
| (unsigned int) len); |
| |
| DEBUG("...leaving %s(memory %s) with return value %d\n", |
| __FUNCTION__, (writebuf == NULL) ? "read" : "write", xfered); |
| |
| return (LONGEST) xfered; |
| } |
| |
| if (object == TARGET_OBJECT_AVAILABLE_FEATURES) |
| { |
| /* We should create and return an XML string here. */ |
| return -1; |
| } |
| |
| printf_filtered(_("\nRequested target_object %d not yet supported with target %s\n"), |
| (int) object, current_target.to_shortname); |
| return -1; |
| } |
| |
| |
| /* Insert a software breakpoint in the program code on the target. |
| |
| Parameters: |
| bpt: information defining the breakpoint. |
| |
| Returns: 0 for success, 1 for failure. */ |
| |
| int |
| arc_elf32_insert_breakpoint (struct bp_target_info *bpt) |
| { |
| TargetOperations *operations = (TargetOperations*) current_target.to_data; |
| const unsigned char *breakpt_instruction; |
| unsigned int bytes; |
| |
| ENTERARGS("0x%08X", (unsigned int) bpt->placed_address); |
| |
| /* Get the breakpoint instruction code, and its size in bytes. */ |
| breakpt_instruction = gdbarch_breakpoint_from_pc (current_gdbarch, |
| &bpt->placed_address, |
| &bpt->placed_size); |
| |
| /* FIXME: alignment of breakpt_instruction data! */ |
| DEBUG("breakpoint size = %d and breakpoint instruction = 0x%x\n", |
| bpt->placed_size, *(unsigned int *) breakpt_instruction); |
| |
| /* Save the existing instruction at the given address as the shadow contents. */ |
| bytes = read_bytes((ARC_Address) bpt->placed_address, |
| (ARC_Byte*) bpt->shadow_contents, |
| (unsigned int) bpt->placed_size); |
| |
| if (bytes == (unsigned int) bpt->placed_size) |
| /* Overwrite the instruction with the breakpoint instruction. */ |
| bytes = write_bytes((ARC_Address) bpt->placed_address, |
| (ARC_Byte*) breakpt_instruction, |
| (unsigned int) bpt->placed_size); |
| |
| return (bytes == (unsigned int) bpt->placed_size) ? 0 : 1; |
| } |
| |
| |
| /* Remove a software breakpoint from the program code on the target. |
| |
| Parameters: |
| bpt: information defining breakpoint. |
| |
| Returns: 0 for success, 1 for failure. */ |
| |
| int |
| arc_elf32_remove_breakpoint (struct bp_target_info *bpt) |
| { |
| unsigned int bytes; |
| |
| /* FIXME: alignment of shadow_contents data! */ |
| ENTERARGS("0x%08X, 0x%lx", (unsigned int) bpt->placed_address, |
| *(unsigned long *) bpt->shadow_contents); |
| |
| /* Write the old code back. */ |
| bytes = write_bytes((ARC_Address) bpt->placed_address, |
| (ARC_Byte*) bpt->shadow_contents, |
| (unsigned int) bpt->placed_size); |
| |
| return (bytes == (unsigned int) bpt->placed_size) ? 0 : 1; |
| } |
| |
| |
| /* Output a warning message related to a core register. */ |
| |
| void |
| arc_elf32_core_warning (RegisterError error, |
| ARC_RegisterNumber hw_regno) |
| { |
| int gdb_regno = arc_core_register_gdb_number(hw_regno); |
| |
| /* N.B. the supplied format string contains a %s specifier which |
| allows the string "extension " to be inserted into the message. */ |
| warning((error == REGISTER_IS_READ_ONLY) ? _("%score register %s is read-only") : |
| (error == ERROR_ON_READING_REGISTER) ? _("failure reading %score register %s") : |
| _("failure writing %score register %s"), |
| IS_EXTENSION_CORE_REGISTER(hw_regno) ? _("extension ") : _(""), |
| gdbarch_register_name(current_gdbarch, gdb_regno)); |
| } |
| |
| |
| /* Output a warning message related to an auxiliary register. */ |
| |
| void |
| arc_elf32_aux_warning (RegisterError error, |
| ARC_RegisterNumber hw_regno) |
| { |
| warning((error == REGISTER_IS_READ_ONLY) ? _("auxiliary register %s (0x%x) is read-only") : |
| (error == ERROR_ON_READING_REGISTER) ? _("failure reading auxiliary register %s (0x%x)") : |
| _("failure writing auxiliary register %s (0x%x)"), |
| arc_aux_register_name_of(hw_regno), hw_regno); |
| } |
| |
| /******************************************************************************/ |