aboutsummaryrefslogtreecommitdiff
path: root/gdb/arc-remote-fileio.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/arc-remote-fileio.c')
-rw-r--r--gdb/arc-remote-fileio.c741
1 files changed, 741 insertions, 0 deletions
diff --git a/gdb/arc-remote-fileio.c b/gdb/arc-remote-fileio.c
new file mode 100644
index 0000000..5bb22ea
--- /dev/null
+++ b/gdb/arc-remote-fileio.c
@@ -0,0 +1,741 @@
+/* Target dependent code for ARC processor family, for GDB, the GNU debugger.
+
+ Copyright 2008, 2009 Free Software Foundation, Inc.
+
+ Contributed by ARC International (www.arc.com)
+
+ Author:
+ 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 implements facilities for intercepting I/O (and other) */
+/* operations attempted on an ARC target and performing them on the host, */
+/* using a RPC (Remote Procedure Call) mechanism. */
+/* */
+/* Mechanism: */
+/* When interception is enabled, this module sets a breakpoint at the */
+/* entry point of each operation which is to be intercepted; it finds the */
+/* entry points of named routines in the C runtime library code by using */
+/* gdb's symbol lookup facilities. */
+/* */
+/* When a breakpoint is triggered on the target, the target monitoring */
+/* loop calls the function 'arc_check_interception_breakpoint' to check */
+/* whether the breakpoint triggered is an interception breakpoint; this */
+/* function will return a code indicating either */
+/* */
+/* a) the breakpoint is an interception breakpoint, the interception */
+/* has been performed and execution of the target program should be */
+/* resumed; or */
+/* */
+/* b) the breakpoint is an interception breakpoint, but the intercepted */
+/* routine is 'exit' and execution should not be resumed; or */
+/* */
+/* c) the breakpoint is not an interception breakpoint, so execution */
+/* should not be resumed and the trigger should be reported to gdb. */
+/* */
+/* In case a), this module then reads the routine's parameters from the */
+/* target's registers, performs whatever conversions are required, and */
+/* constructs a gdb RSP File I/O extension 'F' message which it passes to */
+/* the gdb target_fileio module, which performs the requested operation */
+/* on the host machine. */
+/* */
+/* The target_fileio module is passed a set of operations which allow it */
+/* to read data from target memory, write data to target memory, and */
+/* return a result value (and possibly a error code) to the intercepted */
+/* routine. The result value is written into the target's R0 register; */
+/* the error code (if any) is written into the location of the 'errno' */
+/* variable. */
+/* */
+/* Finally, this module copies the routine return address from the BLINK */
+/* register to the PC register - this ensures that when execution of the */
+/* target is resumed, control returns to the code after the call to the */
+/* intercepted routine. */
+/* */
+/* Notes: */
+/* 1) the set of routines to be intercepted, and the parameters to these */
+/* routines, is defined by a table (see below) - so it is simple to */
+/* add more routines to the set; */
+/* */
+/* 2) the 'open' syscall (see man open(2)) has a 'flags' parameter which */
+/* is a bit mask; unfortunately, the bits differ in meaning between */
+/* the host and the elf-32 target program, so the parameter must be */
+/* converted before it can be passed to the target_fileio module; */
+/* */
+/* 3) the 'fstat' syscall (see man fstat(2)) has an out parameter which */
+/* is a 'struct stat' structure (i.e. the address of such a structure */
+/* is a parameter to the syscall): the target_fileio module writes the */
+/* data of the structure to that location in target memory; however, */
+/* the structure does not have the same layout on host and target, so */
+/* the structure must be converted before it can be written to the */
+/* target; */
+/* */
+/* 4) the interception breakpoints are not handled by the core gdb */
+/* breakpoint mechanism; hence, they are not listed by the 'info break'*/
+/* command, and can not be (accidentally) deleted by the user; though */
+/* they could be handled by gdb, that would require the introduction */
+/* of a new "invisible" breakpoint type, and hence more changes to */
+/* supposedly generic code; */
+/* */
+/* 5) it would be more elegant (from one perspective) to intecept these */
+/* operations by placing a breakpoint at the interrupt vector location */
+/* of the 'swi' (SoftWare Interrupt) handler: only one breakpoint */
+/* would then be required, and all syscalls would be intercepted; */
+/* however, this module would then have to simulate a "return from */
+/* exception" in order to resume target execution, which would be more */
+/* complex than the "restart at return address" method currently used. */
+/* */
+/* Restrictions: */
+/* 1) this module places s/w breakpoints at the entry points; therefore, */
+/* the mechanism will not work with programs that are loaded into read */
+/* -only memory; */
+/* */
+/* 2) this mechanism will probably not work if the user sets breakpoints */
+/* on the entry points of the intercepted routines - there will be a */
+/* conflict! */
+/* */
+/******************************************************************************/
+
+/* system header files */
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+/* gdb header files */
+#include "defs.h"
+#include "symtab.h"
+#include "frame.h"
+#include "block.h"
+#include "target.h"
+#include "target-fileio.h"
+#include "exceptions.h"
+#include "gdb/fileio.h"
+
+/* ARC header files */
+#include "arc-remote-fileio.h"
+#include "config/arc/tm-embed.h"
+
+
+/* -------------------------------------------------------------------------- */
+/* local types */
+/* -------------------------------------------------------------------------- */
+
+#define MAX_SYSCALL_PARAMS 4
+
+/* These are the intercepted routines. */
+typedef enum
+{
+ READ_CALL,
+ WRITE_CALL,
+ OPEN_CALL,
+ CLOSE_CALL,
+ LSEEK_CALL,
+ FSTAT_CALL,
+ GETTIMEOFDAY_CALL,
+ EXIT_CALL
+} SystemCall;
+
+
+struct lib_function
+{
+ SystemCall call;
+ const char *name;
+ const char *format;
+ Boolean bp_is_set;
+ unsigned int param_count;
+ ARC_RegisterNumber param_register[MAX_SYSCALL_PARAMS];
+ struct bp_target_info breakpoint;
+};
+
+
+/* This structure defines a memory image of the 'stat' structure as it is
+ represented on the ARC target. */
+struct arc_stat
+{
+ ARC_Byte fst_dev [4];
+ ARC_Byte fst_ino [4];
+ ARC_Byte fst_mode [2];
+ ARC_Byte fst_nlink [2];
+ ARC_Byte fst_uid [2];
+ ARC_Byte fst_gid [2];
+ ARC_Byte fst_rdev [4];
+ ARC_Byte fst_size [4];
+ ARC_Byte fst_blksize[4];
+ ARC_Byte fst_blocks [4];
+ ARC_Byte fst_atime [8];
+ ARC_Byte fst_mtime [8];
+ ARC_Byte fst_ctime [8];
+};
+
+
+/* -------------------------------------------------------------------------- */
+/* local data */
+/* -------------------------------------------------------------------------- */
+
+/* The %x specifiers in the format strings in this table correspond to the
+ parameters to the intercepted functions; the register number of the target
+ register in which the parameter is passed is given by the corresponding
+ entry in the param_register array.
+
+ N.B. the special value of SL as the number of the register in which a
+ parameter is passed indicates that the preceding parameter was the
+ address of a string, and the length of that string (including the
+ terminating NUL) is required here.
+
+ F<n> indicates that the parameter in register <n> is a word of flag
+ bits which must be handled specially!
+
+ X indicates that the array element is not used. */
+
+#define SL 1000
+#define F2 1002
+#define X 9999
+
+static struct lib_function functions[] =
+{
+ { READ_CALL, "_read_r", "Fread,%x,%x,%x", FALSE, 3, {1, 2, 3, X} },
+ { WRITE_CALL, "_write_r", "Fwrite,%x,%x,%x", FALSE, 3, {1, 2, 3, X} },
+ { OPEN_CALL, "_open_r", "Fopen,%x/%x,%x,%x", FALSE, 4, {1, SL, F2, 3} },
+ { CLOSE_CALL, "_close_r", "Fclose,%x", FALSE, 1, {1, X, X, X} },
+ { LSEEK_CALL, "_lseek_r", "Flseek,%x,%x,%x", FALSE, 3, {1, 2, 3, X} },
+ { FSTAT_CALL, "_fstat_r", "Ffstat,%x,%x", FALSE, 2, {1, 2, X, X} },
+ { GETTIMEOFDAY_CALL, "_gettimeofday_r", "Fgettimeofday,%x,%x", FALSE, 2, {1, 2, X, X} },
+ { EXIT_CALL, "_exit_r", NULL, FALSE, 1, {1, X, X, X} }
+};
+
+
+/* A pointer to the set of target operations for the current target. */
+static TargetOperations *target_operations;
+
+/* TRUE if the operation currently being intercepted has NOT been interrupted
+ by the user typing a Ctrl-C. */
+static Boolean not_interrupted;
+
+/* For the Ctrl-C signal handler. */
+static void (*old_signal_handler) (int);
+
+
+/* -------------------------------------------------------------------------- */
+/* local functions */
+/* -------------------------------------------------------------------------- */
+
+/* Read a number of bytes of data from the given address in target memory. */
+
+static int
+read_bytes (CORE_ADDR memaddr, gdb_byte *myaddr, int len)
+{
+ DEBUG("reading %d bytes from %x\n", len, (unsigned int) memaddr);
+
+ return (int) target_read (&current_target, TARGET_OBJECT_MEMORY, NULL, myaddr, memaddr, len);
+}
+
+
+/* Read a number of bytes of data from the given address in target memory. */
+
+static int
+write_bytes (CORE_ADDR memaddr, gdb_byte *myaddr, int len)
+{
+ DEBUG("writing %d bytes to %x\n", len, (unsigned int) memaddr);
+
+ return (int) target_write (&current_target, TARGET_OBJECT_MEMORY, NULL, myaddr, memaddr, len);
+}
+
+
+/* Perform the reply to the intercepted operation: set up the result of the call
+ and the error code (if any) so that the intercepted operation receives them
+ just as though the operation had really been performed upon the target. */
+
+static void
+reply (int retcode, int error)
+{
+ /* Ignore any Ctrl-Cs while performing the reply. */
+ (void) signal (SIGINT, SIG_IGN);
+
+ DEBUG("reply: retcode = %d, error = %d\n", retcode, error);
+
+ /* If an error has occurred. */
+ if (retcode == -1)
+ {
+ ARC_RegisterContents errno_address;
+
+ if (error == FILEIO_EINTR)
+ {
+ DEBUG("*** interrupted by user!\n");
+ /* Set the global flag so that it can be tested later. */
+ not_interrupted = FALSE;
+ return;
+ }
+
+ /* Read the address of the 'errno' variable from R0. */
+ if (target_operations->read_core_register(0, &errno_address, TRUE))
+ /* Write the error number into the 'errno' variable. */
+ (void) write_bytes((CORE_ADDR) errno_address, (gdb_byte*) &error, BYTES_IN_WORD);
+ }
+
+ /* Write the return code into the function result register R0. */
+ (void) target_operations->write_core_register(0, (ARC_RegisterContents) retcode, TRUE);
+}
+
+
+/* Copy a number of bytes of data from one buffer to another. Note that the
+ buffers are not necessarily of the same size. Perform endianess byte-swapping
+ if necessary. */
+
+static void
+copy_bytes (char *from, size_t from_size,
+ ARC_Byte *to, size_t to_size,
+ Boolean target_is_big_endian)
+{
+ /* We can not copy more data than we have been given in the source buffer,
+ or for which there is room in the destination buffer. */
+ unsigned int bytes = (unsigned int) ((from_size > to_size) ? to_size : from_size);
+ unsigned int i;
+
+ /* N.B. 1) the fio_stat structure created by target-fileio.c has the values
+ in big-endian byte order; so if the ARC target is little-endian
+ we must reverse the order;
+
+ 2) the fields in the fio_stat structure may be smaller (or larger!)
+ than the corresponding fields in the ARC target structure - so we
+ copy the *least* significant bytes of the fields, on the grounds
+ that the most significant bytes are probably just sign-extensions! */
+ for (i = 0; i < bytes; i++)
+ to[i] = (ARC_Byte) ((target_is_big_endian) ? from[i]
+ : from[from_size - 1 - i]);
+}
+
+
+/* Write a 'stat' structure to the target at a given address in memory.
+ 'myaddr' points to a fio_stat structure created by the target-fileio module;
+ this structure is meant for use in the RSP protocol, and is designed for
+ independence of host/target systems - therefore, we must create an equivalent
+ structure which is ARC-specific, and write that structure to the target.
+
+ Return the number of bytes of data written. */
+
+static int
+write_fstat (CORE_ADDR memaddr, gdb_byte *myaddr, int len)
+{
+ Boolean target_is_big_endian = (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG);
+ struct fio_stat *fst = (struct fio_stat*) myaddr;
+ struct arc_stat ast;
+
+ memset(&ast, 0, sizeof(ast));
+
+#define COPY(from, to) copy_bytes(from, sizeof(from), to, sizeof(to), target_is_big_endian)
+
+ COPY(fst->fst_dev, ast.fst_dev);
+ COPY(fst->fst_ino, ast.fst_ino);
+ COPY(fst->fst_mode, ast.fst_mode);
+ COPY(fst->fst_nlink, ast.fst_nlink);
+ COPY(fst->fst_uid, ast.fst_uid);
+ COPY(fst->fst_gid, ast.fst_gid);
+ COPY(fst->fst_rdev, ast.fst_rdev);
+ COPY(fst->fst_size, ast.fst_size);
+ COPY(fst->fst_blksize, ast.fst_blksize);
+ COPY(fst->fst_blocks, ast.fst_blocks);
+ COPY(fst->fst_atime, ast.fst_atime);
+ COPY(fst->fst_mtime, ast.fst_mtime);
+ COPY(fst->fst_ctime, ast.fst_ctime);
+
+ return write_bytes(memaddr, (gdb_byte*) &ast, (int) sizeof(ast));
+}
+
+
+/* Find the address of the entry point of the given routine in the target program.
+ Return 0 if the entry point can not be found, or is not a function. */
+
+static CORE_ADDR
+findEntryPoint (const char *routine)
+{
+ CORE_ADDR entry = 0;
+ int is_a_field_of_this;
+ struct symbol *sym = lookup_symbol (routine,
+ get_selected_block (0),
+ VAR_DOMAIN,
+ &is_a_field_of_this,
+ (struct symtab **) NULL);
+ if (sym)
+ {
+ if (SYMBOL_CLASS (sym) == LOC_BLOCK)
+ entry = BLOCK_START (SYMBOL_BLOCK_VALUE (sym));
+ else
+ warning(_("%s is not a function"), routine);
+ }
+ else
+ warning(_("can not find entry point of function %s"), routine);
+
+ return entry;
+}
+
+
+/* Insert a s/w breakpoint in the target program code at the entry point of the
+ given library function. */
+
+static void
+insert_breakpoint (struct lib_function *f)
+{
+ if (!f->bp_is_set)
+ {
+ if (arc_elf32_insert_breakpoint(&f->breakpoint) == 0)
+ f->bp_is_set = TRUE;
+ else
+ warning(_("can not set breakpoint at entrypoint of %s"), f->name);
+ }
+}
+
+
+/* Remove a s/w breakpoint from the target program code at the entry point of the
+ given library function. */
+
+static void
+remove_breakpoint (struct lib_function *f)
+{
+ if (f->bp_is_set)
+ {
+ if (arc_elf32_remove_breakpoint(&f->breakpoint) == 0)
+ f->bp_is_set = FALSE;
+ else
+ warning(_("can not unset breakpoint at entrypoint of %s"), f->name);
+ }
+}
+
+
+/* This function handles any Ctrl-C typed by the user whilst the interception of
+ an operation is in progress. */
+
+static void
+Ctrl_C_signal_handler (int signo)
+{
+ /* Ignore any more Ctrl-Cs. */
+ (void) signal (SIGINT, SIG_IGN);
+
+ /* We must use the gdb exception mechanism since the target_fileio_request
+ function calls catch_exceptions, and if we do something else (like a long
+ jump) here, gdb's cleanup list would be left in an inconsistent state! */
+ DEBUG("*** throwing RETURN_QUIT...\n");
+ deprecated_throw_reason (RETURN_QUIT);
+}
+
+
+/* This function is called from the gdb target-fileio module: it sets up this
+ module's handler for Ctrl-C interrupts. */
+
+static void
+set_Ctrl_C_signal_handler (void)
+{
+ old_signal_handler = signal (SIGINT, Ctrl_C_signal_handler);
+}
+
+
+/* This function finds the length of a C string stored in target memory at the
+ given address. */
+
+static unsigned int
+find_string_length (ARC_Address address)
+{
+ unsigned int length = 0;
+
+ while (TRUE)
+ {
+ gdb_byte buf[65];
+ int bytes = read_bytes((CORE_ADDR) address,
+ buf,
+ (int) sizeof(buf) - 1);
+ int i;
+
+ for (i = 0; i < bytes; i++)
+ if (buf[i] == (gdb_byte) '\0')
+ return (unsigned int) (length + i + 1);
+
+ address += bytes;
+ length += bytes;
+ }
+}
+
+
+/* Convert flags to target syscall to what they "should" be! */
+
+static ARC_RegisterContents
+convert_flags (ARC_RegisterContents flags)
+{
+ ARC_RegisterContents result = flags;
+
+/* See gcc/src/newlib/libc/sys/arc/sys/fcntl.h */
+
+/* The following values have been changed for uclibc compatibility. */
+#define _FAPPEND 0x0400 /* append (writes guaranteed at the end) */
+#define _FASYNC 0x2000 /* signal pgrp when data ready */
+#define _FCREAT 0x0040 /* open with file create */
+#define _FTRUNC 0x0200 /* open with truncation */
+#define _FEXCL 0x0080 /* error on open if file exists */
+#define _FSYNC 0x1000 /* do all writes synchronously */
+#define _FNONBLOCK 0x0800 /* non blocking I/O (POSIX style) */
+
+#define REMOVE(flag) if (flags & _F ## flag) result &= ~ _F ## flag
+#define ADD(flag) if (flags & _F ## flag) result |= FILEIO_O_ ## flag
+
+ /* N.B. all "old" bits most be removed from the result word before all
+ "new" bits are added, in case the old and new sets intersect! */
+ REMOVE(APPEND);
+// REMOVE(ASYNC); // no equivalent flag in gdb/fileio.h
+ REMOVE(CREAT);
+ REMOVE(TRUNC);
+ REMOVE(EXCL);
+// REMOVE(SYNC); // no equivalent flag in gdb/fileio.h
+// REMOVE(NONBLOCK); // no equivalent flag in gdb/fileio.h
+ ADD(APPEND);
+// ADD(ASYNC); // no equivalent flag in gdb/fileio.h
+ ADD(CREAT);
+ ADD(TRUNC);
+ ADD(EXCL);
+// ADD(SYNC); // no equivalent flag in gdb/fileio.h
+// ADD(NONBLOCK); // no equivalent flag in gdb/fileio.h
+
+ return result;
+}
+
+
+/* Perform the interception of the given library function.
+ Return TRUE if the interception is completed successfully,
+ FALSE if it is interrupted by the user. */
+
+static Boolean
+perform_interception (struct lib_function *f)
+{
+ ARC_RegisterContents params [MAX_SYSCALL_PARAMS];
+ char request[MAX_SYSCALL_PARAMS * 9 + 40];
+ unsigned int i;
+
+ /* These operations allow the target_fileio module to read data from target
+ memory, write data to target memory, and return a result value (and
+ possibly a error code) to the intercepted routine.
+
+ N.B. if the syscsall is 'fstat', we pass a special write function
+ which converts the 'struct stat' structure to target layout before
+ writing it to target memory. */
+ struct file_io_operations io_operations =
+ {
+ read_bytes,
+ (f->call == FSTAT_CALL) ? write_fstat : write_bytes,
+ reply,
+ set_Ctrl_C_signal_handler
+ };
+
+ /* Evaluate the parameters to be passed to the RPC request. */
+ for (i = 0; i < f->param_count; i++)
+ {
+ ARC_RegisterNumber reg = f->param_register[i];
+
+ if (reg == SL)
+ params[i] = find_string_length((ARC_Address) params[i - 1]);
+ else if (reg == F2)
+ {
+ ARC_RegisterContents flags;
+
+ (void) target_operations->read_core_register(2, &flags, TRUE);
+ params[i] = convert_flags(flags);
+ }
+ else
+ (void) target_operations->read_core_register(reg, &params[i], TRUE);
+ }
+
+ /* Do not close the target program's stdin, stdout or stderr streams on the
+ host: it is possible that the program may be re-loaded and re-run on the
+ target in the same debugging session (so re-initializing its I/O system)
+ so it may try to read/write those streams again - instead, just tell the
+ target that the close succeeded. */
+ if (f->call == CLOSE_CALL)
+ {
+ int fd = (int) params[0];
+
+ if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
+ {
+ reply(0, 0);
+ DEBUG("*** RPC close of stream %d ignored\n", fd);
+ return TRUE;
+ }
+ }
+
+ /* Parameters which are extra to those required by the format will simply be
+ ignored. */
+ (void) snprintf(request, sizeof(request), f->format,
+ params[0], params[1], params[2], params[3]);
+
+ DEBUG("RPC request: %s\n", request);
+
+ /* the interception might be interrupted by the user typing Ctrl-C whilst
+ the interception is in progress; if that happens, this flag will be set
+ to FALSE. */
+ not_interrupted = TRUE;
+
+ /* Make the RPC request. */
+ target_fileio_request(request, &io_operations);
+
+ (void) signal (SIGINT, old_signal_handler);
+
+ /* If the call was not interrupted, the interception has been performed. */
+ return not_interrupted;
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* externally visible functions */
+/* -------------------------------------------------------------------------- */
+
+/* Set the state of the I/O interception mechanism:
+ ON : set breakpoints on all the functions to be intercepted
+ OFF : clear breakpoints from all the intercepted functions
+ RESET: mark the breakpoints as not being set (if a new program has been
+ downloaded to the target, the s/w breakpoints in the old program
+ have been lost, and so should not be removed). */
+
+void
+arc_set_IO_interception (TargetOperations *operations,
+ InterceptionState state)
+{
+ unsigned int i;
+
+ DEBUG("*** interception: %s\n", (state == INTERCEPTION_RESET) ? "RESET" :
+ (state == INTERCEPTION_ON) ? "ON" :
+ "OFF");
+
+ target_operations = operations;
+
+ for (i = 0; i < ELEMENTS_IN_ARRAY(functions); i++)
+ {
+ struct lib_function* f = &functions[i];
+
+ switch (state)
+ {
+ case INTERCEPTION_RESET:
+ f->bp_is_set = FALSE;
+ break;
+
+ case INTERCEPTION_ON:
+ /* Set a breakpoint on the entry point of the function. */
+ f->breakpoint.placed_address = findEntryPoint(f->name);
+
+ if (f->breakpoint.placed_address != 0)
+ {
+ DEBUG("intercept 0x%08X : %s\n", (unsigned int) f->breakpoint.placed_address, f->name);
+ insert_breakpoint(f);
+ }
+ break;
+
+ case INTERCEPTION_OFF:
+ if (f->breakpoint.placed_address != 0)
+ {
+ remove_breakpoint(f);
+ f->breakpoint.placed_address = 0;
+ }
+ break;
+ }
+ }
+}
+
+
+/* This function is called when the execution of the target program has been
+ halted by a breakpoint trigger. It checks whether the breakpoint that has
+ been triggered is at the entry point of an intercepted function, and, if so,
+ performs the required interception.
+
+ Returns:
+ INTERCEPTION_RESUME : interception has been performed, execution should be resumed
+ INTERCEPTION_HALT : the program is halted (no interception has been performed)
+ INTERCEPTION_EXIT : the program has exited
+ INTERCEPTION_INTERRUPT : the interception has been interrupted by the user
+
+ If the program has exited, the 'exitcode' parameter is set to the program's exit code. */
+
+InterceptionAction
+arc_check_interception_breakpoint (int *exitcode)
+{
+ ARC_RegisterContents pc;
+
+ ENTERMSG;
+
+ *exitcode = 0;
+
+ /* Get the current execution point from the PCL, rather than the PC - this
+ gives the same result on both ARC700 and ARC600 targets. */
+ if (target_operations->read_core_register(ARC_PCL_REGNUM, &pc, TRUE))
+ {
+ unsigned int i;
+
+ DEBUG("checking for interception at 0x%08X\n", pc);
+
+ /* Look at each of the intercepted operations. */
+ for (i = 0; i < ELEMENTS_IN_ARRAY(functions); i++)
+ {
+ struct lib_function *f = &functions[i];
+
+ if (f->breakpoint.placed_address == (CORE_ADDR) pc)
+ {
+ DEBUG("intercepted function %s\n", f->name);
+
+ if (f->call == EXIT_CALL)
+ {
+ ARC_RegisterContents code;
+
+ /* The exit code is in parameter register R1. */
+ if (target_operations->read_core_register(1, &code, TRUE))
+ *exitcode = (int) code;
+
+ return INTERCEPTION_EXIT;
+ }
+ else
+ {
+ /* If the interception is performed. */
+ if (perform_interception(f))
+ {
+ ARC_RegisterContents blink;
+
+ /* Copy BLINK to PC, so that when execution is re-started,
+ control will return to the point after the call of the
+ intercepted function. */
+ if (target_operations->read_core_register (ARC_BLINK_REGNUM, &blink, TRUE) &&
+ target_operations->write_auxiliary_register(arc_pc_regnum, blink, TRUE))
+ {
+ DEBUG("copied BLINK (%x) to PC (was %x)\n", blink, pc);
+ return INTERCEPTION_RESUME;
+ }
+
+ /* If we couldn't set PC, fall through. */
+ }
+ else
+ {
+ /* The interception has been interrupted by a Ctrl-C
+ from the user - do not change the PC, as we want
+ execution to resume at the same point in the code,
+ so that the I/O request will be performed (and
+ intercepted) again: this e.g. allows the user to
+ break into a program that is in a tight loop doing
+ reads or writes. */
+ return INTERCEPTION_INTERRUPT;
+ }
+ }
+ }
+ }
+ }
+
+ return INTERCEPTION_HALT;
+}
+
+/******************************************************************************/