diff gcc/mips-tfile.c @ 0:a06113de4d67

first commit
author kent <kent@cr.ie.u-ryukyu.ac.jp>
date Fri, 17 Jul 2009 14:47:48 +0900
parents
children 3bfb6c00c1e0
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/mips-tfile.c	Fri Jul 17 14:47:48 2009 +0900
@@ -0,0 +1,5497 @@
+/* Update the symbol table (the .T file) in a MIPS object to
+   contain debugging information specified by the GNU compiler
+   in the form of comments (the mips assembler does not support
+   assembly access to debug information).
+   Copyright (C) 1991, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2001,
+   2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   Free Software Foundation, Inc.
+   Contributed by Michael Meissner (meissner@cygnus.com).
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+
+/* Here is a brief description of the MIPS ECOFF symbol table.  The
+   MIPS symbol table has the following pieces:
+
+	Symbolic Header
+	    |
+	    +--	Auxiliary Symbols
+	    |
+	    +--	Dense number table
+	    |
+	    +--	Optimizer Symbols
+	    |
+	    +--	External Strings
+	    |
+	    +--	External Symbols
+	    |
+	    +--	Relative file descriptors
+	    |
+	    +--	File table
+		    |
+		    +--	Procedure table
+		    |
+		    +--	Line number table
+		    |
+		    +--	Local Strings
+		    |
+		    +--	Local Symbols
+
+   The symbolic header points to each of the other tables, and also
+   contains the number of entries.  It also contains a magic number
+   and MIPS compiler version number, such as 2.0.
+
+   The auxiliary table is a series of 32 bit integers, that are
+   referenced as needed from the local symbol table.  Unlike standard
+   COFF, the aux. information does not follow the symbol that uses
+   it, but rather is a separate table.  In theory, this would allow
+   the MIPS compilers to collapse duplicate aux. entries, but I've not
+   noticed this happening with the 1.31 compiler suite.  The different
+   types of aux. entries are:
+
+    1)	dnLow: Low bound on array dimension.
+
+    2)	dnHigh: High bound on array dimension.
+
+    3)	isym: Index to the local symbol which is the start of the
+	function for the end of function first aux. entry.
+
+    4)	width: Width of structures and bitfields.
+
+    5)	count: Count of ranges for variant part.
+
+    6)	rndx: A relative index into the symbol table.  The relative
+	index field has two parts: rfd which is a pointer into the
+	relative file index table or ST_RFDESCAPE which says the next
+	aux. entry is the file number, and index: which is the pointer
+	into the local symbol within a given file table.  This is for
+	things like references to types defined in another file.
+
+    7)	Type information: This is like the COFF type bits, except it
+	is 32 bits instead of 16; they still have room to add new
+	basic types; and they can handle more than 6 levels of array,
+	pointer, function, etc.  Each type information field contains
+	the following structure members:
+
+	    a)	fBitfield: a bit that says this is a bitfield, and the
+		size in bits follows as the next aux. entry.
+
+	    b)	continued: a bit that says the next aux. entry is a
+		continuation of the current type information (in case
+		there are more than 6 levels of array/ptr/function).
+
+	    c)	bt: an integer containing the base type before adding
+		array, pointer, function, etc. qualifiers.  The
+		current base types that I have documentation for are:
+
+			btNil		-- undefined
+			btAdr		-- address - integer same size as ptr
+			btChar		-- character
+			btUChar		-- unsigned character
+			btShort		-- short
+			btUShort	-- unsigned short
+			btInt		-- int
+			btUInt		-- unsigned int
+			btLong		-- long
+			btULong		-- unsigned long
+			btFloat		-- float (real)
+			btDouble	-- Double (real)
+			btStruct	-- Structure (Record)
+			btUnion		-- Union (variant)
+			btEnum		-- Enumerated
+			btTypedef	-- defined via a typedef isymRef
+			btRange		-- subrange of int
+			btSet		-- pascal sets
+			btComplex	-- fortran complex
+			btDComplex	-- fortran double complex
+			btIndirect	-- forward or unnamed typedef
+			btFixedDec	-- Fixed Decimal
+			btFloatDec	-- Float Decimal
+			btString	-- Varying Length Character String
+			btBit		-- Aligned Bit String
+			btPicture	-- Picture
+			btVoid		-- Void (MIPS cc revision >= 2.00)
+
+	    d)	tq0 - tq5: type qualifier fields as needed.  The
+		current type qualifier fields I have documentation for
+		are:
+
+			tqNil		-- no more qualifiers
+			tqPtr		-- pointer
+			tqProc		-- procedure
+			tqArray		-- array
+			tqFar		-- 8086 far pointers
+			tqVol		-- volatile
+
+
+   The dense number table is used in the front ends, and disappears by
+   the time the .o is created.
+
+   With the 1.31 compiler suite, the optimization symbols don't seem
+   to be used as far as I can tell.
+
+   The linker is the first entity that creates the relative file
+   descriptor table, and I believe it is used so that the individual
+   file table pointers don't have to be rewritten when the objects are
+   merged together into the program file.
+
+   Unlike COFF, the basic symbol & string tables are split into
+   external and local symbols/strings.  The relocation information
+   only goes off of the external symbol table, and the debug
+   information only goes off of the internal symbol table.  The
+   external symbols can have links to an appropriate file index and
+   symbol within the file to give it the appropriate type information.
+   Because of this, the external symbols are actually larger than the
+   internal symbols (to contain the link information), and contain the
+   local symbol structure as a member, though this member is not the
+   first member of the external symbol structure (!).  I suspect this
+   split is to make strip easier to deal with.
+
+   Each file table has offsets for where the line numbers, local
+   strings, local symbols, and procedure table starts from within the
+   global tables, and the indices are reset to 0 for each of those
+   tables for the file.
+
+   The procedure table contains the binary equivalents of the .ent
+   (start of the function address), .frame (what register is the
+   virtual frame pointer, constant offset from the register to obtain
+   the VFP, and what register holds the return address), .mask/.fmask
+   (bitmask of saved registers, and where the first register is stored
+   relative to the VFP) assembler directives.  It also contains the
+   low and high bounds of the line numbers if debugging is turned on.
+
+   The line number table is a compressed form of the normal COFF line
+   table.  Each line number entry is either 1 or 3 bytes long, and
+   contains a signed delta from the previous line, and an unsigned
+   count of the number of instructions this statement takes.
+
+   The local symbol table contains the following fields:
+
+    1)	iss: index to the local string table giving the name of the
+	symbol.
+
+    2)	value: value of the symbol (address, register number, etc.).
+
+    3)	st: symbol type.  The current symbol types are:
+
+	    stNil	  -- Nuthin' special
+	    stGlobal	  -- external symbol
+	    stStatic	  -- static
+	    stParam	  -- procedure argument
+	    stLocal	  -- local variable
+	    stLabel	  -- label
+	    stProc	  -- External Procedure
+	    stBlock	  -- beginning of block
+	    stEnd	  -- end (of anything)
+	    stMember	  -- member (of anything)
+	    stTypedef	  -- type definition
+	    stFile	  -- file name
+	    stRegReloc	  -- register relocation
+	    stForward	  -- forwarding address
+	    stStaticProc  -- Static procedure
+	    stConstant	  -- const
+
+    4)	sc: storage class.  The current storage classes are:
+
+	    scText	  -- text symbol
+	    scData	  -- initialized data symbol
+	    scBss	  -- un-initialized data symbol
+	    scRegister	  -- value of symbol is register number
+	    scAbs	  -- value of symbol is absolute
+	    scUndefined   -- who knows?
+	    scCdbLocal	  -- variable's value is IN se->va.??
+	    scBits	  -- this is a bit field
+	    scCdbSystem	  -- value is IN debugger's address space
+	    scRegImage	  -- register value saved on stack
+	    scInfo	  -- symbol contains debugger information
+	    scUserStruct  -- addr in struct user for current process
+	    scSData	  -- load time only small data
+	    scSBss	  -- load time only small common
+	    scRData	  -- load time only read only data
+	    scVar	  -- Var parameter (fortranpascal)
+	    scCommon	  -- common variable
+	    scSCommon	  -- small common
+	    scVarRegister -- Var parameter in a register
+	    scVariant	  -- Variant record
+	    scSUndefined  -- small undefined(external) data
+	    scInit	  -- .init section symbol
+
+    5)	index: pointer to a local symbol or aux. entry.
+
+
+
+   For the following program:
+
+	#include <stdio.h>
+
+	main(){
+		printf("Hello World!\n");
+		return 0;
+	}
+
+   Mips-tdump produces the following information:
+
+   Global file header:
+       magic number             0x162
+       # sections               2
+       timestamp                645311799, Wed Jun 13 17:16:39 1990
+       symbolic header offset   284
+       symbolic header size     96
+       optional header          56
+       flags                    0x0
+
+   Symbolic header, magic number = 0x7009, vstamp = 1.31:
+
+       Info                      Offset      Number       Bytes
+       ====                      ======      ======      =====
+
+       Line numbers                 380           4           4 [13]
+       Dense numbers                  0           0           0
+       Procedures Tables            384           1          52
+       Local Symbols                436          16         192
+       Optimization Symbols           0           0           0
+       Auxiliary Symbols            628          39         156
+       Local Strings                784          80          80
+       External Strings             864         144         144
+       File Tables                 1008           2         144
+       Relative Files                 0           0           0
+       External Symbols            1152          20         320
+
+   File #0, "hello2.c"
+
+       Name index  = 1          Readin      = No
+       Merge       = No         Endian      = LITTLE
+       Debug level = G2         Language    = C
+       Adr         = 0x00000000
+
+       Info                       Start      Number        Size      Offset
+       ====                       =====      ======        ====      ======
+       Local strings                  0          15          15         784
+       Local symbols                  0           6          72         436
+       Line numbers                   0          13          13         380
+       Optimization symbols           0           0           0           0
+       Procedures                     0           1          52         384
+       Auxiliary symbols              0          14          56         628
+       Relative Files                 0           0           0           0
+
+    There are 6 local symbols, starting at 436
+
+	Symbol# 0: "hello2.c"
+	    End+1 symbol  = 6
+	    String index  = 1
+	    Storage class = Text        Index  = 6
+	    Symbol type   = File        Value  = 0
+
+	Symbol# 1: "main"
+	    End+1 symbol  = 5
+	    Type          = int
+	    String index  = 10
+	    Storage class = Text        Index  = 12
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 2: ""
+	    End+1 symbol  = 4
+	    String index  = 0
+	    Storage class = Text        Index  = 4
+	    Symbol type   = Block       Value  = 8
+
+	Symbol# 3: ""
+	    First symbol  = 2
+	    String index  = 0
+	    Storage class = Text        Index  = 2
+	    Symbol type   = End         Value  = 28
+
+	Symbol# 4: "main"
+	    First symbol  = 1
+	    String index  = 10
+	    Storage class = Text        Index  = 1
+	    Symbol type   = End         Value  = 52
+
+	Symbol# 5: "hello2.c"
+	    First symbol  = 0
+	    String index  = 1
+	    Storage class = Text        Index  = 0
+	    Symbol type   = End         Value  = 0
+
+    There are 14 auxiliary table entries, starting at 628.
+
+	* #0               0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #1              24, [  24/      0], [ 6 0:0 0:0:0:0:0:0]
+	* #2               8, [   8/      0], [ 2 0:0 0:0:0:0:0:0]
+	* #3              16, [  16/      0], [ 4 0:0 0:0:0:0:0:0]
+	* #4              24, [  24/      0], [ 6 0:0 0:0:0:0:0:0]
+	* #5              32, [  32/      0], [ 8 0:0 0:0:0:0:0:0]
+	* #6              40, [  40/      0], [10 0:0 0:0:0:0:0:0]
+	* #7              44, [  44/      0], [11 0:0 0:0:0:0:0:0]
+	* #8              12, [  12/      0], [ 3 0:0 0:0:0:0:0:0]
+	* #9              20, [  20/      0], [ 5 0:0 0:0:0:0:0:0]
+	* #10             28, [  28/      0], [ 7 0:0 0:0:0:0:0:0]
+	* #11             36, [  36/      0], [ 9 0:0 0:0:0:0:0:0]
+	  #12              5, [   5/      0], [ 1 1:0 0:0:0:0:0:0]
+	  #13             24, [  24/      0], [ 6 0:0 0:0:0:0:0:0]
+
+    There are 1 procedure descriptor entries, starting at 0.
+
+	Procedure descriptor 0:
+	    Name index   = 10          Name          = "main"
+	    .mask 0x80000000,-4        .fmask 0x00000000,0
+	    .frame $29,24,$31
+	    Opt. start   = -1          Symbols start = 1
+	    First line # = 3           Last line #   = 6
+	    Line Offset  = 0           Address       = 0x00000000
+
+	There are 4 bytes holding line numbers, starting at 380.
+	    Line           3,   delta     0,   count  2
+	    Line           4,   delta     1,   count  3
+	    Line           5,   delta     1,   count  2
+	    Line           6,   delta     1,   count  6
+
+   File #1, "/usr/include/stdio.h"
+
+    Name index  = 1          Readin      = No
+    Merge       = Yes        Endian      = LITTLE
+    Debug level = G2         Language    = C
+    Adr         = 0x00000000
+
+    Info                       Start      Number        Size      Offset
+    ====                       =====      ======        ====      ======
+    Local strings                 15          65          65         799
+    Local symbols                  6          10         120         508
+    Line numbers                   0           0           0         380
+    Optimization symbols           0           0           0           0
+    Procedures                     1           0           0         436
+    Auxiliary symbols             14          25         100         684
+    Relative Files                 0           0           0           0
+
+    There are 10 local symbols, starting at 442
+
+	Symbol# 0: "/usr/include/stdio.h"
+	    End+1 symbol  = 10
+	    String index  = 1
+	    Storage class = Text        Index  = 10
+	    Symbol type   = File        Value  = 0
+
+	Symbol# 1: "_iobuf"
+	    End+1 symbol  = 9
+	    String index  = 22
+	    Storage class = Info        Index  = 9
+	    Symbol type   = Block       Value  = 20
+
+	Symbol# 2: "_cnt"
+	    Type          = int
+	    String index  = 29
+	    Storage class = Info        Index  = 4
+	    Symbol type   = Member      Value  = 0
+
+	Symbol# 3: "_ptr"
+	    Type          = ptr to char
+	    String index  = 34
+	    Storage class = Info        Index  = 15
+	    Symbol type   = Member      Value  = 32
+
+	Symbol# 4: "_base"
+	    Type          = ptr to char
+	    String index  = 39
+	    Storage class = Info        Index  = 16
+	    Symbol type   = Member      Value  = 64
+
+	Symbol# 5: "_bufsiz"
+	    Type          = int
+	    String index  = 45
+	    Storage class = Info        Index  = 4
+	    Symbol type   = Member      Value  = 96
+
+	Symbol# 6: "_flag"
+	    Type          = short
+	    String index  = 53
+	    Storage class = Info        Index  = 3
+	    Symbol type   = Member      Value  = 128
+
+	Symbol# 7: "_file"
+	    Type          = char
+	    String index  = 59
+	    Storage class = Info        Index  = 2
+	    Symbol type   = Member      Value  = 144
+
+	Symbol# 8: ""
+	    First symbol  = 1
+	    String index  = 0
+	    Storage class = Info        Index  = 1
+	    Symbol type   = End         Value  = 0
+
+	Symbol# 9: "/usr/include/stdio.h"
+	    First symbol  = 0
+	    String index  = 1
+	    Storage class = Text        Index  = 0
+	    Symbol type   = End         Value  = 0
+
+    There are 25 auxiliary table entries, starting at 642.
+
+	* #14             -1, [4095/1048575], [63 1:1 f:f:f:f:f:f]
+	  #15          65544, [   8/     16], [ 2 0:0 1:0:0:0:0:0]
+	  #16          65544, [   8/     16], [ 2 0:0 1:0:0:0:0:0]
+	* #17         196656, [  48/     48], [12 0:0 3:0:0:0:0:0]
+	* #18           8191, [4095/      1], [63 1:1 0:0:0:0:f:1]
+	* #19              1, [   1/      0], [ 0 1:0 0:0:0:0:0:0]
+	* #20          20479, [4095/      4], [63 1:1 0:0:0:0:f:4]
+	* #21              1, [   1/      0], [ 0 1:0 0:0:0:0:0:0]
+	* #22              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #23              2, [   2/      0], [ 0 0:1 0:0:0:0:0:0]
+	* #24            160, [ 160/      0], [40 0:0 0:0:0:0:0:0]
+	* #25              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #26              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #27              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #28              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #29              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #30              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #31              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #32              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #33              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #34              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #35              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #36              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #37              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+	* #38              0, [   0/      0], [ 0 0:0 0:0:0:0:0:0]
+
+    There are 0 procedure descriptor entries, starting at 1.
+
+   There are 20 external symbols, starting at 1152
+
+	Symbol# 0: "_iob"
+	    Type          = array [3 {160}] of struct _iobuf { ifd = 1, index = 1 }
+	    String index  = 0           Ifd    = 1
+	    Storage class = Nil         Index  = 17
+	    Symbol type   = Global      Value  = 60
+
+	Symbol# 1: "fopen"
+	    String index  = 5           Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 2: "fdopen"
+	    String index  = 11          Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 3: "freopen"
+	    String index  = 18          Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 4: "popen"
+	    String index  = 26          Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 5: "tmpfile"
+	    String index  = 32          Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 6: "ftell"
+	    String index  = 40          Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 7: "rewind"
+	    String index  = 46          Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 8: "setbuf"
+	    String index  = 53          Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 9: "setbuffer"
+	    String index  = 60          Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 10: "setlinebuf"
+	    String index  = 70          Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 11: "fgets"
+	    String index  = 81          Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 12: "gets"
+	    String index  = 87          Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 13: "ctermid"
+	    String index  = 92          Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 14: "cuserid"
+	    String index  = 100         Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 15: "tempnam"
+	    String index  = 108         Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 16: "tmpnam"
+	    String index  = 116         Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 17: "sprintf"
+	    String index  = 123         Ifd    = 1
+	    Storage class = Nil         Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 18: "main"
+	    Type          = int
+	    String index  = 131         Ifd    = 0
+	    Storage class = Text        Index  = 1
+	    Symbol type   = Proc        Value  = 0
+
+	Symbol# 19: "printf"
+	    String index  = 136         Ifd    = 0
+	    Storage class = Undefined   Index  = 1048575
+	    Symbol type   = Proc        Value  = 0
+
+   The following auxiliary table entries were unused:
+
+    #0               0  0x00000000  void
+    #2               8  0x00000008  char
+    #3              16  0x00000010  short
+    #4              24  0x00000018  int
+    #5              32  0x00000020  long
+    #6              40  0x00000028  float
+    #7              44  0x0000002c  double
+    #8              12  0x0000000c  unsigned char
+    #9              20  0x00000014  unsigned short
+    #10             28  0x0000001c  unsigned int
+    #11             36  0x00000024  unsigned long
+    #14              0  0x00000000  void
+    #15             24  0x00000018  int
+    #19             32  0x00000020  long
+    #20             40  0x00000028  float
+    #21             44  0x0000002c  double
+    #22             12  0x0000000c  unsigned char
+    #23             20  0x00000014  unsigned short
+    #24             28  0x0000001c  unsigned int
+    #25             36  0x00000024  unsigned long
+    #26             48  0x00000030  struct no name { ifd = -1, index = 1048575 }
+
+*/
+
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "version.h"
+#include "intl.h"
+
+#ifndef __SABER__
+#define saber_stop()
+#endif
+
+/* Include getopt.h for the sake of getopt_long.  */
+#include "getopt.h"
+
+#ifndef __LINE__
+#define __LINE__ 0
+#endif
+
+/* Due to size_t being defined in sys/types.h and different
+   in stddef.h, we have to do this by hand.....  Note, these
+   types are correct for MIPS based systems, and may not be
+   correct for other systems.  Ultrix 4.0 and Silicon Graphics
+   have this fixed, but since the following is correct, and
+   the fact that including stddef.h gets you GCC's version
+   instead of the standard one it's not worth it to fix it.  */
+
+#if defined(__OSF1__) || defined(__OSF__) || defined(__osf__)
+#define Size_t		long unsigned int
+#else
+#define Size_t		unsigned int
+#endif
+#define Ptrdiff_t	long
+
+/* The following might be called from obstack or malloc,
+   so they can't be static.  */
+
+extern void pfatal_with_name (const char *) ATTRIBUTE_NORETURN;
+extern void botch (const char *) ATTRIBUTE_NORETURN;
+
+extern void fatal (const char *format, ...) ATTRIBUTE_PRINTF_1 ATTRIBUTE_NORETURN;
+extern void error (const char *format, ...) ATTRIBUTE_PRINTF_1;
+
+#ifndef MIPS_DEBUGGING_INFO
+
+static int	 line_number;
+static int	 cur_line_start;
+static int	 debug;
+static int	 had_errors;
+static const char *progname;
+static const char *input_name;
+
+int
+main (void)
+{
+  fprintf (stderr, "Mips-tfile should only be run on a MIPS computer!\n");
+  exit (1);
+}
+
+#else				/* MIPS_DEBUGGING defined */
+
+/* The local and global symbols have a field index, so undo any defines
+   of index -> strchr.  */
+
+#undef index
+
+#include <signal.h>
+
+#ifndef CROSS_DIRECTORY_STRUCTURE
+#include <a.out.h>
+#else
+#include "mips/a.out.h"
+#endif /* CROSS_DIRECTORY_STRUCTURE */
+
+#include "gstab.h"
+
+#define STAB_CODE_TYPE enum __stab_debug_code
+
+#ifndef MALLOC_CHECK
+#ifdef	__SABER__
+#define MALLOC_CHECK
+#endif
+#endif
+
+#define IS_ASM_IDENT(ch) \
+  (ISIDNUM (ch) || (ch) == '.' || (ch) == '$')
+
+
+/* Redefinition of storage classes as an enumeration for better
+   debugging.  */
+
+typedef enum sc {
+  sc_Nil	 = scNil,	  /* no storage class */
+  sc_Text	 = scText,	  /* text symbol */
+  sc_Data	 = scData,	  /* initialized data symbol */
+  sc_Bss	 = scBss,	  /* un-initialized data symbol */
+  sc_Register	 = scRegister,	  /* value of symbol is register number */
+  sc_Abs	 = scAbs,	  /* value of symbol is absolute */
+  sc_Undefined	 = scUndefined,	  /* who knows? */
+  sc_CdbLocal	 = scCdbLocal,	  /* variable's value is IN se->va.?? */
+  sc_Bits	 = scBits,	  /* this is a bit field */
+  sc_CdbSystem	 = scCdbSystem,	  /* value is IN CDB's address space */
+  sc_RegImage	 = scRegImage,	  /* register value saved on stack */
+  sc_Info	 = scInfo,	  /* symbol contains debugger information */
+  sc_UserStruct	 = scUserStruct,  /* addr in struct user for current process */
+  sc_SData	 = scSData,	  /* load time only small data */
+  sc_SBss	 = scSBss,	  /* load time only small common */
+  sc_RData	 = scRData,	  /* load time only read only data */
+  sc_Var	 = scVar,	  /* Var parameter (fortran,pascal) */
+  sc_Common	 = scCommon,	  /* common variable */
+  sc_SCommon	 = scSCommon,	  /* small common */
+  sc_VarRegister = scVarRegister, /* Var parameter in a register */
+  sc_Variant	 = scVariant,	  /* Variant record */
+  sc_SUndefined	 = scSUndefined,  /* small undefined(external) data */
+  sc_Init	 = scInit,	  /* .init section symbol */
+  sc_Max	 = scMax	  /* Max storage class+1 */
+} sc_t;
+
+/* Redefinition of symbol type.  */
+
+typedef enum st {
+  st_Nil	= stNil,	/* Nuthin' special */
+  st_Global	= stGlobal,	/* external symbol */
+  st_Static	= stStatic,	/* static */
+  st_Param	= stParam,	/* procedure argument */
+  st_Local	= stLocal,	/* local variable */
+  st_Label	= stLabel,	/* label */
+  st_Proc	= stProc,	/*     "      "	 Procedure */
+  st_Block	= stBlock,	/* beginning of block */
+  st_End	= stEnd,	/* end (of anything) */
+  st_Member	= stMember,	/* member (of anything	- struct/union/enum */
+  st_Typedef	= stTypedef,	/* type definition */
+  st_File	= stFile,	/* file name */
+  st_RegReloc	= stRegReloc,	/* register relocation */
+  st_Forward	= stForward,	/* forwarding address */
+  st_StaticProc	= stStaticProc,	/* load time only static procs */
+  st_Constant	= stConstant,	/* const */
+  st_Str	= stStr,	/* string */
+  st_Number	= stNumber,	/* pure number (i.e. 4 NOR 2+2) */
+  st_Expr	= stExpr,	/* 2+2 vs. 4 */
+  st_Type	= stType,	/* post-coercion SER */
+  st_Max	= stMax		/* max type+1 */
+} st_t;
+
+/* Redefinition of type qualifiers.  */
+
+typedef enum tq {
+  tq_Nil	= tqNil,	/* bt is what you see */
+  tq_Ptr	= tqPtr,	/* pointer */
+  tq_Proc	= tqProc,	/* procedure */
+  tq_Array	= tqArray,	/* duh */
+  tq_Far	= tqFar,	/* longer addressing - 8086/8 land */
+  tq_Vol	= tqVol,	/* volatile */
+  tq_Max	= tqMax		/* Max type qualifier+1 */
+} tq_t;
+
+/* Redefinition of basic types.  */
+
+typedef enum bt {
+  bt_Nil	= btNil,	/* undefined */
+  bt_Adr	= btAdr,	/* address - integer same size as pointer */
+  bt_Char	= btChar,	/* character */
+  bt_UChar	= btUChar,	/* unsigned character */
+  bt_Short	= btShort,	/* short */
+  bt_UShort	= btUShort,	/* unsigned short */
+  bt_Int	= btInt,	/* int */
+  bt_UInt	= btUInt,	/* unsigned int */
+  bt_Long	= btLong,	/* long */
+  bt_ULong	= btULong,	/* unsigned long */
+  bt_Float	= btFloat,	/* float (real) */
+  bt_Double	= btDouble,	/* Double (real) */
+  bt_Struct	= btStruct,	/* Structure (Record) */
+  bt_Union	= btUnion,	/* Union (variant) */
+  bt_Enum	= btEnum,	/* Enumerated */
+  bt_Typedef	= btTypedef,	/* defined via a typedef, isymRef points */
+  bt_Range	= btRange,	/* subrange of int */
+  bt_Set	= btSet,	/* pascal sets */
+  bt_Complex	= btComplex,	/* fortran complex */
+  bt_DComplex	= btDComplex,	/* fortran double complex */
+  bt_Indirect	= btIndirect,	/* forward or unnamed typedef */
+  bt_FixedDec	= btFixedDec,	/* Fixed Decimal */
+  bt_FloatDec	= btFloatDec,	/* Float Decimal */
+  bt_String	= btString,	/* Varying Length Character String */
+  bt_Bit	= btBit,	/* Aligned Bit String */
+  bt_Picture	= btPicture,	/* Picture */
+
+#ifdef btVoid
+  bt_Void	= btVoid,	/* Void */
+#else
+#define bt_Void	bt_Nil
+#endif
+
+  bt_Max	= btMax		/* Max basic type+1 */
+} bt_t;
+
+
+
+/* Basic COFF storage classes.  */
+enum coff_storage {
+  C_EFCN	= -1,
+  C_NULL	= 0,
+  C_AUTO	= 1,
+  C_EXT		= 2,
+  C_STAT	= 3,
+  C_REG		= 4,
+  C_EXTDEF	= 5,
+  C_LABEL	= 6,
+  C_ULABEL	= 7,
+  C_MOS		= 8,
+  C_ARG		= 9,
+  C_STRTAG	= 10,
+  C_MOU		= 11,
+  C_UNTAG	= 12,
+  C_TPDEF	= 13,
+  C_USTATIC	= 14,
+  C_ENTAG	= 15,
+  C_MOE		= 16,
+  C_REGPARM	= 17,
+  C_FIELD	= 18,
+  C_BLOCK	= 100,
+  C_FCN		= 101,
+  C_EOS		= 102,
+  C_FILE	= 103,
+  C_LINE	= 104,
+  C_ALIAS	= 105,
+  C_HIDDEN	= 106,
+  C_MAX		= 107
+} coff_storage_t;
+
+/* Regular COFF fundamental type.  */
+typedef enum coff_type {
+  T_NULL	= 0,
+  T_ARG		= 1,
+  T_CHAR	= 2,
+  T_SHORT	= 3,
+  T_INT		= 4,
+  T_LONG	= 5,
+  T_FLOAT	= 6,
+  T_DOUBLE	= 7,
+  T_STRUCT	= 8,
+  T_UNION	= 9,
+  T_ENUM	= 10,
+  T_MOE		= 11,
+  T_UCHAR	= 12,
+  T_USHORT	= 13,
+  T_UINT	= 14,
+  T_ULONG	= 15,
+  T_MAX		= 16
+} coff_type_t;
+
+/* Regular COFF derived types.  */
+typedef enum coff_dt {
+  DT_NON	= 0,
+  DT_PTR	= 1,
+  DT_FCN	= 2,
+  DT_ARY	= 3,
+  DT_MAX	= 4
+} coff_dt_t;
+
+#define N_BTMASK	017	/* bitmask to isolate basic type */
+#define N_TMASK		003	/* bitmask to isolate derived type */
+#define N_BT_SHIFT	4	/* # bits to shift past basic type */
+#define N_TQ_SHIFT	2	/* # bits to shift derived types */
+#define	N_TQ		6	/* # of type qualifiers */
+
+/* States for whether to hash type or not.  */
+typedef enum hash_state {
+  hash_no	= 0,		/* don't hash type */
+  hash_yes	= 1,		/* ok to hash type, or use previous hash */
+  hash_record	= 2		/* ok to record hash, but don't use prev.  */
+} hash_state_t;
+
+
+/* Types of different sized allocation requests.  */
+enum alloc_type {
+  alloc_type_none,		/* dummy value */
+  alloc_type_scope,		/* nested scopes linked list */
+  alloc_type_vlinks,		/* glue linking pages in varray */
+  alloc_type_shash,		/* string hash element */
+  alloc_type_thash,		/* type hash element */
+  alloc_type_tag,		/* struct/union/tag element */
+  alloc_type_forward,		/* element to hold unknown tag */
+  alloc_type_thead,		/* head of type hash list */
+  alloc_type_varray,		/* general varray allocation */
+  alloc_type_last		/* last+1 element for array bounds */
+};
+
+
+#define WORD_ALIGN(x)  (((x) + (sizeof (long) - 1)) & ~ (sizeof (long) - 1))
+#define DWORD_ALIGN(x) (((x) + 7) & ~7)
+
+
+/* Structures to provide n-number of virtual arrays, each of which can
+   grow linearly, and which are written in the object file as sequential
+   pages.  On systems with a BSD malloc that define USE_MALLOC, the
+   MAX_CLUSTER_PAGES should be 1 less than a power of two, since malloc
+   adds its overhead, and rounds up to the next power of 2.  Pages are
+   linked together via a linked list.  */
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 32768		/* size of varray pages */
+#endif
+
+#define PAGE_USIZE ((Size_t) PAGE_SIZE)
+
+
+#ifndef MAX_CLUSTER_PAGES	/* # pages to get from system */
+#ifndef USE_MALLOC		/* in one memory request */
+#define MAX_CLUSTER_PAGES 64
+#else
+#define MAX_CLUSTER_PAGES 63
+#endif
+#endif
+
+
+/* Linked list connecting separate page allocations.  */
+typedef struct vlinks {
+  struct vlinks	*prev;		/* previous set of pages */
+  struct vlinks *next;		/* next set of pages */
+  union  page   *datum;		/* start of page */
+  unsigned long	 start_index;	/* starting index # of page */
+} vlinks_t;
+
+
+/* Virtual array header.  */
+typedef struct varray {
+  vlinks_t	*first;			/* first page link */
+  vlinks_t	*last;			/* last page link */
+  unsigned long	 num_allocated;		/* # objects allocated */
+  unsigned short object_size;		/* size in bytes of each object */
+  unsigned short objects_per_page;	/* # objects that can fit on a page */
+  unsigned short objects_last_page;	/* # objects allocated on last page */
+} varray_t;
+
+#ifndef MALLOC_CHECK
+#define OBJECTS_PER_PAGE(type) (PAGE_SIZE / sizeof (type))
+#else
+#define OBJECTS_PER_PAGE(type) ((sizeof (type) > 1) ? 1 : PAGE_SIZE)
+#endif
+
+#define INIT_VARRAY(type) {	/* macro to initialize a varray */	\
+  (vlinks_t *) 0,		/* first */				\
+  (vlinks_t *) 0,		/* last */				\
+  0,				/* num_allocated */			\
+  sizeof (type),		/* object_size */			\
+  OBJECTS_PER_PAGE (type),	/* objects_per_page */			\
+  OBJECTS_PER_PAGE (type),	/* objects_last_page */			\
+}
+
+#define INITIALIZE_VARRAY(x,type)			\
+do {							\
+  (x)->object_size = sizeof (type);			\
+  (x)->objects_per_page = OBJECTS_PER_PAGE (type);	\
+  (x)->objects_last_page = OBJECTS_PER_PAGE (type);	\
+} while (0)
+
+/* Master type for indexes within the symbol table.  */
+typedef unsigned long symint_t;
+
+
+/* Linked list support for nested scopes (file, block, structure, etc.).  */
+typedef struct scope {
+  struct scope	*prev;		/* previous scope level */
+  struct scope	*free;		/* free list pointer */
+  SYMR		*lsym;		/* pointer to local symbol node */
+  symint_t	 lnumber;	/* lsym index */
+  st_t		 type;		/* type of the node */
+} scope_t;
+
+
+/* Forward reference list for tags referenced, but not yet defined.  */
+typedef struct forward {
+  struct forward *next;		/* next forward reference */
+  struct forward *free;		/* free list pointer */
+  AUXU		 *ifd_ptr;	/* pointer to store file index */
+  AUXU		 *index_ptr;	/* pointer to store symbol index */
+  AUXU		 *type_ptr;	/* pointer to munge type info */
+} forward_t;
+
+
+/* Linked list support for tags.  The first tag in the list is always
+   the current tag for that block.  */
+typedef struct tag {
+  struct tag	 *free;		/* free list pointer */
+  struct shash	 *hash_ptr;	/* pointer to the hash table head */
+  struct tag	 *same_name;	/* tag with same name in outer scope */
+  struct tag	 *same_block;	/* next tag defined in the same block.  */
+  struct forward *forward_ref;	/* list of forward references */
+  bt_t		  basic_type;	/* bt_Struct, bt_Union, or bt_Enum */
+  symint_t	  ifd;		/* file # tag defined in */
+  symint_t	  indx;		/* index within file's local symbols */
+} tag_t;
+
+
+/* Head of a block's linked list of tags.  */
+typedef struct thead {
+  struct thead	*prev;		/* previous block */
+  struct thead	*free;		/* free list pointer */
+  struct tag	*first_tag;	/* first tag in block defined */
+} thead_t;
+
+
+/* Union containing pointers to each the small structures which are freed up.  */
+typedef union small_free {
+  scope_t	*f_scope;	/* scope structure */
+  thead_t	*f_thead;	/* tag head structure */
+  tag_t		*f_tag;		/* tag element structure */
+  forward_t	*f_forward;	/* forward tag reference */
+} small_free_t;
+
+
+/* String hash table support.  The size of the hash table must fit
+   within a page.  */
+
+#ifndef SHASH_SIZE
+#define SHASH_SIZE 1009
+#endif
+
+#define HASH_LEN_MAX ((1 << 12) - 1)	/* Max length we can store */
+
+typedef struct shash {
+  struct shash	*next;		/* next hash value */
+  char		*string;	/* string we are hashing */
+  symint_t	 len;		/* string length */
+  symint_t	 indx;		/* index within string table */
+  EXTR		*esym_ptr;	/* global symbol pointer */
+  SYMR		*sym_ptr;	/* local symbol pointer */
+  SYMR		*end_ptr;	/* symbol pointer to end block */
+  tag_t		*tag_ptr;	/* tag pointer */
+  PDR		*proc_ptr;	/* procedure descriptor pointer */
+} shash_t;
+
+
+/* Type hash table support.  The size of the hash table must fit
+   within a page with the other extended file descriptor information.
+   Because unique types which are hashed are fewer in number than
+   strings, we use a smaller hash value.  */
+
+#ifndef THASH_SIZE
+#define THASH_SIZE 113
+#endif
+
+typedef struct thash {
+  struct thash	*next;		/* next hash value */
+  AUXU		 type;		/* type we are hashing */
+  symint_t	 indx;		/* index within string table */
+} thash_t;
+
+
+/* Extended file descriptor that contains all of the support necessary
+   to add things to each file separately.  */
+typedef struct efdr {
+  FDR		 fdr;		/* File header to be written out */
+  FDR		*orig_fdr;	/* original file header */
+  char		*name;		/* filename */
+  int		 name_len;	/* length of the filename */
+  symint_t	 void_type;	/* aux. pointer to 'void' type */
+  symint_t	 int_type;	/* aux. pointer to 'int' type */
+  scope_t	*cur_scope;	/* current nested scopes */
+  symint_t	 file_index;	/* current file number */
+  int		 nested_scopes;	/* # nested scopes */
+  varray_t	 strings;	/* local strings */
+  varray_t	 symbols;	/* local symbols */
+  varray_t	 procs;		/* procedures */
+  varray_t	 aux_syms;	/* auxiliary symbols */
+  struct efdr	*next_file;	/* next file descriptor */
+				/* string/type hash tables */
+  shash_t      **shash_head;	/* string hash table */
+  thash_t	*thash_head[THASH_SIZE];
+} efdr_t;
+
+/* Pre-initialized extended file structure.  */
+static int init_file_initialized = 0;
+static efdr_t init_file;
+
+static efdr_t *first_file;			/* first file descriptor */
+static efdr_t **last_file_ptr = &first_file;	/* file descriptor tail */
+
+
+/* Union of various things that are held in pages.  */
+typedef union page {
+  char		byte	[ PAGE_SIZE ];
+  unsigned char	ubyte	[ PAGE_SIZE ];
+  efdr_t	file	[ PAGE_SIZE / sizeof (efdr_t)	 ];
+  FDR		ofile	[ PAGE_SIZE / sizeof (FDR)	 ];
+  PDR		proc	[ PAGE_SIZE / sizeof (PDR)	 ];
+  SYMR		sym	[ PAGE_SIZE / sizeof (SYMR)	 ];
+  EXTR		esym	[ PAGE_SIZE / sizeof (EXTR)	 ];
+  AUXU		aux	[ PAGE_SIZE / sizeof (AUXU)	 ];
+  DNR		dense	[ PAGE_SIZE / sizeof (DNR)	 ];
+  scope_t	scope	[ PAGE_SIZE / sizeof (scope_t)	 ];
+  vlinks_t	vlinks	[ PAGE_SIZE / sizeof (vlinks_t)	 ];
+  shash_t	shash	[ PAGE_SIZE / sizeof (shash_t)	 ];
+  thash_t	thash	[ PAGE_SIZE / sizeof (thash_t)	 ];
+  tag_t		tag	[ PAGE_SIZE / sizeof (tag_t)	 ];
+  forward_t	forward	[ PAGE_SIZE / sizeof (forward_t) ];
+  thead_t	thead	[ PAGE_SIZE / sizeof (thead_t)	 ];
+} page_t;
+
+
+/* Structure holding allocation information for small sized structures.  */
+typedef struct alloc_info {
+  const char	*alloc_name;	/* name of this allocation type (must be first) */
+  page_t	*cur_page;	/* current page being allocated from */
+  small_free_t	 free_list;	/* current free list if any */
+  int		 unallocated;	/* number of elements unallocated on page */
+  int		 total_alloc;	/* total number of allocations */
+  int		 total_free;	/* total number of frees */
+  int		 total_pages;	/* total number of pages allocated */
+} alloc_info_t;
+
+/* Type information collected together.  */
+typedef struct type_info {
+  bt_t	      basic_type;		/* basic type */
+  coff_type_t orig_type;		/* original COFF-based type */
+  int	      num_tq;			/* # type qualifiers */
+  int	      num_dims;			/* # dimensions */
+  int	      num_sizes;		/* # sizes */
+  int	      extra_sizes;		/* # extra sizes not tied with dims */
+  tag_t *     tag_ptr;			/* tag pointer */
+  int	      bitfield;			/* symbol is a bitfield */
+  int	      unknown_tag;		/* this is an unknown tag */
+  tq_t	      type_qualifiers[N_TQ];	/* type qualifiers (ptr, func, array)*/
+  symint_t    dimensions     [N_TQ];	/* dimensions for each array */
+  symint_t    sizes	     [N_TQ+2];	/* sizes of each array slice + size of
+					   struct/union/enum + bitfield size */
+} type_info_t;
+
+/* Pre-initialized type_info struct.  */
+static type_info_t type_info_init = {
+  bt_Nil,				/* basic type */
+  T_NULL,				/* original COFF-based type */
+  0,					/* # type qualifiers */
+  0,					/* # dimensions */
+  0,					/* # sizes */
+  0,					/* sizes not tied with dims */
+  NULL,					/* ptr to tag */
+  0,					/* bitfield */
+  0,					/* unknown tag */
+  {					/* type qualifiers */
+    tq_Nil,
+    tq_Nil,
+    tq_Nil,
+    tq_Nil,
+    tq_Nil,
+    tq_Nil,
+  },
+  {					/* dimensions */
+    0,
+    0,
+    0,
+    0,
+    0,
+    0
+  },
+  {					/* sizes */
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+  },
+};
+
+
+/* Global virtual arrays & hash table for external strings as well as
+   for the tags table and global tables for file descriptors, and
+   dense numbers.  */
+
+static varray_t file_desc	= INIT_VARRAY (efdr_t);
+static varray_t dense_num	= INIT_VARRAY (DNR);
+static varray_t tag_strings	= INIT_VARRAY (char);
+static varray_t ext_strings	= INIT_VARRAY (char);
+static varray_t ext_symbols	= INIT_VARRAY (EXTR);
+
+static shash_t	*orig_str_hash[SHASH_SIZE];
+static shash_t	*ext_str_hash [SHASH_SIZE];
+static shash_t	*tag_hash     [SHASH_SIZE];
+
+/* Static types for int and void.  Also, remember the last function's
+   type (which is set up when we encounter the declaration for the
+   function, and used when the end block for the function is emitted.  */
+
+static type_info_t int_type_info;
+static type_info_t void_type_info;
+static type_info_t last_func_type_info;
+static EXTR	  *last_func_eptr;
+
+
+/* Convert COFF basic type to ECOFF basic type.  The T_NULL type
+   really should use bt_Void, but this causes the current ecoff GDB to
+   issue unsupported type messages, and the Ultrix 4.00 dbx (aka MIPS
+   2.0) doesn't understand it, even though the compiler generates it.
+   Maybe this will be fixed in 2.10 or 2.20 of the MIPS compiler
+   suite, but for now go with what works.  */
+
+static const bt_t map_coff_types[ (int) T_MAX ] = {
+  bt_Nil,			/* T_NULL */
+  bt_Nil,			/* T_ARG */
+  bt_Char,			/* T_CHAR */
+  bt_Short,			/* T_SHORT */
+  bt_Int,			/* T_INT */
+  bt_Long,			/* T_LONG */
+  bt_Float,			/* T_FLOAT */
+  bt_Double,			/* T_DOUBLE */
+  bt_Struct,			/* T_STRUCT */
+  bt_Union,			/* T_UNION */
+  bt_Enum,			/* T_ENUM */
+  bt_Enum,			/* T_MOE */
+  bt_UChar,			/* T_UCHAR */
+  bt_UShort,			/* T_USHORT */
+  bt_UInt,			/* T_UINT */
+  bt_ULong			/* T_ULONG */
+};
+
+/* Convert COFF storage class to ECOFF storage class.  */
+static const sc_t map_coff_storage[ (int) C_MAX ] = {
+  sc_Nil,			/*   0: C_NULL */
+  sc_Abs,			/*   1: C_AUTO	  auto var */
+  sc_Undefined,			/*   2: C_EXT	  external */
+  sc_Data,			/*   3: C_STAT	  static */
+  sc_Register,			/*   4: C_REG	  register */
+  sc_Undefined,			/*   5: C_EXTDEF  ??? */
+  sc_Text,			/*   6: C_LABEL	  label */
+  sc_Text,			/*   7: C_ULABEL  user label */
+  sc_Info,			/*   8: C_MOS	  member of struct */
+  sc_Abs,			/*   9: C_ARG	  argument */
+  sc_Info,			/*  10: C_STRTAG  struct tag */
+  sc_Info,			/*  11: C_MOU	  member of union */
+  sc_Info,			/*  12: C_UNTAG   union tag */
+  sc_Info,			/*  13: C_TPDEF	  typedef */
+  sc_Data,			/*  14: C_USTATIC ??? */
+  sc_Info,			/*  15: C_ENTAG	  enum tag */
+  sc_Info,			/*  16: C_MOE	  member of enum */
+  sc_Register,			/*  17: C_REGPARM register parameter */
+  sc_Bits,			/*  18; C_FIELD	  bitfield */
+  sc_Nil,			/*  19 */
+  sc_Nil,			/*  20 */
+  sc_Nil,			/*  21 */
+  sc_Nil,			/*  22 */
+  sc_Nil,			/*  23 */
+  sc_Nil,			/*  24 */
+  sc_Nil,			/*  25 */
+  sc_Nil,			/*  26 */
+  sc_Nil,			/*  27 */
+  sc_Nil,			/*  28 */
+  sc_Nil,			/*  29 */
+  sc_Nil,			/*  30 */
+  sc_Nil,			/*  31 */
+  sc_Nil,			/*  32 */
+  sc_Nil,			/*  33 */
+  sc_Nil,			/*  34 */
+  sc_Nil,			/*  35 */
+  sc_Nil,			/*  36 */
+  sc_Nil,			/*  37 */
+  sc_Nil,			/*  38 */
+  sc_Nil,			/*  39 */
+  sc_Nil,			/*  40 */
+  sc_Nil,			/*  41 */
+  sc_Nil,			/*  42 */
+  sc_Nil,			/*  43 */
+  sc_Nil,			/*  44 */
+  sc_Nil,			/*  45 */
+  sc_Nil,			/*  46 */
+  sc_Nil,			/*  47 */
+  sc_Nil,			/*  48 */
+  sc_Nil,			/*  49 */
+  sc_Nil,			/*  50 */
+  sc_Nil,			/*  51 */
+  sc_Nil,			/*  52 */
+  sc_Nil,			/*  53 */
+  sc_Nil,			/*  54 */
+  sc_Nil,			/*  55 */
+  sc_Nil,			/*  56 */
+  sc_Nil,			/*  57 */
+  sc_Nil,			/*  58 */
+  sc_Nil,			/*  59 */
+  sc_Nil,			/*  60 */
+  sc_Nil,			/*  61 */
+  sc_Nil,			/*  62 */
+  sc_Nil,			/*  63 */
+  sc_Nil,			/*  64 */
+  sc_Nil,			/*  65 */
+  sc_Nil,			/*  66 */
+  sc_Nil,			/*  67 */
+  sc_Nil,			/*  68 */
+  sc_Nil,			/*  69 */
+  sc_Nil,			/*  70 */
+  sc_Nil,			/*  71 */
+  sc_Nil,			/*  72 */
+  sc_Nil,			/*  73 */
+  sc_Nil,			/*  74 */
+  sc_Nil,			/*  75 */
+  sc_Nil,			/*  76 */
+  sc_Nil,			/*  77 */
+  sc_Nil,			/*  78 */
+  sc_Nil,			/*  79 */
+  sc_Nil,			/*  80 */
+  sc_Nil,			/*  81 */
+  sc_Nil,			/*  82 */
+  sc_Nil,			/*  83 */
+  sc_Nil,			/*  84 */
+  sc_Nil,			/*  85 */
+  sc_Nil,			/*  86 */
+  sc_Nil,			/*  87 */
+  sc_Nil,			/*  88 */
+  sc_Nil,			/*  89 */
+  sc_Nil,			/*  90 */
+  sc_Nil,			/*  91 */
+  sc_Nil,			/*  92 */
+  sc_Nil,			/*  93 */
+  sc_Nil,			/*  94 */
+  sc_Nil,			/*  95 */
+  sc_Nil,			/*  96 */
+  sc_Nil,			/*  97 */
+  sc_Nil,			/*  98 */
+  sc_Nil,			/*  99 */
+  sc_Text,			/* 100: C_BLOCK  block start/end */
+  sc_Text,			/* 101: C_FCN	 function start/end */
+  sc_Info,			/* 102: C_EOS	 end of struct/union/enum */
+  sc_Nil,			/* 103: C_FILE	 file start */
+  sc_Nil,			/* 104: C_LINE	 line number */
+  sc_Nil,			/* 105: C_ALIAS	 combined type info */
+  sc_Nil,			/* 106: C_HIDDEN ??? */
+};
+
+/* Convert COFF storage class to ECOFF symbol type.  */
+static const st_t map_coff_sym_type[ (int) C_MAX ] = {
+  st_Nil,			/*   0: C_NULL */
+  st_Local,			/*   1: C_AUTO	  auto var */
+  st_Global,			/*   2: C_EXT	  external */
+  st_Static,			/*   3: C_STAT	  static */
+  st_Local,			/*   4: C_REG	  register */
+  st_Global,			/*   5: C_EXTDEF  ??? */
+  st_Label,			/*   6: C_LABEL	  label */
+  st_Label,			/*   7: C_ULABEL  user label */
+  st_Member,			/*   8: C_MOS	  member of struct */
+  st_Param,			/*   9: C_ARG	  argument */
+  st_Block,			/*  10: C_STRTAG  struct tag */
+  st_Member,			/*  11: C_MOU	  member of union */
+  st_Block,			/*  12: C_UNTAG   union tag */
+  st_Typedef,			/*  13: C_TPDEF	  typedef */
+  st_Static,			/*  14: C_USTATIC ??? */
+  st_Block,			/*  15: C_ENTAG	  enum tag */
+  st_Member,			/*  16: C_MOE	  member of enum */
+  st_Param,			/*  17: C_REGPARM register parameter */
+  st_Member,			/*  18; C_FIELD	  bitfield */
+  st_Nil,			/*  19 */
+  st_Nil,			/*  20 */
+  st_Nil,			/*  21 */
+  st_Nil,			/*  22 */
+  st_Nil,			/*  23 */
+  st_Nil,			/*  24 */
+  st_Nil,			/*  25 */
+  st_Nil,			/*  26 */
+  st_Nil,			/*  27 */
+  st_Nil,			/*  28 */
+  st_Nil,			/*  29 */
+  st_Nil,			/*  30 */
+  st_Nil,			/*  31 */
+  st_Nil,			/*  32 */
+  st_Nil,			/*  33 */
+  st_Nil,			/*  34 */
+  st_Nil,			/*  35 */
+  st_Nil,			/*  36 */
+  st_Nil,			/*  37 */
+  st_Nil,			/*  38 */
+  st_Nil,			/*  39 */
+  st_Nil,			/*  40 */
+  st_Nil,			/*  41 */
+  st_Nil,			/*  42 */
+  st_Nil,			/*  43 */
+  st_Nil,			/*  44 */
+  st_Nil,			/*  45 */
+  st_Nil,			/*  46 */
+  st_Nil,			/*  47 */
+  st_Nil,			/*  48 */
+  st_Nil,			/*  49 */
+  st_Nil,			/*  50 */
+  st_Nil,			/*  51 */
+  st_Nil,			/*  52 */
+  st_Nil,			/*  53 */
+  st_Nil,			/*  54 */
+  st_Nil,			/*  55 */
+  st_Nil,			/*  56 */
+  st_Nil,			/*  57 */
+  st_Nil,			/*  58 */
+  st_Nil,			/*  59 */
+  st_Nil,			/*  60 */
+  st_Nil,			/*  61 */
+  st_Nil,			/*  62 */
+  st_Nil,			/*  63 */
+  st_Nil,			/*  64 */
+  st_Nil,			/*  65 */
+  st_Nil,			/*  66 */
+  st_Nil,			/*  67 */
+  st_Nil,			/*  68 */
+  st_Nil,			/*  69 */
+  st_Nil,			/*  70 */
+  st_Nil,			/*  71 */
+  st_Nil,			/*  72 */
+  st_Nil,			/*  73 */
+  st_Nil,			/*  74 */
+  st_Nil,			/*  75 */
+  st_Nil,			/*  76 */
+  st_Nil,			/*  77 */
+  st_Nil,			/*  78 */
+  st_Nil,			/*  79 */
+  st_Nil,			/*  80 */
+  st_Nil,			/*  81 */
+  st_Nil,			/*  82 */
+  st_Nil,			/*  83 */
+  st_Nil,			/*  84 */
+  st_Nil,			/*  85 */
+  st_Nil,			/*  86 */
+  st_Nil,			/*  87 */
+  st_Nil,			/*  88 */
+  st_Nil,			/*  89 */
+  st_Nil,			/*  90 */
+  st_Nil,			/*  91 */
+  st_Nil,			/*  92 */
+  st_Nil,			/*  93 */
+  st_Nil,			/*  94 */
+  st_Nil,			/*  95 */
+  st_Nil,			/*  96 */
+  st_Nil,			/*  97 */
+  st_Nil,			/*  98 */
+  st_Nil,			/*  99 */
+  st_Block,			/* 100: C_BLOCK  block start/end */
+  st_Proc,			/* 101: C_FCN	 function start/end */
+  st_End,			/* 102: C_EOS	 end of struct/union/enum */
+  st_File,			/* 103: C_FILE	 file start */
+  st_Nil,			/* 104: C_LINE	 line number */
+  st_Nil,			/* 105: C_ALIAS	 combined type info */
+  st_Nil,			/* 106: C_HIDDEN ??? */
+};
+
+/* Map COFF derived types to ECOFF type qualifiers.  */
+static const tq_t map_coff_derived_type[ (int) DT_MAX ] = {
+  tq_Nil,			/* 0: DT_NON	no more qualifiers */
+  tq_Ptr,			/* 1: DT_PTR	pointer */
+  tq_Proc,			/* 2: DT_FCN	function */
+  tq_Array,			/* 3: DT_ARY	array */
+};
+
+
+/* Keep track of different sized allocation requests.  */
+static alloc_info_t alloc_counts[ (int) alloc_type_last ];
+
+
+/* Pointers and such to the original symbol table that is read in.  */
+static struct filehdr orig_file_header;		/* global object file header */
+
+static HDRR	 orig_sym_hdr;			/* symbolic header on input */
+static char	*orig_linenum;			/* line numbers */
+static DNR	*orig_dense;			/* dense numbers */
+static PDR	*orig_procs;			/* procedures */
+static SYMR	*orig_local_syms;		/* local symbols */
+static OPTR	*orig_opt_syms;			/* optimization symbols */
+static AUXU	*orig_aux_syms;			/* auxiliary symbols */
+static char	*orig_local_strs;		/* local strings */
+static char	*orig_ext_strs;			/* external strings */
+static FDR	*orig_files;			/* file descriptors */
+static symint_t	*orig_rfds;			/* relative file desc's */
+static EXTR	*orig_ext_syms;			/* external symbols */
+
+/* Macros to convert an index into a given object within the original
+   symbol table.  */
+#define CHECK(num,max,str) \
+  (((unsigned long) num > (unsigned long) max) ? out_of_bounds (num, max, str, __LINE__) : 0)
+
+#define ORIG_LINENUM(indx)	(CHECK ((indx), orig_sym_hdr.cbLine,    "line#"), (indx) + orig_linenum)
+#define ORIG_DENSE(indx)	(CHECK ((indx), orig_sym_hdr.idnMax,    "dense"), (indx) + orig_dense)
+#define ORIG_PROCS(indx)	(CHECK ((indx), orig_sym_hdr.ipdMax,    "procs"), (indx) + orig_procs)
+#define ORIG_FILES(indx)	(CHECK ((indx), orig_sym_hdr.ifdMax,    "funcs"), (indx) + orig_files)
+#define ORIG_LSYMS(indx)	(CHECK ((indx), orig_sym_hdr.isymMax,   "lsyms"), (indx) + orig_local_syms)
+#define ORIG_LSTRS(indx)	(CHECK ((indx), orig_sym_hdr.issMax,    "lstrs"), (indx) + orig_local_strs)
+#define ORIG_ESYMS(indx)	(CHECK ((indx), orig_sym_hdr.iextMax,   "esyms"), (indx) + orig_ext_syms)
+#define ORIG_ESTRS(indx)	(CHECK ((indx), orig_sym_hdr.issExtMax, "estrs"), (indx) + orig_ext_strs)
+#define ORIG_OPT(indx)		(CHECK ((indx), orig_sym_hdr.ioptMax,   "opt"),   (indx) + orig_opt_syms)
+#define ORIG_AUX(indx)		(CHECK ((indx), orig_sym_hdr.iauxMax,   "aux"),   (indx) + orig_aux_syms)
+#define ORIG_RFDS(indx)		(CHECK ((indx), orig_sym_hdr.crfd,      "rfds"),  (indx) + orig_rfds)
+
+/* Various other statics.  */
+static HDRR	symbolic_header;		/* symbolic header */
+static efdr_t  *cur_file_ptr	= (efdr_t *) 0;	/* current file desc. header */
+static PDR     *cur_proc_ptr	= (PDR *) 0;	/* current procedure header */
+static SYMR    *cur_oproc_begin	= (SYMR *) 0;	/* original proc. sym begin info */
+static SYMR    *cur_oproc_end	= (SYMR *) 0;	/* original proc. sym end info */
+static PDR     *cur_oproc_ptr	= (PDR *) 0;	/* current original procedure*/
+static thead_t *cur_tag_head	= (thead_t *) 0;/* current tag head */
+static unsigned long file_offset	= 0;	/* current file offset */
+static unsigned long max_file_offset	= 0;	/* maximum file offset */
+static FILE    *object_stream	= (FILE *) 0;	/* file desc. to output .o */
+static FILE    *obj_in_stream	= (FILE *) 0;	/* file desc. to input .o */
+static char    *progname	= (char *) 0;	/* program name for errors */
+static const char *input_name	= "stdin";	/* name of input file */
+static char    *object_name	= (char *) 0;	/* tmp. name of object file */
+static char    *obj_in_name	= (char *) 0;	/* name of input object file */
+static char    *cur_line_start	= (char *) 0;	/* current line read in */
+static char    *cur_line_ptr	= (char *) 0;	/* ptr within current line */
+static unsigned	cur_line_nbytes	= 0;		/* # bytes for current line */
+static unsigned	cur_line_alloc	= 0;		/* # bytes total in buffer */
+static long	line_number	= 0;		/* current input line number */
+static int	debug		= 0;		/* trace functions */
+static int	version		= 0;		/* print version # */
+static int	verbose		= 0;
+static int	had_errors	= 0;		/* != 0 if errors were found */
+static int	rename_output	= 0;		/* != 0 if rename output file*/
+static int	delete_input	= 0;		/* != 0 if delete input after done */
+static int	stabs_seen	= 0;		/* != 0 if stabs have been seen */
+
+
+/* Pseudo symbol to use when putting stabs into the symbol table.  */
+#ifndef STABS_SYMBOL
+#define STABS_SYMBOL "@stabs"
+#endif
+
+static const char stabs_symbol[] = STABS_SYMBOL;
+
+
+/* Forward reference for functions.  See the definition for more details.  */
+
+#ifndef STATIC
+#define STATIC static
+#endif
+
+STATIC int out_of_bounds (symint_t, symint_t, const char *, int);
+STATIC shash_t *hash_string (const char *, Ptrdiff_t, shash_t **, symint_t *);
+STATIC symint_t	add_string (varray_t *, shash_t **, const char *, const char *,
+			    shash_t **);
+STATIC symint_t	add_local_symbol (const char *, const char *, st_t, sc_t,
+				  symint_t, symint_t);
+STATIC symint_t	add_ext_symbol (EXTR *, int);
+STATIC symint_t	add_aux_sym_symint (symint_t);
+STATIC symint_t	add_aux_sym_rndx (int, symint_t);
+STATIC symint_t	add_aux_sym_tir (type_info_t *, hash_state_t, thash_t **);
+STATIC tag_t *	get_tag (const char *, const char *, symint_t, bt_t);
+STATIC void add_unknown_tag (tag_t *);
+STATIC void add_procedure (const char *, const char *);
+STATIC void initialize_init_file (void);
+STATIC void add_file (const char *, const char *);
+STATIC void add_bytes (varray_t *, char *, Size_t);
+STATIC void add_varray_page (varray_t *);
+STATIC void update_headers (void);
+STATIC void write_varray (varray_t *, off_t, const char *);
+STATIC void write_object (void);
+STATIC const char *st_to_string (st_t);
+STATIC const char *sc_to_string (sc_t);
+STATIC char *read_line (void);
+STATIC void parse_input (void);
+STATIC void mark_stabs (const char *);
+STATIC void parse_begin (const char *);
+STATIC void parse_bend (const char *);
+STATIC void parse_def (const char *);
+STATIC void parse_end (const char *);
+STATIC void parse_ent (const char *);
+STATIC void parse_file (const char *);
+STATIC void parse_stabs_common (const char *, const char *, const char *);
+STATIC void parse_stabs (const char *);
+STATIC void parse_stabn (const char *);
+STATIC page_t  *read_seek (Size_t, off_t, const char *);
+STATIC void copy_object (void);
+
+STATIC void catch_signal (int) ATTRIBUTE_NORETURN;
+STATIC page_t *allocate_page (void);
+STATIC page_t *allocate_multiple_pages (Size_t);
+STATIC void	free_multiple_pages (page_t *, Size_t);
+
+#ifndef MALLOC_CHECK
+STATIC page_t  *allocate_cluster (Size_t);
+#endif
+
+STATIC forward_t *allocate_forward (void);
+STATIC scope_t *allocate_scope (void);
+STATIC shash_t *allocate_shash (void);
+STATIC tag_t  *allocate_tag (void);
+STATIC thash_t *allocate_thash (void);
+STATIC thead_t *allocate_thead (void);
+STATIC vlinks_t *allocate_vlinks (void);
+
+STATIC void free_forward (forward_t *);
+STATIC void free_scope (scope_t *);
+STATIC void free_tag (tag_t *);
+STATIC void free_thead (thead_t *);
+
+extern char *optarg;
+extern int   optind;
+extern int   opterr;
+
+/* List of assembler pseudo ops and beginning sequences that need
+   special actions.  Someday, this should be a hash table, and such,
+   but for now a linear list of names and calls to memcmp will
+   do......  */
+
+typedef struct _pseudo_ops {
+  const char *const name;			/* pseudo-op in ascii */
+  const int len;				/* length of name to compare */
+  void (*const func) (const char *);	/* function to handle line */
+} pseudo_ops_t;
+
+static const pseudo_ops_t pseudo_ops[] = {
+  { "#.def",	sizeof("#.def")-1,	parse_def },
+  { "#.begin",	sizeof("#.begin")-1,	parse_begin },
+  { "#.bend",	sizeof("#.bend")-1,	parse_bend },
+  { ".end",	sizeof(".end")-1,	parse_end },
+  { ".ent",	sizeof(".ent")-1,	parse_ent },
+  { ".file",	sizeof(".file")-1,	parse_file },
+  { "#.stabs",	sizeof("#.stabs")-1,	parse_stabs },
+  { "#.stabn",	sizeof("#.stabn")-1,	parse_stabn },
+  { ".stabs",	sizeof(".stabs")-1,	parse_stabs },
+  { ".stabn",	sizeof(".stabn")-1,	parse_stabn },
+  { "#@stabs",	sizeof("#@stabs")-1,	mark_stabs },
+};
+
+
+/* Command line options for getopt_long.  */
+
+static const struct option options[] =
+{
+  { "version", 0, 0, 'V' },
+  { "verbose", 0, 0, 'v' },
+  { 0, 0, 0, 0 }
+};
+
+/* Add a page to a varray object.  */
+
+STATIC void
+add_varray_page (varray_t *vp)
+{
+  vlinks_t *new_links = allocate_vlinks ();
+
+#ifdef MALLOC_CHECK
+  if (vp->object_size > 1)
+    new_links->datum = xcalloc (1, vp->object_size);
+  else
+#endif
+    new_links->datum = allocate_page ();
+
+  alloc_counts[ (int) alloc_type_varray ].total_alloc++;
+  alloc_counts[ (int) alloc_type_varray ].total_pages++;
+
+  new_links->start_index = vp->num_allocated;
+  vp->objects_last_page = 0;
+
+  if (vp->first == (vlinks_t *) 0)		/* first allocation? */
+    vp->first = vp->last = new_links;
+  else
+    {						/* 2nd or greater allocation */
+      new_links->prev = vp->last;
+      vp->last->next = new_links;
+      vp->last = new_links;
+    }
+}
+
+
+/* Compute hash code (from tree.c) */
+
+#define HASHBITS 30
+
+STATIC shash_t *
+hash_string (const char *text, Ptrdiff_t hash_len, shash_t **hash_tbl,
+	     symint_t *ret_hash_index)
+{
+  unsigned long hi;
+  Ptrdiff_t i;
+  shash_t *ptr;
+  int first_ch = *text;
+
+  hi = hash_len;
+  for (i = 0; i < hash_len; i++)
+    hi = ((hi & 0x003fffff) * 613) + (text[i] & 0xff);
+
+  hi &= (1 << HASHBITS) - 1;
+  hi %= SHASH_SIZE;
+
+  if (ret_hash_index != (symint_t *) 0)
+    *ret_hash_index = hi;
+
+  for (ptr = hash_tbl[hi]; ptr != (shash_t *) 0; ptr = ptr->next)
+    if ((symint_t) hash_len == ptr->len
+	&& first_ch == ptr->string[0]
+	&& memcmp (text, ptr->string, hash_len) == 0)
+      break;
+
+  return ptr;
+}
+
+
+/* Add a string (and null pad) to one of the string tables.  A
+   consequence of hashing strings, is that we don't let strings cross
+   page boundaries.  The extra nulls will be ignored.  VP is a string
+   virtual array, HASH_TBL a pointer to the hash table, the string
+   starts at START and the position one byte after the string is given
+   with END_P1, the resulting hash pointer is returned in RET_HASH.  */
+
+STATIC symint_t
+add_string (varray_t *vp, shash_t **hash_tbl, const char *start,
+	    const char *end_p1, shash_t **ret_hash)
+{
+  Ptrdiff_t len = end_p1 - start;
+  shash_t *hash_ptr;
+  symint_t hi;
+
+  if (len >= (Ptrdiff_t) PAGE_USIZE)
+    fatal ("string too big (%ld bytes)", (long) len);
+
+  hash_ptr = hash_string (start, len, hash_tbl, &hi);
+  if (hash_ptr == (shash_t *) 0)
+    {
+      char *p;
+
+      if (vp->objects_last_page + len >= (long) PAGE_USIZE)
+	{
+	  vp->num_allocated
+	    = ((vp->num_allocated + PAGE_USIZE - 1) / PAGE_USIZE) * PAGE_USIZE;
+	  add_varray_page (vp);
+	}
+
+      hash_ptr = allocate_shash ();
+      hash_ptr->next = hash_tbl[hi];
+      hash_tbl[hi] = hash_ptr;
+
+      hash_ptr->len = len;
+      hash_ptr->indx = vp->num_allocated;
+      hash_ptr->string = p = & vp->last->datum->byte[ vp->objects_last_page ];
+
+      vp->objects_last_page += len+1;
+      vp->num_allocated += len+1;
+
+      while (len-- > 0)
+	*p++ = *start++;
+
+      *p = '\0';
+    }
+
+  if (ret_hash != (shash_t **) 0)
+    *ret_hash = hash_ptr;
+
+  return hash_ptr->indx;
+}
+
+
+/* Add a local symbol.  The symbol string starts at STR_START and the
+   first byte after it is marked by STR_END_P1.  The symbol has type
+   TYPE and storage class STORAGE and value VALUE.  INDX is an index
+   to local/aux. symbols.  */
+
+STATIC symint_t
+add_local_symbol (const char *str_start, const char *str_end_p1, st_t type,
+		  sc_t storage,  symint_t value, symint_t indx)
+{
+  symint_t ret;
+  SYMR *psym;
+  scope_t *pscope;
+  thead_t *ptag_head;
+  tag_t *ptag;
+  tag_t *ptag_next;
+  varray_t *vp = &cur_file_ptr->symbols;
+  int scope_delta = 0;
+  shash_t *hash_ptr = (shash_t *) 0;
+
+  if (vp->objects_last_page == vp->objects_per_page)
+    add_varray_page (vp);
+
+  psym = &vp->last->datum->sym[ vp->objects_last_page++ ];
+
+  psym->value = value;
+  psym->st = (unsigned) type;
+  psym->sc = (unsigned) storage;
+  psym->index = indx;
+  psym->iss = (str_start == (const char *) 0)
+		? 0
+		: add_string (&cur_file_ptr->strings,
+			      &cur_file_ptr->shash_head[0],
+			      str_start,
+			      str_end_p1,
+			      &hash_ptr);
+
+  ret = vp->num_allocated++;
+
+  if (MIPS_IS_STAB (psym))
+    return ret;
+
+  /* Save the symbol within the hash table if this is a static
+     item, and it has a name.  */
+  if (hash_ptr != (shash_t *) 0
+      && (type == st_Global || type == st_Static || type == st_Label
+	  || type == st_Proc || type == st_StaticProc))
+    hash_ptr->sym_ptr = psym;
+
+  /* push or pop a scope if appropriate.  */
+  switch (type)
+    {
+    default:
+      break;
+
+    case st_File:			/* beginning of file */
+    case st_Proc:			/* procedure */
+    case st_StaticProc:			/* static procedure */
+    case st_Block:			/* begin scope */
+      pscope = allocate_scope ();
+      pscope->prev = cur_file_ptr->cur_scope;
+      pscope->lsym = psym;
+      pscope->lnumber = ret;
+      pscope->type = type;
+      cur_file_ptr->cur_scope = pscope;
+
+      if (type != st_File)
+	scope_delta = 1;
+
+      /* For every block type except file, struct, union, or
+	 enumeration blocks, push a level on the tag stack.  We omit
+	 file types, so that tags can span file boundaries.  */
+      if (type != st_File && storage != sc_Info)
+	{
+	  ptag_head = allocate_thead ();
+	  ptag_head->first_tag = 0;
+	  ptag_head->prev = cur_tag_head;
+	  cur_tag_head = ptag_head;
+	}
+      break;
+
+    case st_End:
+      pscope = cur_file_ptr->cur_scope;
+      if (pscope == (scope_t *) 0)
+	error ("internal error, too many st_End's");
+
+      else
+	{
+	  st_t begin_type = (st_t) pscope->lsym->st;
+
+	  if (begin_type != st_File)
+	    scope_delta = -1;
+
+	  /* Except for file, structure, union, or enumeration end
+	     blocks remove all tags created within this scope.  */
+	  if (begin_type != st_File && storage != sc_Info)
+	    {
+	      ptag_head = cur_tag_head;
+	      cur_tag_head = ptag_head->prev;
+
+	      for (ptag = ptag_head->first_tag;
+		   ptag != (tag_t *) 0;
+		   ptag = ptag_next)
+		{
+		  if (ptag->forward_ref != (forward_t *) 0)
+		    add_unknown_tag (ptag);
+
+		  ptag_next = ptag->same_block;
+		  ptag->hash_ptr->tag_ptr = ptag->same_name;
+		  free_tag (ptag);
+		}
+
+	      free_thead (ptag_head);
+	    }
+
+	  cur_file_ptr->cur_scope = pscope->prev;
+	  psym->index = pscope->lnumber;	/* blk end gets begin sym # */
+
+	  if (storage != sc_Info)
+	    psym->iss = pscope->lsym->iss;	/* blk end gets same name */
+
+	  if (begin_type == st_File || begin_type == st_Block)
+	    pscope->lsym->index = ret+1;	/* block begin gets next sym # */
+
+	  /* Functions push two or more aux words as follows:
+	     1st word: index+1 of the end symbol
+	     2nd word: type of the function (plus any aux words needed).
+	     Also, tie the external pointer back to the function begin symbol.  */
+	  else
+	    {
+	      symint_t type;
+	      pscope->lsym->index = add_aux_sym_symint (ret+1);
+	      type = add_aux_sym_tir (&last_func_type_info,
+				      hash_no,
+				      &cur_file_ptr->thash_head[0]);
+	      if (last_func_eptr)
+		{
+		  last_func_eptr->ifd = cur_file_ptr->file_index;
+
+		  /* The index for an external st_Proc symbol is the index
+		     of the st_Proc symbol in the local symbol table.  */
+		  last_func_eptr->asym.index = psym->index;
+		}
+	    }
+
+	  free_scope (pscope);
+	}
+    }
+
+  cur_file_ptr->nested_scopes += scope_delta;
+
+  if (debug && type != st_File
+      && (debug > 2 || type == st_Block || type == st_End
+	  || type == st_Proc || type == st_StaticProc))
+    {
+      const char *sc_str = sc_to_string (storage);
+      const char *st_str = st_to_string (type);
+      int depth = cur_file_ptr->nested_scopes + (scope_delta < 0);
+
+      fprintf (stderr,
+	       "\tlsym\tv= %10ld, depth= %2d, sc= %-12s",
+	       value, depth, sc_str);
+
+      if (str_start && str_end_p1 - str_start > 0)
+	fprintf (stderr, " st= %-11s name= %.*s\n",
+		 st_str, (int) (str_end_p1 - str_start), str_start);
+      else
+	{
+	  Size_t len = strlen (st_str);
+	  fprintf (stderr, " st= %.*s\n", (int) (len-1), st_str);
+	}
+    }
+
+  return ret;
+}
+
+
+/* Add an external symbol with symbol pointer ESYM and file index
+   IFD.  */
+
+STATIC symint_t
+add_ext_symbol (EXTR *esym, int ifd)
+{
+  const char *str_start;		/* first byte in string */
+  const char *str_end_p1;		/* first byte after string */
+  EXTR *psym;
+  varray_t *vp = &ext_symbols;
+  shash_t *hash_ptr = (shash_t *) 0;
+
+  str_start = ORIG_ESTRS (esym->asym.iss);
+  str_end_p1 = str_start + strlen (str_start);
+
+  if (debug > 1)
+    {
+      long value = esym->asym.value;
+      const char *sc_str = sc_to_string (esym->asym.sc);
+      const char *st_str = st_to_string (esym->asym.st);
+
+      fprintf (stderr,
+	       "\tesym\tv= %10ld, ifd= %2d, sc= %-12s",
+	       value, ifd, sc_str);
+
+      if (str_start && str_end_p1 - str_start > 0)
+	fprintf (stderr, " st= %-11s name= %.*s\n",
+		 st_str, (int) (str_end_p1 - str_start), str_start);
+      else
+	fprintf (stderr, " st= %s\n", st_str);
+    }
+
+  if (vp->objects_last_page == vp->objects_per_page)
+    add_varray_page (vp);
+
+  psym = &vp->last->datum->esym[ vp->objects_last_page++ ];
+
+  *psym = *esym;
+  psym->ifd = ifd;
+  psym->asym.index = indexNil;
+  psym->asym.iss   = (str_start == (const char *) 0)
+			? 0
+			: add_string (&ext_strings,
+				      &ext_str_hash[0],
+				      str_start,
+				      str_end_p1,
+				      &hash_ptr);
+
+  hash_ptr->esym_ptr = psym;
+  return vp->num_allocated++;
+}
+
+
+/* Add an auxiliary symbol (passing a symint).  */
+
+STATIC symint_t
+add_aux_sym_symint (symint_t aux_word)
+{
+  AUXU *aux_ptr;
+  efdr_t *file_ptr = cur_file_ptr;
+  varray_t *vp = &file_ptr->aux_syms;
+
+  if (vp->objects_last_page == vp->objects_per_page)
+    add_varray_page (vp);
+
+  aux_ptr = &vp->last->datum->aux[ vp->objects_last_page++ ];
+  aux_ptr->isym = aux_word;
+
+  return vp->num_allocated++;
+}
+
+
+/* Add an auxiliary symbol (passing a file/symbol index combo).  */
+
+STATIC symint_t
+add_aux_sym_rndx (int file_index, symint_t sym_index)
+{
+  AUXU *aux_ptr;
+  efdr_t *file_ptr = cur_file_ptr;
+  varray_t *vp = &file_ptr->aux_syms;
+
+  if (vp->objects_last_page == vp->objects_per_page)
+    add_varray_page (vp);
+
+  aux_ptr = &vp->last->datum->aux[ vp->objects_last_page++ ];
+  aux_ptr->rndx.rfd   = file_index;
+  aux_ptr->rndx.index = sym_index;
+
+  return vp->num_allocated++;
+}
+
+
+/* Add an auxiliary symbol (passing the basic type and possibly
+   type qualifiers).  */
+
+STATIC symint_t
+add_aux_sym_tir (type_info_t *t, hash_state_t state, thash_t **hash_tbl)
+{
+  AUXU *aux_ptr;
+  efdr_t *file_ptr = cur_file_ptr;
+  varray_t *vp = &file_ptr->aux_syms;
+  static AUXU init_aux;
+  symint_t ret;
+  int i;
+  AUXU aux;
+
+  aux = init_aux;
+  aux.ti.bt = (int) t->basic_type;
+  aux.ti.continued = 0;
+  aux.ti.fBitfield = t->bitfield;
+
+  aux.ti.tq0 = (int) t->type_qualifiers[0];
+  aux.ti.tq1 = (int) t->type_qualifiers[1];
+  aux.ti.tq2 = (int) t->type_qualifiers[2];
+  aux.ti.tq3 = (int) t->type_qualifiers[3];
+  aux.ti.tq4 = (int) t->type_qualifiers[4];
+  aux.ti.tq5 = (int) t->type_qualifiers[5];
+
+
+  /* For anything that adds additional information, we must not hash,
+     so check here, and reset our state.  */
+
+  if (state != hash_no
+      && (t->type_qualifiers[0] == tq_Array
+	  || t->type_qualifiers[1] == tq_Array
+	  || t->type_qualifiers[2] == tq_Array
+	  || t->type_qualifiers[3] == tq_Array
+	  || t->type_qualifiers[4] == tq_Array
+	  || t->type_qualifiers[5] == tq_Array
+	  || t->basic_type == bt_Struct
+	  || t->basic_type == bt_Union
+	  || t->basic_type == bt_Enum
+	  || t->bitfield
+	  || t->num_dims > 0))
+    state = hash_no;
+
+  /* See if we can hash this type, and save some space, but some types
+     can't be hashed (because they contain arrays or continuations),
+     and others can be put into the hash list, but cannot use existing
+     types because other aux entries precede this one.  */
+
+  if (state != hash_no)
+    {
+      thash_t *hash_ptr;
+      symint_t hi;
+
+      hi = aux.isym & ((1 << HASHBITS) - 1);
+      hi %= THASH_SIZE;
+
+      for (hash_ptr = hash_tbl[hi];
+	   hash_ptr != (thash_t *) 0;
+	   hash_ptr = hash_ptr->next)
+	{
+	  if (aux.isym == hash_ptr->type.isym)
+	    break;
+	}
+
+      if (hash_ptr != (thash_t *) 0 && state == hash_yes)
+	return hash_ptr->indx;
+
+      if (hash_ptr == (thash_t *) 0)
+	{
+	  hash_ptr = allocate_thash ();
+	  hash_ptr->next = hash_tbl[hi];
+	  hash_ptr->type = aux;
+	  hash_ptr->indx = vp->num_allocated;
+	  hash_tbl[hi] = hash_ptr;
+	}
+    }
+
+  /* Everything is set up, add the aux symbol.  */
+  if (vp->objects_last_page == vp->objects_per_page)
+    add_varray_page (vp);
+
+  aux_ptr = &vp->last->datum->aux[ vp->objects_last_page++ ];
+  *aux_ptr = aux;
+
+  ret = vp->num_allocated++;
+
+  /* Add bitfield length if it exists.
+
+     NOTE:  Mips documentation claims bitfield goes at the end of the
+     AUX record, but the DECstation compiler emits it here.
+     (This would only make a difference for enum bitfields.)
+
+     Also note:  We use the last size given since gcc may emit 2
+     for an enum bitfield.  */
+
+  if (t->bitfield)
+    (void) add_aux_sym_symint ((symint_t) t->sizes[t->num_sizes-1]);
+
+
+  /* Add tag information if needed.  Structure, union, and enum
+     references add 2 aux symbols: a [file index, symbol index]
+     pointer to the structure type, and the current file index.  */
+
+  if (t->basic_type == bt_Struct
+      || t->basic_type == bt_Union
+      || t->basic_type == bt_Enum)
+    {
+      symint_t file_index = t->tag_ptr->ifd;
+      symint_t sym_index  = t->tag_ptr->indx;
+
+      if (t->unknown_tag)
+	{
+	  (void) add_aux_sym_rndx (ST_RFDESCAPE, sym_index);
+	  (void) add_aux_sym_symint ((symint_t)-1);
+	}
+      else if (sym_index != indexNil)
+	{
+	  (void) add_aux_sym_rndx (ST_RFDESCAPE, sym_index);
+	  (void) add_aux_sym_symint (file_index);
+	}
+      else
+	{
+	  forward_t *forward_ref = allocate_forward ();
+
+	  forward_ref->type_ptr = aux_ptr;
+	  forward_ref->next = t->tag_ptr->forward_ref;
+	  t->tag_ptr->forward_ref = forward_ref;
+
+	  (void) add_aux_sym_rndx (ST_RFDESCAPE, sym_index);
+	  forward_ref->index_ptr
+	    = &vp->last->datum->aux[ vp->objects_last_page - 1];
+
+	  (void) add_aux_sym_symint (file_index);
+	  forward_ref->ifd_ptr
+	    = &vp->last->datum->aux[ vp->objects_last_page - 1];
+	}
+    }
+
+  /* Add information about array bounds if they exist.  */
+  for (i = 0; i < t->num_dims; i++)
+    {
+      (void) add_aux_sym_rndx (ST_RFDESCAPE,
+			       cur_file_ptr->int_type);
+
+      (void) add_aux_sym_symint (cur_file_ptr->file_index);	/* file index*/
+      (void) add_aux_sym_symint ((symint_t) 0);			/* low bound */
+      (void) add_aux_sym_symint (t->dimensions[i] - 1);		/* high bound*/
+      (void) add_aux_sym_symint ((t->dimensions[i] == 0)	/* stride */
+			      ? 0
+			      : (t->sizes[i] * 8) / t->dimensions[i]);
+    };
+
+  /* NOTE:  Mips documentation claims that the bitfield width goes here.
+     But it needs to be emitted earlier.  */
+
+  return ret;
+}
+
+
+/* Add a tag to the tag table (unless it already exists).  */
+
+STATIC tag_t *
+get_tag (const char *tag_start,		/* 1st byte of tag name */
+	 const char *tag_end_p1,	/* 1st byte after tag name */
+	 symint_t indx,		/* index of tag start block */
+	 bt_t basic_type)		/* bt_Struct, bt_Union, or bt_Enum */
+
+{
+  shash_t *hash_ptr;
+  tag_t *tag_ptr;
+  hash_ptr = hash_string (tag_start,
+			  tag_end_p1 - tag_start,
+			  &tag_hash[0],
+			  (symint_t *) 0);
+
+  if (hash_ptr != (shash_t *) 0
+      && hash_ptr->tag_ptr != (tag_t *) 0)
+  {
+    tag_ptr = hash_ptr->tag_ptr;
+    if (indx != indexNil)
+      {
+	tag_ptr->basic_type = basic_type;
+	tag_ptr->ifd	    = cur_file_ptr->file_index;
+	tag_ptr->indx	    = indx;
+      }
+    return tag_ptr;
+  }
+
+  (void) add_string (&tag_strings,
+		     &tag_hash[0],
+		     tag_start,
+		     tag_end_p1,
+		     &hash_ptr);
+
+  tag_ptr = allocate_tag ();
+  tag_ptr->forward_ref	= (forward_t *) 0;
+  tag_ptr->hash_ptr	= hash_ptr;
+  tag_ptr->same_name	= hash_ptr->tag_ptr;
+  tag_ptr->basic_type	= basic_type;
+  tag_ptr->indx		= indx;
+  tag_ptr->ifd		= (indx == indexNil
+			   ? (symint_t) -1 : cur_file_ptr->file_index);
+  tag_ptr->same_block	= cur_tag_head->first_tag;
+
+  cur_tag_head->first_tag = tag_ptr;
+  hash_ptr->tag_ptr	  = tag_ptr;
+
+  return tag_ptr;
+}
+
+
+/* Add an unknown {struct, union, enum} tag.  */
+
+STATIC void
+add_unknown_tag (tag_t *ptag)
+{
+  shash_t *hash_ptr	= ptag->hash_ptr;
+  char *name_start	= hash_ptr->string;
+  char *name_end_p1	= name_start + hash_ptr->len;
+  forward_t *f_next	= ptag->forward_ref;
+  forward_t *f_cur;
+  int sym_index;
+  int file_index	= cur_file_ptr->file_index;
+
+  if (debug > 1)
+    {
+      const char *agg_type = "{unknown aggregate type}";
+      switch (ptag->basic_type)
+	{
+	case bt_Struct:	agg_type = "struct";	break;
+	case bt_Union:	agg_type = "union";	break;
+	case bt_Enum:	agg_type = "enum";	break;
+	default:				break;
+	}
+
+      fprintf (stderr, "unknown %s %.*s found\n",
+	       agg_type, (int) hash_ptr->len, name_start);
+    }
+
+  sym_index = add_local_symbol (name_start,
+				name_end_p1,
+				st_Block,
+				sc_Info,
+				(symint_t) 0,
+				(symint_t) 0);
+
+  (void) add_local_symbol (name_start,
+			   name_end_p1,
+			   st_End,
+			   sc_Info,
+			   (symint_t) 0,
+			   (symint_t) 0);
+
+  while (f_next != (forward_t *) 0)
+    {
+      f_cur  = f_next;
+      f_next = f_next->next;
+
+      f_cur->ifd_ptr->isym = file_index;
+      f_cur->index_ptr->rndx.index = sym_index;
+
+      free_forward (f_cur);
+    }
+
+  return;
+}
+
+
+/* Add a procedure to the current file's list of procedures, and record
+   this is the current procedure.  If the assembler created a PDR for
+   this procedure, use that to initialize the current PDR.  */
+
+STATIC void
+add_procedure (const char *func_start,  /* 1st byte of func name */
+	       const char *func_end_p1) /* 1st byte after func name */
+{
+  PDR *new_proc_ptr;
+  efdr_t *file_ptr = cur_file_ptr;
+  varray_t *vp = &file_ptr->procs;
+  symint_t value = 0;
+  st_t proc_type = st_Proc;
+  shash_t *shash_ptr = hash_string (func_start,
+				    func_end_p1 - func_start,
+				    &orig_str_hash[0],
+				    (symint_t *) 0);
+
+  if (debug)
+    fputc ('\n', stderr);
+
+  if (vp->objects_last_page == vp->objects_per_page)
+    add_varray_page (vp);
+
+  cur_proc_ptr = new_proc_ptr = &vp->last->datum->proc[ vp->objects_last_page++ ];
+
+  vp->num_allocated++;
+
+
+  /* Did the assembler create this procedure?  If so, get the PDR information.  */
+  cur_oproc_ptr = (PDR *) 0;
+  if (shash_ptr != (shash_t *) 0)
+    {
+      PDR *old_proc_ptr = shash_ptr->proc_ptr;
+      SYMR *sym_ptr = shash_ptr->sym_ptr;
+
+      if (old_proc_ptr != (PDR *) 0
+	  && sym_ptr != (SYMR *) 0
+	  && ((st_t) sym_ptr->st == st_Proc || (st_t) sym_ptr->st == st_StaticProc))
+	{
+	  cur_oproc_begin = sym_ptr;
+	  cur_oproc_end = shash_ptr->end_ptr;
+	  value = sym_ptr->value;
+
+	  cur_oproc_ptr = old_proc_ptr;
+	  proc_type = (st_t) sym_ptr->st;
+	  *new_proc_ptr = *old_proc_ptr;	/* initialize */
+	}
+    }
+
+  if (cur_oproc_ptr == (PDR *) 0)
+    error ("did not find a PDR block for %.*s",
+	   (int) (func_end_p1 - func_start), func_start);
+
+  /* Determine the start of symbols.  */
+  new_proc_ptr->isym = file_ptr->symbols.num_allocated;
+
+  /* Push the start of the function.  */
+  (void) add_local_symbol (func_start, func_end_p1,
+			   proc_type, sc_Text,
+			   value,
+			   (symint_t) 0);
+}
+
+
+/* Initialize the init_file structure.  */
+
+STATIC void
+initialize_init_file (void)
+{
+  union {
+    unsigned char c[4];
+    int i;
+  } endian_test;
+
+  memset (&init_file, 0, sizeof (init_file));
+
+  init_file.fdr.lang = langC;
+  init_file.fdr.fMerge = 1;
+  init_file.fdr.glevel = GLEVEL_2;
+
+  /* mips-tfile doesn't attempt to perform byte swapping and always writes
+     out integers in its native ordering.  For cross-compilers, this need
+     not be the same as either the host or the target.  The simplest thing
+     to do is skip the configury and perform an introspective test.  */
+  /* ??? Despite the name, mips-tfile is currently only used on alpha/Tru64
+     and would/may require significant work to be used in cross-compiler
+     configurations, so we could simply admit defeat and hard code this as
+     little-endian, i.e. init_file.fdr.fBigendian = 0.  */
+  endian_test.i = 1;
+  if (endian_test.c[3])
+    init_file.fdr.fBigendian = 1;
+
+  INITIALIZE_VARRAY (&init_file.strings, char);
+  INITIALIZE_VARRAY (&init_file.symbols, SYMR);
+  INITIALIZE_VARRAY (&init_file.procs, PDR);
+  INITIALIZE_VARRAY (&init_file.aux_syms, AUXU);
+
+  init_file_initialized = 1;
+}
+
+/* Add a new filename, and set up all of the file relative
+   virtual arrays (strings, symbols, aux syms, etc.).  Record
+   where the current file structure lives.  */
+
+STATIC void
+add_file (const char *file_start,  /* first byte in string */
+	  const char *file_end_p1) /* first byte after string */
+{
+  static char zero_bytes[2] = { '\0', '\0' };
+
+  Ptrdiff_t len = file_end_p1 - file_start;
+  int first_ch = *file_start;
+  efdr_t *file_ptr;
+
+  if (debug)
+    fprintf (stderr, "\tfile\t%.*s\n", (int) len, file_start);
+
+  /* See if the file has already been created.  */
+  for (file_ptr = first_file;
+       file_ptr != (efdr_t *) 0;
+       file_ptr = file_ptr->next_file)
+    {
+      if (first_ch == file_ptr->name[0]
+	  && file_ptr->name[len] == '\0'
+	  && memcmp (file_start, file_ptr->name, len) == 0)
+	{
+	  cur_file_ptr = file_ptr;
+	  break;
+	}
+    }
+
+  /* If this is a new file, create it.  */
+  if (file_ptr == (efdr_t *) 0)
+    {
+      if (file_desc.objects_last_page == file_desc.objects_per_page)
+	add_varray_page (&file_desc);
+
+      if (! init_file_initialized)
+	initialize_init_file ();
+
+      file_ptr = cur_file_ptr
+	= &file_desc.last->datum->file[ file_desc.objects_last_page++ ];
+      *file_ptr = init_file;
+
+      file_ptr->file_index = file_desc.num_allocated++;
+
+      /* Allocate the string hash table.  */
+      file_ptr->shash_head = (shash_t **) allocate_page ();
+
+      /* Make sure 0 byte in string table is null  */
+      add_string (&file_ptr->strings,
+		  &file_ptr->shash_head[0],
+		  &zero_bytes[0],
+		  &zero_bytes[0],
+		  (shash_t **) 0);
+
+      if (file_end_p1 - file_start > (long) PAGE_USIZE-2)
+	fatal ("filename goes over one page boundary");
+
+      /* Push the start of the filename. We assume that the filename
+         will be stored at string offset 1.  */
+      (void) add_local_symbol (file_start, file_end_p1, st_File, sc_Text,
+			       (symint_t) 0, (symint_t) 0);
+      file_ptr->fdr.rss = 1;
+      file_ptr->name = &file_ptr->strings.last->datum->byte[1];
+      file_ptr->name_len = file_end_p1 - file_start;
+
+      /* Update the linked list of file descriptors.  */
+      *last_file_ptr = file_ptr;
+      last_file_ptr = &file_ptr->next_file;
+
+      /* Add void & int types to the file (void should be first to catch
+	 errant 0's within the index fields).  */
+      file_ptr->void_type = add_aux_sym_tir (&void_type_info,
+					     hash_yes,
+					     &cur_file_ptr->thash_head[0]);
+
+      file_ptr->int_type = add_aux_sym_tir (&int_type_info,
+					    hash_yes,
+					    &cur_file_ptr->thash_head[0]);
+    }
+}
+
+
+/* Add a stream of random bytes to a varray.  */
+
+STATIC void
+add_bytes (varray_t *vp,	/* virtual array to add too */
+	   char *input_ptr,	/* start of the bytes */
+	   Size_t nitems)	/* # items to move */
+{
+  Size_t move_items;
+  Size_t move_bytes;
+  char *ptr;
+
+  while (nitems > 0)
+    {
+      if (vp->objects_last_page >= vp->objects_per_page)
+	add_varray_page (vp);
+
+      ptr = &vp->last->datum->byte[ vp->objects_last_page * vp->object_size ];
+      move_items = vp->objects_per_page - vp->objects_last_page;
+      if (move_items > nitems)
+	move_items = nitems;
+
+      move_bytes = move_items * vp->object_size;
+      nitems -= move_items;
+
+      if (move_bytes >= 32)
+	{
+	  (void) memcpy (ptr, input_ptr, move_bytes);
+	  input_ptr += move_bytes;
+	}
+      else
+	{
+	  while (move_bytes-- > 0)
+	    *ptr++ = *input_ptr++;
+	}
+    }
+}
+
+
+/* Convert storage class to string.  */
+
+STATIC const char *
+sc_to_string (sc_t storage_class)
+{
+  switch (storage_class)
+    {
+    case sc_Nil:	 return "Nil,";
+    case sc_Text:	 return "Text,";
+    case sc_Data:	 return "Data,";
+    case sc_Bss:	 return "Bss,";
+    case sc_Register:	 return "Register,";
+    case sc_Abs:	 return "Abs,";
+    case sc_Undefined:	 return "Undefined,";
+    case sc_CdbLocal:	 return "CdbLocal,";
+    case sc_Bits:	 return "Bits,";
+    case sc_CdbSystem:	 return "CdbSystem,";
+    case sc_RegImage:	 return "RegImage,";
+    case sc_Info:	 return "Info,";
+    case sc_UserStruct:	 return "UserStruct,";
+    case sc_SData:	 return "SData,";
+    case sc_SBss:	 return "SBss,";
+    case sc_RData:	 return "RData,";
+    case sc_Var:	 return "Var,";
+    case sc_Common:	 return "Common,";
+    case sc_SCommon:	 return "SCommon,";
+    case sc_VarRegister: return "VarRegister,";
+    case sc_Variant:	 return "Variant,";
+    case sc_SUndefined:	 return "SUndefined,";
+    case sc_Init:	 return "Init,";
+    case sc_Max:	 return "Max,";
+    }
+
+  return "???,";
+}
+
+
+/* Convert symbol type to string.  */
+
+STATIC const char *
+st_to_string (st_t symbol_type)
+{
+  switch (symbol_type)
+    {
+    case st_Nil:	return "Nil,";
+    case st_Global:	return "Global,";
+    case st_Static:	return "Static,";
+    case st_Param:	return "Param,";
+    case st_Local:	return "Local,";
+    case st_Label:	return "Label,";
+    case st_Proc:	return "Proc,";
+    case st_Block:	return "Block,";
+    case st_End:	return "End,";
+    case st_Member:	return "Member,";
+    case st_Typedef:	return "Typedef,";
+    case st_File:	return "File,";
+    case st_RegReloc:	return "RegReloc,";
+    case st_Forward:	return "Forward,";
+    case st_StaticProc:	return "StaticProc,";
+    case st_Constant:	return "Constant,";
+    case st_Str:	return "String,";
+    case st_Number:	return "Number,";
+    case st_Expr:	return "Expr,";
+    case st_Type:	return "Type,";
+    case st_Max:	return "Max,";
+    }
+
+  return "???,";
+}
+
+
+/* Read a line from standard input, and return the start of the buffer
+   (which is grows if the line is too big).  We split lines at the
+   semi-colon, and return each logical line independently.  */
+
+STATIC char *
+read_line (void)
+{
+  static   int line_split_p	= 0;
+  int string_p		= 0;
+  int comment_p	= 0;
+  int ch;
+  char *ptr;
+
+  if (cur_line_start == (char *) 0)
+    {				/* allocate initial page */
+      cur_line_start = (char *) allocate_page ();
+      cur_line_alloc = PAGE_SIZE;
+    }
+
+  if (!line_split_p)
+    line_number++;
+
+  line_split_p = 0;
+  cur_line_nbytes = 0;
+
+  for (ptr = cur_line_start; (ch = getchar ()) != EOF; *ptr++ = ch)
+    {
+      if (++cur_line_nbytes >= cur_line_alloc-1)
+	{
+	  int num_pages = cur_line_alloc / PAGE_SIZE;
+	  char *old_buffer = cur_line_start;
+
+	  cur_line_alloc += PAGE_SIZE;
+	  cur_line_start = (char *) allocate_multiple_pages (num_pages+1);
+	  memcpy (cur_line_start, old_buffer, num_pages * PAGE_SIZE);
+
+	  ptr = cur_line_start + cur_line_nbytes - 1;
+	}
+
+      if (ch == '\n')
+	{
+	  *ptr++ = '\n';
+	  *ptr = '\0';
+	  cur_line_ptr = cur_line_start;
+	  return cur_line_ptr;
+	}
+
+      else if (ch == '\0')
+	error ("null character found in input");
+
+      else if (!comment_p)
+	{
+	  if (ch == '"')
+	    string_p = !string_p;
+
+	  else if (ch == '#')
+	    comment_p++;
+
+	  else if (ch == ';' && !string_p)
+	    {
+	      line_split_p = 1;
+	      *ptr++ = '\n';
+	      *ptr = '\0';
+	      cur_line_ptr = cur_line_start;
+	      return cur_line_ptr;
+	    }
+	}
+    }
+
+  if (ferror (stdin))
+    pfatal_with_name (input_name);
+
+  cur_line_ptr = (char *) 0;
+  return (char *) 0;
+}
+
+
+/* Parse #.begin directives which have a label as the first argument
+   which gives the location of the start of the block.  */
+
+STATIC void
+parse_begin (const char *start)
+{
+  const char *end_p1;			/* end of label */
+  int ch;
+  shash_t *hash_ptr;			/* hash pointer to lookup label */
+
+  if (cur_file_ptr == (efdr_t *) 0)
+    {
+      error ("#.begin directive without a preceding .file directive");
+      return;
+    }
+
+  if (cur_proc_ptr == (PDR *) 0)
+    {
+      error ("#.begin directive without a preceding .ent directive");
+      return;
+    }
+
+  for (end_p1 = start; (ch = *end_p1) != '\0' && !ISSPACE (ch); end_p1++)
+    ;
+
+  hash_ptr = hash_string (start,
+			  end_p1 - start,
+			  &orig_str_hash[0],
+			  (symint_t *) 0);
+
+  if (hash_ptr == (shash_t *) 0)
+    {
+      error ("label %.*s not found for #.begin",
+	     (int) (end_p1 - start), start);
+      return;
+    }
+
+  if (cur_oproc_begin == (SYMR *) 0)
+    {
+      error ("procedure table %.*s not found for #.begin",
+	     (int) (end_p1 - start), start);
+      return;
+    }
+
+  (void) add_local_symbol ((const char *) 0, (const char *) 0,
+			   st_Block, sc_Text,
+			   (symint_t) hash_ptr->sym_ptr->value - cur_oproc_begin->value,
+			   (symint_t) 0);
+}
+
+
+/* Parse #.bend directives which have a label as the first argument
+   which gives the location of the end of the block.  */
+
+STATIC void
+parse_bend (const char *start)
+{
+  const char *end_p1;			/* end of label */
+  int ch;
+  shash_t *hash_ptr;			/* hash pointer to lookup label */
+
+  if (cur_file_ptr == (efdr_t *) 0)
+    {
+      error ("#.begin directive without a preceding .file directive");
+      return;
+    }
+
+  if (cur_proc_ptr == (PDR *) 0)
+    {
+      error ("#.bend directive without a preceding .ent directive");
+      return;
+    }
+
+  for (end_p1 = start; (ch = *end_p1) != '\0' && !ISSPACE (ch); end_p1++)
+    ;
+
+  hash_ptr = hash_string (start,
+			  end_p1 - start,
+			  &orig_str_hash[0],
+			  (symint_t *) 0);
+
+  if (hash_ptr == (shash_t *) 0)
+    {
+      error ("label %.*s not found for #.bend", (int) (end_p1 - start), start);
+      return;
+    }
+
+  if (cur_oproc_begin == (SYMR *) 0)
+    {
+      error ("procedure table %.*s not found for #.bend",
+	     (int) (end_p1 - start), start);
+      return;
+    }
+
+  (void) add_local_symbol ((const char *) 0, (const char *) 0,
+			   st_End, sc_Text,
+			   (symint_t) hash_ptr->sym_ptr->value - cur_oproc_begin->value,
+			   (symint_t) 0);
+}
+
+
+/* Parse #.def directives, which are contain standard COFF subdirectives
+   to describe the debugging format.  These subdirectives include:
+
+	.scl	specify storage class
+	.val	specify a value
+	.endef	specify end of COFF directives
+	.type	specify the type
+	.size	specify the size of an array
+	.dim	specify an array dimension
+	.tag	specify a tag for a struct, union, or enum.  */
+
+STATIC void
+parse_def (const char *name_start)
+{
+  const char *dir_start;			/* start of current directive*/
+  const char *dir_end_p1;			/* end+1 of current directive*/
+  const char *arg_start;			/* start of current argument */
+  const char *arg_end_p1;			/* end+1 of current argument */
+  const char *name_end_p1;			/* end+1 of label */
+  const char *tag_start	  = 0;			/* start of tag name */
+  const char *tag_end_p1  = 0;			/* end+1 of tag name */
+  sc_t storage_class	  = sc_Nil;
+  st_t symbol_type	  = st_Nil;
+  type_info_t t;
+  EXTR *eptr		  = (EXTR *) 0;		/* ext. sym equivalent to def*/
+  int is_function	  = 0;			/* != 0 if function */
+  symint_t value	  = 0;
+  symint_t indx		  = cur_file_ptr->void_type;
+  int error_line	  = 0;
+  symint_t arg_number;
+  symint_t temp_array[ N_TQ ];
+  int arg_was_number;
+  int ch, i;
+  Ptrdiff_t len;
+
+  static int inside_enumeration = 0;		/* is this an enumeration? */
+
+
+  /* Initialize the type information.  */
+  t = type_info_init;
+
+
+  /* Search for the end of the name being defined.  */
+  /* Allow spaces and such in names for G++ templates, which produce stabs
+     that look like:
+
+     #.def   SMANIP<long unsigned int>; .scl 10; .type 0x8; .size 8; .endef */
+
+  for (name_end_p1 = name_start; (ch = *name_end_p1) != ';' && ch != '\0'; name_end_p1++)
+    ;
+
+  if (ch == '\0')
+    {
+      error_line = __LINE__;
+      saber_stop ();
+      goto bomb_out;
+    }
+
+  /* Parse the remaining subdirectives now.  */
+  dir_start = name_end_p1+1;
+  for (;;)
+    {
+      while ((ch = *dir_start) == ' ' || ch == '\t')
+	++dir_start;
+
+      if (ch != '.')
+	{
+	  error_line = __LINE__;
+	  saber_stop ();
+	  goto bomb_out;
+	}
+
+      /* Are we done? */
+      if (dir_start[1] == 'e'
+	  && memcmp (dir_start, ".endef", sizeof (".endef")-1) == 0)
+	break;
+
+      /* Pick up the subdirective now.  */
+      for (dir_end_p1 = dir_start+1;
+	   (ch = *dir_end_p1) != ' ' && ch != '\t';
+	   dir_end_p1++)
+	{
+	  if (ch == '\0' || ISSPACE (ch))
+	    {
+	      error_line = __LINE__;
+	      saber_stop ();
+	      goto bomb_out;
+	    }
+	}
+
+      /* Pick up the subdirective argument now.  */
+      arg_was_number = arg_number = 0;
+      arg_end_p1 = 0;
+      arg_start = dir_end_p1+1;
+      ch = *arg_start;
+      while (ch == ' ' || ch == '\t')
+	ch = *++arg_start;
+
+      if (ISDIGIT (ch) || ch == '-' || ch == '+')
+	{
+	  int ch2;
+	  arg_number = strtol (arg_start, (char **) &arg_end_p1, 0);
+	  /* It's only a number if followed by ';' or ','. */
+	  if (arg_end_p1 != arg_start && (((ch2 = *arg_end_p1) == ';') || ch2 == ','))
+	    arg_was_number++;
+	}
+
+      else if (ch == '\0' || ISSPACE (ch))
+	{
+	  error_line = __LINE__;
+	  saber_stop ();
+	  goto bomb_out;
+	}
+
+      if (!arg_was_number)
+	{
+	  /* Allow spaces and such in names for G++ templates.  */
+	  for (arg_end_p1 = arg_start+1;
+	       (ch = *arg_end_p1) != ';' && ch != '\0';
+	       arg_end_p1++)
+	    ;
+
+	  if (ch == '\0')
+	    {
+	      error_line = __LINE__;
+	      saber_stop ();
+	      goto bomb_out;
+	    }
+	}
+
+      /* Classify the directives now.  */
+      len = dir_end_p1 - dir_start;
+      switch (dir_start[1])
+	{
+	default:
+	  error_line = __LINE__;
+	  saber_stop ();
+	  goto bomb_out;
+
+	case 'd':
+	  if (len == sizeof (".dim")-1
+	      && memcmp (dir_start, ".dim", sizeof (".dim")-1) == 0
+	      && arg_was_number)
+	    {
+	      symint_t *t_ptr = &temp_array[ N_TQ-1 ];
+
+	      *t_ptr = arg_number;
+	      while (*arg_end_p1 == ',' && arg_was_number)
+		{
+		  arg_start = arg_end_p1+1;
+		  ch = *arg_start;
+		  while (ch == ' ' || ch == '\t')
+		    ch = *++arg_start;
+
+		  arg_was_number = 0;
+		  if (ISDIGIT (ch) || ch == '-' || ch == '+')
+		    {
+		      int ch2;
+		      arg_number = strtol (arg_start, (char **) &arg_end_p1, 0);
+		      if (arg_end_p1 != arg_start && (((ch2 = *arg_end_p1) == ';') || ch2 == ','))
+			arg_was_number++;
+
+		      if (t_ptr == &temp_array[0])
+			{
+			  error_line = __LINE__;
+			  saber_stop ();
+			  goto bomb_out;
+			}
+
+		      *--t_ptr = arg_number;
+		    }
+		}
+
+	      /* Reverse order of dimensions.  */
+	      while (t_ptr <= &temp_array[ N_TQ-1 ])
+		{
+		  if (t.num_dims >= N_TQ-1)
+		    {
+		      error_line = __LINE__;
+		      saber_stop ();
+		      goto bomb_out;
+		    }
+
+		  t.dimensions[ t.num_dims++ ] = *t_ptr++;
+		}
+	      break;
+	    }
+	  else
+	    {
+	      error_line = __LINE__;
+	      saber_stop ();
+	      goto bomb_out;
+	    }
+
+
+	case 's':
+	  if (len == sizeof (".scl")-1
+	      && memcmp (dir_start, ".scl", sizeof (".scl")-1) == 0
+	      && arg_was_number
+	      && arg_number < ((symint_t) C_MAX))
+	    {
+	      /* If the symbol is a static or external, we have
+		 already gotten the appropriate type and class, so
+		 make sure we don't override those values.  This is
+		 needed because there are some type and classes that
+		 are not in COFF, such as short data, etc.  */
+	      if (symbol_type == st_Nil)
+		{
+		  symbol_type   = map_coff_sym_type[arg_number];
+		  storage_class = map_coff_storage [arg_number];
+		}
+	      break;
+	    }
+
+	  else if (len == sizeof (".size")-1
+		   && memcmp (dir_start, ".size", sizeof (".size")-1) == 0
+		   && arg_was_number)
+	    {
+	      symint_t *t_ptr = &temp_array[ N_TQ-1 ];
+
+	      *t_ptr = arg_number;
+	      while (*arg_end_p1 == ',' && arg_was_number)
+		{
+		  arg_start = arg_end_p1+1;
+		  ch = *arg_start;
+		  while (ch == ' ' || ch == '\t')
+		    ch = *++arg_start;
+
+		  arg_was_number = 0;
+		  if (ISDIGIT (ch) || ch == '-' || ch == '+')
+		    {
+		      int ch2;
+		      arg_number = strtol (arg_start, (char **) &arg_end_p1, 0);
+		      if (arg_end_p1 != arg_start && (((ch2 = *arg_end_p1) == ';') || ch2 == ','))
+			arg_was_number++;
+
+		      if (t_ptr == &temp_array[0])
+			{
+			  error_line = __LINE__;
+			  saber_stop ();
+			  goto bomb_out;
+			}
+
+		      *--t_ptr = arg_number;
+		    }
+		}
+
+	      /* Reverse order of sizes.  */
+	      while (t_ptr <= &temp_array[ N_TQ-1 ])
+		{
+		  if (t.num_sizes >= N_TQ-1)
+		    {
+		      error_line = __LINE__;
+		      saber_stop ();
+		      goto bomb_out;
+		    }
+
+		  t.sizes[ t.num_sizes++ ] = *t_ptr++;
+		}
+	      break;
+	    }
+
+	  else
+	    {
+	      error_line = __LINE__;
+	      saber_stop ();
+	      goto bomb_out;
+	    }
+
+
+	case 't':
+	  if (len == sizeof (".type")-1
+	      && memcmp (dir_start, ".type", sizeof (".type")-1) == 0
+	      && arg_was_number)
+	    {
+	      tq_t *tq_ptr = &t.type_qualifiers[0];
+
+	      t.orig_type = (coff_type_t) (arg_number & N_BTMASK);
+	      t.basic_type = map_coff_types [(int) t.orig_type];
+	      for (i = N_TQ-1; i >= 0; i--)
+		{
+		  int dt = (arg_number >> ((i * N_TQ_SHIFT) + N_BT_SHIFT)
+			    & N_TMASK);
+
+		  if (dt != (int) DT_NON)
+		    *tq_ptr++ = map_coff_derived_type [dt];
+		}
+
+	      /* If this is a function, ignore it, so that we don't get
+		 two entries (one from the .ent, and one for the .def
+		 that precedes it).  Save the type information so that
+		 the end block can properly add it after the begin block
+		 index.  For MIPS knows what reason, we must strip off
+		 the function type at this point.  */
+	      if (tq_ptr != &t.type_qualifiers[0] && tq_ptr[-1] == tq_Proc)
+		{
+		  is_function = 1;
+		  tq_ptr[-1] = tq_Nil;
+		}
+
+	      break;
+	    }
+
+	  else if (len == sizeof (".tag")-1
+	      && memcmp (dir_start, ".tag", sizeof (".tag")-1) == 0)
+	    {
+	      tag_start = arg_start;
+	      tag_end_p1 = arg_end_p1;
+	      break;
+	    }
+
+	  else
+	    {
+	      error_line = __LINE__;
+	      saber_stop ();
+	      goto bomb_out;
+	    }
+
+
+	case 'v':
+	  if (len == sizeof (".val")-1
+	      && memcmp (dir_start, ".val", sizeof (".val")-1) == 0)
+	    {
+	      if (arg_was_number)
+		value = arg_number;
+
+	      /* If the value is not an integer value, it must be the
+		 name of a static or global item.  Look up the name in
+		 the original symbol table to pick up the storage
+		 class, symbol type, etc.  */
+	      else
+		{
+		  shash_t *orig_hash_ptr;	/* hash within orig sym table*/
+		  shash_t *ext_hash_ptr;	/* hash within ext. sym table*/
+
+		  ext_hash_ptr = hash_string (arg_start,
+					      arg_end_p1 - arg_start,
+					      &ext_str_hash[0],
+					      (symint_t *) 0);
+
+		  if (ext_hash_ptr != (shash_t *) 0
+		      && ext_hash_ptr->esym_ptr != (EXTR *) 0)
+		    eptr = ext_hash_ptr->esym_ptr;
+
+		  orig_hash_ptr = hash_string (arg_start,
+					       arg_end_p1 - arg_start,
+					       &orig_str_hash[0],
+					       (symint_t *) 0);
+
+		  if ((orig_hash_ptr == (shash_t *) 0
+		       || orig_hash_ptr->sym_ptr == (SYMR *) 0)
+		      && eptr == (EXTR *) 0)
+		    {
+		      fprintf (stderr, "warning, %.*s not found in original or external symbol tables, value defaults to 0\n",
+			       (int) (arg_end_p1 - arg_start),
+			       arg_start);
+		      value = 0;
+		    }
+		  else
+		    {
+		      SYMR *ptr = (orig_hash_ptr != (shash_t *) 0
+				   && orig_hash_ptr->sym_ptr != (SYMR *) 0)
+					? orig_hash_ptr->sym_ptr
+					: &eptr->asym;
+
+		      symbol_type = (st_t) ptr->st;
+		      storage_class = (sc_t) ptr->sc;
+		      value = ptr->value;
+		    }
+		}
+	      break;
+	    }
+	  else
+	    {
+	      error_line = __LINE__;
+	      saber_stop ();
+	      goto bomb_out;
+	    }
+	}
+
+      /* Set up to find next directive.  */
+      dir_start = arg_end_p1 + 1;
+    }
+
+
+  if (storage_class == sc_Bits)
+    {
+      t.bitfield = 1;
+      t.extra_sizes = 1;
+    }
+  else
+    t.extra_sizes = 0;
+
+  if (t.num_dims > 0)
+    {
+      int num_real_sizes = t.num_sizes - t.extra_sizes;
+      int diff = t.num_dims - num_real_sizes;
+      int i = t.num_dims - 1;
+      int j;
+
+      if (num_real_sizes != 1 || diff < 0)
+	{
+	  error_line = __LINE__;
+	  saber_stop ();
+	  goto bomb_out;
+	}
+
+      /* If this is an array, make sure the same number of dimensions
+	 and sizes were passed, creating extra sizes for multiply
+	 dimensioned arrays if not passed.  */
+
+      if (diff)
+	{
+	  for (j = ARRAY_SIZE (t.sizes) - 1; j >= 0; j--)
+	    t.sizes[ j ] = ((j-diff) >= 0) ? t.sizes[ j-diff ] : 0;
+
+	  t.num_sizes = i + 1;
+	  for ( i--; i >= 0; i-- )
+	    {
+	      if (t.dimensions[ i+1 ])
+		t.sizes[ i ] = t.sizes[ i+1 ] / t.dimensions[ i+1 ];
+	      else
+		t.sizes[ i ] = t.sizes[ i+1 ];
+	    }
+	}
+    }
+
+  /* Except for enumeration members & begin/ending of scopes, put the
+     type word in the aux. symbol table.  */
+
+  if (symbol_type == st_Block || symbol_type == st_End)
+    indx = 0;
+
+  else if (inside_enumeration)
+    indx = cur_file_ptr->void_type;
+
+  else
+    {
+      if (t.basic_type == bt_Struct
+	  || t.basic_type == bt_Union
+	  || t.basic_type == bt_Enum)
+	{
+	  if (tag_start == (char *) 0)
+	    {
+	      error ("no tag specified for %.*s",
+		     (int) (name_end_p1 - name_start),
+		     name_start);
+	      return;
+	    }
+
+	  t.tag_ptr = get_tag (tag_start, tag_end_p1,  (symint_t) indexNil,
+			       t.basic_type);
+	}
+
+      if (is_function)
+	{
+	  last_func_type_info = t;
+	  last_func_eptr = eptr;
+	  return;
+	}
+
+      indx = add_aux_sym_tir (&t,
+			      hash_yes,
+			      &cur_file_ptr->thash_head[0]);
+    }
+
+
+  /* If this is an external or static symbol, update the appropriate
+     external symbol.  */
+
+  if (eptr != (EXTR *) 0
+      && (eptr->asym.index == indexNil || cur_proc_ptr == (PDR *) 0))
+    {
+      eptr->ifd = cur_file_ptr->file_index;
+      eptr->asym.index = indx;
+    }
+
+
+  /* Do any last minute adjustments that are necessary.  */
+  switch (symbol_type)
+    {
+    default:
+      break;
+
+
+      /* For the beginning of structs, unions, and enumerations, the
+	 size info needs to be passed in the value field.  */
+
+    case st_Block:
+      if (t.num_sizes - t.num_dims - t.extra_sizes != 1)
+	{
+	  error_line = __LINE__;
+	  saber_stop ();
+	  goto bomb_out;
+	}
+
+      else
+	value = t.sizes[0];
+
+      inside_enumeration = (t.orig_type == T_ENUM);
+      break;
+
+
+      /* For the end of structs, unions, and enumerations, omit the
+	 name which is always ".eos".  This needs to be done last, so
+	 that any error reporting above gives the correct name.  */
+
+    case st_End:
+      name_start = name_end_p1 = 0;
+      value = inside_enumeration = 0;
+      break;
+
+
+      /* Members of structures and unions that aren't bitfields, need
+	 to adjust the value from a byte offset to a bit offset.
+	 Members of enumerations do not have the value adjusted, and
+	 can be distinguished by indx == indexNil.  For enumerations,
+	 update the maximum enumeration value.  */
+
+    case st_Member:
+      if (!t.bitfield && !inside_enumeration)
+	value *= 8;
+
+      break;
+    }
+
+
+  /* Add the symbol, except for global symbols outside of functions,
+     for which the external symbol table is fine enough.  */
+
+  if (eptr == (EXTR *) 0
+      || eptr->asym.st == (int) st_Nil
+      || cur_proc_ptr != (PDR *) 0)
+    {
+      symint_t isym = add_local_symbol (name_start, name_end_p1,
+					symbol_type, storage_class,
+					value,
+					indx);
+
+      /* Deal with struct, union, and enum tags.  */
+      if (symbol_type == st_Block)
+        {
+	  /* Create or update the tag information.  */
+	  tag_t *tag_ptr = get_tag (name_start,
+				    name_end_p1,
+				    isym,
+				    t.basic_type);
+
+	  /* If there are any forward references, fill in the appropriate
+	     file and symbol indexes.  */
+
+	  symint_t file_index  = cur_file_ptr->file_index;
+	  forward_t *f_next = tag_ptr->forward_ref;
+	  forward_t *f_cur;
+
+	  while (f_next != (forward_t *) 0)
+	    {
+	      f_cur  = f_next;
+	      f_next = f_next->next;
+
+	      f_cur->ifd_ptr->isym = file_index;
+	      f_cur->index_ptr->rndx.index = isym;
+
+	      free_forward (f_cur);
+	    }
+
+	  tag_ptr->forward_ref = (forward_t *) 0;
+        }
+    }
+
+  /* Normal return  */
+  return;
+
+  /* Error return, issue message.  */
+bomb_out:
+  if (error_line)
+    error ("compiler error, badly formed #.def (internal line # = %d)", error_line);
+  else
+    error ("compiler error, badly formed #.def");
+
+  return;
+}
+
+
+/* Parse .end directives.  */
+
+STATIC void
+parse_end (const char *start)
+{
+  const char *start_func, *end_func_p1;
+  int ch;
+  symint_t value;
+  FDR *orig_fdr;
+
+  if (cur_file_ptr == (efdr_t *) 0)
+    {
+      error (".end directive without a preceding .file directive");
+      return;
+    }
+
+  if (cur_proc_ptr == (PDR *) 0)
+    {
+      error (".end directive without a preceding .ent directive");
+      return;
+    }
+
+  /* Get the function name, skipping whitespace.  */
+  for (start_func = start; ISSPACE ((unsigned char)*start_func); start_func++)
+    ;
+
+  ch = *start_func;
+  if (!IS_ASM_IDENT (ch))
+    {
+      error (".end directive has no name");
+      return;
+    }
+
+  for (end_func_p1 = start_func; IS_ASM_IDENT (ch); ch = *++end_func_p1)
+    ;
+
+
+  /* Get the value field for creating the end from the original object
+     file (which we find by locating the procedure start, and using the
+     pointer to the end+1 block and backing up.  The index points to a
+     two word aux. symbol, whose first word is the index of the end
+     symbol, and the second word is the type of the function return
+     value.  */
+
+  orig_fdr = cur_file_ptr->orig_fdr;
+  value = 0;
+  if (orig_fdr != (FDR *) 0 && cur_oproc_end != (SYMR *) 0)
+    value = cur_oproc_end->value;
+
+  else
+    error ("cannot find .end block for %.*s",
+	   (int) (end_func_p1 - start_func), start_func);
+
+  (void) add_local_symbol (start_func, end_func_p1,
+			   st_End, sc_Text,
+			   value,
+			   (symint_t) 0);
+
+  cur_proc_ptr = cur_oproc_ptr = (PDR *) 0;
+}
+
+
+/* Parse .ent directives.  */
+
+STATIC void
+parse_ent (const char *start)
+{
+  const char *start_func, *end_func_p1;
+  int ch;
+
+  if (cur_file_ptr == (efdr_t *) 0)
+    {
+      error (".ent directive without a preceding .file directive");
+      return;
+    }
+
+  if (cur_proc_ptr != (PDR *) 0)
+    {
+      error ("second .ent directive found before .end directive");
+      return;
+    }
+
+  for (start_func = start; ISSPACE ((unsigned char)*start_func); start_func++)
+    ;
+
+  ch = *start_func;
+  if (!IS_ASM_IDENT (ch))
+    {
+      error (".ent directive has no name");
+      return;
+    }
+
+  for (end_func_p1 = start_func; IS_ASM_IDENT (ch); ch = *++end_func_p1)
+    ;
+
+  (void) add_procedure (start_func, end_func_p1);
+}
+
+
+/* Parse .file directives.  */
+
+STATIC void
+parse_file (const char *start)
+{
+  char *p;
+  char *start_name, *end_name_p1;
+
+  (void) strtol (start, &p, 0);
+  if (start == p
+      || (start_name = strchr (p, '"')) == (char *) 0
+      || (end_name_p1 = strrchr (++start_name, '"')) == (char *) 0)
+    {
+      error ("invalid .file directive");
+      return;
+    }
+
+  if (cur_proc_ptr != (PDR *) 0)
+    {
+      error ("no way to handle .file within .ent/.end section");
+      return;
+    }
+
+  add_file (start_name, end_name_p1);
+}
+
+
+/* Make sure the @stabs symbol is emitted.  */
+
+static void
+mark_stabs (const char *start ATTRIBUTE_UNUSED)
+{
+  if (!stabs_seen)
+    {
+      /* Add a dummy @stabs symbol.  */
+      stabs_seen = 1;
+      (void) add_local_symbol (stabs_symbol,
+			       stabs_symbol + sizeof (stabs_symbol),
+			       stNil, scInfo, -1, MIPS_MARK_STAB (0));
+
+    }
+}
+
+
+/* Parse .stabs directives.
+
+   .stabs directives have five fields:
+	"string"	a string, encoding the type information.
+	code		a numeric code, defined in <stab.h>
+	0		a zero
+	0		a zero or line number
+	value		a numeric value or an address.
+
+    If the value is relocatable, we transform this into:
+	iss		points as an index into string space
+	value		value from lookup of the name
+	st		st from lookup of the name
+	sc		sc from lookup of the name
+	index		code|CODE_MASK
+
+    If the value is not relocatable, we transform this into:
+	iss		points as an index into string space
+	value		value
+	st		st_Nil
+	sc		sc_Nil
+	index		code|CODE_MASK
+
+    .stabn directives have four fields (string is null):
+	code		a numeric code, defined in <stab.h>
+	0		a zero
+	0		a zero or a line number
+	value		a numeric value or an address.  */
+
+STATIC void
+parse_stabs_common (const char *string_start,	/* start of string or NULL */
+		    const char *string_end,	/* end+1 of string or NULL */
+		    const char *rest)		/* rest of the directive.  */
+{
+  efdr_t *save_file_ptr = cur_file_ptr;
+  symint_t code;
+  symint_t value;
+  char *p;
+  st_t st;
+  sc_t sc;
+  int ch;
+
+  if (stabs_seen == 0)
+    mark_stabs ("");
+
+  /* Read code from stabs.  */
+  if (!ISDIGIT (*rest))
+    {
+      error ("invalid .stabs/.stabn directive, code is non-numeric");
+      return;
+    }
+
+  code = strtol (rest, &p, 0);
+
+  /* Line number stabs are handled differently, since they have two values,
+     the line number and the address of the label.  We use the index field
+     (aka code) to hold the line number, and the value field to hold the
+     address.  The symbol type is st_Label, which should be different from
+     the other stabs, so that gdb can recognize it.  */
+
+  if (code == (int) N_SLINE)
+    {
+      SYMR *sym_ptr, dummy_symr;
+      shash_t *shash_ptr;
+
+      /* Skip ,0, */
+      if (p[0] != ',' || p[1] != '0' || p[2] != ',' || !ISDIGIT (p[3]))
+	{
+	  error ("invalid line number .stabs/.stabn directive");
+	  return;
+	}
+
+      code = strtol (p+3, &p, 0);
+      ch = *++p;
+      if (p[-1] != ',' || ISDIGIT (ch) || !IS_ASM_IDENT (ch))
+	{
+	  error ("invalid line number .stabs/.stabn directive");
+	  return;
+	}
+
+      dummy_symr.index = code;
+      if (dummy_symr.index != code)
+	{
+	  error ("line number (%lu) for .stabs/.stabn directive cannot fit in index field (20 bits)",
+		 code);
+
+	  return;
+	}
+
+      shash_ptr = hash_string (p,
+			       strlen (p) - 1,
+			       &orig_str_hash[0],
+			       (symint_t *) 0);
+
+      if (shash_ptr == (shash_t *) 0
+	  || (sym_ptr = shash_ptr->sym_ptr) == (SYMR *) 0)
+	{
+	  error ("invalid .stabs/.stabn directive, value not found");
+	  return;
+	}
+
+      if ((st_t) sym_ptr->st != st_Label)
+	{
+	  error ("invalid line number .stabs/.stabn directive");
+	  return;
+	}
+
+      st = st_Label;
+      sc = (sc_t) sym_ptr->sc;
+      value = sym_ptr->value;
+    }
+  else
+    {
+      /* Skip ,<num>,<num>, */
+      if (*p++ != ',')
+	goto failure;
+      for (; ISDIGIT (*p); p++)
+	;
+      if (*p++ != ',')
+	goto failure;
+      for (; ISDIGIT (*p); p++)
+	;
+      if (*p++ != ',')
+	goto failure;
+      ch = *p;
+      if (!IS_ASM_IDENT (ch) && ch != '-')
+	{
+	failure:
+	  error ("invalid .stabs/.stabn directive, bad character");
+	  return;
+	}
+
+      if (ISDIGIT (ch) || ch == '-')
+	{
+	  st = st_Nil;
+	  sc = sc_Nil;
+	  value = strtol (p, &p, 0);
+	  if (*p != '\n')
+	    {
+	      error ("invalid .stabs/.stabn directive, stuff after numeric value");
+	      return;
+	    }
+	}
+      else if (!IS_ASM_IDENT (ch))
+	{
+	  error ("invalid .stabs/.stabn directive, bad character");
+	  return;
+	}
+      else
+	{
+	  SYMR *sym_ptr;
+	  shash_t *shash_ptr;
+	  const char *start, *end_p1;
+
+	  start = p;
+	  if ((end_p1 = strchr (start, '+')) == (char *) 0)
+	    {
+	      if ((end_p1 = strchr (start, '-')) == (char *) 0)
+		end_p1 = start + strlen (start) - 1;
+	    }
+
+	  shash_ptr = hash_string (start,
+				   end_p1 - start,
+				   &orig_str_hash[0],
+				   (symint_t *) 0);
+
+	  if (shash_ptr == (shash_t *) 0
+	      || (sym_ptr = shash_ptr->sym_ptr) == (SYMR *) 0)
+	    {
+	      shash_ptr = hash_string (start,
+				       end_p1 - start,
+				       &ext_str_hash[0],
+				       (symint_t *) 0);
+
+	      if (shash_ptr == (shash_t *) 0
+		  || shash_ptr->esym_ptr == (EXTR *) 0)
+		{
+		  error ("invalid .stabs/.stabn directive, value not found");
+		  return;
+		}
+	      else
+		sym_ptr = &(shash_ptr->esym_ptr->asym);
+	    }
+
+	  /* Traditionally, N_LBRAC and N_RBRAC are *not* relocated.  */
+	  if (code == (int) N_LBRAC || code == (int) N_RBRAC)
+	    {
+	      sc = scNil;
+	      st = stNil;
+	    }
+	  else
+	    {
+	      sc = (sc_t) sym_ptr->sc;
+	      st = (st_t) sym_ptr->st;
+	    }
+	  value = sym_ptr->value;
+
+	  ch = *end_p1++;
+	  if (ch != '\n')
+	    {
+	      if (((!ISDIGIT (*end_p1)) && (*end_p1 != '-'))
+		  || ((ch != '+') && (ch != '-')))
+		{
+		  error ("invalid .stabs/.stabn directive, badly formed value");
+		  return;
+		}
+	      if (ch == '+')
+		value += strtol (end_p1, &p, 0);
+	      else if (ch == '-')
+		value -= strtol (end_p1, &p, 0);
+
+	      if (*p != '\n')
+		{
+		  error ("invalid .stabs/.stabn directive, stuff after numeric value");
+		  return;
+		}
+	    }
+	}
+      code = MIPS_MARK_STAB (code);
+    }
+
+  (void) add_local_symbol (string_start, string_end, st, sc, value, code);
+  /* Restore normal file type.  */
+  cur_file_ptr = save_file_ptr;
+}
+
+
+STATIC void
+parse_stabs (const char *start)
+{
+  const char *end = strchr (start+1, '"');
+
+  if (*start != '"' || end == (const char *) 0 || end[1] != ',')
+    {
+      error ("invalid .stabs directive, no string");
+      return;
+    }
+
+  parse_stabs_common (start+1, end, end+2);
+}
+
+
+STATIC void
+parse_stabn (const char *start)
+{
+  parse_stabs_common ((const char *) 0, (const char *) 0, start);
+}
+
+
+/* Parse the input file, and write the lines to the output file
+   if needed.  */
+
+STATIC void
+parse_input (void)
+{
+  char *p;
+  Size_t i;
+  thead_t *ptag_head;
+  tag_t *ptag;
+  tag_t *ptag_next;
+
+  if (debug)
+    fprintf (stderr, "\tinput\n");
+
+  /* Add a dummy scope block around the entire compilation unit for
+     structures defined outside of blocks.  */
+  ptag_head = allocate_thead ();
+  ptag_head->first_tag = 0;
+  ptag_head->prev = cur_tag_head;
+  cur_tag_head = ptag_head;
+
+  while ((p = read_line ()) != (char *) 0)
+    {
+      /* Skip leading blanks.  */
+      while (ISSPACE ((unsigned char)*p))
+	p++;
+
+      /* See if it's a directive we handle.  If so, dispatch handler.  */
+      for (i = 0; i < ARRAY_SIZE (pseudo_ops); i++)
+	if (memcmp (p, pseudo_ops[i].name, pseudo_ops[i].len) == 0
+	    && ISSPACE ((unsigned char)(p[pseudo_ops[i].len])))
+	  {
+	    p += pseudo_ops[i].len;	/* skip to first argument */
+	    while (ISSPACE ((unsigned char)*p))
+	      p++;
+
+	    (*pseudo_ops[i].func)( p );
+	    break;
+	  }
+    }
+
+  /* Process any tags at global level.  */
+  ptag_head = cur_tag_head;
+  cur_tag_head = ptag_head->prev;
+
+  for (ptag = ptag_head->first_tag;
+       ptag != (tag_t *) 0;
+       ptag = ptag_next)
+    {
+      if (ptag->forward_ref != (forward_t *) 0)
+	add_unknown_tag (ptag);
+
+      ptag_next = ptag->same_block;
+      ptag->hash_ptr->tag_ptr = ptag->same_name;
+      free_tag (ptag);
+    }
+
+  free_thead (ptag_head);
+
+}
+
+
+/* Update the global headers with the final offsets in preparation
+   to write out the .T file.  */
+
+STATIC void
+update_headers (void)
+{
+  symint_t i;
+  efdr_t *file_ptr;
+
+  /* Set up the symbolic header.  */
+  file_offset = sizeof (symbolic_header) + orig_file_header.f_symptr;
+  symbolic_header.magic = orig_sym_hdr.magic;
+  symbolic_header.vstamp = orig_sym_hdr.vstamp;
+
+  /* Set up global counts.  */
+  symbolic_header.issExtMax = ext_strings.num_allocated;
+  symbolic_header.idnMax    = dense_num.num_allocated;
+  symbolic_header.ifdMax    = file_desc.num_allocated;
+  symbolic_header.iextMax   = ext_symbols.num_allocated;
+  symbolic_header.ilineMax  = orig_sym_hdr.ilineMax;
+  symbolic_header.ioptMax   = orig_sym_hdr.ioptMax;
+  symbolic_header.cbLine    = orig_sym_hdr.cbLine;
+  symbolic_header.crfd      = orig_sym_hdr.crfd;
+
+
+  /* Loop through each file, figuring out how many local syms,
+     line numbers, etc. there are.  Also, put out end symbol
+     for the filename.  */
+
+  for (file_ptr = first_file;
+       file_ptr != (efdr_t *) 0;
+       file_ptr = file_ptr->next_file)
+    {
+      SYMR *sym_start;
+      SYMR *sym;
+      SYMR *sym_end_p1;
+      FDR *fd_ptr = file_ptr->orig_fdr;
+
+      cur_file_ptr = file_ptr;
+
+      /* Copy st_Static symbols from the original local symbol table if
+	 they did not get added to the new local symbol table.
+	 This happens with stabs-in-ecoff or if the source file is
+	 compiled without debugging.  */
+      sym_start = ORIG_LSYMS (fd_ptr->isymBase);
+      sym_end_p1 = sym_start + fd_ptr->csym;
+      for (sym = sym_start; sym < sym_end_p1; sym++)
+	{
+	  if ((st_t) sym->st == st_Static)
+	    {
+	      char *str = ORIG_LSTRS (fd_ptr->issBase + sym->iss);
+	      Size_t len = strlen (str);
+	      shash_t *hash_ptr;
+
+	      /* Ignore internal labels.  */
+	      if (str[0] == '$' && str[1] == 'L')
+		continue;
+	      hash_ptr = hash_string (str,
+				      (Ptrdiff_t) len,
+				      &file_ptr->shash_head[0],
+				      (symint_t *) 0);
+	      if (hash_ptr == (shash_t *) 0)
+		{
+		  (void) add_local_symbol (str, str + len,
+					   (st_t) sym->st, (sc_t) sym->sc,
+					   (symint_t) sym->value,
+					   (symint_t) indexNil);
+		}
+	    }
+	}
+      (void) add_local_symbol ((const char *) 0, (const char *) 0,
+			       st_End, sc_Text,
+			       (symint_t) 0,
+			       (symint_t) 0);
+
+      file_ptr->fdr.cpd = file_ptr->procs.num_allocated;
+      file_ptr->fdr.ipdFirst = symbolic_header.ipdMax;
+      symbolic_header.ipdMax += file_ptr->fdr.cpd;
+
+      file_ptr->fdr.csym = file_ptr->symbols.num_allocated;
+      file_ptr->fdr.isymBase = symbolic_header.isymMax;
+      symbolic_header.isymMax += file_ptr->fdr.csym;
+
+      file_ptr->fdr.caux = file_ptr->aux_syms.num_allocated;
+      file_ptr->fdr.iauxBase = symbolic_header.iauxMax;
+      symbolic_header.iauxMax += file_ptr->fdr.caux;
+
+      file_ptr->fdr.cbSs = file_ptr->strings.num_allocated;
+      file_ptr->fdr.issBase = symbolic_header.issMax;
+      symbolic_header.issMax += file_ptr->fdr.cbSs;
+    }
+
+#ifndef ALIGN_SYMTABLE_OFFSET
+#define ALIGN_SYMTABLE_OFFSET(OFFSET) (OFFSET)
+#endif
+
+  file_offset = ALIGN_SYMTABLE_OFFSET (file_offset);
+  i = WORD_ALIGN (symbolic_header.cbLine);	/* line numbers */
+  if (i > 0)
+    {
+      symbolic_header.cbLineOffset = file_offset;
+      file_offset += i;
+      file_offset = ALIGN_SYMTABLE_OFFSET (file_offset);
+    }
+
+  i = symbolic_header.ioptMax;			/* optimization symbols */
+  if (((long) i) > 0)
+    {
+      symbolic_header.cbOptOffset = file_offset;
+      file_offset += i * sizeof (OPTR);
+      file_offset = ALIGN_SYMTABLE_OFFSET (file_offset);
+    }
+
+  i = symbolic_header.idnMax;			/* dense numbers */
+  if (i > 0)
+    {
+      symbolic_header.cbDnOffset = file_offset;
+      file_offset += i * sizeof (DNR);
+      file_offset = ALIGN_SYMTABLE_OFFSET (file_offset);
+    }
+
+  i = symbolic_header.ipdMax;			/* procedure tables */
+  if (i > 0)
+    {
+      symbolic_header.cbPdOffset = file_offset;
+      file_offset += i * sizeof (PDR);
+      file_offset = ALIGN_SYMTABLE_OFFSET (file_offset);
+    }
+
+  i = symbolic_header.isymMax;			/* local symbols */
+  if (i > 0)
+    {
+      symbolic_header.cbSymOffset = file_offset;
+      file_offset += i * sizeof (SYMR);
+      file_offset = ALIGN_SYMTABLE_OFFSET (file_offset);
+    }
+
+  i = symbolic_header.iauxMax;			/* aux syms.  */
+  if (i > 0)
+    {
+      symbolic_header.cbAuxOffset = file_offset;
+      file_offset += i * sizeof (TIR);
+      file_offset = ALIGN_SYMTABLE_OFFSET (file_offset);
+    }
+
+  i = WORD_ALIGN (symbolic_header.issMax);	/* local strings */
+  if (i > 0)
+    {
+      symbolic_header.cbSsOffset = file_offset;
+      file_offset += i;
+      file_offset = ALIGN_SYMTABLE_OFFSET (file_offset);
+    }
+
+  i = WORD_ALIGN (symbolic_header.issExtMax);	/* external strings */
+  if (i > 0)
+    {
+      symbolic_header.cbSsExtOffset = file_offset;
+      file_offset += i;
+      file_offset = ALIGN_SYMTABLE_OFFSET (file_offset);
+    }
+
+  i = symbolic_header.ifdMax;			/* file tables */
+  if (i > 0)
+    {
+      symbolic_header.cbFdOffset = file_offset;
+      file_offset += i * sizeof (FDR);
+      file_offset = ALIGN_SYMTABLE_OFFSET (file_offset);
+    }
+
+  i = symbolic_header.crfd;			/* relative file descriptors */
+  if (i > 0)
+    {
+      symbolic_header.cbRfdOffset = file_offset;
+      file_offset += i * sizeof (symint_t);
+      file_offset = ALIGN_SYMTABLE_OFFSET (file_offset);
+    }
+
+  i = symbolic_header.iextMax;			/* external symbols */
+  if (i > 0)
+    {
+      symbolic_header.cbExtOffset = file_offset;
+      file_offset += i * sizeof (EXTR);
+      file_offset = ALIGN_SYMTABLE_OFFSET (file_offset);
+    }
+}
+
+
+/* Write out a varray at a given location.  */
+
+STATIC void
+write_varray (varray_t *vp,    /* virtual array */
+	      off_t offset,    /* offset to write varray to */
+	      const char *str) /* string to print out when tracing */
+{
+  int num_write, sys_write;
+  vlinks_t *ptr;
+
+  if (vp->num_allocated == 0)
+    return;
+
+  if (debug)
+    fprintf (stderr, "\twarray\tvp = %p, offset = %7lu, size = %7lu, %s\n",
+	     (void *) vp, (unsigned long) offset,
+	     vp->num_allocated * vp->object_size, str);
+
+  if (file_offset != (unsigned long) offset
+      && fseek (object_stream, (long) offset, SEEK_SET) < 0)
+    pfatal_with_name (object_name);
+
+  for (ptr = vp->first; ptr != (vlinks_t *) 0; ptr = ptr->next)
+    {
+      num_write = (ptr->next == (vlinks_t *) 0)
+	? vp->objects_last_page * vp->object_size
+	: vp->objects_per_page  * vp->object_size;
+
+      sys_write = fwrite (ptr->datum, 1, num_write, object_stream);
+      if (sys_write <= 0)
+	pfatal_with_name (object_name);
+
+      else if (sys_write != num_write)
+	fatal ("wrote %d bytes to %s, system returned %d",
+	       num_write,
+	       object_name,
+	       sys_write);
+
+      file_offset += num_write;
+    }
+}
+
+
+/* Write out the symbol table in the object file.  */
+
+STATIC void
+write_object (void)
+{
+  int sys_write;
+  efdr_t *file_ptr;
+  off_t offset;
+
+  if (debug)
+    fprintf (stderr, "\n\twrite\tvp = %p, offset = %7u, size = %7lu, %s\n",
+	     (void *) &symbolic_header, 0,
+	     (unsigned long) sizeof (symbolic_header), "symbolic header");
+
+  sys_write = fwrite (&symbolic_header,
+		      1,
+		      sizeof (symbolic_header),
+		      object_stream);
+
+  if (sys_write < 0)
+    pfatal_with_name (object_name);
+
+  else if (sys_write != sizeof (symbolic_header))
+    fatal ("wrote %d bytes to %s, system returned %d",
+	   (int) sizeof (symbolic_header),
+	   object_name,
+	   sys_write);
+
+
+  file_offset = sizeof (symbolic_header) + orig_file_header.f_symptr;
+
+  if (symbolic_header.cbLine > 0)		/* line numbers */
+    {
+      long sys_write;
+
+      if (file_offset != (unsigned long) symbolic_header.cbLineOffset
+	  && fseek (object_stream, symbolic_header.cbLineOffset, SEEK_SET) != 0)
+	pfatal_with_name (object_name);
+
+      if (debug)
+	fprintf (stderr, "\twrite\tvp = %p, offset = %7lu, size = %7lu, %s\n",
+		 (void *) &orig_linenum, (long) symbolic_header.cbLineOffset,
+		 (long) symbolic_header.cbLine, "Line numbers");
+
+      sys_write = fwrite (orig_linenum,
+			  1,
+			  symbolic_header.cbLine,
+			  object_stream);
+
+      if (sys_write <= 0)
+	pfatal_with_name (object_name);
+
+      else if (sys_write != symbolic_header.cbLine)
+	fatal ("wrote %ld bytes to %s, system returned %ld",
+	       (long) symbolic_header.cbLine,
+	       object_name,
+	       sys_write);
+
+      file_offset = symbolic_header.cbLineOffset + symbolic_header.cbLine;
+    }
+
+  if (symbolic_header.ioptMax > 0)		/* optimization symbols */
+    {
+      long sys_write;
+      long num_write = symbolic_header.ioptMax * sizeof (OPTR);
+
+      if (file_offset != (unsigned long) symbolic_header.cbOptOffset
+	  && fseek (object_stream, symbolic_header.cbOptOffset, SEEK_SET) != 0)
+	pfatal_with_name (object_name);
+
+      if (debug)
+	fprintf (stderr, "\twrite\tvp = %p, offset = %7lu, size = %7lu, %s\n",
+		 (void *) &orig_opt_syms, (long) symbolic_header.cbOptOffset,
+		 num_write, "Optimizer symbols");
+
+      sys_write = fwrite (orig_opt_syms,
+			  1,
+			  num_write,
+			  object_stream);
+
+      if (sys_write <= 0)
+	pfatal_with_name (object_name);
+
+      else if (sys_write != num_write)
+	fatal ("wrote %ld bytes to %s, system returned %ld",
+	       num_write,
+	       object_name,
+	       sys_write);
+
+      file_offset = symbolic_header.cbOptOffset + num_write;
+    }
+
+  if (symbolic_header.idnMax > 0)		/* dense numbers */
+    write_varray (&dense_num, (off_t) symbolic_header.cbDnOffset, "Dense numbers");
+
+  if (symbolic_header.ipdMax > 0)		/* procedure tables */
+    {
+      offset = symbolic_header.cbPdOffset;
+      for (file_ptr = first_file;
+	   file_ptr != (efdr_t *) 0;
+	   file_ptr = file_ptr->next_file)
+	{
+	  write_varray (&file_ptr->procs, offset, "Procedure tables");
+	  offset = file_offset;
+	}
+    }
+
+  if (symbolic_header.isymMax > 0)		/* local symbols */
+    {
+      offset = symbolic_header.cbSymOffset;
+      for (file_ptr = first_file;
+	   file_ptr != (efdr_t *) 0;
+	   file_ptr = file_ptr->next_file)
+	{
+	  write_varray (&file_ptr->symbols, offset, "Local symbols");
+	  offset = file_offset;
+	}
+    }
+
+  if (symbolic_header.iauxMax > 0)		/* aux symbols */
+    {
+      offset = symbolic_header.cbAuxOffset;
+      for (file_ptr = first_file;
+	   file_ptr != (efdr_t *) 0;
+	   file_ptr = file_ptr->next_file)
+	{
+	  write_varray (&file_ptr->aux_syms, offset, "Aux. symbols");
+	  offset = file_offset;
+	}
+    }
+
+  if (symbolic_header.issMax > 0)		/* local strings */
+    {
+      offset = symbolic_header.cbSsOffset;
+      for (file_ptr = first_file;
+	   file_ptr != (efdr_t *) 0;
+	   file_ptr = file_ptr->next_file)
+	{
+	  write_varray (&file_ptr->strings, offset, "Local strings");
+	  offset = file_offset;
+	}
+    }
+
+  if (symbolic_header.issExtMax > 0)		/* external strings */
+    write_varray (&ext_strings, symbolic_header.cbSsExtOffset, "External strings");
+
+  if (symbolic_header.ifdMax > 0)		/* file tables */
+    {
+      offset = symbolic_header.cbFdOffset;
+      if (file_offset != (unsigned long) offset
+	  && fseek (object_stream, (long) offset, SEEK_SET) < 0)
+	pfatal_with_name (object_name);
+
+      file_offset = offset;
+      for (file_ptr = first_file;
+	   file_ptr != (efdr_t *) 0;
+	   file_ptr = file_ptr->next_file)
+	{
+	  if (debug)
+	    fprintf (stderr, "\twrite\tvp = %p, offset = %7lu, size = %7lu, %s\n",
+		     (void *) &file_ptr->fdr, file_offset,
+		     (unsigned long) sizeof (FDR), "File header");
+
+	  sys_write = fwrite (&file_ptr->fdr,
+			      1,
+			      sizeof (FDR),
+			      object_stream);
+
+	  if (sys_write < 0)
+	    pfatal_with_name (object_name);
+
+	  else if (sys_write != sizeof (FDR))
+	    fatal ("wrote %d bytes to %s, system returned %d",
+		   (int) sizeof (FDR),
+		   object_name,
+		   sys_write);
+
+	  file_offset = offset += sizeof (FDR);
+	}
+    }
+
+  if (symbolic_header.crfd > 0)			/* relative file descriptors */
+    {
+      long sys_write;
+      symint_t num_write = symbolic_header.crfd * sizeof (symint_t);
+
+      if (file_offset != (unsigned long) symbolic_header.cbRfdOffset
+	  && fseek (object_stream, symbolic_header.cbRfdOffset, SEEK_SET) != 0)
+	pfatal_with_name (object_name);
+
+      if (debug)
+	fprintf (stderr, "\twrite\tvp = %p, offset = %7lu, size = %7lu, %s\n",
+		 (void *) &orig_rfds, (long) symbolic_header.cbRfdOffset,
+		 num_write, "Relative file descriptors");
+
+      sys_write = fwrite (orig_rfds,
+			  1,
+			  num_write,
+			  object_stream);
+
+      if (sys_write <= 0)
+	pfatal_with_name (object_name);
+
+      else if (sys_write != (long) num_write)
+	fatal ("wrote %lu bytes to %s, system returned %ld",
+	       num_write,
+	       object_name,
+	       sys_write);
+
+      file_offset = symbolic_header.cbRfdOffset + num_write;
+    }
+
+  if (symbolic_header.issExtMax > 0)		/* external symbols */
+    write_varray (&ext_symbols, (off_t) symbolic_header.cbExtOffset, "External symbols");
+
+  if (fclose (object_stream) != 0)
+    pfatal_with_name (object_name);
+}
+
+
+/* Read some bytes at a specified location, and return a pointer.  */
+
+STATIC page_t *
+read_seek (Size_t size,		/* # bytes to read */
+	   off_t offset,	/* offset to read at */
+	   const char *str)	/* name for tracing */
+{
+  page_t *ptr;
+  long sys_read = 0;
+
+  if (size == 0)		/* nothing to read */
+    return (page_t *) 0;
+
+  if (debug)
+    fprintf (stderr,
+	     "\trseek\tsize = %7lu, offset = %7lu, currently at %7lu, %s\n",
+	     (unsigned long) size, (unsigned long) offset, file_offset, str);
+
+#ifndef MALLOC_CHECK
+  ptr = allocate_multiple_pages ((size + PAGE_USIZE - 1) / PAGE_USIZE);
+#else
+  ptr = xcalloc (1, size);
+#endif
+
+  /* If we need to seek, and the distance is nearby, just do some reads,
+     to speed things up.  */
+  if (file_offset != (unsigned long) offset)
+    {
+      symint_t difference = offset - file_offset;
+
+      if (difference < 8)
+	{
+	  char small_buffer[8];
+
+	  sys_read = fread (small_buffer, 1, difference, obj_in_stream);
+	  if (sys_read <= 0)
+	    pfatal_with_name (obj_in_name);
+
+	  if ((symint_t) sys_read != difference)
+	    fatal ("wanted to read %lu bytes from %s, system returned %ld",
+		   (unsigned long) size,
+		   obj_in_name,
+		   sys_read);
+	}
+      else if (fseek (obj_in_stream, offset, SEEK_SET) < 0)
+	pfatal_with_name (obj_in_name);
+    }
+
+  sys_read = fread (ptr, 1, size, obj_in_stream);
+  if (sys_read <= 0)
+    pfatal_with_name (obj_in_name);
+
+  if (sys_read != (long) size)
+    fatal ("wanted to read %lu bytes from %s, system returned %ld",
+	   (unsigned long) size,
+	   obj_in_name,
+	   sys_read);
+
+  file_offset = offset + size;
+
+  if (file_offset > max_file_offset)
+    max_file_offset = file_offset;
+
+  return ptr;
+}
+
+
+/* Read the existing object file (and copy to the output object file
+   if it is different from the input object file), and remove the old
+   symbol table.  */
+
+STATIC void
+copy_object (void)
+{
+  char buffer[ PAGE_SIZE ];
+  int sys_read;
+  int remaining;
+  int num_write;
+  int sys_write;
+  int fd, es;
+  int delete_ifd = 0;
+  int *remap_file_number;
+  struct stat stat_buf;
+
+  if (debug)
+    fprintf (stderr, "\tcopy\n");
+
+  if (fstat (fileno (obj_in_stream), &stat_buf) != 0
+      || fseek (obj_in_stream, 0L, SEEK_SET) != 0)
+    pfatal_with_name (obj_in_name);
+
+  sys_read = fread (&orig_file_header,
+		    1,
+		    sizeof (struct filehdr),
+		    obj_in_stream);
+
+  if (sys_read < 0)
+    pfatal_with_name (obj_in_name);
+
+  else if (sys_read == 0 && feof (obj_in_stream))
+    return;			/* create a .T file sans file header */
+
+  else if (sys_read < (int) sizeof (struct filehdr))
+    fatal ("wanted to read %d bytes from %s, system returned %d",
+	   (int) sizeof (struct filehdr),
+	   obj_in_name,
+	   sys_read);
+
+
+  if (orig_file_header.f_nsyms != sizeof (HDRR))
+    fatal ("%s symbolic header wrong size (%ld bytes, should be %ld)",
+	   input_name, (long) orig_file_header.f_nsyms, (long) sizeof (HDRR));
+
+
+  /* Read in the current symbolic header.  */
+  if (fseek (obj_in_stream, (long) orig_file_header.f_symptr, SEEK_SET) != 0)
+    pfatal_with_name (input_name);
+
+  sys_read = fread (&orig_sym_hdr,
+		    1,
+		    sizeof (orig_sym_hdr),
+		    obj_in_stream);
+
+  if (sys_read < 0)
+    pfatal_with_name (object_name);
+
+  else if (sys_read < (int) sizeof (struct filehdr))
+    fatal ("wanted to read %d bytes from %s, system returned %d",
+	   (int) sizeof (struct filehdr),
+	   obj_in_name,
+	   sys_read);
+
+
+  /* Read in each of the sections if they exist in the object file.
+     We read things in the order the mips assembler creates the
+     sections, so in theory no extra seeks are done.
+
+     For simplicity sake, round each read up to a page boundary,
+     we may want to revisit this later....  */
+
+  file_offset =  orig_file_header.f_symptr + sizeof (struct filehdr);
+
+  if (orig_sym_hdr.cbLine > 0)			/* line numbers */
+    orig_linenum = (char *) read_seek (orig_sym_hdr.cbLine,
+				       orig_sym_hdr.cbLineOffset,
+				       "Line numbers");
+
+  if (orig_sym_hdr.ipdMax > 0)			/* procedure tables */
+    orig_procs = (PDR *) read_seek (orig_sym_hdr.ipdMax * sizeof (PDR),
+				    orig_sym_hdr.cbPdOffset,
+				    "Procedure tables");
+
+  if (orig_sym_hdr.isymMax > 0)			/* local symbols */
+    orig_local_syms = (SYMR *) read_seek (orig_sym_hdr.isymMax * sizeof (SYMR),
+					  orig_sym_hdr.cbSymOffset,
+					  "Local symbols");
+
+  if (orig_sym_hdr.iauxMax > 0)			/* aux symbols */
+    orig_aux_syms = (AUXU *) read_seek (orig_sym_hdr.iauxMax * sizeof (AUXU),
+					orig_sym_hdr.cbAuxOffset,
+					"Aux. symbols");
+
+  if (orig_sym_hdr.issMax > 0)			/* local strings */
+    orig_local_strs = (char *) read_seek (orig_sym_hdr.issMax,
+					  orig_sym_hdr.cbSsOffset,
+					  "Local strings");
+
+  if (orig_sym_hdr.issExtMax > 0)		/* external strings */
+    orig_ext_strs = (char *) read_seek (orig_sym_hdr.issExtMax,
+					orig_sym_hdr.cbSsExtOffset,
+					"External strings");
+
+  if (orig_sym_hdr.ifdMax > 0)			/* file tables */
+    orig_files = (FDR *) read_seek (orig_sym_hdr.ifdMax * sizeof (FDR),
+				    orig_sym_hdr.cbFdOffset,
+				    "File tables");
+
+  if (orig_sym_hdr.crfd > 0)			/* relative file descriptors */
+    orig_rfds = (symint_t *) read_seek (orig_sym_hdr.crfd * sizeof (symint_t),
+					orig_sym_hdr.cbRfdOffset,
+					"Relative file descriptors");
+
+  if (orig_sym_hdr.issExtMax > 0)		/* external symbols */
+    orig_ext_syms = (EXTR *) read_seek (orig_sym_hdr.iextMax * sizeof (EXTR),
+					orig_sym_hdr.cbExtOffset,
+					"External symbols");
+
+  if (orig_sym_hdr.idnMax > 0)			/* dense numbers */
+    {
+      orig_dense = (DNR *) read_seek (orig_sym_hdr.idnMax * sizeof (DNR),
+				      orig_sym_hdr.cbDnOffset,
+				      "Dense numbers");
+
+      add_bytes (&dense_num, (char *) orig_dense, orig_sym_hdr.idnMax);
+    }
+
+  if (orig_sym_hdr.ioptMax > 0)			/* opt symbols */
+    orig_opt_syms = (OPTR *) read_seek (orig_sym_hdr.ioptMax * sizeof (OPTR),
+					orig_sym_hdr.cbOptOffset,
+					"Optimizer symbols");
+
+
+
+  /* The symbol table should be last.  */
+  if (max_file_offset != (unsigned long) stat_buf.st_size)
+    fatal ("symbol table is not last (symbol table ends at %ld, .o ends at %ld",
+	   max_file_offset,
+	   (long) stat_buf.st_size);
+
+
+  /* If the first original file descriptor is a dummy which the assembler
+     put out, but there are no symbols in it, skip it now.  */
+  if (orig_sym_hdr.ifdMax > 1
+      && orig_files->csym == 2
+      && orig_files->caux == 0)
+    {
+      char *filename = orig_local_strs + (orig_files->issBase + orig_files->rss);
+      char *suffix = strrchr (filename, '.');
+
+      if (suffix != (char *) 0 && strcmp (suffix, ".s") == 0)
+	delete_ifd = 1;
+    }
+
+
+  /* Create array to map original file numbers to the new file numbers
+     (in case there are duplicate filenames, we collapse them into one
+     file section, the MIPS assembler may or may not collapse them).  */
+
+  remap_file_number = (int *) alloca (sizeof (int) * orig_sym_hdr.ifdMax);
+
+  for (fd = delete_ifd; fd < orig_sym_hdr.ifdMax; fd++)
+    {
+      FDR *fd_ptr = ORIG_FILES (fd);
+      char *filename = ORIG_LSTRS (fd_ptr->issBase + fd_ptr->rss);
+
+      /* file support itself.  */
+      add_file (filename, filename + strlen (filename));
+      remap_file_number[fd] = cur_file_ptr->file_index;
+    }
+
+  if (delete_ifd > 0)		/* just in case */
+    remap_file_number[0] = remap_file_number[1];
+
+
+  /* Loop, adding each of the external symbols.  These must be in
+     order or otherwise we would have to change the relocation
+     entries.  We don't just call add_bytes, because we need to have
+     the names put into the external hash table.  We set the type to
+     'void' for now, and parse_def will fill in the correct type if it
+     is in the symbol table.  We must add the external symbols before
+     the locals, since the locals do lookups against the externals.  */
+
+  if (debug)
+    fprintf (stderr, "\tehash\n");
+
+  for (es = 0; es < orig_sym_hdr.iextMax; es++)
+    {
+      EXTR *eptr = orig_ext_syms + es;
+      int ifd = eptr->ifd;
+
+      (void) add_ext_symbol (eptr, ((long) ifd < orig_sym_hdr.ifdMax)
+			     ? remap_file_number[ ifd ] : ifd );
+    }
+
+
+  /* For each of the files in the object file, copy the symbols, and such
+     into the varrays for the new object file.  */
+
+  for (fd = delete_ifd; fd < orig_sym_hdr.ifdMax; fd++)
+    {
+      FDR *fd_ptr = ORIG_FILES (fd);
+      char *filename = ORIG_LSTRS (fd_ptr->issBase + fd_ptr->rss);
+      SYMR *sym_start;
+      SYMR *sym;
+      SYMR *sym_end_p1;
+      PDR *proc_start;
+      PDR *proc;
+      PDR *proc_end_p1;
+
+      /* file support itself.  */
+      add_file (filename, filename + strlen (filename));
+      cur_file_ptr->orig_fdr = fd_ptr;
+
+      /* Copy stuff that's just passed through (such as line #'s) */
+      cur_file_ptr->fdr.adr	     = fd_ptr->adr;
+      cur_file_ptr->fdr.ilineBase    = fd_ptr->ilineBase;
+      cur_file_ptr->fdr.cline	     = fd_ptr->cline;
+      cur_file_ptr->fdr.rfdBase	     = fd_ptr->rfdBase;
+      cur_file_ptr->fdr.crfd	     = fd_ptr->crfd;
+      cur_file_ptr->fdr.cbLineOffset = fd_ptr->cbLineOffset;
+      cur_file_ptr->fdr.cbLine	     = fd_ptr->cbLine;
+      cur_file_ptr->fdr.fMerge	     = fd_ptr->fMerge;
+      cur_file_ptr->fdr.fReadin	     = fd_ptr->fReadin;
+      cur_file_ptr->fdr.glevel	     = fd_ptr->glevel;
+
+      if (debug)
+	fprintf (stderr, "\thash\tstart, filename %s\n", filename);
+
+      /* For each of the static and global symbols defined, add them
+	 to the hash table of original symbols, so we can look up
+	 their values.  */
+
+      sym_start = ORIG_LSYMS (fd_ptr->isymBase);
+      sym_end_p1 = sym_start + fd_ptr->csym;
+      for (sym = sym_start; sym < sym_end_p1; sym++)
+	{
+	  switch ((st_t) sym->st)
+	    {
+	    default:
+	      break;
+
+	    case st_Global:
+	    case st_Static:
+	    case st_Label:
+	    case st_Proc:
+	    case st_StaticProc:
+	      {
+		auto symint_t hash_index;
+		char *str = ORIG_LSTRS (fd_ptr->issBase + sym->iss);
+		Size_t len = strlen (str);
+		shash_t *shash_ptr = hash_string (str,
+						  (Ptrdiff_t) len,
+						  &orig_str_hash[0],
+						  &hash_index);
+
+		if (shash_ptr != (shash_t *) 0)
+		  error ("internal error, %s is already in original symbol table", str);
+
+		else
+		  {
+		    shash_ptr = allocate_shash ();
+		    shash_ptr->next = orig_str_hash[hash_index];
+		    orig_str_hash[hash_index] = shash_ptr;
+
+		    shash_ptr->len = len;
+		    shash_ptr->indx = indexNil;
+		    shash_ptr->string = str;
+		    shash_ptr->sym_ptr = sym;
+		  }
+	      }
+	      break;
+
+	    case st_End:
+	      if ((sc_t) sym->sc == sc_Text)
+		{
+		  char *str = ORIG_LSTRS (fd_ptr->issBase + sym->iss);
+
+		  if (*str != '\0')
+		    {
+		      Size_t len = strlen (str);
+		      shash_t *shash_ptr = hash_string (str,
+							(Ptrdiff_t) len,
+							&orig_str_hash[0],
+							(symint_t *) 0);
+
+		      if (shash_ptr != (shash_t *) 0)
+			shash_ptr->end_ptr = sym;
+		    }
+		}
+	      break;
+
+	    }
+	}
+
+      if (debug)
+	{
+	  fprintf (stderr, "\thash\tdone,  filename %s\n", filename);
+	  fprintf (stderr, "\tproc\tstart, filename %s\n", filename);
+	}
+
+      /* Go through each of the procedures in this file, and add the
+	 procedure pointer to the hash entry for the given name.  */
+
+      proc_start = ORIG_PROCS (fd_ptr->ipdFirst);
+      proc_end_p1 = proc_start + fd_ptr->cpd;
+      for (proc = proc_start; proc < proc_end_p1; proc++)
+	{
+	  SYMR *proc_sym = ORIG_LSYMS (fd_ptr->isymBase + proc->isym);
+	  char *str = ORIG_LSTRS (fd_ptr->issBase + proc_sym->iss);
+	  Size_t len = strlen (str);
+	  shash_t *shash_ptr = hash_string (str,
+					    (Ptrdiff_t) len,
+					    &orig_str_hash[0],
+					    (symint_t *) 0);
+
+	  if (shash_ptr == (shash_t *) 0)
+	    error ("internal error, function %s is not in original symbol table", str);
+
+	  else
+	    shash_ptr->proc_ptr = proc;
+	}
+
+      if (debug)
+	fprintf (stderr, "\tproc\tdone,  filename %s\n", filename);
+
+    }
+  cur_file_ptr = first_file;
+
+
+  /* Copy all of the object file up to the symbol table.  Originally
+     we were going to use ftruncate, but that doesn't seem to work
+     on Ultrix 3.1....  */
+
+  if (fseek (obj_in_stream, (long) 0, SEEK_SET) != 0)
+    pfatal_with_name (obj_in_name);
+
+  if (fseek (object_stream, (long) 0, SEEK_SET) != 0)
+    pfatal_with_name (object_name);
+
+  for (remaining = orig_file_header.f_symptr;
+       remaining > 0;
+       remaining -= num_write)
+    {
+      num_write
+	= (remaining <= (int) sizeof (buffer))
+	  ? remaining : (int) sizeof (buffer);
+      sys_read = fread (buffer, 1, num_write, obj_in_stream);
+      if (sys_read <= 0)
+	pfatal_with_name (obj_in_name);
+
+      else if (sys_read != num_write)
+	fatal ("wanted to read %d bytes from %s, system returned %d",
+	       num_write,
+	       obj_in_name,
+	       sys_read);
+
+      sys_write = fwrite (buffer, 1, num_write, object_stream);
+      if (sys_write <= 0)
+	pfatal_with_name (object_name);
+
+      else if (sys_write != num_write)
+	fatal ("wrote %d bytes to %s, system returned %d",
+	       num_write,
+	       object_name,
+	       sys_write);
+    }
+}
+
+
+/* Ye olde main program.  */
+
+extern int main (int, char **);
+
+int
+main (int argc, char **argv)
+{
+  int iflag = 0;
+  char *p = strrchr (argv[0], '/');
+  char *num_end;
+  int option;
+  int i;
+
+  progname = (p != 0) ? p+1 : argv[0];
+
+  (void) signal (SIGSEGV, catch_signal);
+  (void) signal (SIGBUS,  catch_signal);
+  (void) signal (SIGABRT, catch_signal);
+
+#if !defined(__SABER__) && !defined(lint)
+  if (sizeof (efdr_t) > PAGE_USIZE)
+    fatal ("efdr_t has a sizeof %d bytes, when it should be less than %d",
+	   (int) sizeof (efdr_t),
+	   (int) PAGE_USIZE);
+
+  if (sizeof (page_t) != PAGE_USIZE)
+    fatal ("page_t has a sizeof %d bytes, when it should be %d",
+	   (int) sizeof (page_t),
+	   (int) PAGE_USIZE);
+
+#endif
+
+  alloc_counts[ alloc_type_none    ].alloc_name = "none";
+  alloc_counts[ alloc_type_scope   ].alloc_name = "scope";
+  alloc_counts[ alloc_type_vlinks  ].alloc_name = "vlinks";
+  alloc_counts[ alloc_type_shash   ].alloc_name = "shash";
+  alloc_counts[ alloc_type_thash   ].alloc_name = "thash";
+  alloc_counts[ alloc_type_tag     ].alloc_name = "tag";
+  alloc_counts[ alloc_type_forward ].alloc_name = "forward";
+  alloc_counts[ alloc_type_thead   ].alloc_name = "thead";
+  alloc_counts[ alloc_type_varray  ].alloc_name = "varray";
+
+  int_type_info  = type_info_init;
+  int_type_info.basic_type = bt_Int;
+
+  void_type_info = type_info_init;
+  void_type_info.basic_type = bt_Void;
+
+  while ((option = getopt_long (argc, argv, "d:i:I:o:v", options, NULL)) != -1)
+    switch (option)
+      {
+      default:
+	had_errors++;
+	break;
+
+      case 'd':
+	debug = strtol (optarg, &num_end, 0);
+	if ((unsigned) debug > 4 || num_end == optarg)
+	  had_errors++;
+
+	break;
+
+      case 'I':
+	if (rename_output || obj_in_name != (char *) 0)
+	  had_errors++;
+	else
+	  rename_output = 1;
+
+	/* Fall through to 'i' case.  */
+
+      case 'i':
+	if (obj_in_name == (char *) 0)
+	  {
+	    obj_in_name = optarg;
+	    iflag++;
+	  }
+	else
+	  had_errors++;
+	break;
+
+      case 'o':
+	if (object_name == (char *) 0)
+	  object_name = optarg;
+	else
+	  had_errors++;
+	break;
+
+      case 'v':
+	verbose++;
+	break;
+
+      case 'V':
+	version++;
+	break;
+      }
+
+  if (version)
+    {
+      printf (_("mips-tfile %s%s\n"), pkgversion_string, version_string);
+      fputs ("Copyright (C) 2009 Free Software Foundation, Inc.\n", stdout);
+      fputs (_("This is free software; see the source for copying conditions.  There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"),
+	     stdout);
+      exit (0);
+    }
+
+  if (obj_in_name == (char *) 0 && optind <= argc - 2)
+    obj_in_name = argv[--argc];
+
+  if (object_name == (char *) 0 && optind <= argc - 2)
+    object_name = argv[--argc];
+
+  /* If there is an output name, but no input name use
+     the same file for both, deleting the name between
+     opening it for input and opening it for output.  */
+  if (obj_in_name == (char *) 0 && object_name != (char *) 0)
+    {
+      obj_in_name = object_name;
+      delete_input = 1;
+    }
+
+  if (optind != argc - 1)
+    had_errors++;
+
+  if (verbose || had_errors)
+    {
+      fprintf (stderr, _("mips-tfile (GCC) %s"), version_string);
+#ifdef TARGET_VERSION
+      TARGET_VERSION;
+#endif
+      fputc ('\n', stderr);
+    }
+
+  if (object_name == (char *) 0 || had_errors)
+    {
+      fprintf (stderr, _("Calling Sequence:\n"));
+      fprintf (stderr, _("\tmips-tfile [-d <num>] [-v] [-i <o-in-file>] -o <o-out-file> <s-file> (or)\n"));
+      fprintf (stderr, _("\tmips-tfile [-d <num>] [-v] [-I <o-in-file>] -o <o-out-file> <s-file> (or)\n"));
+      fprintf (stderr, _("\tmips-tfile [-d <num>] [-v] <s-file> <o-in-file> <o-out-file>\n"));
+      fprintf (stderr, "\n");
+      fprintf (stderr, _("Debug levels are:\n"));
+      fprintf (stderr, _("    1\tGeneral debug + trace functions/blocks.\n"));
+      fprintf (stderr, _("    2\tDebug level 1 + trace externals.\n"));
+      fprintf (stderr, _("    3\tDebug level 2 + trace all symbols.\n"));
+      fprintf (stderr, _("    4\tDebug level 3 + trace memory allocations.\n"));
+      return 1;
+    }
+
+  if (obj_in_name == (char *) 0)
+    obj_in_name = object_name;
+
+  if (rename_output && rename (object_name, obj_in_name) != 0)
+    {
+      char *buffer = (char *) allocate_multiple_pages (4);
+      int len;
+      int len2;
+      int in_fd;
+      int out_fd;
+
+      /* Rename failed, copy input file */
+      in_fd = open (object_name, O_RDONLY, 0666);
+      if (in_fd < 0)
+	pfatal_with_name (object_name);
+
+      out_fd = open (obj_in_name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+      if (out_fd < 0)
+	pfatal_with_name (obj_in_name);
+
+      while ((len = read (in_fd, buffer, 4*PAGE_SIZE)) > 0)
+	{
+	  len2 = write (out_fd, buffer, len);
+	  if (len2 < 0)
+	    pfatal_with_name (object_name);
+
+	  if (len != len2)
+	    fatal ("wrote %d bytes to %s, expected to write %d", len2, obj_in_name, len);
+	}
+
+      free_multiple_pages ((page_t *) buffer, 4);
+
+      if (len < 0)
+	pfatal_with_name (object_name);
+
+      if (close (in_fd) < 0)
+	pfatal_with_name (object_name);
+
+      if (close (out_fd) < 0)
+	pfatal_with_name (obj_in_name);
+    }
+
+  /* Must open input before output, since the output may be the same file, and
+     we need to get the input handle before truncating it.  */
+  obj_in_stream = fopen (obj_in_name, "r");
+  if (obj_in_stream == (FILE *) 0)
+    pfatal_with_name (obj_in_name);
+
+  if (delete_input && unlink (obj_in_name) != 0)
+    pfatal_with_name (obj_in_name);
+
+  object_stream = fopen (object_name, "w");
+  if (object_stream == (FILE *) 0)
+    pfatal_with_name (object_name);
+
+  if (strcmp (argv[optind], "-") != 0)
+    {
+      input_name = argv[optind];
+      if (freopen (argv[optind], "r", stdin) != stdin)
+	pfatal_with_name (argv[optind]);
+    }
+
+  copy_object ();			/* scan & copy object file */
+  parse_input ();			/* scan all of input */
+
+  update_headers ();			/* write out tfile */
+  write_object ();
+
+  if (debug)
+    {
+      fprintf (stderr, "\n\tAllocation summary:\n\n");
+      for (i = (int) alloc_type_none; i < (int) alloc_type_last; i++)
+	if (alloc_counts[i].total_alloc)
+	  {
+	    fprintf (stderr,
+		     "\t%s\t%5d allocation(s), %5d free(s), %2d page(s)\n",
+		     alloc_counts[i].alloc_name,
+		     alloc_counts[i].total_alloc,
+		     alloc_counts[i].total_free,
+		     alloc_counts[i].total_pages);
+	  }
+    }
+
+  return (had_errors) ? 1 : 0;
+}
+
+
+/* Catch a signal and exit without dumping core.  */
+
+STATIC void
+catch_signal (int signum)
+{
+  (void) signal (signum, SIG_DFL);	/* just in case...  */
+  fatal ("%s", strsignal (signum));
+}
+
+/* Print a fatal error message.  NAME is the text.
+   Also include a system error message based on `errno'.  */
+
+void
+pfatal_with_name (const char *msg)
+{
+  int save_errno = errno;		/* just in case....  */
+  if (line_number > 0)
+    fprintf (stderr, "%s, %s:%ld ", progname, input_name, line_number);
+  else
+    fprintf (stderr, "%s:", progname);
+
+  errno = save_errno;
+  if (errno == 0)
+    fprintf (stderr, "[errno = 0] %s\n", msg);
+  else
+    perror (msg);
+
+  exit (1);
+}
+
+
+/* Procedure to die with an out of bounds error message.  It has
+   type int, so it can be used with an ?: expression within the
+   ORIG_xxx macros, but the function never returns.  */
+
+static int
+out_of_bounds (symint_t indx,	/* index that is out of bounds */
+	       symint_t max,	/* maximum index */
+	       const char *str, /* string to print out */
+	       int prog_line)	/* line number within mips-tfile.c */
+{
+  if (indx < max)		/* just in case */
+    return 0;
+
+  fprintf (stderr, "%s, %s:%ld index %lu is out of bounds for %s, max is %lu, mips-tfile.c line# %d\n",
+	   progname, input_name, line_number, indx, str, max, prog_line);
+
+  exit (1);
+  return 0;			/* turn off warning messages */
+}
+
+
+/* Allocate a cluster of pages.  USE_MALLOC says that malloc does not
+   like sbrk's behind its back (or sbrk isn't available).  If we use
+   sbrk, we assume it gives us zeroed pages.  */
+
+#ifndef MALLOC_CHECK
+#ifdef USE_MALLOC
+
+STATIC page_t *
+allocate_cluster (Size_t npages)
+{
+  page_t *value = xcalloc (npages, PAGE_USIZE);
+
+  if (debug > 3)
+    fprintf (stderr, "\talloc\tnpages = %d, value = 0x%.8x\n", npages, value);
+
+  return value;
+}
+
+#else /* USE_MALLOC */
+
+STATIC page_t *
+allocate_cluster (Size_t npages)
+{
+  page_t *ptr = (page_t *) sbrk (0);	/* current sbreak */
+  unsigned long offset = ((unsigned long) ptr) & (PAGE_SIZE - 1);
+
+  if (offset != 0)			/* align to a page boundary */
+    {
+      if (sbrk (PAGE_USIZE - offset) == (char *)-1)
+	pfatal_with_name ("allocate_cluster");
+
+      ptr = (page_t *) (((char *) ptr) + PAGE_SIZE - offset);
+    }
+
+  if (sbrk (npages * PAGE_USIZE) == (char *) -1)
+    pfatal_with_name ("allocate_cluster");
+
+  if (debug > 3)
+    fprintf (stderr, "\talloc\tnpages = %lu, value = %p\n",
+	     (unsigned long) npages, (void *) ptr);
+
+  return ptr;
+}
+
+#endif /* USE_MALLOC */
+
+
+static page_t	*cluster_ptr	= NULL;
+static unsigned	 pages_left	= 0;
+
+#endif /* MALLOC_CHECK */
+
+
+/* Allocate some pages (which is initialized to 0).  */
+
+STATIC page_t *
+allocate_multiple_pages (Size_t npages)
+{
+#ifndef MALLOC_CHECK
+  if (pages_left == 0 && npages < MAX_CLUSTER_PAGES)
+    {
+      pages_left = MAX_CLUSTER_PAGES;
+      cluster_ptr = allocate_cluster (MAX_CLUSTER_PAGES);
+    }
+
+  if (npages <= pages_left)
+    {
+      page_t *ptr = cluster_ptr;
+      cluster_ptr += npages;
+      pages_left -= npages;
+      return ptr;
+    }
+
+  return allocate_cluster (npages);
+
+#else	/* MALLOC_CHECK */
+  return xcalloc (npages, PAGE_SIZE);
+
+#endif	/* MALLOC_CHECK */
+}
+
+
+/* Release some pages.  */
+
+STATIC void
+free_multiple_pages (page_t *page_ptr, Size_t npages)
+{
+#ifndef MALLOC_CHECK
+  if (pages_left == 0)
+    {
+      cluster_ptr = page_ptr;
+      pages_left = npages;
+    }
+
+  else if ((page_ptr + npages) == cluster_ptr)
+    {
+      cluster_ptr -= npages;
+      pages_left += npages;
+    }
+
+  /* otherwise the page is not freed.  If more than call is
+     done, we probably should worry about it, but at present,
+     the free pages is done right after an allocate.  */
+
+#else	/* MALLOC_CHECK */
+  free (page_ptr);
+
+#endif	/* MALLOC_CHECK */
+}
+
+
+/* Allocate one page (which is initialized to 0).  */
+
+STATIC page_t *
+allocate_page (void)
+{
+#ifndef MALLOC_CHECK
+  if (pages_left == 0)
+    {
+      pages_left = MAX_CLUSTER_PAGES;
+      cluster_ptr = allocate_cluster (MAX_CLUSTER_PAGES);
+    }
+
+  pages_left--;
+  return cluster_ptr++;
+
+#else	/* MALLOC_CHECK */
+  return xcalloc (1, PAGE_SIZE);
+
+#endif	/* MALLOC_CHECK */
+}
+
+
+/* Allocate scoping information.  */
+
+STATIC scope_t *
+allocate_scope (void)
+{
+  scope_t *ptr;
+  static scope_t initial_scope;
+
+#ifndef MALLOC_CHECK
+  ptr = alloc_counts[ (int) alloc_type_scope ].free_list.f_scope;
+  if (ptr != (scope_t *) 0)
+    alloc_counts[ (int) alloc_type_scope ].free_list.f_scope = ptr->free;
+
+  else
+    {
+      int unallocated	= alloc_counts[ (int) alloc_type_scope ].unallocated;
+      page_t *cur_page	= alloc_counts[ (int) alloc_type_scope ].cur_page;
+
+      if (unallocated == 0)
+	{
+	  unallocated = PAGE_SIZE / sizeof (scope_t);
+	  alloc_counts[ (int) alloc_type_scope ].cur_page = cur_page = allocate_page ();
+	  alloc_counts[ (int) alloc_type_scope ].total_pages++;
+	}
+
+      ptr = &cur_page->scope[ --unallocated ];
+      alloc_counts[ (int) alloc_type_scope ].unallocated = unallocated;
+    }
+
+#else
+  ptr = xmalloc (sizeof (scope_t));
+
+#endif
+
+  alloc_counts[ (int) alloc_type_scope ].total_alloc++;
+  *ptr = initial_scope;
+  return ptr;
+}
+
+/* Free scoping information.  */
+
+STATIC void
+free_scope (scope_t *ptr)
+{
+  alloc_counts[ (int) alloc_type_scope ].total_free++;
+
+#ifndef MALLOC_CHECK
+  ptr->free = alloc_counts[ (int) alloc_type_scope ].free_list.f_scope;
+  alloc_counts[ (int) alloc_type_scope ].free_list.f_scope = ptr;
+
+#else
+  free (ptr);
+#endif
+
+}
+
+
+/* Allocate links for pages in a virtual array.  */
+
+STATIC vlinks_t *
+allocate_vlinks (void)
+{
+  vlinks_t *ptr;
+  static vlinks_t initial_vlinks;
+
+#ifndef MALLOC_CHECK
+  int unallocated	= alloc_counts[ (int) alloc_type_vlinks ].unallocated;
+  page_t *cur_page	= alloc_counts[ (int) alloc_type_vlinks ].cur_page;
+
+  if (unallocated == 0)
+    {
+      unallocated = PAGE_SIZE / sizeof (vlinks_t);
+      alloc_counts[ (int) alloc_type_vlinks ].cur_page = cur_page = allocate_page ();
+      alloc_counts[ (int) alloc_type_vlinks ].total_pages++;
+    }
+
+  ptr = &cur_page->vlinks[ --unallocated ];
+  alloc_counts[ (int) alloc_type_vlinks ].unallocated = unallocated;
+
+#else
+  ptr = xmalloc (sizeof (vlinks_t));
+
+#endif
+
+  alloc_counts[ (int) alloc_type_vlinks ].total_alloc++;
+  *ptr = initial_vlinks;
+  return ptr;
+}
+
+
+/* Allocate string hash buckets.  */
+
+STATIC shash_t *
+allocate_shash (void)
+{
+  shash_t *ptr;
+  static shash_t initial_shash;
+
+#ifndef MALLOC_CHECK
+  int unallocated	= alloc_counts[ (int) alloc_type_shash ].unallocated;
+  page_t *cur_page	= alloc_counts[ (int) alloc_type_shash ].cur_page;
+
+  if (unallocated == 0)
+    {
+      unallocated = PAGE_SIZE / sizeof (shash_t);
+      alloc_counts[ (int) alloc_type_shash ].cur_page = cur_page = allocate_page ();
+      alloc_counts[ (int) alloc_type_shash ].total_pages++;
+    }
+
+  ptr = &cur_page->shash[ --unallocated ];
+  alloc_counts[ (int) alloc_type_shash ].unallocated = unallocated;
+
+#else
+  ptr = xmalloc (sizeof (shash_t));
+
+#endif
+
+  alloc_counts[ (int) alloc_type_shash ].total_alloc++;
+  *ptr = initial_shash;
+  return ptr;
+}
+
+
+/* Allocate type hash buckets.  */
+
+STATIC thash_t *
+allocate_thash (void)
+{
+  thash_t *ptr;
+  static thash_t initial_thash;
+
+#ifndef MALLOC_CHECK
+  int unallocated	= alloc_counts[ (int) alloc_type_thash ].unallocated;
+  page_t *cur_page	= alloc_counts[ (int) alloc_type_thash ].cur_page;
+
+  if (unallocated == 0)
+    {
+      unallocated = PAGE_SIZE / sizeof (thash_t);
+      alloc_counts[ (int) alloc_type_thash ].cur_page = cur_page = allocate_page ();
+      alloc_counts[ (int) alloc_type_thash ].total_pages++;
+    }
+
+  ptr = &cur_page->thash[ --unallocated ];
+  alloc_counts[ (int) alloc_type_thash ].unallocated = unallocated;
+
+#else
+  ptr = xmalloc (sizeof (thash_t));
+
+#endif
+
+  alloc_counts[ (int) alloc_type_thash ].total_alloc++;
+  *ptr = initial_thash;
+  return ptr;
+}
+
+
+/* Allocate structure, union, or enum tag information.  */
+
+STATIC tag_t *
+allocate_tag (void)
+{
+  tag_t *ptr;
+  static tag_t initial_tag;
+
+#ifndef MALLOC_CHECK
+  ptr = alloc_counts[ (int) alloc_type_tag ].free_list.f_tag;
+  if (ptr != (tag_t *) 0)
+    alloc_counts[ (int) alloc_type_tag ].free_list.f_tag = ptr->free;
+
+  else
+    {
+      int unallocated	= alloc_counts[ (int) alloc_type_tag ].unallocated;
+      page_t *cur_page	= alloc_counts[ (int) alloc_type_tag ].cur_page;
+
+      if (unallocated == 0)
+	{
+	  unallocated = PAGE_SIZE / sizeof (tag_t);
+	  alloc_counts[ (int) alloc_type_tag ].cur_page = cur_page = allocate_page ();
+	  alloc_counts[ (int) alloc_type_tag ].total_pages++;
+	}
+
+      ptr = &cur_page->tag[ --unallocated ];
+      alloc_counts[ (int) alloc_type_tag ].unallocated = unallocated;
+    }
+
+#else
+  ptr = xmalloc (sizeof (tag_t));
+
+#endif
+
+  alloc_counts[ (int) alloc_type_tag ].total_alloc++;
+  *ptr = initial_tag;
+  return ptr;
+}
+
+/* Free scoping information.  */
+
+STATIC void
+free_tag (tag_t *ptr)
+{
+  alloc_counts[ (int) alloc_type_tag ].total_free++;
+
+#ifndef MALLOC_CHECK
+  ptr->free = alloc_counts[ (int) alloc_type_tag ].free_list.f_tag;
+  alloc_counts[ (int) alloc_type_tag ].free_list.f_tag = ptr;
+
+#else
+  free (ptr);
+#endif
+
+}
+
+
+/* Allocate forward reference to a yet unknown tag.  */
+
+STATIC forward_t *
+allocate_forward (void)
+{
+  forward_t *ptr;
+  static forward_t initial_forward;
+
+#ifndef MALLOC_CHECK
+  ptr = alloc_counts[ (int) alloc_type_forward ].free_list.f_forward;
+  if (ptr != (forward_t *) 0)
+    alloc_counts[ (int) alloc_type_forward ].free_list.f_forward = ptr->free;
+
+  else
+    {
+      int unallocated	= alloc_counts[ (int) alloc_type_forward ].unallocated;
+      page_t *cur_page	= alloc_counts[ (int) alloc_type_forward ].cur_page;
+
+      if (unallocated == 0)
+	{
+	  unallocated = PAGE_SIZE / sizeof (forward_t);
+	  alloc_counts[ (int) alloc_type_forward ].cur_page = cur_page = allocate_page ();
+	  alloc_counts[ (int) alloc_type_forward ].total_pages++;
+	}
+
+      ptr = &cur_page->forward[ --unallocated ];
+      alloc_counts[ (int) alloc_type_forward ].unallocated = unallocated;
+    }
+
+#else
+  ptr = xmalloc (sizeof (forward_t));
+
+#endif
+
+  alloc_counts[ (int) alloc_type_forward ].total_alloc++;
+  *ptr = initial_forward;
+  return ptr;
+}
+
+/* Free scoping information.  */
+
+STATIC void
+free_forward (forward_t *ptr)
+{
+  alloc_counts[ (int) alloc_type_forward ].total_free++;
+
+#ifndef MALLOC_CHECK
+  ptr->free = alloc_counts[ (int) alloc_type_forward ].free_list.f_forward;
+  alloc_counts[ (int) alloc_type_forward ].free_list.f_forward = ptr;
+
+#else
+  free (ptr);
+#endif
+
+}
+
+
+/* Allocate head of type hash list.  */
+
+STATIC thead_t *
+allocate_thead (void)
+{
+  thead_t *ptr;
+  static thead_t initial_thead;
+
+#ifndef MALLOC_CHECK
+  ptr = alloc_counts[ (int) alloc_type_thead ].free_list.f_thead;
+  if (ptr != (thead_t *) 0)
+    alloc_counts[ (int) alloc_type_thead ].free_list.f_thead = ptr->free;
+
+  else
+    {
+      int unallocated	= alloc_counts[ (int) alloc_type_thead ].unallocated;
+      page_t *cur_page	= alloc_counts[ (int) alloc_type_thead ].cur_page;
+
+      if (unallocated == 0)
+	{
+	  unallocated = PAGE_SIZE / sizeof (thead_t);
+	  alloc_counts[ (int) alloc_type_thead ].cur_page = cur_page = allocate_page ();
+	  alloc_counts[ (int) alloc_type_thead ].total_pages++;
+	}
+
+      ptr = &cur_page->thead[ --unallocated ];
+      alloc_counts[ (int) alloc_type_thead ].unallocated = unallocated;
+    }
+
+#else
+  ptr = xmalloc (sizeof (thead_t));
+
+#endif
+
+  alloc_counts[ (int) alloc_type_thead ].total_alloc++;
+  *ptr = initial_thead;
+  return ptr;
+}
+
+/* Free scoping information.  */
+
+STATIC void
+free_thead (thead_t *ptr)
+{
+  alloc_counts[ (int) alloc_type_thead ].total_free++;
+
+#ifndef MALLOC_CHECK
+  ptr->free = (thead_t *) alloc_counts[ (int) alloc_type_thead ].free_list.f_thead;
+  alloc_counts[ (int) alloc_type_thead ].free_list.f_thead = ptr;
+
+#else
+  free (ptr);
+#endif
+
+}
+
+#endif /* MIPS_DEBUGGING_INFO */
+
+
+/* Output an error message and exit.  */
+
+void
+fatal (const char *format, ...)
+{
+  va_list ap;
+
+  va_start (ap, format);
+
+  if (line_number > 0)
+    fprintf (stderr, "%s, %s:%ld ", progname, input_name, line_number);
+  else
+    fprintf (stderr, "%s:", progname);
+
+  vfprintf (stderr, format, ap);
+  va_end (ap);
+  fprintf (stderr, "\n");
+  if (line_number > 0)
+    fprintf (stderr, "line:\t%s\n", cur_line_start);
+
+  saber_stop ();
+  exit (1);
+}
+
+void
+error (const char *format, ...)
+{
+  va_list ap;
+
+  va_start (ap, format);
+
+  if (line_number > 0)
+    fprintf (stderr, "%s, %s:%ld ", progname, input_name, line_number);
+  else
+    fprintf (stderr, "%s:", progname);
+
+  vfprintf (stderr, format, ap);
+  fprintf (stderr, "\n");
+  if (line_number > 0)
+    fprintf (stderr, "line:\t%s\n", cur_line_start);
+
+  had_errors++;
+  va_end (ap);
+
+  saber_stop ();
+}
+
+/* More 'friendly' abort that prints the line and file.  */
+
+void
+fancy_abort (const char *file, int line, const char *func)
+{
+  fatal ("abort in %s, at %s:%d", func, file, line);
+}
+
+
+/* When `malloc.c' is compiled with `rcheck' defined,
+   it calls this function to report clobberage.  */
+
+void
+botch (const char *s)
+{
+  fatal ("%s", s);
+}