File: irix5-nat.c

package info (click to toggle)
gnat-gdb 5.3.gnat.0.0.20030225-8
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 75,144 kB
  • ctags: 101,348
  • sloc: ansic: 873,511; exp: 46,950; sh: 16,123; makefile: 11,757; yacc: 6,092; asm: 5,027; cpp: 4,044; perl: 2,624; lex: 877; sed: 550; lisp: 394; awk: 170; pascal: 57; java: 7; fortran: 5
file content (1152 lines) | stat: -rw-r--r-- 31,484 bytes parent folder | download
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
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
/* Native support for the SGI Iris running IRIX version 5, for GDB.
   Copyright 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998,
   1999, 2000, 2001, 2002 Free Software Foundation, Inc.
   Contributed by Alessandro Forin(af@cs.cmu.edu) at CMU
   and by Per Bothner(bothner@cs.wisc.edu) at U.Wisconsin.
   Implemented for Irix 4.x by Garrett A. Wollman.
   Modified for Irix 5.x by Ian Lance Taylor.

   This file is part of GDB.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include "defs.h"
#include "inferior.h"
#include "gdbcore.h"
#include "target.h"
#include "regcache.h"

#include "gdb_string.h"
#include <sys/time.h>
#include <sys/procfs.h>
#include <setjmp.h>		/* For JB_XXX.  */

/* Prototypes for supply_gregset etc. */
#include "gregset.h"

#ifdef HAVE_SPYTHREAD
#include "irix6-spyThread.h"
#include <poll.h>
#include <sys/errno.h>

/* Auxiliary information attached to each spyProc_t */
typedef struct {
  int nthreads;			/* Number thread_status slots used. */
  int max_nthreads;		/* Size of thread_status array */
  prstatus_t* thread_status; 
  int num_initial_uthreads;	/* Number of kernel threads when attached */
  prstatus_t* initial_uthread_status;
				/* Initial kernel thread statuses */
} thread_data_t;

/* Auxiliary information for spyProc_t* HANDLE. */
#define THREAD_INFO(handle) ((thread_data_t*) ((handle)->sp_client))

/* True if IRIX 6 pthreads appear to be present. */
static int libspy_present = 0;
/* True if the spythread library has been enabled. */
int libspy_enabled = 0;

/* Vector of spyThread operations, initialized by initialize_libspy. */
spyThreadCalls_t spy_ops;
#endif

static void fetch_core_registers (char *, unsigned int, int, CORE_ADDR);

/* Size of elements in jmpbuf */

#define JB_ELEMENT_SIZE 4

/*
 * See the comment in m68k-tdep.c regarding the utility of these functions.
 *
 * These definitions are from the MIPS SVR4 ABI, so they may work for
 * any MIPS SVR4 target.
 */

void
supply_gregset (gregset_t *gregsetp)
{
  register int regi;
  register greg_t *regp = &(*gregsetp)[0];
  int gregoff = sizeof (greg_t) - MIPS_REGSIZE;
  static char zerobuf[32] = {0};

  for (regi = 0; regi <= CTX_RA; regi++)
    supply_register (regi, (char *) (regp + regi) + gregoff);

  supply_register (PC_REGNUM, (char *) (regp + CTX_EPC) + gregoff);
  supply_register (HI_REGNUM, (char *) (regp + CTX_MDHI) + gregoff);
  supply_register (LO_REGNUM, (char *) (regp + CTX_MDLO) + gregoff);
  supply_register (CAUSE_REGNUM, (char *) (regp + CTX_CAUSE) + gregoff);

  /* Fill inaccessible registers with zero.  */
  supply_register (BADVADDR_REGNUM, zerobuf);
}

void
fill_gregset (gregset_t *gregsetp, int regno)
{
  int regi;
  register greg_t *regp = &(*gregsetp)[0];

  /* Under Irix6, if GDB is built with N32 ABI and is debugging an O32
     executable, we have to sign extend the registers to 64 bits before
     filling in the gregset structure.  */

  for (regi = 0; regi <= CTX_RA; regi++)
    if ((regno == -1) || (regno == regi))
      *(regp + regi) =
	extract_signed_integer (&registers[REGISTER_BYTE (regi)],
				REGISTER_RAW_SIZE (regi));

  if ((regno == -1) || (regno == PC_REGNUM))
    *(regp + CTX_EPC) =
      extract_signed_integer (&registers[REGISTER_BYTE (PC_REGNUM)],
			      REGISTER_RAW_SIZE (PC_REGNUM));

  if ((regno == -1) || (regno == CAUSE_REGNUM))
    *(regp + CTX_CAUSE) =
      extract_signed_integer (&registers[REGISTER_BYTE (CAUSE_REGNUM)],
			      REGISTER_RAW_SIZE (CAUSE_REGNUM));

  if ((regno == -1) || (regno == HI_REGNUM))
    *(regp + CTX_MDHI) =
      extract_signed_integer (&registers[REGISTER_BYTE (HI_REGNUM)],
			      REGISTER_RAW_SIZE (HI_REGNUM));

  if ((regno == -1) || (regno == LO_REGNUM))
    *(regp + CTX_MDLO) =
      extract_signed_integer (&registers[REGISTER_BYTE (LO_REGNUM)],
			      REGISTER_RAW_SIZE (LO_REGNUM));
}

