summaryrefslogtreecommitdiff
path: root/gdb/arc-remote-fileio.c
blob: 5bb22eafe31e0de43db3540a60474f91aa804c44 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
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;
}

/******************************************************************************/