/*
 * Now we do the same thing for floating-point registers.
 * We don't bother to condition on FP0_REGNUM since any
 * reasonable MIPS configuration has an R3010 in it.
 *
 * Again, see the comments in m68k-tdep.c.
 */

void
supply_fpregset (fpregset_t *fpregsetp)
{
  register int regi;
  static char zerobuf[32] = {0};

  /* FIXME, this is wrong for the N32 ABI which has 64 bit FP regs. */

  for (regi = 0; regi < 32; regi++)
    supply_register (FP0_REGNUM + regi,
		     (char *) &fpregsetp->fp_r.fp_regs[regi]);

  supply_register (FCRCS_REGNUM, (char *) &fpregsetp->fp_csr);

  /* FIXME: how can we supply FCRIR_REGNUM?  SGI doesn't tell us. */
  supply_register (FCRIR_REGNUM, zerobuf);
}

void
fill_fpregset (fpregset_t *fpregsetp, int regno)
{
  int regi;
  char *from, *to;

  /* FIXME, this is wrong for the N32 ABI which has 64 bit FP regs. */

  for (regi = FP0_REGNUM; regi < FP0_REGNUM + 32; regi++)
    {
      if ((regno == -1) || (regno == regi))
	{
	  from = (char *) &registers[REGISTER_BYTE (regi)];
	  to = (char *) &(fpregsetp->fp_r.fp_regs[regi - FP0_REGNUM]);
	  memcpy (to, from, REGISTER_RAW_SIZE (regi));
	}
    }

  if ((regno == -1) || (regno == FCRCS_REGNUM))
    fpregsetp->fp_csr = *(unsigned *) &registers[REGISTER_BYTE (FCRCS_REGNUM)];
}


/* Figure out where the longjmp will land.
   We expect the first arg to be a pointer to the jmp_buf structure from which
   we extract the pc (JB_PC) that we will land at.  The pc is copied into PC.
   This routine returns true on success. */

int
get_longjmp_target (CORE_ADDR *pc)
{
  char *buf;
  CORE_ADDR jb_addr;

  buf = alloca (TARGET_PTR_BIT / TARGET_CHAR_BIT);
  jb_addr = read_register (A0_REGNUM);

  if (target_read_memory (jb_addr + JB_PC * JB_ELEMENT_SIZE, buf,
			  TARGET_PTR_BIT / TARGET_CHAR_BIT))
    return 0;

  *pc = extract_address (buf, TARGET_PTR_BIT / TARGET_CHAR_BIT);

  return 1;
}

/* Provide registers to GDB from a core file.

   CORE_REG_SECT points to an array of bytes, which were obtained from
   a core file which BFD thinks might contain register contents. 
   CORE_REG_SIZE is its size.

   Normally, WHICH says which register set corelow suspects this is:
     0 --- the general-purpose register set
     2 --- the floating-point register set
   However, for Irix 5, WHICH isn't used.

   REG_ADDR is also unused.  */

static void
fetch_core_registers (char *core_reg_sect, unsigned core_reg_size,
		      int which, CORE_ADDR reg_addr)
{
  if (core_reg_size == REGISTER_BYTES)
    {
      memcpy ((char *) registers, core_reg_sect, core_reg_size);
    }
  else if (MIPS_REGSIZE == 4 &&
	   core_reg_size == (2 * MIPS_REGSIZE) * NUM_REGS)
    {
      /* This is a core file from a N32 executable, 64 bits are saved
         for all registers.  */
      char *srcp = core_reg_sect;
      char *dstp = registers;
      int regno;

      for (regno = 0; regno < NUM_REGS; regno++)
	{
	  if (regno >= FP0_REGNUM && regno < (FP0_REGNUM + 32))
	    {
	      /* FIXME, this is wrong, N32 has 64 bit FP regs, but GDB
	         currently assumes that they are 32 bit.  */
	      *dstp++ = *srcp++;
	      *dstp++ = *srcp++;
	      *dstp++ = *srcp++;
	      *dstp++ = *srcp++;
	      if (REGISTER_RAW_SIZE (regno) == 4)
		{
		  /* copying 4 bytes from eight bytes?
		     I don't see how this can be right...  */
		  srcp += 4;
		}
	      else
		{
		  /* copy all 8 bytes (sizeof(double)) */
		  *dstp++ = *srcp++;
		  *dstp++ = *srcp++;
		  *dstp++ = *srcp++;
		  *dstp++ = *srcp++;
		}
	    }
	  else
	    {
	      srcp += 4;
	      *dstp++ = *srcp++;
	      *dstp++ = *srcp++;
	      *dstp++ = *srcp++;
	      *dstp++ = *srcp++;
	    }
	}
    }
  else
    {
      warning ("wrong size gregset struct in core file");
      return;
    }

  registers_fetched ();
}

			/* IRIX 6.5 pthread support */

#ifdef HAVE_SPYTHREAD
/* The callback structure used to give the IRIX spyThread module
   access to the program's symbol table and (when applicable) the 
   core file. */
static int spy_symbol_addr (void* data, char* name, off64_t* result);
static int spy_memory_read (void*, int, off64_t, char*, size_t);
static int spy_register_read (void*, int, void*);

static spyCallBack_t spy_callbacks = { 
  spy_symbol_addr, 0, 
  spy_memory_read, 0, 
  spy_register_read, 0
};

/* Arguments passed to callback for resuming kernel threads. */
typedef struct {
  prrun_t* prrun_p;
  int num_resumed;
} resume_info_t;

#endif

/* General Notes on IRIX pthreads. 

1. On IRIX 6.5, there are both kernel threads (uthreads) and pthreads
   that run on top of them.  There are numerous complications, most 
   importantly:

   + When a pthread is not associated with a uthread, the system 
     interface only allows fetching the registers of the thread: there 
     is no other status, no pending signals, etc.  
   + The status returned for the process itself contains a mixture of
     flags that apply to the process as a whole plus flags from what
     is essentially a *random* uthread---one that does not necessarily
     correspond to the "current thread", and is not necessarily stopped
     on an event of interest!  Therefore, to find a useful status for
     the process, we must find the uthreads and synthesize a more
     useful status, containing the "interesting" flags from the
     current thread, plus the process-specific flags.
   + It is possible for there to be uthreads without associated
     pthreads, and the current thread can be a pure uthread.  We
     report such threads as distinct threads identified by their
     uthread (lwpid).  Since a pthread can move from uthread to
     uthread, this results in some uthreads appearing and disappearing
     repeatedly (as that uthread is associated with one pthread or
     another and then becomes idle, and unattached to any pthread).
     This is less than ideal, but the alternatives seem to be either
     seeing no trace of the "unassigned" uthreads, or seeing two 
     threads (one a uthread, one a pthread) that really represent the
     same thread.

2. Debugging Irix 6 pthreads requires use of a dynamic library, 
   libpthread.so, which the program will dynamically load if it uses
   pthreads.  We test for its presence by looking for the symbol 
   __pt_lib_info -- a kludgy technique, perhaps, but taken directly
   from Workshop. 

3. We usurp certain fields of the prstatus structures (apparently
   previously unused by GDB on IRIX) for our purposes.  Specifically,

      pr_nthreads:  For the main process's status: total number of threads
      pr_who (== pr_un.pr_st.who): For the main process's status:
                    current thread id.  For a kernel thread: the kernel
                    thread number.
  */


/*

   LOCAL FUNCTION

   initialize_libspy -- test for the presence of the pthread debugging library.

   SYNOPSIS

   static void initialize_libspy ()

   DESCRIPTION

   Called after each shared-library load, this routine sets the
   variable libspy_present according to whether an indicative symbol 
   is defined.

 */

void
initialize_libspy (void)
{
#if defined (HAVE_SPYTHREAD)
  libspy_present = 
    lookup_symbol ("__pt_lib_info", NULL, VAR_NAMESPACE, 0, NULL) != NULL;
  if (! libspy_present)
    libspy_enabled = 0;
  else if (!libspy_enabled) 
    {
      spyThreadInit (&spy_ops, NULL);
      libspy_enabled = 1;
    }
#endif 
}

#ifdef HAVE_SPYTHREAD
/* The next three functions are callbacks used by the spyThread
   library to access the symbol table and core image.  I believe that
   core-image access should be unnecessary. */

/*

   LOCAL FUNCTION

   spy_symbol_addr -- Get address of symbol

   SYNOPSIS

   static int spy_symbol_addr (void* dummy, char* name, off64_t* result)

   DESCRIPTION

   Store address of external symbol NAME in *RESULT. 

 */

static int
spy_symbol_addr (void* dummy, char* name, off64_t* result)
{
  struct symbol* sym = lookup_symbol (name, NULL, VAR_NAMESPACE, 0, NULL);
  if (sym == NULL) {
    return -1;
  }
  if (SYMBOL_CLASS (sym) == LOC_BLOCK)
    *result = BLOCK_START (SYMBOL_BLOCK_VALUE (sym));
  else if (SYMBOL_CLASS (sym) == LOC_STATIC)
    *result = SYMBOL_VALUE_ADDRESS (sym);
  else
    {
      warning ("spy_symbol_addr: %s has wrong class", name);
      return -1;
    }
  return 0;
}

static int
spy_memory_read (void* a0, int threadnum, off64_t vaddr, char* outbuf,
		     size_t len) 
{
  warning ("spy_memory_read called unexpectedly");
  return -1;
}

static int
spy_register_read (void* a0, int threadnum, void* outbuf)
{
  warning ("spy_register_read called unexpectedly");
  return -1;
}

/*

   GLOBAL FUNCTION

   irix_spy_open -- Initialize spyThread handle to a process

   SYNOPSIS

   int irix_spy_open (int fd, spyProc_t** handle_p)

   DESCRIPTION

   Set *HANDLE_P to point to a spy-thread library handle for the process
   at with file descriptor FD, if the pthread facilities have been 
   enabled.  Otherwise, set to NULL. Returns 1 on success, 0 if
   thread library not enabled. 

 */

int
irix_spy_open (int fd, spyProc_t** handle_p)
{
  spyProc_t* handle;
  thread_data_t* thread_info;
  prstatus_t* kernel_threads;
  int nthreads;

  if (! libspy_enabled)
    return 0;
  if (*handle_p != NULL)
    return 1;
  handle = *handle_p = (spyProc_t*) xmalloc (sizeof (spyProc_t));
  handle->sp_abi = SP_N32;  /* FIXME */
  handle->sp_procfd = fd;
  handle->sp_client = thread_info =
    (thread_data_t*) xmalloc (sizeof (thread_data_t));
  handle->sp_vec = &spy_callbacks;

  thread_info->nthreads = thread_info->max_nthreads = 0;
  thread_info->thread_status = NULL;

  spy_ops.stc_ProcNew (*handle_p);
  nthreads = thread_info->num_initial_uthreads 
    = irix_get_kernel_threads (*handle_p, &kernel_threads);
  if (nthreads > 0) 
    {
      thread_info->initial_uthread_status =
	(prstatus_t*) xmalloc (nthreads * sizeof (prstatus_t));
      memcpy (thread_info->initial_uthread_status, kernel_threads, 
	      nthreads * sizeof (prstatus_t));
    }
  else
    thread_info->initial_uthread_status = NULL;
  return 1;  
}

/*

   GLOBAL FUNCTION

   irix_spy_close -- 

   SYNOPSIS

   void irix_spy_close (spyProc_t* handle)

   DESCRIPTION

   Release resources associated with HANDLE.  Does nothing if
   HANDLE is null.

 */

void
irix_spy_close (spyProc_t* handle)
{
  if (handle != NULL) 
    {
      xfree (THREAD_INFO (handle)->thread_status);
      xfree (THREAD_INFO (handle)->initial_uthread_status);
      xfree (THREAD_INFO (handle));
      spy_ops.stc_ProcDel (handle);
    }
  xfree (handle);
}

/*

   LOCAL FUNCTION

   thread_scan -- Operate on a pthread, or on all pthreads

   SYNOPSIS

   static int thread_scan (spyProc_t* handle, int op, uint_t domain, 
                           spyThread_t thread, scan_callback_t f,
                           void* result_buffer, void* data)

   DESCRIPTION

   Interface to the stc_ScanOp operation.  Here, we describe only the 
   operations used. 

   Perform OP on selected pthreads under the process accessed by HANDLE.  
   OP may be one of the PIOC* values for ioctl (see /proc).  For a thread 
   with no kernel context, on PIOCGREG and PIOCGFPREG work.  OP may also 
   be SPYCGNAME, which is essentially a no-op useful when scanning
   multiple threads.  For the PIOC operations, RESULT_BUFFER is the
   expected buffer operand to ioctl.

   Thread selection depends on value of DOMAIN:
   
   + STC_THREAD: operate only on THREAD (a kernel thread number
     or user thread number)
   + STC_SCAN_ALL: operate on all threads (THREAD ignored)
   + STC_SCAN_KERN: operate on all kernel threads (THREAD ignored).
   
   If non-null, F is called for each selected thread as follows:
       F(HANDLE, t, RESULT_BUFFER, DATA)
   where 't' is THREAD (for STC_THREAD), or the kernel thread number,
   if present, or (for a thread without kernel context), the pthread number.
   When the OP is SPYCGNAME, RESULT_BUFFER should be a pointer to a 
   spyThread_t, which will be set to the pthread number (user thread
   id), if present, and otherwise to the kernel thread number (for
   kernel threads without user context).  An STC_SCAN operation terminates
   when the callback returns non-zero.  

   When called with STC_THREAD, returns the value of the operation
   selected.  Otherwise, returns 0 (OK) or the error code.

 */

static int
thread_scan (spyProc_t* handle, 
	     int op, uint_t domain, spyThread_t thread, scan_callback_t f, 
	     void* result_buffer, void* data)
{
  spyScanOp_t args;
  args.sso_dom = domain; 
  args.sso_thd = thread;
  args.sso_cb = f;
  args.sso_cbArg = data;
  return spy_ops.stc_ScanOp (handle, op, result_buffer, &args);
}

static int
get_thread_callback (spyProc_t* handle, spyThread_t uthread_id, 
		     void* pthread_idp, void* uthread_idp)
{
  *((spyThread_t*) uthread_idp) = uthread_id;
  return 0;
}

/*

   LOCAL FUNCTION

   get_kernel_thread -- find the kernel thread running a given pthread.

   SYNOPSIS

   static spyThread_t  get_kernel_thread (spyProc_t* handle, spyThread_t id)

   DESCRIPTION

   Return the kernel thread number running ID on the process accessed
   by HANDLE, if any.  Simply returns ID if there is none.

 */

static spyThread_t 
get_kernel_thread (spyProc_t* handle, spyThread_t id)
{
  spyThread_t uthread, pthread;
  if (handle == NULL)
    return id;
  thread_scan (handle, SPYCGNAME, STC_THREAD, id, get_thread_callback,
	       &pthread, &uthread);
  return uthread;
}

static int
collect_pthread_callback (spyProc_t* handle, spyThread_t uthread_id, 
			  void* pthread_idp0, void* unused)
{
  int i;
  thread_data_t* info = THREAD_INFO (handle);
  spyThread_t* pthread_idp = (spyThread_t*) pthread_idp0;
  if (info->nthreads >= info->max_nthreads) 
    {
      info->max_nthreads = info->max_nthreads == 0 ? 8 : 2*info->max_nthreads;
      info->thread_status = 
	(prstatus_t*) xrealloc (info->thread_status, 
				info->max_nthreads * sizeof (prstatus_t));
    }
  if (IS_LWP_ID (uthread_id))
    {
      if (thread_scan (handle, PIOCSTATUS, STC_THREAD, uthread_id, NULL, 
		       &info->thread_status[info->nthreads], NULL) < 0) 
	warning ("irix_get_threads: cannot get status for thread %x",
		 uthread_id);
    }
  else
    {
      if (thread_scan (handle, PIOCGREG, STC_THREAD, uthread_id, NULL,
		       &info->thread_status[info->nthreads].pr_reg, NULL) < 0)	
	warning ("irix_get_threads: cannot get registers for thread %x",
		 uthread_id);
      info->thread_status[info->nthreads].pr_flags = 
	PR_STOPPED | PR_ISTOP | PR_RETURN;
      info->thread_status[info->nthreads].pr_why = PR_REQUESTED;
      info->thread_status[info->nthreads].pr_what = 0;
    }
  info->thread_status[info->nthreads].pr_who = *pthread_idp;      
  info->nthreads += 1;
  return 0;
}
  
static int
collect_uthread_callback (spyProc_t* handle, spyThread_t uthread_id, 
			  void* pthread_idp0, void* unused)
{
  int code = 
    collect_pthread_callback (handle, uthread_id, pthread_idp0, NULL);
  if (code == 0)
    {
      thread_data_t* info = THREAD_INFO (handle);
      info->thread_status[info->nthreads-1].pr_who = uthread_id;
    }
  return code;
}


/*

   GLOBAL FUNCTION

   irix_get_threads -- Collect status information on all threads

   SYNOPSIS

   int irix_get_threads (spyProc_t* handle, prstatus_t** thread_list)

   DESCRIPTION

   Set *THREAD_LIST to a pointer to a vector of status blocks for all
   threads running under the process accessed by HANDLE.  Returns the 
   number of threads.  

 */

int
irix_get_threads (spyProc_t* handle, prstatus_t** thread_list)
{
  if (handle != NULL) 
    {
      spyThread_t buffer;

      THREAD_INFO (handle)->nthreads = 0;
      thread_scan (handle, SPYCGNAME, STC_SCAN_ALL, 0, 
		   collect_pthread_callback, &buffer, NULL);
      *thread_list = THREAD_INFO(handle)->thread_status;
      return THREAD_INFO(handle)->nthreads;    
    } 
  *thread_list = NULL;
  return 0;
}

/*

   GLOBAL FUNCTION

   irix_get_kernel_threads -- Collect status information on kernel threads.

   SYNOPSIS

   int irix_get_kernel_threads (spyProc_t* handle, prstatus_t** thread_list)

   DESCRIPTION

   Set *THREAD_LIST to a pointer to a vector of status blocks for all
   threads running under the process accessed by HANDLE.  Returns the 
   number of threads.  The pr_who fields of these status blocks
   contain kernel thread numbers.

 */

int
irix_get_kernel_threads (spyProc_t* handle, prstatus_t** thread_list)
{
  if (handle != NULL) {
    spyThread_t buffer;

    THREAD_INFO (handle)->nthreads = 0;
    thread_scan (handle, SPYCGNAME, STC_SCAN_KERN, 0, 
		 collect_uthread_callback, &buffer, NULL);
      
    *thread_list = THREAD_INFO(handle)->thread_status;
    return THREAD_INFO(handle)->nthreads;    
  }
  return 0;
}

static int
update_status_from_thread_callback (spyProc_t* handle, spyThread_t uthread, 
				    void* pthreadp0, void* prstatp0)
{
  prstatus_t ustatus;
  prstatus_t* prstatp = (prstatus_t*) prstatp0;
  spyThread_t* pthreadp = (spyThread_t*) pthreadp0;

  if (thread_scan (handle, PIOCSTATUS, STC_THREAD, uthread, NULL,
		   &ustatus, NULL) >= 0)
    {
      ustatus.pr_nthreads = prstatp->pr_nthreads += 1;
      ustatus.pr_who = *pthreadp;
      if (prstatp->pr_nthreads == 1
	  || (prstatp->pr_flags & ~ustatus.pr_flags & (PR_ISTOP | PR_STOPPED))
	  || ((prstatp->pr_flags & ustatus.pr_flags & PR_ISTOP)
	      && prstatp->pr_why == PR_REQUESTED
	      && ustatus.pr_why != PR_REQUESTED))
	memcpy (prstatp, &ustatus, sizeof (ustatus));
    }
  return 0;
}     

/*

   LOCAL FUNCTION

   complete_status -- fill in main process status from current thread.

   SYNOPSIS

   static void complete_status (spyProc_t* handle, prstatus_t* prstatusp)

   DESCRIPTION

   Update main process status *PRSTATUSP as needed from kernel threads
   controlled by HANDLE (does nothing if HANDLE is null).  Attempts to 
   emulate Solaris threads, in that the flags and registers of the 
   updated *PRSTATUSP come from a kernel thread that is
   not stopped, stopped at an event of interest other than
   PR_REQUESTED, or stopped at a REQUESTED stop (in that order of
   first to last preference). 

 */

static void
complete_status (spyProc_t* handle, prstatus_t* prstatusp)
{
  if (handle == NULL)
    {
      prstatusp->pr_nthreads = 0;
      prstatusp->pr_who = 0;
    } 
  else
    {
      spyThread_t thread;
      prstatus_t ustatus;
      ustatus.pr_nthreads = 0;
      ustatus.pr_who = 0;
      thread_scan (handle, SPYCGNAME, STC_SCAN_KERN, 0, 
		   update_status_from_thread_callback, &thread, &ustatus);
      if (ustatus.pr_nthreads > 0) 
	{
	  /* Copy in relevant fields from representative uthread. */
	  prstatusp->pr_flags &= 
	    ~(PR_STEP | PR_ISTOP | PR_STOPPED | PR_DSTOP |
	      PR_ASLEEP | PR_PCINVAL | PR_RETURN);
	  prstatusp->pr_flags |= ustatus.pr_flags &
	    (PR_STEP | PR_ISTOP | PR_STOPPED | PR_DSTOP |
	     PR_ASLEEP | PR_PCINVAL | PR_RETURN);
	  prstatusp->pr_why = ustatus.pr_why;
	  prstatusp->pr_what = ustatus.pr_what;
	  prstatusp->pr_who = ustatus.pr_who;
	  memcpy (&prstatusp->pr_reg, &ustatus.pr_reg, 
		  sizeof (ustatus.pr_reg));
	}
    }
}

/*

   GLOBAL FUNCTION

   irix_get_status -- Get status of process or thread.

   SYNOPSIS

   int irix_get_status (spyProc_t* handle, int fd, spyThread_t pthread,
                        prstatus_t* prstatusp)

   DESCRIPTION

   Fill in *PRSTATUS for the process with /proc file descriptor FD,
   thread number PTHREAD.  If PTHREAD is 0, gets status of the main process,
   and fills in flags, why, what, and register information from 
   a "representative" kernel thread, if there are any. 
   If PTHREAD has no kernel context, then gets general registers only,
   and "fakes" the status flags to show a PR_REQUESTED stop (no other 
   information is available for a user thread with no kernel
   context).  Simply fetches the normal status when called with
   a thread that has a kernel context.  Returns as for the PIOCSTATUS ioctl.

 */

int
irix_get_status (spyProc_t* handle, int fd, spyThread_t pthread, 
		 prstatus_t* prstatusp)
{
  int win;
  if (handle == NULL || pthread == 0)
    {
      /* Main process */
      win = ioctl (fd, PIOCSTATUS, prstatusp);
      if (win >= 0) 
	complete_status (handle, prstatusp);
    }	  
  else
    {
      int uthread = get_kernel_thread (handle, pthread);

      if (IS_LWP_ID (uthread))
	win = thread_scan (handle, PIOCSTATUS, STC_THREAD, uthread, NULL,
			   prstatusp, NULL);
      else
	{
	  /* Thread with no kernel context; fake flags and get the registers */
	  prstatusp->pr_flags = PR_STOPPED | PR_ISTOP | PR_RETURN;
	  prstatusp->pr_why = PR_REQUESTED;
	  prstatusp->pr_what = 0;
	  win = thread_scan (handle, PIOCGREG, STC_THREAD, pthread, NULL,
			     &prstatusp->pr_reg, NULL);
	}
    } 

  return win;
}


/*

   GLOBAL FUNCTION

   irix_wait_proc -- Wait for process to stop on event of interest.

   SYNOPSIS

   int  irix_wait_proc (spyProc_t* handle, int fd, prstatus_t* prstatusp)

   DESCRIPTION

   Waits for the process with open /proc file descriptor FD and
   accessed through HANDLE (if non-null) to stop on an "event of interest",
   setting *PRSTATUSP as for the PIOCWSTOP ioctl.  

 */

int 
irix_wait_proc (spyProc_t* handle, int fd, prstatus_t* prstatusp)
{
  int win;
  if (! libspy_enabled || handle == NULL)
    win = ioctl (fd, PIOCWSTOP, prstatusp);
  else
    {
      /* Apparently, PIOCWSTOP hangs when there are kernel threads,
	 but poll still works.  */	 
      struct pollfd request;
      request.fd = fd;
      request.events = POLLPRI;
      win = poll (&request, 1, -1);
      if (win < 0)
	return win;
      win = irix_get_status (handle, fd, 0, prstatusp);
      if (win && ! (prstatusp->pr_flags & (PR_STOPPED | PR_ISTOP)))
	warning ("irix_wait_proc: process not stopped");
    }
  return win;
}

static int
resume_step_callback (spyProc_t* handle, spyThread_t uthread_id, 
		 void* pthread_idp0, void* argp0)
{
  prstatus_t status;
  spyThread_t* pthread_idp = (spyThread_t*) pthread_idp0;
  resume_info_t* argp = (resume_info_t*) argp0;
  if (thread_scan (handle, PIOCSTATUS, STC_THREAD, uthread_id, NULL,
		   &status, NULL) >= 0
      && (status.pr_flags & PR_RETURN))
    {
      argp->num_resumed += 1;
      if (thread_scan (handle, PIOCRUN, STC_THREAD, uthread_id, NULL,
		       argp->prrun_p, NULL) < 0)
	return -1;
    }
  return 0;
}
      

/*

   GLOBAL FUNCTION

   irix_resume -- Resume process or thread.

   SYNOPSIS

   int irix_resume (spyProc_t* handle, int fd, spyThread_t pthread, 
                    prrun_t* prrun_p)

   DESCRIPTION

   Resume thread PTHREAD (all threads if 0) of the process with open 
   file descriptor FD and accessed through HANDLE (if non-null).  PRRUN_P
   is as for the PIOCRUN ioctl.  

 */

int
irix_resume (spyProc_t* handle, int fd, spyThread_t pthread, prrun_t* prrun_p)
{
  if (! (prrun_p->pr_flags & PRSTEP)) 
    prrun_p->pr_flags |= PRCSTEP;
  if (pthread == 0 && handle != NULL && (prrun_p->pr_flags & PRSTEP)) 
    {
      /* Apparently, we will hang in irix_wait_proc if we attempt to
	 step any thread that does not have PR_RETURN on, so avoid
	 doing so. */

      resume_info_t args;
      spyThread_t thread;
      int win;
      args.prrun_p = prrun_p; 
      args.num_resumed = 0;
      win = thread_scan (handle, SPYCGNAME, STC_SCAN_KERN, 0, 
			 resume_step_callback, &thread, &args);
      if (args.num_resumed > 0 && win >= 0)
	return 0;
    }

  if (pthread == 0)
    return ioctl (fd, PIOCRUN, prrun_p);
  else 
    return thread_scan (handle, PIOCRUN, STC_THREAD, 
			get_kernel_thread (handle, pthread), NULL, 
			prrun_p, NULL);
}

/*

   GLOBAL FUNCTION

   irix_reset_held_signals -- Restore kernel thread held signals

   SYNOPSIS

   int irix_reset_held_signals (spyProc_t* handle)

   DESCRIPTION

   Restore the held signal masks for all kernel threads present at the
   time HANDLE was created.  (On IRIX, held signals are per-kernel
   thread, if there are any kernel threads.)

 */

int
irix_reset_held_signals (spyProc_t* handle)
{
  int code;
  if (handle == NULL)
    code = 0;
  else
    {
      thread_data_t* info = THREAD_INFO (handle);
      int i;
      code = 1;
      for (i = 0; i < info->num_initial_uthreads; i += 1)
	if (thread_scan (handle, PIOCSHOLD, STC_THREAD, 
			 info->initial_uthread_status[i].pr_who, NULL,
			 &info->initial_uthread_status[i].pr_sighold, NULL) < 0)
	  code = 0;
    }
  return code;
}


/*

   GLOBAL FUNCTION

   irix_get_fpregs -- Get floating-point registers

   SYNOPSIS

   int  irix_get_fpregs (spyProc_t* handle, spyThread_t tid, 
                         gdb_fpregset_t* fpregset)

   DESCRIPTION

   Fetch floating-point registers (to FPREGSET) for thread TID of process
   accessed through HANDLE. 

 */

int 
irix_get_fpregs (spyProc_t* handle, spyThread_t tid,
		 gdb_fpregset_t* fpregset)
{
  return thread_scan (handle, PIOCGFPREG, STC_THREAD, tid,
		      NULL, fpregset, NULL);
}


/*

   GLOBAL FUNCTION

   irix_set_gregs -- Set general registers

   SYNOPSIS

   int irix_set_gregs (spyProc_t* handle, spyThread_t tid, gdb_gregset_t* gregs)

   DESCRIPTION

   Set general registers (from GREGS) for thread TID of process
   accessed through HANDLE. 

 */

int
irix_set_gregs (spyProc_t* handle, spyThread_t tid, gdb_gregset_t* gregs)
{
  return thread_scan (handle, PIOCSREG, STC_THREAD, tid, 
		      NULL, gregs, NULL);
}

/*

   GLOBAL FUNCTION

   irix_set_fregs -- Set general registers

   SYNOPSIS

   int irix_set_fregs (spyProc_t* handle, spyThread_t tid, 
                       gdb_fpregset_t* fregs)

   DESCRIPTION

   Set floating-point registers (from FREGS) for thread TID of process
   accessed through HANDLE. 

 */

int
irix_set_fregs (spyProc_t* handle, spyThread_t tid, gdb_fpregset_t* fregs)
{
  return thread_scan (handle, PIOCSFPREG, STC_THREAD, tid, 
		      NULL, fregs, NULL);
}

				/* Debugging utilities */

static int
dbg_callback1 (spyProc_t* handle, spyThread_t id1, void* id2p0, void* unused)
{
  spyThread_t* id2p = (spyThread_t*) id2p0;
  fprintf (stderr, "uthread: %x, pthread: %x\n", id1, *id2p);
  return 0;
}


static void
procfs_dbg_cgname (spyProc_t* handle, spyThread_t id) 
{
  spyThread_t result;	
  thread_scan (handle, SPYCGNAME, STC_THREAD, id, NULL, &result, NULL);
  fprintf (stderr, "%x -> %x\n", id, result);
}

static void
procfs_dbg_uthreads (spyProc_t* handle) 
{
  spyThread_t result;	
  thread_scan (handle, SPYCGNAME, STC_SCAN_KERN, 0, 
	       dbg_callback1, &result, NULL);
}

#endif /* HAVE_SPYTHREAD */

/* Register that we are able to handle irix5 core file formats.
   This really is bfd_target_unknown_flavour */

static struct core_fns irix5_core_fns =
{
  bfd_target_unknown_flavour,		/* core_flavour */
  default_check_format,			/* check_format */
  default_core_sniffer,			/* core_sniffer */
  fetch_core_registers,			/* core_read_registers */
  NULL					/* next */
};

void
_initialize_core_irix5 (void)
{
  add_core_fns (&irix5_core_fns);
}