* write.c (write_contents): Compute the relocs before writing out
authorIan Lance Taylor <ian@airs.com>
Mon, 15 Mar 1993 23:58:22 +0000 (23:58 +0000)
committerIan Lance Taylor <ian@airs.com>
Mon, 15 Mar 1993 23:58:22 +0000 (23:58 +0000)
the section contents.
* config/obj-ecoff.h, config/obj-ecoff.c: Numerous changes to get
symbol table and values right.
* config/tc-mips.h (LOCAL_LABEL): If OBJ_ECOFF, any label starting
with $L is local.
* config/tc-mips.c (tc_gen_reloc): If OBJ_ECOFF, adjust the addend
by the section vma.

* config/z8k.mt (TARG_CPU_DEPENDENTS): The relevant file is
z8k-opc.h, not z8k.h.

gas/ChangeLog
gas/config/obj-ecoff.c [new file with mode: 0644]
gas/config/tc-mips.c [new file with mode: 0644]
gas/config/z8k.mt
gas/write.c

index ae505441f98e12b38d6978ef4cb0ca5adf9bd2fc..bfc4e5426fe872f4a3ab999f1c02555198cf484b 100644 (file)
@@ -1,5 +1,17 @@
 Mon Mar 15 12:17:28 1993  Ian Lance Taylor  (ian@cygnus.com)
 
+       * write.c (write_contents): Compute the relocs before writing out
+       the section contents.
+       * config/obj-ecoff.h, config/obj-ecoff.c: Numerous changes to get
+       symbol table and values right.
+       * config/tc-mips.h (LOCAL_LABEL): If OBJ_ECOFF, any label starting
+       with $L is local.
+       * config/tc-mips.c (tc_gen_reloc): If OBJ_ECOFF, adjust the addend
+       by the section vma.
+
+       * config/z8k.mt (TARG_CPU_DEPENDENTS): The relevant file is
+       z8k-opc.h, not z8k.h.
+
        * config/obj-coffbfd.c (obj_coff_endef): Correct test for .bf
        symbol.
 
diff --git a/gas/config/obj-ecoff.c b/gas/config/obj-ecoff.c
new file mode 100644 (file)
index 0000000..1e29ab8
--- /dev/null
@@ -0,0 +1,4822 @@
+/* ECOFF object file format.
+   Copyright (C) 1993 Free Software Foundation, Inc.
+   Contributed by Cygnus Support.
+   This file was put together by Ian Lance Taylor <ian@cygnus.com>.  A
+   good deal of it comes directly from mips-tfile.c, by Michael
+   Meissner <meissner@osf.org>.
+
+   This file is part of GAS.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   GAS 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 GAS; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "as.h"
+#include "coff/internal.h"
+#include "coff/mips.h"
+#include "coff/sym.h"
+#include "coff/symconst.h"
+#include "coff/ecoff-ext.h"
+#include "aout/stab_gnu.h"
+#include "../bfd/libecoff.h"
+
+/* Why isn't this in coff/sym.h?  */
+#define ST_RFDESCAPE 0xfff
+
+/* The ECOFF file format uses COFF style sections, but with a unique
+   debugging format.  We just build generic BFD sections since BFD
+   knows how to write them out.  The debugging format, however, we
+   must construct here; all BFD knows at the moment is to write it out
+   as a large block of data.
+
+   We support both COFF style debugging and stabs debugging (the stabs
+   symbols are encapsulated in COFF symbols).  This should let us
+   handle anything the compiler might throw at us.  Parsing the COFF
+   and stabs debugging information is similar to work done by COFF and
+   a.out targets, but since the result is completely different the
+   code is not shared.  */
+
+/* Here is a brief description of the MIPS ECOFF symbol table, by
+   Michael Meissner.  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 indexs 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 }
+\f
+*/
+/* Redefinition of 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 (ie. 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 */
+  bt_Void      = btVoid,       /* Void */
+  bt_Max       = btMax         /* Max basic type+1 */
+} bt_t;
+
+#define N_TQ itqMax
+
+/* 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_lineno,           /* line number list */
+  alloc_type_last              /* last+1 element for array bounds */
+};
+
+/* Types of auxiliary type information.  */
+enum aux_type {
+  aux_tir,                     /* TIR type information */
+  aux_rndx,                    /* relative index into symbol table */
+  aux_dnLow,                   /* low dimension */
+  aux_dnHigh,                  /* high dimension */
+  aux_isym,                    /* symbol table index (end of proc) */
+  aux_iss,                     /* index into string space (not used) */
+  aux_width,                   /* width for non-default sized struc fields */
+  aux_count                    /* count of ranges for variant arm */
+};
+\f
+/* 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, the
+   MAX_CLUSTER_PAGES should be 1 less than a power of two, since
+   malloc adds it's overhead, and rounds up to the next power of 2.
+   Pages are linked together via a linked list.
+
+   If PAGE_SIZE is > 4096, the string length in the shash_t structure
+   can't be represented (assuming there are strings > 4096 bytes).  */
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096         /* size of varray pages */
+#endif
+
+#define PAGE_USIZE ((unsigned long) PAGE_SIZE)
+
+
+#ifndef MAX_CLUSTER_PAGES      /* # pages to get from system */
+#define MAX_CLUSTER_PAGES 63
+#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 */                 \
+}
+
+
+/* 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 */
+  struct localsym *lsym;       /* pointer to local symbol node */
+  st_t          type;          /* type of the node */
+} scope_t;
+
+
+/* For a local symbol we store a gas symbol as well as the debugging
+   information we generate.  The gas symbol will be NULL if this is
+   only a debugging symbol.  */
+typedef struct localsym {
+  symbolS *as_sym;             /* symbol as seen by gas */
+  struct efdr *file_ptr;       /* file pointer */
+  struct ecoff_proc *proc_ptr; /* proc pointer */
+  struct localsym *begin_ptr;  /* symbol at start of block */
+  struct ecoff_aux *index_ptr; /* index value to be filled in */
+  struct forward *forward_ref; /* forward references to this symbol */
+  long sym_index;              /* final symbol index */
+  SYMR ecoff_sym;              /* ECOFF debugging symbol */
+} localsym_t;
+
+
+/* For aux information we keep the type and the data.  */
+typedef struct ecoff_aux {
+  enum aux_type type;          /* aux type */
+  AUXU data;                   /* aux data */
+} aux_t;
+
+/* For a procedure we store the gas symbol as well as the PDR
+   debugging information.  */
+typedef struct ecoff_proc {
+  localsym_t *sym;             /* associated symbol */
+  PDR pdr;                     /* ECOFF debugging info */
+} proc_t;
+
+/* Number of proc_t structures allocated.  */
+static unsigned long proc_cnt;
+
+
+/* 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 */
+  aux_t                 *ifd_ptr;      /* pointer to store file index */
+  aux_t                 *index_ptr;    /* pointer to store symbol index */
+} 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 */
+  localsym_t    *sym;          /* 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 entry.  */
+
+typedef struct shash {
+  char         *string;        /* string we are hashing */
+  symint_t      indx;          /* index within string table */
+  EXTR         *esym_ptr;      /* global symbol pointer */
+  localsym_t   *sym_ptr;       /* local symbol pointer */
+  localsym_t   *end_ptr;       /* symbol pointer to end block */
+  tag_t                *tag_ptr;       /* tag pointer */
+  proc_t       *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.  */
+
+#define HASHBITS 30
+
+#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 */
+  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 */
+  struct hash_control *str_hash;       /* string hash table */
+  thash_t      *thash_head[THASH_SIZE];
+} efdr_t;
+
+/* Pre-initialized extended file structure.  */
+static const efdr_t init_file = 
+{
+  {                    /* FDR structure */
+    0,                 /* adr:         memory address of beginning of file */
+    0,                 /* rss:         file name (of source, if known) */
+    0,                 /* issBase:     file's string space */
+    0,                 /* cbSs:        number of bytes in the ss */
+    0,                 /* isymBase:    beginning of symbols */
+    0,                 /* csym:        count file's of symbols */
+    0,                 /* ilineBase:   file's line symbols */
+    0,                 /* cline:       count of file's line symbols */
+    0,                 /* ioptBase:    file's optimization entries */
+    0,                 /* copt:        count of file's optimization entries */
+    0,                 /* ipdFirst:    start of procedures for this file */
+    0,                 /* cpd:         count of procedures for this file */
+    0,                 /* iauxBase:    file's auxiliary entries */
+    0,                 /* caux:        count of file's auxiliary entries */
+    0,                 /* rfdBase:     index into the file indirect table */
+    0,                 /* crfd:        count file indirect entries */
+    langC,             /* lang:        language for this file */
+    1,                 /* fMerge:      whether this file can be merged */
+    0,                 /* fReadin:     true if read in (not just created) */
+#ifdef TARGET_BYTES_BIG_ENDIAN
+    1,                 /* fBigendian:  if 1, compiled on big endian machine */
+#else
+    0,                 /* fBigendian:  if 1, compiled on big endian machine */
+#endif
+    GLEVEL_2,          /* glevel:      level this file was compiled with */
+    0,                 /* reserved:    reserved for future use */
+    0,                 /* cbLineOffset: byte offset from header for this file ln's */
+    0,                 /* cbLine:      size of lines for this file */
+  },
+
+  (FDR *)0,            /* orig_fdr:    original file header pointer */
+  (char *)0,           /* name:        pointer to filename */
+  0,                   /* void_type:   ptr to aux node for void type */
+  0,                   /* int_type:    ptr to aux node for int type */
+  (scope_t *)0,                /* cur_scope:   current scope being processed */
+  0,                   /* file_index:  current file # */
+  0,                   /* nested_scopes: # nested scopes */
+  INIT_VARRAY (char),  /* strings:     local string varray */
+  INIT_VARRAY (localsym_t),    /* symbols:     local symbols varray */
+  INIT_VARRAY (proc_t),        /* procs:       procedure varray */
+  INIT_VARRAY (aux_t), /* aux_syms:    auxiliary symbols varray */
+
+  (struct efdr *)0,    /* next_file:   next file structure */
+
+  (struct hash_control *)0,    /* str_hash:    string hash table */
+  { 0 },               /* thash_head:  type hash table */
+};
+
+
+static efdr_t *first_file;                     /* first file descriptor */
+static efdr_t **last_file_ptr = &first_file;   /* file descriptor tail */
+
+
+/* Line number information is kept in a list until the assembly is
+   finished.  */
+typedef struct lineno_list {
+  struct lineno_list *next;    /* next element in list */
+  efdr_t *file;                        /* file this line is in */
+  proc_t *proc;                        /* procedure this line is in */
+  fragS *frag;                 /* fragment this line number is in */
+  unsigned long paddr;         /* offset within fragment */
+  long lineno;                 /* actual line number */
+} lineno_list_t;
+
+static lineno_list_t *first_lineno;
+static lineno_list_t **last_lineno_ptr = &first_lineno;
+
+/* Sometimes there will be some .loc statements before a .ent.  We
+   keep them in this list so that we can fill in the procedure pointer
+   after we see the .ent.  */
+static lineno_list_t *noproc_lineno;
+
+/* 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)           ];
+  proc_t       proc    [ PAGE_SIZE / sizeof (proc_t)        ];
+  localsym_t   sym     [ PAGE_SIZE / sizeof (localsym_t)    ];
+  aux_t                aux     [ PAGE_SIZE / sizeof (aux_t)         ];
+  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)       ];
+  lineno_list_t        lineno  [ PAGE_SIZE / sizeof (lineno_list_t) ];
+} page_t;
+
+
+/* Structure holding allocation information for small sized structures.  */
+typedef struct alloc_info {
+  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 */
+  int        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 */
+  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 const 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 */
+  {                                    /* 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 hash table for the tags table and global table for file
+   descriptors.  */
+
+static varray_t file_desc      = INIT_VARRAY (efdr_t);
+
+static struct hash_control *tag_hash;
+
+/* 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 symbolS *last_func_sym_value;
+
+
+/* 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.
+
+   It would make sense for the .type and .scl directives to use the
+   ECOFF numbers directly, rather than using the COFF numbers and
+   mapping them.  Unfortunately, this is historically what mips-tfile
+   expects, and changing gcc now would be a considerable pain (the
+   native compiler generates debugging information internally, rather
+   than via the assembler, so it will never use .type or .scl).  */
+
+static const bt_t map_coff_types[] = {
+  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[] = {
+  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[] = {
+  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 ??? */
+};
+
+
+/* Keep track of different sized allocation requests.  */
+static alloc_info_t alloc_counts[ (int)alloc_type_last ];
+\f
+/* Various statics.  */
+static efdr_t  *cur_file_ptr   = (efdr_t *) 0; /* current file desc. header */
+static proc_t  *cur_proc_ptr   = (proc_t *) 0; /* current procedure header */
+static thead_t *cur_tag_head   = (thead_t *)0; /* current tag head */
+#ifdef ECOFF_DEBUG
+static int     debug           = 0;            /* trace functions */
+#endif
+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 char stabs_symbol[] = STABS_SYMBOL;
+\f
+/* Prototypes for functions defined in this file.  */
+
+static void add_varray_page PARAMS ((varray_t *vp));
+static symint_t add_string PARAMS ((varray_t *vp,
+                                   struct hash_control *hash_tbl,
+                                   const char *str,
+                                   shash_t **ret_hash));
+static localsym_t *add_ecoff_symbol PARAMS ((const char *str, st_t type,
+                                            sc_t storage, symbolS *sym,
+                                            symint_t value,
+                                            symint_t indx));
+static symint_t add_aux_sym_symint PARAMS ((symint_t aux_word));
+static symint_t add_aux_sym_rndx PARAMS ((int file_index,
+                                         symint_t sym_index));
+static symint_t add_aux_sym_tir PARAMS ((type_info_t *t,
+                                        hash_state_t state,
+                                        thash_t **hash_tbl));
+static tag_t *get_tag PARAMS ((const char *tag, localsym_t *sym,
+                              bt_t basic_type));
+static void add_unknown_tag PARAMS ((tag_t *ptag));
+static void add_procedure PARAMS ((char *func));
+static void add_file PARAMS ((const char *file_name, int indx));
+#ifdef ECOFF_DEBUG
+static char *sc_to_string PARAMS ((sc_t storage_class));
+static char *st_to_string PARAMS ((st_t symbol_type));
+#endif
+static void obj_ecoff_def PARAMS ((int));
+static void obj_ecoff_dim PARAMS ((int));
+static void obj_ecoff_endef PARAMS ((int));
+static void obj_ecoff_file PARAMS ((int));
+static void obj_ecoff_scl PARAMS ((int));
+static void obj_ecoff_size PARAMS ((int));
+static void obj_ecoff_tag PARAMS ((int));
+static void obj_ecoff_type PARAMS ((int));
+static void obj_ecoff_val PARAMS ((int));
+static void obj_ecoff_stab PARAMS ((int));
+static void obj_ecoff_ent PARAMS ((int));
+static void obj_ecoff_begin PARAMS ((int));
+static void obj_ecoff_bend PARAMS ((int));
+static void obj_ecoff_end PARAMS ((int));
+static void obj_ecoff_fmask PARAMS ((int));
+static void obj_ecoff_frame PARAMS ((int));
+static void obj_ecoff_loc PARAMS ((int));
+static void obj_ecoff_mask PARAMS ((int));
+static void mark_stabs PARAMS ((int));
+static char *ecoff_add_bytes PARAMS ((char **buf, char **bufend,
+                                     char *bufptr, long need));
+static long ecoff_longword_adjust PARAMS ((char **buf, char **bufend,
+                                          long offset, char **bufptrptr));
+static long ecoff_build_lineno PARAMS ((char **buf, char **bufend,
+                                       long offset, long *linecntptr));
+static long ecoff_build_symbols PARAMS ((char **buf, char **bufend,
+                                        long offset,
+                                        char **extbuf, char **extbufend,
+                                        long *extoffset,
+                                        varray_t *ext_strings,
+                                        struct hash_control *ext_str_hash));
+static long ecoff_build_procs PARAMS ((char **buf, char **bufend,
+                                      long offset));
+static long ecoff_build_aux PARAMS ((char **buf, char **bufend,
+                                    long offset));
+static long ecoff_build_strings PARAMS ((char **buf, char **bufend,
+                                        long offset,
+                                        varray_t *vp));
+static long ecoff_build_ss PARAMS ((char **buf, char **bufend,
+                                    long offset));
+static long ecoff_build_fdr PARAMS ((char **buf, char **bufend,
+                                    long offset));
+static void ecoff_set_vma PARAMS ((void));
+static page_t *allocate_cluster PARAMS ((unsigned long npages));
+static page_t *allocate_page PARAMS ((void));
+static scope_t *allocate_scope PARAMS ((void));
+static void free_scope PARAMS ((scope_t *ptr));
+static vlinks_t *allocate_vlinks PARAMS ((void));
+static shash_t *allocate_shash PARAMS ((void));
+static thash_t *allocate_thash PARAMS ((void));
+static tag_t *allocate_tag PARAMS ((void));
+static void free_tag PARAMS ((tag_t *ptr));
+static forward_t *allocate_forward PARAMS ((void));
+static thead_t *allocate_thead PARAMS ((void));
+static void free_thead PARAMS ((thead_t *ptr));
+static lineno_list_t *allocate_lineno_list PARAMS ((void));
+
+/* Why isn't this in some header file somewhere?  In fact, is it even
+   necessary?  */
+#define SKIP_WHITESPACES() \
+  do \
+    { \
+      while (*input_line_pointer == ' ' \
+            || *input_line_pointer == '\t') \
+       ++input_line_pointer; \
+    } \
+  while (0)
+\f
+/* These are the pseudo-ops we support in this file.  Only those
+   relating to debugging information are supported here.
+
+   The following pseudo-ops from the Kane and Heinrich MIPS book
+   should be defined here, but are currently unsupported: .aent,
+   .bgnb, .endb, .verstamp, .vreg.
+
+   The following pseudo-ops from the Kane and Heinrich MIPS book are
+   MIPS CPU specific, and should be defined by tc-mips.c: .alias,
+   .extern, .galive, .gjaldef, .gjrlive, .livereg, .noalias, .option,
+   .rdata, .sdata, .set.
+
+   The following pseudo-ops from the Kane and Heinrich MIPS book are
+   not MIPS CPU specific, but are also not ECOFF specific.  I have
+   only listed the ones which are not already in read.c.  It's not
+   completely clear where these should be defined, but tc-mips.c is
+   probably the most reasonable place: .asciiz, .asm0, .endr, .err,
+   .half, .lab, .repeat, .struct, .weakext.  */
+
+const pseudo_typeS obj_pseudo_table[] =
+{
+  /* COFF style debugging information. .ln is not used; .loc is used
+     instead.  */
+  { "def",     obj_ecoff_def,          0 },
+  { "dim",     obj_ecoff_dim,          0 },
+  { "endef",   obj_ecoff_endef,        0 },
+  { "file",    obj_ecoff_file,         0 },
+  { "scl",     obj_ecoff_scl,          0 },
+  { "size",    obj_ecoff_size,         0 },
+  { "tag",     obj_ecoff_tag,          0 },
+  { "type",    obj_ecoff_type,         0 },
+  { "val",     obj_ecoff_val,          0 },
+
+  /* stabs debugging information.  */
+  { "stabd",   obj_ecoff_stab,         'd' },
+  { "stabn",   obj_ecoff_stab,         'n' },
+  { "stabs",   obj_ecoff_stab,         's' },
+
+  /* ECOFF specific debugging information.  */
+  { "begin",   obj_ecoff_begin,        0 },
+  { "bend",    obj_ecoff_bend,         0 },
+  { "end",     obj_ecoff_end,          0 },
+  { "ent",     obj_ecoff_ent,          0 },
+  { "fmask",   obj_ecoff_fmask,        0 },
+  { "frame",   obj_ecoff_frame,        0 },
+  { "loc",     obj_ecoff_loc,          0 },
+  { "mask",    obj_ecoff_mask,         0 },
+
+  /* Sentinel.  */
+  { NULL }
+};
+\f
+/* This function is called when the assembler starts up.  */
+
+void
+obj_read_begin_hook ()
+{
+  tag_hash = hash_new ();
+  if (tag_hash == (struct hash_control *) NULL)
+    as_fatal ("Can't create hash table");
+}
+
+/* This function is called when a symbol is created.  */
+
+void
+obj_symbol_new_hook (symbolP)
+     symbolS *symbolP;
+{
+  symbolP->ecoff_file = cur_file_ptr;
+  symbolP->ecoff_symbol = 0;
+}
+\f
+/* Add a page to a varray object.  */
+
+static void
+add_varray_page (vp)
+     varray_t *vp;                             /* varray to add page to */
+{
+  vlinks_t *new_links = allocate_vlinks ();
+
+#ifdef MALLOC_CHECK
+  if (vp->object_size > 1)
+    new_links->datum = (page_t *) 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 *) NULL)          /* 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;
+    }
+}
+\f
+/* Add a string (and null pad) to one of the string tables.  */
+
+static symint_t
+add_string (vp, hash_tbl, str, ret_hash)
+     varray_t *vp;                     /* string obstack */
+     struct hash_control *hash_tbl;    /* ptr to hash table */
+     const char *str;                  /* string */
+     shash_t **ret_hash;               /* return hash pointer */
+{
+  register unsigned int len = strlen (str);
+  register shash_t *hash_ptr;
+
+  if (len >= PAGE_USIZE)
+    as_fatal ("String too big (%lu bytes)", len);
+
+  hash_ptr = (shash_t *) hash_find (hash_tbl, str);
+  if (hash_ptr == (shash_t *) NULL)
+    {
+      register char *err;
+
+      if (vp->objects_last_page + len >= 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->indx = vp->num_allocated;
+
+      hash_ptr->string = &vp->last->datum->byte[vp->objects_last_page];
+
+      vp->objects_last_page += len + 1;
+      vp->num_allocated += len + 1;
+
+      strcpy (hash_ptr->string, str);
+
+      err = hash_insert (hash_tbl, str, (char *) hash_ptr);
+      if (*err != '\0')
+       as_fatal ("Inserting \"%s\" into string hash table: %s",
+                 str, err);
+    }
+
+  if (ret_hash != (shash_t **) NULL)
+    *ret_hash = hash_ptr;
+
+  return hash_ptr->indx;
+}
+\f
+/* Add debugging information for a symbol.  */
+
+static localsym_t *
+add_ecoff_symbol (str, type, storage, sym_value, value, indx)
+     const char *str;                  /* symbol name */
+     st_t type;                                /* symbol type */
+     sc_t storage;                     /* storage class */
+     symbolS *sym_value;               /* associated symbol.  */
+     symint_t value;                   /* value of symbol */
+     symint_t indx;                    /* index to local/aux. syms */
+{
+  localsym_t *psym;
+  register scope_t *pscope;
+  register thead_t *ptag_head;
+  register tag_t *ptag;
+  register tag_t *ptag_next;
+  register varray_t *vp = &cur_file_ptr->symbols;
+  register int scope_delta = 0;
+  shash_t *hash_ptr = (shash_t *) NULL;
+
+  if (cur_file_ptr == (efdr_t *) NULL)
+    as_fatal ("no current file pointer");
+
+  vp = &cur_file_ptr->symbols;
+
+ if (vp->objects_last_page == vp->objects_per_page)
+    add_varray_page (vp);
+
+  psym = &vp->last->datum->sym[ vp->objects_last_page++ ];
+
+  psym->as_sym = sym_value;
+  if (sym_value != (symbolS *) NULL)
+    sym_value->ecoff_symbol = 1;
+  psym->file_ptr = cur_file_ptr;
+  psym->proc_ptr = cur_proc_ptr;
+  psym->begin_ptr = (localsym_t *) NULL;
+  psym->index_ptr = (aux_t *) NULL;
+  psym->forward_ref = (forward_t *) NULL;
+  psym->sym_index = -1;
+  psym->ecoff_sym.value = value;
+  psym->ecoff_sym.st = (unsigned) type;
+  psym->ecoff_sym.sc = (unsigned) storage;
+  psym->ecoff_sym.index = indx;
+
+  /* If there is an associated symbol, we wait until the end of the
+     assembly before deciding where to put the name (it may be just an
+     external symbol).  Otherwise, this is just a debugging symbol and
+     the name should go with the current file.  */
+  if (sym_value == (symbolS *) NULL)
+    psym->ecoff_sym.iss = ((str == (const char *) NULL)
+                          ? 0
+                          : add_string (&cur_file_ptr->strings,
+                                        cur_file_ptr->str_hash,
+                                        str,
+                                        &hash_ptr));
+
+  ++vp->num_allocated;
+
+  if (MIPS_IS_STAB (&psym->ecoff_sym))
+    return psym;
+
+  /* Save the symbol within the hash table if this is a static
+     item, and it has a name.  */
+  if (hash_ptr != (shash_t *) NULL
+      && (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->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 *) NULL)
+       as_fatal ("too many st_End's");
+      else
+       {
+         st_t begin_type = (st_t) pscope->lsym->ecoff_sym.st;
+
+         psym->begin_ptr = pscope->lsym;
+
+         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 *) NULL;
+                  ptag = ptag_next)
+               {
+                 if (ptag->forward_ref != (forward_t *) NULL)
+                   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;
+
+         /* block begin gets next sym #.  This is set when we know
+            the symbol index value.  */
+
+         /* Functions push two or more aux words as follows:
+            1st word: index+1 of the end symbol (filled in later).
+            2nd word: type of the function (plus any aux words needed).
+            Also, tie the external pointer back to the function begin symbol.  */
+         if (begin_type != st_File && begin_type != st_Block)
+           {
+             symint_t type;
+             varray_t *vp = &cur_file_ptr->aux_syms;
+
+             pscope->lsym->ecoff_sym.index = add_aux_sym_symint (0);
+             pscope->lsym->index_ptr =
+               &vp->last->datum->aux[vp->objects_last_page - 1];
+             type = add_aux_sym_tir (&last_func_type_info,
+                                     hash_no,
+                                     &cur_file_ptr->thash_head[0]);
+/*
+             if (last_func_sym_value != (symbolS *) NULL)
+               {
+                 last_func_sym_value->ifd = cur_file_ptr->file_index;
+                 last_func_sym_value->index = type;
+               }
+ */
+           }
+
+         free_scope (pscope);
+       }
+    }
+
+  cur_file_ptr->nested_scopes += scope_delta;
+
+#ifdef ECOFF_DEBUG
+  if (debug && type != st_File
+      && (debug > 2 || type == st_Block || type == st_End
+         || type == st_Proc || type == st_StaticProc))
+    {
+      char *sc_str = sc_to_string (storage);
+      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, str_end_p1 - str_start, str_start);
+      else
+       {
+         unsigned long len = strlen (st_str);
+         fprintf (stderr, " st= %.*s\n", len-1, st_str);
+       }
+    }
+#endif
+
+  return psym;
+}
+\f
+/* Add an auxiliary symbol (passing a symint).  This is actually used
+   for integral aux types, not just symints.  */
+
+static symint_t
+add_aux_sym_symint (aux_word)
+     symint_t aux_word;                /* auxiliary information word */
+{
+  register varray_t *vp;
+  register aux_t *aux_ptr;
+
+  if (cur_file_ptr == (efdr_t *) NULL)
+    as_fatal ("no current file pointer");
+
+  vp = &cur_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->type = aux_isym;
+  aux_ptr->data.isym = aux_word;
+
+  return vp->num_allocated++;
+}
+
+
+/* Add an auxiliary symbol (passing a file/symbol index combo).  */
+
+static symint_t
+add_aux_sym_rndx (file_index, sym_index)
+     int file_index;
+     symint_t sym_index;
+{
+  register varray_t *vp;
+  register aux_t *aux_ptr;
+
+  if (cur_file_ptr == (efdr_t *) NULL)
+    as_fatal ("no current file pointer");
+
+  vp = &cur_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->type = aux_rndx;
+  aux_ptr->data.rndx.rfd   = file_index;
+  aux_ptr->data.rndx.index = sym_index;
+
+  return vp->num_allocated++;
+}
+\f
+/* Add an auxiliary symbol (passing the basic type and possibly
+   type qualifiers).  */
+
+static symint_t
+add_aux_sym_tir (t, state, hash_tbl)
+     type_info_t *t;           /* current type information */
+     hash_state_t state;       /* whether to hash type or not */
+     thash_t **hash_tbl;       /* pointer to hash table to use */
+{
+  register varray_t *vp;
+  register aux_t *aux_ptr;
+  static AUXU init_aux;
+  symint_t ret;
+  int i;
+  AUXU aux;
+
+  if (cur_file_ptr == (efdr_t *) NULL)
+    as_fatal ("no current file pointer");
+
+  vp = &cur_file_ptr->aux_syms;
+
+  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)
+    {
+      register thash_t *hash_ptr;
+      register 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 *) NULL && state == hash_yes)
+       return hash_ptr->indx;
+
+      if (hash_ptr == (thash_t *) NULL)
+       {
+         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->type = aux_tir;
+  aux_ptr->data = 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)
+    {
+      register symint_t file_index = t->tag_ptr->ifd;
+      register localsym_t *sym  = t->tag_ptr->sym;
+      register forward_t *forward_ref = allocate_forward ();
+
+      if (sym != (localsym_t *) NULL)
+       {
+         forward_ref->next = sym->forward_ref;
+         sym->forward_ref = forward_ref;
+       }
+      else
+       {
+         forward_ref->next = t->tag_ptr->forward_ref;
+         t->tag_ptr->forward_ref = forward_ref;
+       }
+
+      (void) add_aux_sym_rndx (ST_RFDESCAPE, indexNil);
+      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;
+}
+\f
+/* Add a tag to the tag table (unless it already exists).  */
+
+static tag_t *
+get_tag (tag, sym, basic_type)
+     const char *tag;                  /* tag name */
+     localsym_t *sym;                  /* tag start block */
+     bt_t basic_type;                  /* bt_Struct, bt_Union, or bt_Enum */
+{
+  shash_t *hash_ptr;
+  char *err;
+  tag_t *tag_ptr;
+
+  if (cur_file_ptr == (efdr_t *) NULL)
+    as_fatal ("no current file pointer");
+
+  hash_ptr = (shash_t *) hash_find (tag_hash, tag);
+
+  if (hash_ptr != (shash_t *) NULL
+      && hash_ptr->tag_ptr != (tag_t *) NULL)
+  {
+    tag_ptr = hash_ptr->tag_ptr;
+    if (sym != (localsym_t *) NULL)
+      {
+       tag_ptr->basic_type = basic_type;
+       tag_ptr->ifd        = cur_file_ptr->file_index;
+       tag_ptr->sym        = sym;
+      }
+    return tag_ptr;
+  }
+
+  if (hash_ptr == (shash_t *) NULL)
+    {
+      hash_ptr = allocate_shash ();
+      err = hash_insert (tag_hash, tag, (char *) hash_ptr);
+      if (*err != '\0')
+       as_fatal ("Inserting \"%s\" into tag hash table: %s",
+                 tag, err);
+    }
+
+  tag_ptr = allocate_tag ();
+  tag_ptr->forward_ref = (forward_t *) NULL;
+  tag_ptr->hash_ptr    = hash_ptr;
+  tag_ptr->same_name   = hash_ptr->tag_ptr;
+  tag_ptr->basic_type  = basic_type;
+  tag_ptr->sym         = sym;
+  tag_ptr->ifd         = ((sym == (localsym_t *) NULL)
+                          ? -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;
+}
+\f
+/* Add an unknown {struct, union, enum} tag.  */
+
+static void
+add_unknown_tag (ptag)
+     tag_t     *ptag;          /* pointer to tag information */
+{
+  shash_t *hash_ptr    = ptag->hash_ptr;
+  char *name           = hash_ptr->string;
+  localsym_t *sym;
+  forward_t **pf;
+
+#ifdef ECOFF_DEBUG
+  if (debug > 1)
+    {
+      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,
+              hash_ptr->len, name_start);
+    }
+#endif
+
+  sym = add_ecoff_symbol (name,
+                         st_Block,
+                         sc_Info,
+                         (symbolS *) NULL,
+                         (symint_t) 0,
+                         (symint_t) 0);
+
+  (void) add_ecoff_symbol (name,
+                          st_End,
+                          sc_Info,
+                          (symbolS *) NULL,
+                          (symint_t) 0,
+                          (symint_t) 0);
+
+  for (pf = &sym->forward_ref; *pf != (forward_t *) NULL; pf = &(*pf)->next)
+    ;
+  *pf = ptag->forward_ref;
+}
+\f
+/* Add a procedure to the current file's list of procedures, and record
+   this is the current procedure.  */
+
+static void
+add_procedure (func)
+     char *func;                       /* func name */
+{
+  register varray_t *vp;
+  register proc_t *new_proc_ptr;
+
+#ifdef ECOFF_DEBUG
+  if (debug)
+    fputc ('\n', stderr);
+#endif
+
+  if (cur_file_ptr == (efdr_t *) NULL)
+    as_fatal ("no current file pointer");
+
+  vp = &cur_file_ptr->procs;
+
+  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++;
+
+  new_proc_ptr->pdr.isym = -1;
+  new_proc_ptr->pdr.iline = -1;
+
+  /* Push the start of the function.  */
+  new_proc_ptr->sym = add_ecoff_symbol (func, st_Proc, sc_Text,
+                                       symbol_find_or_make (func),
+                                       (symint_t) 0, (symint_t) 0);
+
+  ++proc_cnt;
+
+  /* Fill in the linenos preceding the .ent, if any.  */
+  if (noproc_lineno != (lineno_list_t *) NULL)
+    {
+      lineno_list_t *l;
+
+      for (l = noproc_lineno; l != (lineno_list_t *) NULL; l = l->next)
+       l->proc = new_proc_ptr;
+      *last_lineno_ptr = noproc_lineno;
+      while (*last_lineno_ptr != NULL)
+       last_lineno_ptr = &(*last_lineno_ptr)->next;
+      noproc_lineno = (lineno_list_t *) NULL;
+    }
+}
+\f
+/* 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 (file_name, indx)
+     const char *file_name;            /* file name */
+     int indx;
+{
+  register int first_ch;
+  register efdr_t *fil_ptr;
+
+#ifdef ECOFF_DEBUG
+  if (debug)
+    fprintf (stderr, "\tfile\t%.*s\n", len, file_start);
+#endif
+
+  /* If the file name is NULL, then no .file symbol appeared, and we
+     want to use the actual file name.  Unfortunately, we don't have a
+     clean way to access it.  */
+  if (file_name == (const char *) NULL)
+    {
+      extern char *logical_input_file;
+      extern char *physical_input_file;
+
+      if (first_file != (efdr_t *) NULL)
+       as_fatal ("fake .file after real one");
+      file_name = logical_input_file;
+      if (file_name == (const char *) NULL)
+       {
+         file_name = physical_input_file;
+         if (file_name == (const char *) NULL)
+           file_name = "UNKNOWN";
+       }
+    }
+
+  first_ch = *file_name;
+
+  /* See if the file has already been created.  */
+  for (fil_ptr = first_file;
+       fil_ptr != (efdr_t *) NULL;
+       fil_ptr = fil_ptr->next_file)
+    {
+      if (first_ch == fil_ptr->name[0]
+         && strcmp (file_name, fil_ptr->name) == 0)
+       {
+         cur_file_ptr = fil_ptr;
+         break;
+       }
+    }
+
+  /* If this is a new file, create it. */
+  if (fil_ptr == (efdr_t *) NULL)
+    {
+      if (file_desc.objects_last_page == file_desc.objects_per_page)
+       add_varray_page (&file_desc);
+
+      fil_ptr = cur_file_ptr =
+       &file_desc.last->datum->file[file_desc.objects_last_page++];
+      *fil_ptr = init_file;
+
+      fil_ptr->file_index = indx;
+      ++file_desc.num_allocated;
+
+      /* Allocate the string hash table.  */
+      fil_ptr->str_hash = hash_new ();
+      if (fil_ptr->str_hash == (struct hash_control *) NULL)
+       as_fatal ("Can't create hash table");
+
+      /* Make sure 0 byte in string table is null  */
+      add_string (&fil_ptr->strings,
+                 fil_ptr->str_hash,
+                 "",
+                 (shash_t **)0);
+
+      if (strlen (file_name) > PAGE_USIZE - 2)
+       as_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_ecoff_symbol (file_name, st_File, sc_Text,
+                              (symbolS *) NULL,
+                              (symint_t) 0, (symint_t) 0);
+      fil_ptr->fdr.rss = 1;
+      fil_ptr->name = &fil_ptr->strings.last->datum->byte[1];
+
+      /* Update the linked list of file descriptors.  */
+      *last_file_ptr = fil_ptr;
+      last_file_ptr = &fil_ptr->next_file;
+
+      /* Add void & int types to the file (void should be first to catch
+        errant 0's within the index fields).  */
+      fil_ptr->void_type = add_aux_sym_tir (&void_type_info,
+                                           hash_yes,
+                                           &cur_file_ptr->thash_head[0]);
+
+      fil_ptr->int_type = add_aux_sym_tir (&int_type_info,
+                                          hash_yes,
+                                          &cur_file_ptr->thash_head[0]);
+    }
+}
+\f
+#ifdef ECOFF_DEBUG
+
+/* Convert storage class to string.  */
+
+static char *
+sc_to_string(storage_class)
+     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 "???,";
+}
+
+#endif /* DEBUG */
+\f
+#ifdef ECOFF_DEBUG
+
+/* Convert symbol type to string.  */
+
+static char *
+st_to_string(symbol_type)
+     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 "???,";
+}
+
+#endif /* DEBUG */
+\f
+/* Parse .begin directives which have a label as the first argument
+   which gives the location of the start of the block.  */
+
+static void
+obj_ecoff_begin (ignore)
+     int ignore;
+{
+  char *name;
+  char name_end;
+
+  if (cur_file_ptr == (efdr_t *) NULL)
+    {
+      as_warn (".begin directive without a preceding .file directive");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  if (cur_proc_ptr == (proc_t *) NULL)
+    {
+      as_warn (".begin directive without a preceding .ent directive");
+      demand_empty_rest_of_line ();
+      return;
+    }
+  
+  name = input_line_pointer;
+  name_end = get_symbol_end ();
+
+  (void) add_ecoff_symbol ((const char *) NULL, st_Block, sc_Text,
+                          symbol_find_or_make (name),
+                          (symint_t) 0, (symint_t) 0);
+
+  *input_line_pointer = name_end;
+
+  demand_empty_rest_of_line ();
+}
+\f
+/* Parse .bend directives which have a label as the first argument
+   which gives the location of the end of the block.  */
+
+static void
+obj_ecoff_bend (ignore)
+     int ignore;
+{
+  char *name;
+  char name_end;
+  symbolS *begin;
+
+  if (cur_file_ptr == (efdr_t *) NULL)
+    {
+      as_warn (".bend directive without a preceding .file directive");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  if (cur_proc_ptr == (proc_t *) NULL)
+    {
+      as_warn (".bend directive without a preceding .ent directive");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  name = input_line_pointer;
+  name_end = get_symbol_end ();
+
+  /* The value is the distance between the .bend directive and the
+     corresponding symbol.  We create a fake symbol to hold the
+     current location, and put in the offset when we write out the
+     symbol.  */
+  begin = symbol_find (name);
+  if (begin == (symbolS *) NULL)
+    as_warn (".bend directive names unknown symbol");
+  else
+    (void) add_ecoff_symbol (name, st_End, sc_Text,
+                            symbol_new ("L0\001", now_seg,
+                                        frag_now_fix (), frag_now),
+                            (symint_t) 0, (symint_t) 0);
+
+  *input_line_pointer = name_end;
+
+  demand_empty_rest_of_line ();
+}
+\f
+/* COFF debugging information is provided as a series of directives
+   (.def, .scl, etc.).  We build up information as we read the
+   directives in the following static variables, and file it away when
+   we reach the .endef directive.  */
+static char *coff_sym_name;
+static type_info_t coff_type;
+static sc_t coff_storage_class;
+static st_t coff_symbol_type;
+static int coff_is_function;
+static char *coff_tag;
+static long coff_value;        /* FIXME: Might be 64 bits.  */
+symbolS *coff_sym_value;
+static int coff_inside_enumeration;
+
+/* Handle a .def directive: start defining a symbol.  */
+
+static void
+obj_ecoff_def (ignore)
+     int ignore;
+{
+  char *name;
+  char name_end;
+
+  SKIP_WHITESPACES ();
+
+  name = input_line_pointer;
+  name_end = get_symbol_end ();
+
+  if (coff_sym_name != (char *) NULL)
+    as_warn (".def pseudo-op used inside of .def/.endef; ignored");
+  else if (*name == '\0')
+    as_warn ("Empty symbol name in .def; ignored");
+  else
+    {
+      if (coff_sym_name != (char *) NULL)
+       free (coff_sym_name);
+      if (coff_tag != (char *) NULL)
+       free (coff_tag);
+      coff_sym_name = (char *) xmalloc (strlen (name) + 1);
+      strcpy (coff_sym_name, name);
+      coff_type = type_info_init;
+      coff_storage_class = sc_Nil;
+      coff_symbol_type = st_Nil;
+      coff_is_function = 0;
+      coff_tag = (char *) NULL;
+      coff_value = 0;
+      coff_sym_value = (symbolS *) NULL;
+    }
+
+  *input_line_pointer = name_end;
+
+  demand_empty_rest_of_line ();
+}
+
+/* Handle a .dim directive, used to give dimensions for an array.  The
+   arguments are comma separated numbers.  mips-tfile assumes that
+   there will not be more than 6 dimensions, and gdb won't read any
+   more than that anyhow, so I will also make that assumption.  */
+
+static void
+obj_ecoff_dim (ignore)
+     int ignore;
+{
+  int dimens[N_TQ];
+  int i;
+
+  if (coff_sym_name == (char *) NULL)
+    {
+      as_warn (".dim pseudo-op used outside of .def/.endef; ignored");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  for (i = 0; i < N_TQ; i++)
+    {
+      SKIP_WHITESPACES ();
+      dimens[i] = get_absolute_expression ();
+      if (*input_line_pointer == ',')
+       ++input_line_pointer;
+      else
+       {
+         if (*input_line_pointer != '\n'
+             && *input_line_pointer != ';')
+           as_warn ("Badly formed .dim directive");
+         break;
+       }
+    }
+
+  if (i == N_TQ)
+    --i;
+
+  /* The dimensions are stored away in reverse order.  */
+  for (; i >= 0; i--)
+    {
+      if (coff_type.num_dims >= N_TQ)
+       {
+         as_warn ("Too many .dim entries");
+         break;
+       }
+      coff_type.dimensions[coff_type.num_dims] = dimens[i];
+      ++coff_type.num_dims;
+    }
+
+  demand_empty_rest_of_line ();
+}
+
+/* Handle a .scl directive, which sets the COFF storage class of the
+   symbol.  */
+
+static void
+obj_ecoff_scl (ignore)
+     int ignore;
+{
+  long val;
+
+  if (coff_sym_name == (char *) NULL)
+    {
+      as_warn (".scl pseudo-op used outside of .def/.endef; ignored");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  val = get_absolute_expression ();
+
+  /* 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 (coff_symbol_type == st_Nil)
+    {
+      coff_symbol_type = map_coff_sym_type[val];
+      coff_storage_class = map_coff_storage[val];
+    }
+
+  demand_empty_rest_of_line ();
+}
+
+/* Handle a .size directive.  For some reason mips-tfile.c thinks that
+   .size can have multiple arguments.  We humor it, although gcc will
+   never generate more than one argument.  */
+
+static void
+obj_ecoff_size (ignore)
+     int ignore;
+{
+  int sizes[N_TQ];
+  int i;
+
+  if (coff_sym_name == (char *) NULL)
+    {
+      as_warn (".size pseudo-op used outside of .def/.endef; ignored");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  for (i = 0; i < N_TQ; i++)
+    {
+      SKIP_WHITESPACES ();
+      sizes[i] = get_absolute_expression ();
+      if (*input_line_pointer == ',')
+       ++input_line_pointer;
+      else
+       {
+         if (*input_line_pointer != '\n'
+             && *input_line_pointer != ';')
+           as_warn ("Badly formed .size directive");
+         break;
+       }
+    }
+
+  if (i == N_TQ)
+    --i;
+
+  /* The sizes are stored away in reverse order.  */
+  for (; i >= 0; i--)
+    {
+      if (coff_type.num_sizes >= N_TQ)
+       {
+         as_warn ("Too many .size entries");
+         break;
+       }
+      coff_type.sizes[coff_type.num_sizes] = sizes[i];
+      ++coff_type.num_sizes;
+    }
+
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .type directive, which gives the COFF type of the
+   symbol.  */
+
+static void
+obj_ecoff_type (ignore)
+     int ignore;
+{
+  long val;
+  tq_t *tq_ptr;
+
+  if (coff_sym_name == (char *) NULL)
+    {
+      as_warn (".type pseudo-op used outside of .def/.endef; ignored");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  val = get_absolute_expression ();
+
+  coff_type.orig_type = BTYPE (val);
+  coff_type.basic_type = map_coff_types[coff_type.orig_type];
+
+  tq_ptr = &coff_type.type_qualifiers[0];
+  while (val &~ N_BTMASK)
+    {
+      if (tq_ptr == &coff_type.type_qualifiers[N_TQ])
+       {
+         as_warn ("Too derived values in .type argument");
+         break;
+       }
+      if (ISPTR (val))
+       *tq_ptr++ = tq_Ptr;
+      else if (ISFCN (val))
+       *tq_ptr++ = tq_Proc;
+      else if (ISARY (val))
+       *tq_ptr++ = tq_Array;
+      else
+       as_fatal ("Unrecognized .type argument");
+
+      val = DECREF (val);
+    }
+
+  if (tq_ptr != &coff_type.type_qualifiers[0] && tq_ptr[-1] == tq_Proc)
+    {
+      /* 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.  */
+      coff_is_function = 1;
+      tq_ptr[-1] = tq_Nil;
+    }
+
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .tag directive, which gives the name of a structure,
+   union or enum.  */
+
+static void
+obj_ecoff_tag (ignore)
+     int ignore;
+{
+  char *name;
+  char name_end;
+
+  if (coff_sym_name == (char *) NULL)
+    {
+      as_warn (".tag pseudo-op used outside of .def/.endef; ignored");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  name = input_line_pointer;
+  name_end = get_symbol_end ();
+
+  coff_tag = (char *) xmalloc (strlen (name) + 1);
+  strcpy (coff_tag, name);
+
+  *input_line_pointer = name_end;
+
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .val directive, which gives the value of the symbol.  It
+   may be the name of a static or global symbol.  */
+
+static void
+obj_ecoff_val (ignore)
+     int ignore;
+{
+  if (coff_sym_name == (char *) NULL)
+    {
+      as_warn (".val pseudo-op used outside of .def/.endef; ignored");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  if (! is_name_beginner ((unsigned char) *input_line_pointer))
+    coff_value = get_absolute_expression ();
+  else
+    {
+      char *name;
+      char name_end;
+
+      name = input_line_pointer;
+      name_end = get_symbol_end ();
+
+      if (strcmp (name, ".") == 0)
+       as_warn ("`.val .' not supported");
+      else
+       coff_sym_value = symbol_find_or_make (name);
+
+      *input_line_pointer = name_end;
+
+      /* FIXME: gcc can generate address expressions here in unusual
+        cases (search for "obscure" in sdbout.c), although this is
+        very unlikely for a MIPS chip.  */
+    }
+
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .endef directive, which terminates processing of COFF
+   debugging information for a symbol.  */
+
+static void
+obj_ecoff_endef (ignore)
+     int ignore;
+{
+  symint_t indx;
+  localsym_t *sym;
+
+  demand_empty_rest_of_line ();
+
+  if (coff_sym_name == (char *) NULL)
+    {
+      as_warn (".endef pseudo-op used before .def; ignored");
+      return;
+    }
+
+  coff_type.extra_sizes = coff_tag != (char *) NULL;
+  if (coff_type.num_dims > 0)
+    {
+      int diff = coff_type.num_dims - coff_type.num_sizes;
+      int i = coff_type.num_dims - 1;
+      int j;
+
+      if (coff_type.num_sizes != 1 || diff < 0)
+       {
+         as_warn ("Bad COFF debugging info");
+         return;
+       }
+
+      /* 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.  */
+      coff_type.extra_sizes = 0;
+      if (diff)
+       {
+         j = (sizeof (coff_type.sizes) / sizeof (coff_type.sizes[0])) - 1;
+         while (j >= 0)
+           {
+             coff_type.sizes[j] = (((j - diff) >= 0)
+                                   ? coff_type.sizes[j - diff]
+                                   : 0);
+             j--;
+           }
+
+         coff_type.num_sizes = i + 1;
+         for (i--; i >= 0; i--)
+           coff_type.sizes[i] = (coff_type.sizes[i + 1]
+                                 / coff_type.dimensions[i + 1]);
+       }
+    }
+  else if (coff_symbol_type == st_Member
+          && coff_type.num_sizes - coff_type.extra_sizes == 1)
+    {
+      /* Is this a bitfield?  This is indicated by a structure memeber
+        having a size field that isn't an array.  */
+      coff_type.bitfield = 1;
+    }
+
+  /* Except for enumeration members & begin/ending of scopes, put the
+     type word in the aux. symbol table.  */
+  if (coff_symbol_type == st_Block || coff_symbol_type == st_End)
+    indx = 0;
+  else if (coff_inside_enumeration)
+    indx = cur_file_ptr->void_type;
+  else
+    {
+      if (coff_type.basic_type == bt_Struct
+         || coff_type.basic_type == bt_Union
+         || coff_type.basic_type == bt_Enum)
+       {
+         if (coff_tag == (char *) NULL)
+           {
+             as_warn ("No tag specified for %s", coff_sym_name);
+             return;
+           }
+
+         coff_type.tag_ptr = get_tag (coff_tag, (localsym_t *) NULL,
+                                      coff_type.basic_type);
+       }
+
+      if (coff_is_function)
+       {
+         last_func_type_info = coff_type;
+         last_func_sym_value = coff_sym_value;
+         return;
+       }
+
+      indx = add_aux_sym_tir (&coff_type,
+                             hash_yes,
+                             &cur_file_ptr->thash_head[0]);
+    }
+
+  /* Do any last minute adjustments that are necessary.  */
+  switch (coff_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 (coff_type.num_sizes - coff_type.num_dims - coff_type.extra_sizes
+         != 1)
+       {
+         as_warn ("Bad COFF debugging information");
+         return;
+       }
+      else
+       coff_value = coff_type.sizes[0];
+
+      coff_inside_enumeration = (coff_type.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:
+      free (coff_sym_name);
+      coff_sym_name = (char *) NULL;
+      coff_value = 0;
+      coff_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 (! coff_type.bitfield && ! coff_inside_enumeration)
+       coff_value *= 8;
+
+      break;
+    }
+
+  /* Add the symbol.  */
+  sym = add_ecoff_symbol (coff_sym_name,
+                         coff_symbol_type,
+                         coff_storage_class,
+                         coff_sym_value,
+                         coff_value,
+                         indx);
+
+  /* deal with struct, union, and enum tags.  */
+  if (coff_symbol_type == st_Block)
+    {
+      /* Create or update the tag information.  */
+      tag_t *tag_ptr = get_tag (coff_sym_name,
+                               sym,
+                               coff_type.basic_type);
+      forward_t **pf;
+
+      /* Remember any forward references.  */
+      for (pf = &sym->forward_ref;
+          *pf != (forward_t *) NULL;
+          pf = &(*pf)->next)
+       ;
+      *pf = tag_ptr->forward_ref;
+      tag_ptr->forward_ref = (forward_t *) NULL;
+    }
+}
+\f
+/* Parse .end directives.  */
+
+static void
+obj_ecoff_end (ignore)
+     int ignore;
+{
+  char *name;
+  char name_end;
+  register int ch;
+  symbolS *ent;
+
+  if (cur_file_ptr == (efdr_t *) NULL)
+    {
+      as_warn (".end directive without a preceding .file directive");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  if (cur_proc_ptr == (proc_t *) NULL)
+    {
+      as_warn (".end directive without a preceding .ent directive");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  name = input_line_pointer;
+  name_end = get_symbol_end ();
+  
+  ch = *name;
+  if (! is_name_beginner (ch))
+    {
+      as_warn (".end directive has no name");
+      *input_line_pointer = name_end;
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  /* The value is the distance between the .end directive and the
+     corresponding symbol.  We create a fake symbol to hold the
+     current location, and put in the offset when we write out the
+     symbol.  */
+  ent = symbol_find (name);
+  if (ent == (symbolS *) NULL)
+    as_warn (".end directive names unknown symbol");
+  else
+    (void) add_ecoff_symbol (name, st_End, sc_Text,
+                            symbol_new ("L0\001", now_seg,
+                                        frag_now_fix (), frag_now),
+                            (symint_t) 0, (symint_t) 0);
+
+  cur_proc_ptr = (proc_t *) NULL;
+
+  *input_line_pointer = name_end;
+  demand_empty_rest_of_line ();
+}
+\f
+/* Parse .ent directives.  */
+
+static void
+obj_ecoff_ent (ignore)
+     int ignore;
+{
+  char *name;
+  char name_end;
+  register int ch;
+
+  if (cur_file_ptr == (efdr_t *) NULL)
+    add_file ((const char *) NULL, 0);
+
+  if (cur_proc_ptr != (proc_t *) NULL)
+    {
+      as_warn ("second .ent directive found before .end directive");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  name = input_line_pointer;
+  name_end = get_symbol_end ();
+
+  ch = *name;
+  if (! is_name_beginner (ch))
+    {
+      as_warn (".ent directive has no name");
+      *input_line_pointer = name_end;
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  add_procedure (name);
+
+  *input_line_pointer = name_end;
+  demand_empty_rest_of_line ();
+}
+\f
+/* Parse .file directives.  */
+
+static void
+obj_ecoff_file (ignore)
+     int ignore;
+{
+  int indx;
+  char *name;
+  int len;
+
+  if (cur_proc_ptr != (proc_t *) NULL)
+    {
+      as_warn ("No way to handle .file within .ent/.end section");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  indx = (int) get_absolute_expression ();
+
+  /* FIXME: we don't have to save the name here.  */
+  name = demand_copy_C_string (&len);
+
+  add_file (name, indx - 1);
+
+  demand_empty_rest_of_line ();
+}
+\f
+/* Parse .fmask directives.  */
+
+static void
+obj_ecoff_fmask (ignore)
+     int ignore;
+{
+  long val;
+
+  if (cur_proc_ptr == (proc_t *) NULL)
+    {
+      as_warn (".fmask outside of .ent");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  if (get_absolute_expression_and_terminator (&val) != ',')
+    {
+      as_warn ("Bad .fmask directive");
+      --input_line_pointer;
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  cur_proc_ptr->pdr.fregmask = val;
+  cur_proc_ptr->pdr.fregoffset = get_absolute_expression ();
+
+  demand_empty_rest_of_line ();
+}
+\f
+/* Parse .frame directives.  */
+
+static void
+obj_ecoff_frame (ignore)
+     int ignore;
+{
+  long val;
+
+  if (cur_proc_ptr == (proc_t *) NULL)
+    {
+      as_warn (".frame outside of .ent");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  cur_proc_ptr->pdr.framereg = tc_get_register ();
+
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer++ != ','
+      || get_absolute_expression_and_terminator (&val) != ',')
+    {
+      as_warn ("Bad .frame directive");
+      --input_line_pointer;
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  cur_proc_ptr->pdr.frameoffset = val;
+
+  cur_proc_ptr->pdr.pcreg = tc_get_register ();
+
+  demand_empty_rest_of_line ();
+}
+\f
+/* Parse .mask directives.  */
+
+static void
+obj_ecoff_mask (ignore)
+     int ignore;
+{
+  long val;
+
+  if (cur_proc_ptr == (proc_t *) NULL)
+    {
+      as_warn (".mask outside of .ent");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  if (get_absolute_expression_and_terminator (&val) != ',')
+    {
+      as_warn ("Bad .mask directive");
+      --input_line_pointer;
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  cur_proc_ptr->pdr.regmask = val;
+  cur_proc_ptr->pdr.regoffset = get_absolute_expression ();
+
+  demand_empty_rest_of_line ();
+}
+\f
+/* Parse .loc directives.  */
+
+static void
+obj_ecoff_loc (ignore)
+     int ignore;
+{
+  char buf[20];
+  lineno_list_t *list;
+
+  if (cur_file_ptr == (efdr_t *) NULL)
+    {
+      as_warn (".loc before .file");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  if (now_seg != text_section)
+    {
+      as_warn (".loc outside of .text");
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  /* FIXME: .loc directives look like ``.loc 1 4'' where the first
+     number is the file index and the second number is the line
+     number.  Unfortunately, do_scrub_next_char removes the space,
+     producing ``.loc 14''.  Urrrk.  I'm afraid I'll break something
+     if I change do_scrub_next_char, so instead we do this gross hack.
+     Note that file_index is one less than the value in the .loc,
+     because we adjusted it by one in the call to add_file.  This
+     actually won't work in the unfortunate circumstance of the same
+     file appearing twice with different indices with different
+     numbers of digits, which is possible.  */
+  SKIP_WHITESPACE ();
+  input_line_pointer += 1 + (cur_file_ptr->file_index + 1) / 10;
+
+  list = allocate_lineno_list ();
+
+  list->next = (lineno_list_t *) NULL;
+  list->file = cur_file_ptr;
+  list->proc = cur_proc_ptr;
+  list->frag = frag_now;
+  list->paddr = frag_now_fix ();
+  list->lineno = get_absolute_expression ();
+
+  /* A .loc directive will sometimes appear before a .ent directive,
+     which means that cur_proc_ptr will be NULL here.  Arrange to
+     patch this up.  */
+  if (cur_proc_ptr == (proc_t *) NULL)
+    {
+      lineno_list_t **pl;
+
+      pl = &noproc_lineno;
+      while (*pl != (lineno_list_t *) NULL)
+       pl = &(*pl)->next;
+      *pl = list;
+    }
+  else
+    {
+      *last_lineno_ptr = list;
+      last_lineno_ptr = &list->next;
+    }
+}
+\f
+/* Make sure the @stabs symbol is emitted.  */
+
+static void
+mark_stabs (ignore)
+     int ignore;
+{
+  if (! stabs_seen)
+    {
+      /* Add a dummy @stabs dymbol. */
+      stabs_seen = 1;
+      (void) add_ecoff_symbol (stabs_symbol, stNil, scInfo,
+                              (symbolS *) NULL,
+                              (symint_t) -1, MIPS_MARK_STAB (0));
+    }
+}
+\f
+/* 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
+obj_ecoff_stab (type)
+     int type;
+{
+  char *string;
+  efdr_t *save_file_ptr = cur_file_ptr;
+  symint_t code;
+  symint_t value;
+  symbolS *sym;
+  st_t st;
+  sc_t sc;
+
+  if (stabs_seen == 0)
+    mark_stabs (0);
+
+  if (type != 's')
+    string = (char *) NULL;
+  else
+    {
+      int len;
+
+      string = demand_copy_C_string (&len);
+      SKIP_WHITESPACE ();
+      if (*input_line_pointer == ',')
+       input_line_pointer++;
+      else
+       {
+         as_warn ("Bad .stab%c directive", type);
+         demand_empty_rest_of_line ();
+         return;
+       }
+    }
+
+  code = (symint_t) get_absolute_expression ();
+
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer++ != ',')
+    {
+      as_warn ("Bad .stab%c directive", type);
+      --input_line_pointer;
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  if (get_absolute_expression () != 0)
+    {
+      as_warn ("Bad .stab%c directive (expected 0)", type);
+      demand_empty_rest_of_line ();
+      return;
+    }
+      
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer++ != ',')
+    {
+      as_warn ("Bad .stab%c directive", type);
+      --input_line_pointer;
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  /* 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 == N_SLINE)
+    {
+      SYMR dummy_symr;
+      char *name;
+      char name_end;
+
+      code = (symint_t) get_absolute_expression ();
+
+      if (*input_line_pointer++ != ',')
+       {
+         as_warn ("Bad .stab%c directive", type);
+         --input_line_pointer;
+         demand_empty_rest_of_line ();
+         return;
+       }
+
+      dummy_symr.index = code;
+      if (dummy_symr.index != code)
+       {
+         as_warn ("Line number (%d) for .stab%c directive cannot fit in index field (20 bits)",
+                  code, type);
+         demand_empty_rest_of_line ();
+         return;
+       }
+
+      SKIP_WHITESPACE ();
+      name = input_line_pointer;
+      name_end = get_symbol_end ();
+
+      sym = symbol_find_or_make (name);
+      *input_line_pointer = name_end;
+
+      value = 0;
+      st = st_Label;
+      sc = sc_Undefined;
+    }
+  else
+    {
+      /* Skip 0, */
+      if (get_absolute_expression () != 0)
+       {
+         as_warn ("Bad .stab%c directive (expected 0)", type);
+         demand_empty_rest_of_line ();
+         return;
+       }
+      
+      SKIP_WHITESPACE ();
+      if (*input_line_pointer++ != ',')
+       {
+         as_warn ("Bad .stab%c directive", type);
+         --input_line_pointer;
+         demand_empty_rest_of_line ();
+         return;
+       }
+
+      SKIP_WHITESPACE ();
+      if (isdigit (*input_line_pointer)
+         || *input_line_pointer == '-'
+         || *input_line_pointer == '+')
+       {
+         st = st_Nil;
+         sc = sc_Nil;
+         sym = (symbolS *) NULL;
+         value = get_absolute_expression ();
+       }
+      else if (! is_name_beginner ((unsigned char) *input_line_pointer))
+       {
+         as_warn ("Illegal .stab%c directive, bad character", type);
+         demand_empty_rest_of_line ();
+         return;
+       }
+      else
+       {
+         char *name;
+         char name_end;
+
+         name = input_line_pointer;
+         name_end = get_symbol_end ();
+
+         sym = symbol_find_or_make (name);
+
+         /* Traditionally, N_LBRAC and N_RBRAC are *not* relocated. */
+         if (code == N_LBRAC || code == N_RBRAC)
+           {
+             sc = sc_Nil;
+             st = st_Nil;
+           }
+         else
+           {
+             sc = sc_Undefined;
+             st = st_Nil;
+           }
+         value = 0;
+
+         *input_line_pointer = name_end;
+         if (name_end == '+' || name_end == '-')
+           {
+             ++input_line_pointer;
+             value = get_absolute_expression ();
+             if (name_end == '-')
+               value = - value;
+           }
+       }
+
+      code = MIPS_MARK_STAB (code);
+    }
+
+  (void) add_ecoff_symbol (string, st, sc, sym, value, code);
+
+  /* Restore normal file type.  */
+  cur_file_ptr = save_file_ptr;
+}
+\f
+/* Add bytes to the symbolic information buffer.  */
+
+static char *
+ecoff_add_bytes (buf, bufend, bufptr, need)
+     char **buf;
+     char **bufend;
+     char *bufptr;
+     long need;
+{
+  unsigned long at;
+  unsigned long want;
+
+  at = bufptr - *buf;
+  need -= *bufend - bufptr;
+  if (need < PAGE_SIZE)
+    need = PAGE_SIZE;
+  want = (*bufend - *buf) + need;
+  *buf = xrealloc (*buf, want);
+  *bufend = *buf + want;
+  return *buf + at;
+}
+
+/* Adjust the symbolic information buffer to a longword boundary.  */
+
+static long
+ecoff_longword_adjust (buf, bufend, offset, bufptrptr)
+     char **buf;
+     char **bufend;
+     long offset;
+     char **bufptrptr;
+{
+  if ((offset & 3) != 0)
+    {
+      long add;
+
+      add = 4 - (offset & 3);
+      if (*bufend - (*buf + offset) < add)
+       (void) ecoff_add_bytes (buf, bufend, *buf + offset, add);
+      offset += add;
+      if (bufptrptr != (char **) NULL)
+       *bufptrptr = *buf + offset;
+    }
+
+  return offset;
+}
+
+/* Build the line number information.  */
+
+static long
+ecoff_build_lineno (buf, bufend, offset, linecntptr)
+     char **buf;
+     char **bufend;
+     long offset;
+     long *linecntptr;
+{
+  char *bufptr;
+  register lineno_list_t *l;
+  lineno_list_t *last;
+  efdr_t *file;
+  proc_t *proc;
+  long c;
+  long iline;
+
+  bufptr = *buf + offset;
+
+  file = (efdr_t *) NULL;
+  proc = (proc_t *) NULL;
+  last = (lineno_list_t *) NULL;
+  c = offset;
+  iline = 0;
+  for (l = first_lineno; l != (lineno_list_t *) NULL; l = l->next)
+    {
+      long count;
+      long delta;
+      int didone;
+
+      if (l->file != file || l->proc != proc)
+       {
+         if (l->proc != proc && proc != (proc_t *) NULL)
+           proc->pdr.lnHigh = last->lineno;
+         if (l->file != file && file != (efdr_t *) NULL)
+           {
+             file->fdr.cbLine = c - file->fdr.cbLineOffset;
+             file->fdr.cline = iline - file->fdr.ilineBase;
+           }
+
+         c = ecoff_longword_adjust (buf, bufend, c, &bufptr);
+
+         if (l->file != file)
+           {
+             file = l->file;
+             file->fdr.ilineBase = iline;
+             file->fdr.cbLineOffset = c;
+           }
+         if (l->proc != proc)
+           {
+             proc = l->proc;
+             if (proc != (proc_t *) NULL)
+               {
+                 /* The iline field is ill-documented.  This is a
+                    guess at the right value.  */
+                 proc->pdr.iline = l->frag->fr_address + l->paddr;
+                 proc->pdr.lnLow = l->lineno;
+                 proc->pdr.cbLineOffset = c - file->fdr.cbLineOffset;
+               }
+           }
+
+         last = (lineno_list_t *) NULL;
+       }      
+
+      /* Get the offset to the memory address of the next line number
+        (in words).  */
+      if (l->next == (lineno_list_t *) NULL)
+       count = 0;
+      else
+       {
+         count = (((l->next->frag->fr_address + l->next->paddr
+                    - (l->frag->fr_address + l->paddr))
+                   >> 2)
+                  - 1);
+         if (count < 0)
+           {
+             /* Don't change last, so we still get the right delta.  */
+             continue;
+           }
+       }
+
+      /* Get the offset to this line number.  */
+      if (last == (lineno_list_t *) NULL)
+       delta = 0;
+      else
+       delta = l->lineno - last->lineno;
+
+      /* We can only adjust the address by 16 words at a time.  */
+      didone = 0;
+      while (count > 0x10)
+       {
+         if (bufptr >= *bufend)
+           bufptr = ecoff_add_bytes (buf, bufend, bufptr, (long) 1);
+         if (delta >= 7)
+           {
+             *bufptr++ = 0x0f + (7 << 4);
+             delta -= 7;
+           }
+         else if (delta <= -7)
+           {
+             *bufptr++ = 0x0f + (9 << 4);
+             delta += 7;
+           }
+         else
+           {
+             *bufptr++ = 0x0f + (delta << 4);
+             delta = 0;
+           }
+         ++c;
+         count -= 0x10;
+         didone = 1;
+       }
+
+      /* Put in the offset to this line number.  */
+      while (delta != 0 || ! didone)
+       {
+         if (delta >= -7 && delta <= 7)
+           {
+             if (bufptr >= *bufend)
+               bufptr = ecoff_add_bytes (buf, bufend, bufptr, (long) 1);
+             *bufptr++ = count + (delta << 4);
+             delta = 0;
+             ++c;
+           }
+         else
+           {
+             int set;
+
+             if (*bufend - bufptr < 3)
+               bufptr = ecoff_add_bytes (buf, bufend, bufptr, (long) 3);
+             *bufptr++ = count + (8 << 4);
+             if (delta < -0x8000)
+               {
+                 set = -0x8000;
+                 delta += 0x8000;
+               }
+             else if (delta > 0x7fff)
+               {
+                 set = 0x7fff;
+                 delta -= 0x7fff;
+               }
+             else
+               {
+                 set = delta;
+                 delta = 0;
+               }
+             *bufptr++ = set >> 8;
+             *bufptr++ = set & 0xffff;
+             c += 3;
+           }
+         count = 0;
+         didone = 1;
+       }
+
+      ++iline;
+      last = l;
+    }
+
+  if (proc != (proc_t *) NULL)
+    proc->pdr.lnHigh = last->lineno;
+  if (file != (efdr_t *) NULL)
+    {
+      file->fdr.cbLine = c - file->fdr.cbLineOffset;
+      file->fdr.cline = iline - file->fdr.ilineBase;
+    }
+
+  c = ecoff_longword_adjust (buf, bufend, c, &bufptr);
+
+  if (linecntptr != (long *) NULL)
+    *linecntptr = iline;
+
+  return c;
+}
+
+/* Build and swap out the symbols.  */
+
+static long
+ecoff_build_symbols (buf,
+                    bufend,
+                    offset,
+                    extbuf,
+                    extbufend,
+                    extoffset,
+                    ext_strings,
+                    ext_str_hash)
+     char **buf;
+     char **bufend;
+     long offset;
+     char **extbuf;
+     char **extbufend;
+     long *extoffset;
+     varray_t *ext_strings;
+     struct hash_control *ext_str_hash;
+{
+  struct sym_ext *sym_out;
+  struct ext_ext *ext_out;
+  long isym;
+  long iext;
+  vlinks_t *file_link;
+
+  sym_out = (struct sym_ext *) (*buf + offset);
+  ext_out = (struct ext_ext *) (*extbuf + *extoffset);
+
+  isym = 0;
+  iext = 0;
+
+  /* The symbols are stored by file.  */
+  for (file_link = file_desc.first;
+       file_link != (vlinks_t *) NULL;
+       file_link = file_link->next)
+    {
+      int ifilesym;
+      int fil_cnt;
+      efdr_t *fil_ptr;
+      efdr_t *fil_end;
+
+      ifilesym = isym;
+
+      if (file_link->next == (vlinks_t *) NULL)
+       fil_cnt = file_desc.objects_last_page;
+      else
+       fil_cnt = file_desc.objects_per_page;
+      fil_ptr = file_link->datum->file;
+      fil_end = fil_ptr + fil_cnt;
+      for (; fil_ptr < fil_end; fil_ptr++)
+       {
+         vlinks_t *sym_link;
+
+         fil_ptr->fdr.isymBase = isym;
+         for (sym_link = fil_ptr->symbols.first;
+              sym_link != (vlinks_t *) NULL;
+              sym_link = sym_link->next)
+           {
+             int sym_cnt;
+             localsym_t *sym_ptr;
+             localsym_t *sym_end;
+
+             if (sym_link->next == (vlinks_t *) NULL)
+               sym_cnt = fil_ptr->symbols.objects_last_page;
+             else
+               sym_cnt = fil_ptr->symbols.objects_per_page;
+             sym_ptr = sym_link->datum->sym;
+             sym_end = sym_ptr + sym_cnt;
+             for (; sym_ptr < sym_end; sym_ptr++)
+               {
+                 int local;
+                 forward_t *f;
+
+                 know (sym_ptr->file_ptr == fil_ptr);
+
+                 /* If there is no associated gas symbol, then this
+                    is a pure debugging symbol.  We have already
+                    added the name (if any) to fil_ptr->strings.
+                    Otherwise we must decide whether this is an
+                    external or a local symbol (actually, it may be
+                    both if the local provides additional debugging
+                    information for the external).  */
+                 local = 1;
+                 if (sym_ptr->as_sym != (symbolS *) NULL)
+                   {
+                     sym_ptr->ecoff_sym.value = S_GET_VALUE (sym_ptr->as_sym);
+
+                     /* This is just an external symbol if it is
+                        outside a procedure and it has a type.  */
+                     if ((S_IS_EXTERNAL (sym_ptr->as_sym)
+                          || ! S_IS_DEFINED (sym_ptr->as_sym))
+                         && sym_ptr->proc_ptr == (proc_t *) NULL
+                         && sym_ptr->ecoff_sym.st != (int) st_Nil)
+                       local = 0;
+
+                     /* If an st_end symbol has an associated gas
+                        symbol, then it is a fake created for a .bend
+                        or .end directive.  */
+                     if (local && sym_ptr->ecoff_sym.st != st_End)
+                       sym_ptr->ecoff_sym.iss =
+                         add_string (&fil_ptr->strings,
+                                     fil_ptr->str_hash,
+                                     S_GET_NAME (sym_ptr->as_sym),
+                                     (shash_t **) NULL);
+                   }
+
+                 /* We now know the index of this symbol; fill in
+                    locations that have been waiting for that
+                    information.  */
+                 if (sym_ptr->begin_ptr != (localsym_t *) NULL)
+                   {
+                     localsym_t *begin_ptr;
+                     st_t begin_type;
+
+                     know (local);
+                     begin_ptr = sym_ptr->begin_ptr;
+                     know (begin_ptr->sym_index != -1);
+                     sym_ptr->ecoff_sym.index = begin_ptr->sym_index;
+                     if (sym_ptr->ecoff_sym.sc != (int) sc_Info)
+                       sym_ptr->ecoff_sym.iss = begin_ptr->ecoff_sym.iss;
+
+                     begin_type = begin_ptr->ecoff_sym.st;
+                     if (begin_type == st_File
+                         || begin_type == st_Block)
+                       {
+                         begin_ptr->ecoff_sym.index = isym - ifilesym + 1;
+                         ecoff_swap_sym_out (stdoutput,
+                                             &begin_ptr->ecoff_sym,
+                                             (((struct sym_ext *)
+                                               (*buf + offset))
+                                              + begin_ptr->sym_index));
+                       }
+                     else
+                       {
+                         know (sym_ptr->begin_ptr->index_ptr
+                               != (aux_t *) NULL);
+                         sym_ptr->begin_ptr->index_ptr->data.isym =
+                           isym - ifilesym + 1;
+                       }
+
+                     /* The value of the symbol marking the end of a
+                        procedure or block is the size of the
+                        procedure or block.  */
+                     if (begin_type == st_Proc || begin_type == st_Block)
+                       {
+                         know (sym_ptr->as_sym != (symbolS *) NULL);
+                         know (begin_ptr->as_sym != (symbolS *) NULL);
+                         sym_ptr->ecoff_sym.value =
+                           (S_GET_VALUE (sym_ptr->as_sym)
+                            - S_GET_VALUE (begin_ptr->as_sym));
+                       }
+                   }
+
+                 for (f = sym_ptr->forward_ref;
+                      f != (forward_t *) NULL;
+                      f = f->next)
+                   {
+                     know (local);
+                     f->ifd_ptr->data.isym = fil_ptr->file_index;
+                     f->index_ptr->data.rndx.index = isym - ifilesym;
+                   }
+
+                 if (local)
+                   {
+                     if (*bufend - (char *) sym_out < sizeof (struct sym_ext))
+                       sym_out = ((struct sym_ext *)
+                                  ecoff_add_bytes (buf, bufend,
+                                                   (char *) sym_out,
+                                                   sizeof (struct sym_ext)));
+                     ecoff_swap_sym_out (stdoutput, &sym_ptr->ecoff_sym,
+                                         sym_out);
+                     ++sym_out;
+                     sym_ptr->sym_index = isym;
+                     ++isym;
+
+                     if (sym_ptr->proc_ptr != (proc_t *) NULL
+                         && sym_ptr->proc_ptr->sym == sym_ptr)
+                       sym_ptr->proc_ptr->pdr.isym = isym - ifilesym;
+                   }
+
+                 /* If this is an external symbol, swap it out.  */
+                 if (sym_ptr->as_sym != (symbolS *) NULL
+                     && (S_IS_EXTERNAL (sym_ptr->as_sym)
+                         || ! S_IS_DEFINED (sym_ptr->as_sym)))
+                   {
+                     EXTR ext;
+
+                     memset (&ext, 0, sizeof ext);
+                     ext.asym = sym_ptr->ecoff_sym;
+                     ext.ifd = fil_ptr->file_index;
+                     ext.asym.iss = add_string (ext_strings,
+                                                ext_str_hash,
+                                                S_GET_NAME (sym_ptr->as_sym),
+                                                (shash_t **) NULL);
+                     if (*extbufend - (char *) ext_out
+                         < sizeof (struct ext_ext))
+                       ext_out = ((struct ext_ext *)
+                                  ecoff_add_bytes (extbuf, extbufend,
+                                                   (char *) ext_out,
+                                                   sizeof (struct ext_ext)));
+                     ecoff_swap_ext_out (stdoutput, &ext, ext_out);
+                     ecoff_set_sym_index (sym_ptr->as_sym->bsym, iext);
+                     ++ext_out;
+                     ++iext;
+                   }
+               }
+           }
+         fil_ptr->fdr.csym = isym - fil_ptr->fdr.isymBase;
+       }
+    }
+
+  *extoffset += iext * sizeof (struct ext_ext);
+  return offset + isym * sizeof (struct sym_ext);
+}
+
+/* Swap out the procedure information.  */
+
+static long
+ecoff_build_procs (buf, bufend, offset)
+     char **buf;
+     char **bufend;
+     long offset;
+{
+  struct pdr_ext *pdr_out;
+  long iproc;
+  vlinks_t *file_link;
+
+  pdr_out = (struct pdr_ext *) (*buf + offset);
+  
+  iproc = 0;
+
+  /* The procedures are stored by file.  */
+  for (file_link = file_desc.first;
+       file_link != (vlinks_t *) NULL;
+       file_link = file_link->next)
+    {
+      int fil_cnt;
+      efdr_t *fil_ptr;
+      efdr_t *fil_end;
+
+      if (file_link->next == (vlinks_t *) NULL)
+       fil_cnt = file_desc.objects_last_page;
+      else
+       fil_cnt = file_desc.objects_per_page;
+      fil_ptr = file_link->datum->file;
+      fil_end = fil_ptr + fil_cnt;
+      for (; fil_ptr < fil_end; fil_ptr++)
+       {
+         vlinks_t *proc_link;
+         int first;
+
+         fil_ptr->fdr.ipdFirst = iproc;
+         first = 1;
+         for (proc_link = fil_ptr->procs.first;
+              proc_link != (vlinks_t *) NULL;
+              proc_link = proc_link->next)
+           {
+             int proc_cnt;
+             proc_t *proc_ptr;
+             proc_t *proc_end;
+
+             if (proc_link->next == (vlinks_t *) NULL)
+               proc_cnt = fil_ptr->procs.objects_last_page;
+             else
+               proc_cnt = fil_ptr->procs.objects_per_page;
+             proc_ptr = proc_link->datum->proc;
+             proc_end = proc_ptr + proc_cnt;
+             for (; proc_ptr < proc_end; proc_ptr++)
+               {
+                 unsigned long adr;
+
+                 adr = S_GET_VALUE (proc_ptr->sym->as_sym);
+                 if (first)
+                   {
+                     fil_ptr->fdr.adr = adr;
+                     first = 0;
+                   }
+                 proc_ptr->pdr.adr = adr - fil_ptr->fdr.adr;
+                 if (*bufend - (char *) pdr_out < sizeof (struct pdr_ext))
+                   pdr_out = ((struct pdr_ext *)
+                              ecoff_add_bytes (buf, bufend,
+                                               (char *) pdr_out,
+                                               sizeof (struct pdr_ext)));
+                 ecoff_swap_pdr_out (stdoutput, &proc_ptr->pdr, pdr_out);
+                 ++pdr_out;
+                 ++iproc;
+               }
+           }
+         fil_ptr->fdr.cpd = iproc - fil_ptr->fdr.ipdFirst;
+       }
+    }
+
+  return offset + iproc * sizeof (struct pdr_ext);
+}
+
+/* Swap out the aux information.  */
+
+static long
+ecoff_build_aux (buf, bufend, offset)
+     char **buf;
+     char **bufend;
+     long offset;
+{
+  int bigendian;
+  union aux_ext *aux_out;
+  long iaux;
+  vlinks_t *file_link;
+
+  bigendian = stdoutput->xvec->header_byteorder_big_p;
+
+  aux_out = (union aux_ext *) (*buf + offset);
+  
+  iaux = 0;
+
+  /* The aux entries are stored by file.  */
+  for (file_link = file_desc.first;
+       file_link != (vlinks_t *) NULL;
+       file_link = file_link->next)
+    {
+      int fil_cnt;
+      efdr_t *fil_ptr;
+      efdr_t *fil_end;
+
+      if (file_link->next == (vlinks_t *) NULL)
+       fil_cnt = file_desc.objects_last_page;
+      else
+       fil_cnt = file_desc.objects_per_page;
+      fil_ptr = file_link->datum->file;
+      fil_end = fil_ptr + fil_cnt;
+      for (; fil_ptr < fil_end; fil_ptr++)
+       {
+         vlinks_t *aux_link;
+
+         fil_ptr->fdr.fBigendian = bigendian;
+         fil_ptr->fdr.iauxBase = iaux;
+         for (aux_link = fil_ptr->aux_syms.first;
+              aux_link != (vlinks_t *) NULL;
+              aux_link = aux_link->next)
+           {
+             int aux_cnt;
+             aux_t *aux_ptr;
+             aux_t *aux_end;
+
+             if (aux_link->next == (vlinks_t *) NULL)
+               aux_cnt = fil_ptr->aux_syms.objects_last_page;
+             else
+               aux_cnt = fil_ptr->aux_syms.objects_per_page;
+             aux_ptr = aux_link->datum->aux;
+             aux_end = aux_ptr + aux_cnt;
+             for (; aux_ptr < aux_end; aux_ptr++)
+               {
+                 if (*bufend - (char *) aux_out < sizeof (union aux_ext))
+                   aux_out = ((union aux_ext *)
+                              ecoff_add_bytes (buf, bufend,
+                                               (char *) aux_out,
+                                               sizeof (union aux_ext)));
+                 switch (aux_ptr->type)
+                   {
+                   case aux_tir:
+                     ecoff_swap_tir_out (bigendian, &aux_ptr->data.ti,
+                                         &aux_out->a_ti);
+                     break;
+                   case aux_rndx:
+                     ecoff_swap_rndx_out (bigendian, &aux_ptr->data.rndx,
+                                          &aux_out->a_rndx);
+                     break;
+                   case aux_dnLow:
+                     AUX_PUT_DNLOW (bigendian, aux_ptr->data.dnLow,
+                                    aux_out);
+                     break;
+                   case aux_dnHigh:
+                     AUX_PUT_DNHIGH (bigendian, aux_ptr->data.dnHigh,
+                                     aux_out);
+                     break;
+                   case aux_isym:
+                     AUX_PUT_ISYM (bigendian, aux_ptr->data.isym,
+                                   aux_out);
+                     break;
+                   case aux_iss:
+                     AUX_PUT_ISS (bigendian, aux_ptr->data.iss,
+                                  aux_out);
+                     break;
+                   case aux_width:
+                     AUX_PUT_WIDTH (bigendian, aux_ptr->data.width,
+                                    aux_out);
+                     break;
+                   case aux_count:
+                     AUX_PUT_COUNT (bigendian, aux_ptr->data.count,
+                                    aux_out);
+                     break;
+                   }
+
+                 ++aux_out;
+                 ++iaux;
+               }
+           }
+         fil_ptr->fdr.caux = iaux - fil_ptr->fdr.iauxBase;
+       }
+    }
+
+  return offset + iaux * sizeof (union aux_ext);
+}
+
+/* Copy out the strings from a varray_t.  This returns the number of
+   bytes copied, rather than the new offset.  */
+
+static long
+ecoff_build_strings (buf, bufend, offset, vp)
+     char **buf;
+     char **bufend;
+     long offset;
+     varray_t *vp;
+{
+  long istr;
+  char *str_out;
+  vlinks_t *str_link;
+
+  str_out = *buf + offset;
+
+  istr = 0;
+
+  for (str_link = vp->first;
+       str_link != (vlinks_t *) NULL;
+       str_link = str_link->next)
+    {
+      long str_cnt;
+
+      if (str_link->next == (vlinks_t *) NULL)
+       str_cnt = vp->objects_last_page;
+      else
+       str_cnt = vp->objects_per_page;
+
+      if (*bufend - str_out < str_cnt)
+       str_out = ecoff_add_bytes (buf, bufend, str_out, str_cnt);
+
+      memcpy (str_out, str_link->datum->byte, str_cnt);
+      str_out += str_cnt;
+      istr += str_cnt;
+    }
+
+  return istr;
+}
+
+/* Dump out the local strings.  */
+
+static long
+ecoff_build_ss (buf, bufend, offset)
+     char **buf;
+     char **bufend;
+     long offset;
+{
+  long iss;
+  vlinks_t *file_link;
+
+  iss = 0;
+
+  for (file_link = file_desc.first;
+       file_link != (vlinks_t *) NULL;
+       file_link = file_link->next)
+    {
+      int fil_cnt;
+      efdr_t *fil_ptr;
+      efdr_t *fil_end;
+
+      if (file_link->next == (vlinks_t *) NULL)
+       fil_cnt = file_desc.objects_last_page;
+      else
+       fil_cnt = file_desc.objects_per_page;
+      fil_ptr = file_link->datum->file;
+      fil_end = fil_ptr + fil_cnt;
+      for (; fil_ptr < fil_end; fil_ptr++)
+       {
+         long ss_cnt;
+
+         fil_ptr->fdr.issBase = iss;
+         ss_cnt = ecoff_build_strings (buf, bufend, offset + iss,
+                                       &fil_ptr->strings);
+         fil_ptr->fdr.cbSs = ss_cnt;
+         iss += ss_cnt;
+       }
+    }
+
+  return ecoff_longword_adjust (buf, bufend, offset + iss, (char **) NULL);
+}
+
+/* Swap out the file descriptors.  */
+
+static long
+ecoff_build_fdr (buf, bufend, offset)
+     char **buf;
+     char **bufend;
+     long offset;
+{
+  long ifile;
+  struct fdr_ext *fdr_out;
+  vlinks_t *file_link;
+
+  ifile = 0;
+
+  fdr_out = (struct fdr_ext *) (*buf + offset);
+
+  for (file_link = file_desc.first;
+       file_link != (vlinks_t *) NULL;
+       file_link = file_link->next)
+    {
+      int fil_cnt;
+      efdr_t *fil_ptr;
+      efdr_t *fil_end;
+
+      if (file_link->next == (vlinks_t *) NULL)
+       fil_cnt = file_desc.objects_last_page;
+      else
+       fil_cnt = file_desc.objects_per_page;
+      fil_ptr = file_link->datum->file;
+      fil_end = fil_ptr + fil_cnt;
+      for (; fil_ptr < fil_end; fil_ptr++)
+       {
+         if (*bufend - (char *) fdr_out < sizeof (struct fdr_ext))
+           fdr_out = ((struct fdr_ext *)
+                      ecoff_add_bytes (buf, bufend, (char *) fdr_out,
+                                       sizeof (struct fdr_ext)));
+         ecoff_swap_fdr_out (stdoutput, &fil_ptr->fdr, fdr_out);
+         ++fdr_out;
+         ++ifile;
+       }
+    }
+
+  return offset + ifile * sizeof (struct fdr_ext);
+}
+
+/* Set the vma for all the sections.  */
+
+static void
+ecoff_set_vma ()
+{
+  register bfd_vma addr;
+  register asection *sec;
+
+  addr = 0;
+  for (sec = stdoutput->sections; sec != (asection *) NULL; sec = sec->next)
+    {
+      bfd_set_section_vma (stdoutput, sec, addr);
+      addr += bfd_section_size (stdoutput, sec);
+    }
+}
+
+/* Adjust the value of a symbol by the vma of the section.  */
+
+void
+ecoff_frob_symbol (sym)
+     symbolS *sym;
+{
+  static int setvma = 0;
+
+  if (! setvma)
+    {
+      ecoff_set_vma ();
+      setvma = 1;
+    }
+
+  S_SET_VALUE (sym,
+              (S_GET_VALUE (sym)
+               + bfd_get_section_vma (stdoutput,
+                                      bfd_get_section (sym->bsym))));
+}
+
+/* Swap out the symbols and debugging information for BFD.  */
+
+void
+ecoff_frob_file ()
+{
+  efdr_t *fil_ptr;
+  efdr_t *hold_file_ptr;
+  proc_t * hold_proc_ptr;
+  symbolS *sym;
+  HDRR *hdr;
+  char *buf;
+  char *bufend;
+  long offset;
+  char *extbuf;
+  char *extbufend;
+  long extoffset;
+  varray_t ext_strings;
+  static varray_t init_ext_strings = INIT_VARRAY (char);
+  struct hash_control *ext_str_hash;
+  char *set;
+
+  /* Output an ending symbol for all the files.  We have to do this
+     here for the last file, so we may as well do it for all of the
+     files.  */
+  for (fil_ptr = first_file;
+       fil_ptr != (efdr_t *) NULL;
+       fil_ptr = fil_ptr->next_file)
+    {
+      cur_file_ptr = fil_ptr;
+      (void) add_ecoff_symbol ((const char *) NULL,
+                              st_End, sc_Text,
+                              (symbolS *) NULL,
+                              (symint_t) 0,
+                              (symint_t) 0);
+    }
+
+  /* Look through the symbols.  Add debugging information for each
+     symbol that has not already received it.  */
+  hold_file_ptr = cur_file_ptr;
+  hold_proc_ptr = cur_proc_ptr;
+  cur_proc_ptr = (proc_t *) NULL;
+  for (sym = symbol_rootP; sym != (symbolS *) NULL; sym = symbol_next (sym))
+    {
+      st_t st;
+      sc_t sc;
+
+      if (sym->ecoff_symbol
+         || sym->ecoff_file == (efdr_t *) NULL)
+       continue;
+
+      if (S_IS_EXTERNAL (sym) || ! S_IS_DEFINED (sym))
+       st = st_Global;
+      else if (S_GET_SEGMENT (sym) == text_section)
+       st = st_Label;
+      else
+       st = st_Static;
+
+      if (! S_IS_DEFINED (sym))
+       sc = sc_Undefined;
+      else if (S_IS_COMMON (sym))
+       sc = sc_Common;
+      else if (S_GET_SEGMENT (sym) == text_section)
+       sc = sc_Text;
+      else if (S_GET_SEGMENT (sym) == data_section)
+       sc = sc_Data;
+      else if (S_GET_SEGMENT (sym) == bss_section)
+       sc = sc_Bss;
+      else
+       abort ();
+
+      cur_file_ptr = sym->ecoff_file;
+      add_ecoff_symbol (S_GET_NAME (sym), st, sc, sym,
+                       S_GET_VALUE (sym), indexNil);
+    }
+  cur_proc_ptr = hold_proc_ptr;
+  cur_file_ptr = hold_file_ptr;
+
+  /* Build the symbolic information.  */
+  hdr = &ecoff_data (stdoutput)->symbolic_header;
+  offset = 0;
+  buf = xmalloc (PAGE_SIZE);
+  bufend = buf + PAGE_SIZE;
+
+  /* Build the line number information.  */
+  hdr->cbLineOffset = offset;
+  offset = ecoff_build_lineno (&buf, &bufend, offset, &hdr->ilineMax);
+  hdr->cbLine = offset - hdr->cbLineOffset;
+
+  /* We don't use dense numbers at all.  */
+  hdr->idnMax = 0;
+  hdr->cbDnOffset = 0;
+
+  /* We can't build the PDR table until we have built the symbols,
+     because a PDR contains a symbol index.  However, we set aside
+     space at this point.  */
+  hdr->ipdMax = proc_cnt;
+  hdr->cbPdOffset = offset;
+  if (bufend - (buf + offset) < proc_cnt * sizeof (struct pdr_ext))
+    (void) ecoff_add_bytes (&buf, &bufend, buf + offset,
+                           proc_cnt * sizeof (struct pdr_ext));
+  offset += proc_cnt * sizeof (struct pdr_ext);
+
+  /* Build the symbols.  It's convenient to build both the local and
+     external symbols at the same time.  We can put the local symbols
+     directly into the buffer, but we have to hold the external
+     symbols apart until we know where they are going to go.  */
+  extbuf = xmalloc (PAGE_SIZE);
+  extbufend = extbuf + PAGE_SIZE;
+  extoffset = 0;
+  ext_strings = init_ext_strings;
+  ext_str_hash = hash_new ();
+  hdr->cbSymOffset = offset;
+  offset = ecoff_build_symbols (&buf, &bufend, offset,
+                               &extbuf, &extbufend, &extoffset,
+                               &ext_strings, ext_str_hash);
+  hdr->isymMax = (offset - hdr->cbSymOffset) / sizeof (struct sym_ext);
+
+  /* Building the symbols initializes the symbol index in the PDR's.
+     Now we can swap out the PDR's.  */
+  (void) ecoff_build_procs (&buf, &bufend, hdr->cbPdOffset);
+
+  /* We don't use optimization symbols.  */
+  hdr->ioptMax = 0;
+  hdr->cbOptOffset = 0;
+
+  /* Swap out the auxiliary type information.  */
+  hdr->cbAuxOffset = offset;
+  offset = ecoff_build_aux (&buf, &bufend, offset);
+  hdr->iauxMax = (offset - hdr->cbAuxOffset) / sizeof (union aux_ext);
+
+  /* Copy out the local strings.  */
+  hdr->cbSsOffset = offset;
+  offset = ecoff_build_ss (&buf, &bufend, offset);
+  hdr->issMax = offset - hdr->cbSsOffset;
+
+  /* Copy out the external strings.  */
+  hdr->cbSsExtOffset = offset;
+  offset += ecoff_build_strings (&buf, &bufend, offset, &ext_strings);
+  offset = ecoff_longword_adjust (&buf, &bufend, offset, (char **) NULL);
+  hdr->issExtMax = offset - hdr->cbSsExtOffset;
+
+  /* We don't use relative file descriptors.  */
+  hdr->crfd = 0;
+  hdr->cbRfdOffset = 0;
+
+  /* Swap out the file descriptors.  */
+  hdr->cbFdOffset = offset;
+  offset = ecoff_build_fdr (&buf, &bufend, offset);
+  hdr->ifdMax = (offset - hdr->cbFdOffset) / sizeof (struct fdr_ext);
+
+  /* Copy out the external symbols.  */
+  hdr->cbExtOffset = offset;
+  if (bufend - (buf + offset) < extoffset)
+    (void) ecoff_add_bytes (&buf, &bufend, buf + offset, extoffset);
+  memcpy (buf + offset, extbuf, extoffset);
+  offset += extoffset;
+  hdr->iextMax = (offset - hdr->cbExtOffset) / sizeof (struct ext_ext);
+
+  know ((offset & 3) == 0);
+
+  /* That completes the symbolic debugging information.  We must now
+     finish up the symbolic header and the ecoff_tdata structure.  */
+  set = buf;
+#define SET(ptr, count, type) \
+  ecoff_data (stdoutput)->ptr = (type *) set; \
+  set += hdr->count * sizeof (type)
+
+  SET (line, cbLine, unsigned char);
+  SET (external_dnr, idnMax, struct dnr_ext);
+  SET (external_pdr, ipdMax, struct pdr_ext);
+  SET (external_sym, isymMax, struct sym_ext);
+  SET (external_opt, ioptMax, struct opt_ext);
+  SET (external_aux, iauxMax, union aux_ext);
+  SET (ss, issMax, char);
+  SET (ssext, issExtMax, char);
+  SET (external_rfd, crfd, struct rfd_ext);
+  SET (external_fdr, ifdMax, struct fdr_ext);
+  SET (external_ext, iextMax, struct ext_ext);
+
+#undef SET
+
+  /* FIXME: set the gp value.  */
+
+  /* FIXME: set the register masks.  */
+
+  ecoff_data (stdoutput)->raw_size = offset;
+  ecoff_data (stdoutput)->raw_syments = buf;
+
+  hdr->magic = magicSym;
+  /* FIXME: what should hdr->vstamp be?  */
+}
+\f
+/* Allocate a cluster of pages.  */
+
+#ifndef MALLOC_CHECK
+
+static page_t *
+allocate_cluster (npages)
+     unsigned long npages;
+{
+  register page_t *value = (page_t *) xmalloc (npages * PAGE_USIZE);
+
+#ifdef ECOFF_DEBUG
+  if (debug > 3)
+    fprintf (stderr, "\talloc\tnpages = %d, value = 0x%.8x\n", npages, value);
+#endif
+
+  memset (value, 0, npages * PAGE_USIZE);
+
+  return value;
+}
+
+
+static page_t *cluster_ptr = NULL;
+static unsigned long pages_left = 0;
+
+#endif /* MALLOC_CHECK */
+
+/* Allocate one page (which is initialized to 0).  */
+
+static page_t *
+allocate_page ()
+{
+#ifndef MALLOC_CHECK
+
+  if (pages_left == 0)
+    {
+      pages_left = MAX_CLUSTER_PAGES;
+      cluster_ptr = allocate_cluster (pages_left);
+    }
+
+  pages_left--;
+  return cluster_ptr++;
+
+#else  /* MALLOC_CHECK */
+
+  page_t *ptr;
+
+  ptr = xmalloc (PAGE_USIZE);
+  memset (ptr, 0, PAGE_USIZE);
+  return ptr;
+
+#endif /* MALLOC_CHECK */
+}
+\f
+/* Allocate scoping information.  */
+
+static scope_t *
+allocate_scope ()
+{
+  register 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 *) NULL)
+    alloc_counts[ (int)alloc_type_scope ].free_list.f_scope = ptr->free;
+  else
+    {
+      register int unallocated = alloc_counts[(int)alloc_type_scope].unallocated;
+      register 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 = (scope_t *) 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 (ptr)
+     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) ptr);
+#endif
+}
+\f
+/* Allocate links for pages in a virtual array.  */
+
+static vlinks_t *
+allocate_vlinks ()
+{
+  register vlinks_t *ptr;
+  static vlinks_t initial_vlinks;
+
+#ifndef MALLOC_CHECK
+
+  register int unallocated = alloc_counts[(int)alloc_type_vlinks].unallocated;
+  register 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 = (vlinks_t *) xmalloc (sizeof (vlinks_t));
+
+#endif
+
+  alloc_counts[(int)alloc_type_vlinks].total_alloc++;
+  *ptr = initial_vlinks;
+  return ptr;
+}
+\f
+/* Allocate string hash buckets.  */
+
+static shash_t *
+allocate_shash ()
+{
+  register shash_t *ptr;
+  static shash_t initial_shash;
+
+#ifndef MALLOC_CHECK
+
+  register int unallocated = alloc_counts[(int)alloc_type_shash].unallocated;
+  register 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 = (shash_t *) xmalloc (sizeof (shash_t));
+
+#endif
+
+  alloc_counts[(int)alloc_type_shash].total_alloc++;
+  *ptr = initial_shash;
+  return ptr;
+}
+\f
+/* Allocate type hash buckets.  */
+
+static thash_t *
+allocate_thash ()
+{
+  register thash_t *ptr;
+  static thash_t initial_thash;
+
+#ifndef MALLOC_CHECK
+
+  register int unallocated = alloc_counts[(int)alloc_type_thash].unallocated;
+  register 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 = (thash_t *) xmalloc (sizeof (thash_t));
+
+#endif
+
+  alloc_counts[(int)alloc_type_thash].total_alloc++;
+  *ptr = initial_thash;
+  return ptr;
+}
+\f
+/* Allocate structure, union, or enum tag information.  */
+
+static tag_t *
+allocate_tag ()
+{
+  register 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 *) NULL)
+    alloc_counts[(int)alloc_type_tag].free_list.f_tag = ptr->free;
+  else
+    {
+      register int unallocated = alloc_counts[(int)alloc_type_tag].unallocated;
+      register 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 = (tag_t *) 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 (ptr)
+     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_T) ptr);
+#endif
+}
+\f
+/* Allocate forward reference to a yet unknown tag.  */
+
+static forward_t *
+allocate_forward ()
+{
+  register forward_t *ptr;
+  static forward_t initial_forward;
+
+#ifndef MALLOC_CHECK
+
+  register int unallocated = alloc_counts[(int)alloc_type_forward].unallocated;
+  register 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 = (forward_t *) xmalloc (sizeof (forward_t));
+
+#endif
+
+  alloc_counts[(int)alloc_type_forward].total_alloc++;
+  *ptr = initial_forward;
+  return ptr;
+}
+\f
+/* Allocate head of type hash list.  */
+
+static thead_t *
+allocate_thead ()
+{
+  register 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 *) NULL)
+    alloc_counts[ (int)alloc_type_thead ].free_list.f_thead = ptr->free;
+  else
+    {
+      register int unallocated = alloc_counts[(int)alloc_type_thead].unallocated;
+      register 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 = (thead_t *) 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 (ptr)
+     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_T) ptr);
+#endif
+}
+\f
+static lineno_list_t *
+allocate_lineno_list ()
+{
+  register lineno_list_t *ptr;
+  static lineno_list_t initial_lineno_list;
+
+#ifndef MALLOC_CHECK
+
+  register int unallocated = alloc_counts[(int)alloc_type_lineno].unallocated;
+  register page_t *cur_page = alloc_counts[(int)alloc_type_lineno].cur_page;
+
+  if (unallocated == 0)
+    {
+      unallocated = PAGE_SIZE / sizeof (lineno_list_t);
+      alloc_counts[(int)alloc_type_lineno].cur_page = cur_page = allocate_page ();
+      alloc_counts[(int)alloc_type_lineno].total_pages++;
+    }
+
+  ptr = &cur_page->lineno[--unallocated];
+  alloc_counts[(int)alloc_type_lineno].unallocated = unallocated;
+
+#else
+
+  ptr = (lineno_list_t *) xmalloc (sizeof (lineno_list_t));
+
+#endif
+
+  alloc_counts[(int)alloc_type_lineno].total_alloc++;
+  *ptr = initial_lineno_list;
+  return ptr;
+}
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
new file mode 100644 (file)
index 0000000..f59dca2
--- /dev/null
@@ -0,0 +1,2980 @@
+/* tc-mips.c -- assemble code for a MIPS chip.
+   Copyright (C) 1993 Free Software Foundation, Inc.
+   Contributed by the OSF and Ralph Campbell.
+   Written by Keith Knowles and Ralph Campbell, working independently.
+   Modified for ECOFF support by Ian Lance Taylor of Cygnus Support.
+
+   This file is part of GAS.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   GAS 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 GAS; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "as.h"
+
+#include <ctype.h>
+
+#ifndef __STDC__
+#ifndef NO_STDARG
+#define NO_STDARG
+#endif
+#endif
+
+#ifndef NO_STDARG
+#include <stdarg.h>
+#else
+#ifndef NO_VARARGS
+#include <varargs.h>
+#endif /* NO_VARARGS */
+#endif /* NO_STDARG */
+
+#include "mips-opcode.h"
+
+#define AT  1
+#define RA  31
+
+static int mips_warn_about_macros;
+static int mips_noreorder;
+static int mips_nomove;
+static int mips_noat;
+static int mips_nobopt;
+
+#define N_RMASK 0xc4
+#define N_VFP   0xd4
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+/* This array holds the chars that always start a comment.  If the
+    pre-processor is disabled, these aren't very useful */
+const char comment_chars[] = "#";
+
+/* This array holds the chars that only start a comment at the beginning of
+   a line.  If the line seems to have the form '# 123 filename'
+   .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+   first line of the input file.  This is because the compiler outputs
+   #NO_APP at the beginning of its output. */
+/* Also note that C style comments are always supported.  */
+const char line_comment_chars[] = "#";
+
+/* This array holds machine specific line separator characters. */
+const char line_separator_chars[] = "";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or    0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+   changed in read.c .  Ideally it shouldn't have to know about it at all,
+   but nothing is ideal around here.
+ */
+
+static char    *insn_error;
+
+static int byte_order = BYTE_ORDER;
+
+static int auto_align = 1;
+\f
+/* Prototypes for static functions.  */
+
+#ifdef __STDC__
+#define internalError() \
+    as_fatal ("internal Error, line %d, %s", __LINE__, __FILE__)
+#else
+#define internalError() as_fatal ("MIPS internal Error");
+#endif
+
+static void append_insn PARAMS ((struct mips_cl_insn *ip,
+                                expressionS *p,
+                                bfd_reloc_code_real_type r));
+static void macro_build PARAMS ((int *counter, expressionS *ep,
+                                const char *name, const char *fmt,
+                                ...));
+static void macro_build_lui PARAMS ((int *counter, expressionS *ep,
+                                    int regnum));
+static void set_at PARAMS ((int *counter, int reg));
+static void set_at_unsigned PARAMS ((int *counter, int reg));
+static void check_absolute_expr PARAMS ((struct mips_cl_insn *ip,
+                                        expressionS *expr));
+static void load_register PARAMS ((int *counter,
+                                  struct mips_cl_insn *ip,
+                                  int reg, expressionS *ep));
+static void macro PARAMS ((struct mips_cl_insn *ip));
+static void mips_ip PARAMS ((char *str, struct mips_cl_insn *ip));
+static int my_getSmallExpression PARAMS ((expressionS *ep, char *str));
+static void my_getExpression PARAMS ((expressionS *ep, char *str));
+static symbolS *get_symbol PARAMS ((void));
+static long get_optional_absolute_expression PARAMS ((void));
+static void s_align PARAMS ((int));
+static void s_change_sec PARAMS ((int));
+static void s_cons PARAMS ((int));
+static void s_err PARAMS ((int));
+static void s_extern PARAMS ((int));
+static void s_float_cons PARAMS ((int));
+static void s_option PARAMS ((int));
+static void s_mipsset PARAMS ((int));
+#ifndef OBJ_ECOFF
+static void md_obj_begin PARAMS ((void));
+static void md_obj_end PARAMS ((void));
+static long get_number PARAMS ((void));
+static void s_ent PARAMS ((int));
+static void s_mipsend PARAMS ((int));
+static void s_file PARAMS ((int));
+static void s_frame PARAMS ((int));
+static void s_loc PARAMS ((int));
+static void s_mask PARAMS ((char));
+#endif
+\f
+/* Pseudo-op table.
+
+   The following pseudo-ops from the Kane and Heinrich MIPS book
+   should be defined here, but are currently unsupported: .alias,
+   .galive, .gjaldef, .gjrlive, .livereg, .noalias.
+
+   The following pseudo-ops from the Kane and Heinrich MIPS book are
+   specific to the type of debugging information being generated, and
+   should be defined by the object format: .aent, .begin, .bend,
+   .bgnb, .end, .endb, .ent, .fmask, .frame, .loc, .mask, .verstamp,
+   .vreg.
+
+   The following pseudo-ops from the Kane and Heinrich MIPS book are
+   not MIPS CPU specific, but are also not specific to the object file
+   format.  This file is probably the best place to define them, but
+   they are not currently supported: .asm0, .endr, .lab, .repeat,
+   .struct, .weakext.  */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+  /* MIPS specific pseudo-ops.  */
+  { "option",          s_option,       0       },
+  { "set",             s_mipsset,      0       },
+  { "rdata",           s_change_sec,   'r',    },
+  { "sdata",           s_change_sec,   's',    },
+
+  /* Relatively generic pseudo-ops that happen to be used on MIPS
+     chips.  */
+  { "asciiz",          stringer,       1       },
+  { "bss",             s_change_sec,   'b'     },
+  { "err",             s_err,          0       },
+  { "half",            s_cons,         1       },
+
+  /* These pseudo-ops are defined in read.c, but must be overridden
+     here for one reason or another.  */
+  { "align",           s_align,        0       },
+  { "byte",            s_cons,         0       },
+  { "data",            s_change_sec,   'd'     },
+  { "double",          s_float_cons,   1       },
+  { "extern",          s_extern,       0       },
+  { "float",           s_float_cons,   0       },
+  { "text",            s_change_sec,   't'     },
+  { "word",            s_cons,         2       },
+
+#ifndef OBJ_ECOFF
+  /* These pseudo-ops should be defined by the object file format.
+     However, ECOFF is the only format which currently defines them,
+     so we have versions here for a.out.  */
+  { "aent",            s_ent,          1       },
+  { "end",             s_mipsend,      0       },
+  { "ent",             s_ent,          0       },
+  { "file",            s_file,         0       },
+  { "fmask",           s_ignore,       'F'     },
+  { "frame",           s_ignore,       0       },
+  { "loc",             s_ignore,       0       },
+  { "mask",            s_ignore,       'R'     },
+  { "verstamp",                s_ignore,       0       },
+#endif
+
+  /* Sentinel.  */
+  { NULL }
+};
+\f
+const relax_typeS md_relax_table[] = {
+       0
+};
+
+
+static char *expr_end;
+
+static expressionS imm_expr;
+static expressionS offset_expr;
+static bfd_reloc_code_real_type imm_reloc;
+static bfd_reloc_code_real_type offset_reloc;
+
+/*
+ * This function is called once, at assembler startup time.  It should
+ * set up all the tables, etc. that the MD part of the assembler will need.
+ */
+void
+md_begin()
+{
+    register char *retval = NULL;
+    register unsigned int i = 0;
+
+    if ((op_hash = hash_new()) == NULL) {
+       as_fatal("Virtual memory exhausted");
+    }
+    for (i = 0; i < NUMOPCODES;) {
+       const char *name = mips_opcodes[i].name;
+
+       retval = hash_insert(op_hash, name, &mips_opcodes[i]);
+       if (retval != NULL && *retval != '\0') {
+           fprintf (stderr, "internal error: can't hash `%s': %s\n",
+               mips_opcodes[i].name, retval);
+           as_fatal ("Broken assembler.  No assembly attempted.");
+       }
+       do {
+           if ((mips_opcodes[i].match & mips_opcodes[i].mask) !=
+             mips_opcodes[i].match) {
+               fprintf (stderr, "internal error: bad opcode: `%s' \"%s\"\n",
+                   mips_opcodes[i].name, mips_opcodes[i].args);
+               as_fatal ("Broken assembler.  No assembly attempted.");
+           }
+           ++i;
+       } while ((i < NUMOPCODES) && !strcmp(mips_opcodes[i].name, name));
+    }
+
+#ifndef OBJ_ECOFF
+    md_obj_begin ();
+#endif
+}
+
+void
+md_end ()
+{
+#ifndef OBJ_ECOFF
+  md_obj_end ();
+#endif
+}
+
+void
+md_assemble(str)
+    char *str;
+{
+    struct mips_cl_insn insn;
+    static int init;
+
+    if (!init) {
+       /* set the default alignment for the text section (2**2) */
+       /* This should go in md_begin but text_section isn't initialized then */
+       record_alignment(text_section, 2);
+       init = 1;
+    }
+
+    imm_expr.X_seg = absent_section;
+    offset_expr.X_seg = absent_section;
+
+    mips_ip(str, &insn);
+    if (insn_error) {
+       as_bad("%s `%s'", insn_error, str);
+       return;
+    }
+    if (insn.insn_mo->pinfo == INSN_MACRO) {
+       macro(&insn);
+    } else {
+       if (imm_expr.X_seg != absent_section)
+           append_insn(&insn, &imm_expr, imm_reloc);
+       else if (offset_expr.X_seg != absent_section)
+           append_insn(&insn, &offset_expr, offset_reloc);
+       else
+           append_insn(&insn, NULL, BFD_RELOC_UNUSED);
+    }
+}
+
+#define ALIGN_ERR "Attempt to assemble instruction onto non word boundary."
+#define ALIGN_ERR2 "GAS doesn't do implicit alignment; use .align directive."
+
+/*
+ *                                append insn
+ * Output an instruction.
+ */
+static void
+append_insn(ip, address_expr, reloc_type)
+    struct mips_cl_insn *ip;
+    expressionS *address_expr;
+    bfd_reloc_code_real_type reloc_type;
+{
+    char *f;
+
+    f = frag_more(4);
+#if 0 /* This is testing the address of the frag, not the alignment of
+        the instruction in the current section.  */
+    if ((int) f & 3) {
+       as_bad(ALIGN_ERR);
+       as_bad(ALIGN_ERR2);
+    }
+#endif
+    if (address_expr != NULL) {
+       fixS *fixP;
+
+       if (address_expr->X_seg == &bfd_abs_section) {
+           switch (reloc_type) {
+           case BFD_RELOC_32:
+               ip->insn_opcode |= address_expr->X_add_number;
+               break;
+
+           case BFD_RELOC_LO16:
+               ip->insn_opcode |= address_expr->X_add_number & 0xffff;
+               break;
+
+           case BFD_RELOC_MIPS_JMP:
+           case BFD_RELOC_16_PCREL_S2:
+               goto need_reloc;
+
+           default:
+               internalError();
+           }
+       } else {
+           assert(reloc_type != BFD_RELOC_UNUSED);
+       need_reloc:
+           fixP = fix_new(frag_now, f - frag_now->fr_literal, 4,
+               address_expr->X_add_symbol,
+               address_expr->X_subtract_symbol,
+               address_expr->X_add_number,
+                reloc_type == BFD_RELOC_16_PCREL_S2,
+               reloc_type);
+       }
+    }
+    md_number_to_chars(f, ip->insn_opcode, 4);
+
+    /*
+     * Fill all delay slots with nops.
+     */
+    if (!mips_noreorder) {
+       if (ip->insn_mo->pinfo & ANY_DELAY) {
+           f = frag_more(4);
+           md_number_to_chars(f, 0, 4);
+       };
+
+       /* One extra nop */
+       if (ip->insn_mo->pinfo & INSN_EXTRA_DELAY) {
+           f = frag_more(4);
+           md_number_to_chars(f, 0, 4);
+       }
+    }
+}
+
+#ifndef NO_STDARG
+static void
+macro_build (int *counter,
+            expressionS *ep,
+            const char *name,
+            const char *fmt,
+            ...)
+#else /* ! defined (NO_STDARG) */
+static void
+macro_build (counter, ep, name, fmt, va_alist)
+     int *counter;
+     expressionS *ep;
+     const char *name;
+     const char *fmt;
+     va_dcl
+#endif /* ! defined (NO_STDARG) */
+{
+    struct mips_cl_insn insn;
+    bfd_reloc_code_real_type r;
+    va_list args;
+
+#ifndef NO_STDARG
+    va_start(args, fmt);
+#else
+    va_start(args);
+#endif
+
+    /*
+     * If the macro is about to expand into a second instruction,
+     * print a warning if needed. We need to pass ip as a parameter
+     * to generate a better warning message here...
+     */
+    if (mips_warn_about_macros && *counter == 1)
+      as_warn("Macro instruction expanded into multiple instructions");
+
+    *counter += 1;  /* bump instruction counter */
+
+    r = BFD_RELOC_UNUSED;
+    insn.insn_mo = (struct mips_opcode *) hash_find(op_hash, name);
+    assert(insn.insn_mo);
+    assert(strcmp(name, insn.insn_mo->name) == 0);
+
+    while (strcmp(fmt, insn.insn_mo->args) != 0) {
+       ++insn.insn_mo;
+       assert(insn.insn_mo->name);
+       assert(strcmp(name, insn.insn_mo->name) == 0);
+    }
+    assert(insn.insn_mo->pinfo != INSN_MACRO);
+    insn.insn_opcode = insn.insn_mo->match;
+    for (;;) {
+       switch (*fmt++) {
+       case '\0':
+           break;
+
+       case ',':
+       case '(':
+       case ')':
+           continue;
+
+       case 't':
+       case 'w':
+           insn.insn_opcode |= va_arg(args, int) << 16;
+           continue;
+
+       case 'c':
+       case 'T':
+       case 'W':
+           insn.insn_opcode |= va_arg(args, int) << 16;
+           continue;
+
+       case 'd':
+           insn.insn_opcode |= va_arg(args, int) << 11;
+           continue;
+
+       case 'V':
+       case 'S':
+           insn.insn_opcode |= va_arg(args, int) << 11;
+           continue;
+
+       case '<':
+           insn.insn_opcode |= va_arg(args, int) << 6;
+           continue;
+
+       case 'D':
+           insn.insn_opcode |= va_arg(args, int) << 6;
+           continue;
+
+       case 'b':
+       case 's':
+       case 'r':
+       case 'v':
+           insn.insn_opcode |= va_arg(args, int) << 21;
+           continue;
+
+       case 'i':
+       case 'j':
+       case 'o':
+           r = BFD_RELOC_LO16;
+           continue;
+
+       case 'p':
+           assert(ep != NULL);
+           /*
+            * This allows macro() to pass an immediate expression for
+            * creating short branches without creating a symbol.
+            * Note that the expression still might come from the assembly
+            * input, in which case the value is not checked for range nor
+            * is a relocation entry generated (yuck).
+            */
+           if (ep->X_add_symbol == NULL && ep->X_seg == &bfd_abs_section) {
+               insn.insn_opcode |= (ep->X_add_number >> 2) & 0xffff;
+               ep = NULL;
+           } else
+               r = BFD_RELOC_16_PCREL_S2;
+           continue;
+
+       default:
+           internalError();
+       }
+       break;
+    }
+    va_end(args);
+    assert(r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+    append_insn(&insn, ep, r);
+}
+
+/*
+ * Generate a "lui" instruction.
+ */
+static void
+macro_build_lui (counter, ep, regnum)
+     int *counter;
+     expressionS *ep;
+     int regnum;
+{
+    expressionS high_expr;
+    struct mips_cl_insn insn;
+    bfd_reloc_code_real_type r;
+    CONST char *name = "lui";
+    CONST char *fmt = "t,u";
+
+    high_expr = *ep;
+
+    if (high_expr.X_seg == &bfd_abs_section) {
+       /* we can compute the instruction now without a relocation entry */
+       if (high_expr.X_add_number & 0x8000)
+           high_expr.X_add_number += 0x10000;
+       high_expr.X_add_number =
+           ((unsigned long) high_expr.X_add_number >> 16) & 0xffff;
+       r = BFD_RELOC_UNUSED;
+    } else
+       r = BFD_RELOC_HI16_S;
+
+    /*
+     * If the macro is about to expand into a second instruction,
+     * print a warning if needed. We need to pass ip as a parameter
+     * to generate a better warning message here...
+     */
+    if (mips_warn_about_macros && *counter == 1)
+      as_warn("Macro instruction expanded into multiple instructions");
+
+    *counter += 1;  /* bump instruction counter */
+
+    insn.insn_mo = (struct mips_opcode *) hash_find(op_hash, name);
+    assert(insn.insn_mo);
+    assert(strcmp(name, insn.insn_mo->name) == 0);
+    assert(strcmp(fmt, insn.insn_mo->args) == 0);
+
+    insn.insn_opcode = insn.insn_mo->match | (regnum << 16);
+    if (r == BFD_RELOC_UNUSED) {
+       insn.insn_opcode |= high_expr.X_add_number;
+       append_insn(&insn, NULL, r);
+    } else
+       append_insn(&insn, &high_expr, r);
+}
+
+/*                     set_at()
+ * Generates code to set the $at register to true (one)
+ * if reg is less than the immediate expression.
+ */
+static void
+set_at (counter, reg)
+     int *counter;
+     int reg;
+{
+
+  switch (imm_expr.X_add_number & 0xffff8000) {
+  case 0:
+  case 0xffff8000:
+    macro_build(counter, &imm_expr, "slti", "t,r,j", AT, reg);
+    return;
+
+  case 0x8000:
+    macro_build(counter, &imm_expr, "ori", "t,r,i", AT, 0);
+    break;
+
+  default:
+    macro_build_lui(counter, &imm_expr, AT);
+    if (imm_expr.X_add_number & 0xffff)
+      macro_build(counter, &imm_expr, "addiu", "t,r,j", AT, AT);
+  }
+  macro_build(counter, NULL, "slt", "d,v,t", AT, reg, AT);
+}
+
+/*                     set_at_unsigned()
+ * Generates code to set the $at register to true (one)
+ * if reg is less than the immediate expression.
+ * Unsigned comparison is perfomed.
+ */
+static void
+set_at_unsigned (counter, reg)
+     int *counter;
+     int reg;
+{
+
+  switch (imm_expr.X_add_number & 0xffff8000) {
+  case 0:
+  case 0xffff8000:
+    macro_build(counter, &imm_expr, "sltiu", "t,r,j", AT, reg);
+    return;
+
+  case 0x8000:
+    macro_build(counter, &imm_expr, "ori", "t,r,i", AT, 0);
+    break;
+
+  default:
+    macro_build_lui(counter, &imm_expr, AT);
+    if (imm_expr.X_add_number & 0xffff)
+      macro_build(counter, &imm_expr, "addiu", "t,r,j", AT, AT);
+  }
+  macro_build(counter, NULL, "sltu", "d,v,t", AT, reg, AT);
+}
+
+static void
+check_absolute_expr (ip, expr)
+     struct mips_cl_insn *ip;
+     expressionS *expr;
+{
+
+  if (expr->X_seg != &bfd_abs_section)
+    as_warn("Instruction %s requires absolute expression", ip->insn_mo->name);
+}
+
+/*                     load_register()
+ *  This routine generates the least number of instructions neccessary to load
+ *  an absolute expression value into a register.
+ */
+static void
+load_register (counter, ip, reg, ep)
+    int *counter;
+    struct mips_cl_insn *ip;
+    int reg;
+    expressionS *ep;
+{
+  switch (ep->X_add_number & 0xffff8000) {
+  case 0:
+  case 0xffff8000:
+    macro_build(counter, ep, "addiu", "t,r,j", reg, 0);
+    break;
+
+  case 0x8000:
+    macro_build(counter, ep, "ori", "t,r,i", reg, 0);
+    break;
+
+  default:
+    macro_build_lui(counter, ep, reg);
+    if (ep->X_add_number & 0xffff)
+      macro_build(counter, ep, "addiu", "t,r,j", reg, reg);
+  }
+}
+
+
+/*
+ *                     Build macros
+ *   This routine implements the seemingly endless macro or synthesized
+ * instructions and addressing modes in the mips assembly language. Many
+ * of these macros are simple and are similar to each other. These could
+ * probably be handled by some kind of table or grammer aproach instead of
+ * this verbose method. Others are not simple macros but are more like
+ * optimizing code generation.
+ *   One interesting optimization is when several store macros appear
+ * consecutivly that would load AT with the upper half of the same address.
+ * The ensuing load upper instructions are ommited. This implies some kind
+ * of global optimization. We currently only optimize within a single macro.
+ *   For many of the load and store macros if the address is specified as a
+ * constant expression in the first 64k of memory (ie ld $2,0x4000c) we
+ * first load register 'at' with zero and use it as the base register. The
+ * mips assembler simply uses register $zero. Just one tiny optimization
+ * we're missing.
+ */
+static void
+macro (ip)
+     struct mips_cl_insn *ip;
+{
+    register int treg, sreg, dreg, breg;
+    int tempreg;
+    int mask;
+    int icnt = 0;
+    int used_at;
+    int save_reorder_condition;
+    expressionS expr1;
+    const char *s;
+
+    treg = (ip->insn_opcode >> 16) & 0x1f;
+    dreg = (ip->insn_opcode >> 11) & 0x1f;
+    sreg = breg = (ip->insn_opcode >> 21) & 0x1f;
+    mask = ip->insn_mo->mask;
+
+    expr1.X_seg = &bfd_abs_section;
+    expr1.X_subtract_symbol = NULL;
+    expr1.X_add_symbol = NULL;
+    expr1.X_add_number = 1;
+
+    switch (mask) {
+    case M_ABS:
+    case M_ABSU:
+       /*
+       Note: mips algorithm requires the move in the delay slot.
+       <main>:         bgez $a0,0x4001bc <main+12>
+       <main+4>:       move v0,$a0
+       <main+8>:       sub v0,$zero,$a0
+       <main+12>:      nop
+       */
+
+       save_reorder_condition = mips_noreorder;
+       mips_noreorder = 1;
+
+       expr1.X_add_number = 8;
+       macro_build(&icnt, &expr1, "bgez", "s,p", sreg);
+       macro_build(&icnt, NULL, "move", "d,s", dreg, sreg, 0);
+       macro_build(&icnt, NULL, mask == M_ABS ? "sub" : "subu", "d,v,t",
+           dreg, 0, sreg);
+
+       mips_noreorder = save_reorder_condition;
+        return;
+
+    case M_ADD_I:
+    case M_ADDU_I:
+       switch (imm_expr.X_add_number & 0xffff8000) {
+       case 0: 
+       case 0xffff8000: 
+           macro_build(&icnt, &imm_expr,
+               mask == M_ADD_I ? "addi" : "addiu", "t,r,j", treg, sreg);
+           return;
+
+       case 0x8000: 
+           macro_build(&icnt, &imm_expr, "ori", "t,r,i", AT, 0);
+           break;
+
+       default: 
+           macro_build_lui(&icnt, &imm_expr, AT);
+           if (imm_expr.X_add_number & 0xffff)
+               macro_build(&icnt, &imm_expr, "addiu", "t,r,j", AT, AT);
+           break;
+       }
+       macro_build(&icnt, NULL,
+           mask == M_ADD_I ? "add" : "addu", "d,v,t", treg, sreg, AT);
+       break;
+
+    case M_AND_I:
+    case M_OR_I:
+    case M_NOR_I:
+    case M_XOR_I:
+       switch (imm_expr.X_add_number & 0xffff8000) {
+       case 0:
+       case 0x8000:
+           switch (mask) {
+           case M_AND_I:
+               macro_build(&icnt, &imm_expr, "andi", "t,r,i", treg, sreg);
+               return;
+           case M_OR_I:
+               macro_build(&icnt, &imm_expr, "ori", "t,r,i", treg, sreg);
+               return;
+           case M_NOR_I:
+               macro_build(&icnt, &imm_expr, "ori", "t,r,i", treg, sreg);
+               macro_build(&icnt, &imm_expr, "nor", "d,v,t", treg, treg, 0);
+               return;
+           case M_XOR_I:
+               macro_build(&icnt, &imm_expr, "xori", "t,r,i", treg, sreg);
+               return;
+           default:
+               internalError();
+           }
+
+       case 0xffff8000:
+           macro_build(&icnt, &imm_expr, "addiu", "t,r,j", AT, 0);
+           break;
+
+       default:
+           macro_build_lui(&icnt, &imm_expr, AT);
+           if (imm_expr.X_add_number & 0xffff)
+               macro_build(&icnt, &imm_expr, "addiu", "t,r,j", AT, AT);
+       }
+       switch (mask) {
+       case M_AND_I:
+           macro_build(&icnt, NULL, "and", "d,v,t", treg, sreg, AT);
+           break;
+       case M_OR_I:
+           macro_build(&icnt, NULL, "or", "d,v,t", treg, sreg, AT);
+           break;
+       case M_NOR_I:
+           macro_build(&icnt, NULL, "nor", "d,v,t", treg, sreg, AT);
+           break;
+       case M_XOR_I:
+           macro_build(&icnt, NULL, "xor", "d,v,t", treg, sreg, AT);
+           break;
+       default:
+           internalError();
+       }
+       break;
+
+    case M_BEQ_I:
+    case M_BNE_I:
+       if (imm_expr.X_add_number == 0) {
+           macro_build(&icnt, &offset_expr, mask == M_BEQ_I ? "beq" : "bne",
+               "s,t,p", sreg, 0);
+           return;
+       }
+       load_register(&icnt, ip, AT, &imm_expr);
+       macro_build(&icnt, &offset_expr, mask == M_BEQ_I ? "beq" : "bne",
+           "s,t,p", sreg, AT);
+       break;
+
+    case M_BGE:
+       if (treg == 0) {
+           macro_build(&icnt, &offset_expr, "bgez", "s,p", sreg);
+           return;
+       }
+       macro_build(&icnt, NULL, "slt", "d,v,t", AT, sreg, treg);
+       macro_build(&icnt, &offset_expr, "beq", "s,t,p", AT, 0);
+       break;
+
+    case M_BGT_I:
+       imm_expr.X_add_number++;
+       /* FALLTHROUGH */
+    case M_BGE_I:
+       if (imm_expr.X_add_number == 0) {
+           macro_build(&icnt, &offset_expr, "bgez", "s,p", sreg);
+           return;
+       }
+       if (imm_expr.X_add_number == 1) {
+           macro_build(&icnt, &offset_expr, "bgtz", "s,p", sreg);
+           return;
+       }
+       set_at(&icnt, sreg);
+       macro_build(&icnt, &offset_expr, "beq", "s,t,p", AT, 0);
+       break;
+
+    case M_BGEU:
+       if (treg == 0) {
+           macro_build(&icnt, &offset_expr, "b", "p");
+           return;
+       }
+       macro_build(&icnt, NULL, "sltu", "d,v,t", AT, sreg, treg);
+       macro_build(&icnt, &offset_expr, "beq", "s,t,p", AT, 0);
+       break;
+
+    case M_BGEU_I:
+       if (imm_expr.X_add_number == 0) {
+           macro_build(&icnt, &offset_expr, "b", "p");
+           return;
+       }
+       if (imm_expr.X_add_number == 1) {
+           macro_build(&icnt, &offset_expr, "bne", "s,t,p", sreg, 0);
+           return;
+       }
+       set_at_unsigned(&icnt, sreg);
+       macro_build(&icnt, &offset_expr, "beq", "s,t,p", AT, 0);
+       break;
+
+    case M_BGT:
+       if (treg == 0) {
+           macro_build(&icnt, &offset_expr, "bgtz", "s,p", sreg);
+           return;
+       }
+       macro_build(&icnt, NULL, "slt", "d,v,t", AT, treg, sreg);
+       macro_build(&icnt, &offset_expr, "bne", "s,t,p", AT, 0);
+       break;
+
+    case M_BGTU:
+       if (treg == 0) {
+           macro_build(&icnt, &offset_expr, "bne", "s,t,p", sreg, 0);
+           return;
+       }
+       macro_build(&icnt, NULL, "sltu", "d,v,t", AT, treg, sreg);
+       macro_build(&icnt, &offset_expr, "bne", "s,t,p", AT, 0);
+       break;
+
+    case M_BGTU_I:
+       if (imm_expr.X_add_number == 0) {
+           macro_build(&icnt, &offset_expr, "bne", "s,t,p", sreg, 0);
+           return;
+       }
+       if (imm_expr.X_add_number == -1) {
+           /* NOP */
+           if (mips_noreorder)
+               as_warn("Instruction %s is a nop; deleted", ip->insn_mo->name);
+           return;
+       }
+       imm_expr.X_add_number++;
+       set_at_unsigned(&icnt, sreg);
+       macro_build(&icnt, &offset_expr, "beq", "s,t,p", AT, 0);
+       break;
+
+    case M_BLE:
+       if (treg == 0) {
+           macro_build(&icnt, &offset_expr, "blez", "s,p", sreg);
+           return;
+        }
+       macro_build(&icnt, NULL, "slt", "d,v,t", AT, treg, sreg);
+       macro_build(&icnt, &offset_expr, "beq", "s,t,p", AT, 0);
+       break;
+
+    case M_BLE_I:
+       if (imm_expr.X_add_number == 0) {
+           macro_build(&icnt, &offset_expr, "blez", "s,p", sreg);
+           return;
+        }
+       if (imm_expr.X_add_number == -1) {
+           macro_build(&icnt, &offset_expr, "bltz", "s,p", sreg);
+           return;
+       }
+       imm_expr.X_add_number++;
+       set_at(&icnt, sreg);
+       macro_build(&icnt, &offset_expr, "bne", "s,t,p", AT, 0);
+       break;
+
+    case M_BLEU:
+       if (treg == 0) {
+           macro_build(&icnt, &offset_expr, "beq", "s,t,p", sreg, 0);
+           return;
+       }
+       macro_build(&icnt, NULL, "sltu", "d,v,t", AT, treg, sreg);
+       macro_build(&icnt, &offset_expr, "beq", "s,t,p", AT, 0);
+       break;
+
+    case M_BLEU_I:
+       if (imm_expr.X_add_number == 0) {
+           macro_build(&icnt, &offset_expr, "beq", "s,t,p", sreg, 0);
+           return;
+       }
+       if (imm_expr.X_add_number == -1) {
+           macro_build(&icnt, &offset_expr, "b", "p");
+           return;
+       }
+       imm_expr.X_add_number++;
+       set_at_unsigned(&icnt, sreg);
+       macro_build(&icnt, &offset_expr, "bne", "s,t,p", AT, 0);
+       break;
+
+    case M_BLT:
+       if (treg == 0) {
+           macro_build(&icnt, &offset_expr, "bltz", "s,p", sreg);
+           return;
+       }
+       macro_build(&icnt, NULL, "slt", "d,v,t", AT, sreg, treg);
+       macro_build(&icnt, &offset_expr, "bne", "s,t,p", AT, 0);
+       break;
+
+    case M_BLT_I:
+       if (imm_expr.X_add_number == 0) {
+           macro_build(&icnt, &offset_expr, "bltz", "s,p", sreg);
+           return;
+       }
+       if (imm_expr.X_add_number == 1) {
+           macro_build(&icnt, &offset_expr, "blez", "s,p", sreg);
+           return;
+       }
+       set_at(&icnt, sreg);
+       macro_build(&icnt, &offset_expr, "bne", "s,t,p", AT, 0);
+       break;
+
+    case M_BLTU:
+       if (treg == 0) {
+           /* NOP */
+           if (mips_noreorder)
+               as_warn("Instruction %s is a nop; deleted", ip->insn_mo->name);
+           return;
+       }
+       macro_build(&icnt, NULL, "sltu", "d,v,t", AT, sreg, treg);
+       macro_build(&icnt, &offset_expr, "bne", "s,t,p", AT, 0);
+       break;
+
+    case M_BLTU_I:
+       if (imm_expr.X_add_number == 0) {
+           /* NOP */
+           if (mips_noreorder)
+               as_warn("Instruction %s is a nop; deleted", ip->insn_mo->name);
+           return;
+       }
+       if (imm_expr.X_add_number == 1) {
+           macro_build(&icnt, &offset_expr, "beq", "s,t,p", sreg, 0);
+           return;
+       }
+       set_at_unsigned(&icnt, sreg);
+       macro_build(&icnt, &offset_expr, "bne", "s,t,p", AT, 0);
+       break;
+
+    case M_DIV_3:
+    case M_REM_3:
+       if (treg == 0) {
+           as_warn("Divide by zero.");
+           macro_build(&icnt, NULL, "break", "c", 7);
+           return;
+       }
+
+       save_reorder_condition = mips_noreorder;
+       mips_noreorder = 1;
+       macro_build(&icnt, NULL, "div", "s,t", sreg, treg);
+       expr1.X_add_number = 8;
+       macro_build(&icnt, &expr1, "bne", "s,t,p", treg, 0);
+       macro_build(&icnt, NULL, "nop", "", 0);
+       macro_build(&icnt, NULL, "break", "c", 7);
+       expr1.X_add_number = -1;
+       macro_build(&icnt, &expr1, "addiu", "t,r,j", AT, 0);
+       expr1.X_add_number = 16;
+       macro_build(&icnt, &expr1, "bne", "s,t,p", treg, AT);
+       expr1.X_add_number = 0x80000000;
+       macro_build_lui(&icnt, &expr1, AT);
+       expr1.X_add_number = 8;
+       macro_build(&icnt, &expr1, "bne", "s,t,p", sreg, AT);
+       macro_build(&icnt, NULL, "nop", "", 0);
+       macro_build(&icnt, NULL, "break", "c", 6);
+       mips_noreorder = save_reorder_condition;
+       macro_build(&icnt, NULL, mask == M_DIV_3 ? "mflo" : "mfhi", "d", dreg);
+        /* with reorder on there will be two implicit nop instructions here. */
+        break;
+
+    case M_DIV_3I:
+    case M_DIVU_3I:
+    case M_REM_3I:
+    case M_REMU_3I:
+       if (imm_expr.X_add_number == 0) {
+           as_warn("Divide by zero.");
+           macro_build(&icnt, NULL, "break", "c", 7);
+           return;
+       }
+       if (imm_expr.X_add_number == 1) {
+         if (mask == (int)M_DIV_3I || mask == (int)M_DIVU_3I)
+           macro_build(&icnt, NULL, "move", "d,s", dreg, sreg);
+         else
+           macro_build(&icnt, NULL, "move", "d,s", dreg, 0);
+         return;
+       }
+
+       load_register(&icnt, ip, AT, &imm_expr);
+       if (mask == (int)M_DIV_3I || mask == (int)M_REM_3I)
+         macro_build(&icnt, NULL, "div", "s,t", sreg, AT);
+       else
+         macro_build(&icnt, NULL, "divu", "s,t", sreg, AT);
+
+       if (mask == (int)M_DIV_3I || mask == (int)M_DIVU_3I)
+         macro_build(&icnt, NULL, "mflo", "d", dreg);
+       else
+         macro_build(&icnt, NULL, "mfhi", "d", dreg);
+       /* two implicit nop's required for mflo or mfhi */
+       break;
+
+      case M_DIVU_3:
+      case M_REMU_3:
+       save_reorder_condition = mips_noreorder;
+       mips_noreorder = 1;
+       macro_build(&icnt, NULL, "divu", "s,t", sreg, treg);
+       expr1.X_add_number = 8;
+       macro_build(&icnt, &expr1, "bne", "s,t,p", treg, 0);
+       macro_build(&icnt, NULL, "nop", "", 0);
+       macro_build(&icnt, NULL, "break", "c", 7);
+       mips_noreorder = save_reorder_condition;
+       macro_build(&icnt, NULL, mask == M_DIVU_3 ? "mflo" : "mfhi", "d", dreg);
+        /* with reorder on there will be two implicit nop instructions here. */
+        return;
+
+    case M_LA:
+       if (offset_expr.X_seg == &bfd_abs_section) {
+           load_register(&icnt, ip, treg, &offset_expr);
+           return;
+       }
+       macro_build_lui(&icnt, &offset_expr, treg);
+       macro_build(&icnt, &offset_expr, "addiu", "t,r,j", treg, treg);
+       return;
+
+    case M_LA_AB:
+       tempreg = (breg == treg) ? AT : treg;
+       if (offset_expr.X_seg == &bfd_abs_section)
+           load_register(&icnt, ip, tempreg, &offset_expr);
+       else {
+           macro_build_lui(&icnt, &offset_expr, tempreg);
+           macro_build(&icnt, &offset_expr, "addiu", "t,r,j", tempreg, tempreg);
+        }
+       if (breg != 0)
+           macro_build(&icnt, NULL, "addu", "d,v,t", treg, tempreg, breg);
+       if (breg == treg)
+               break;
+       return;
+
+    case M_LB_AB:
+       s = "lb";
+       goto ld;
+    case M_LBU_AB:
+       s = "lbu";
+       goto ld;
+    case M_LH_AB:
+       s = "lh";
+       goto ld;
+    case M_LHU_AB:
+       s = "lhu";
+       goto ld;
+    case M_LW_AB:
+       s = "lw";
+       goto ld;
+    case M_LWC0_AB:
+       s = "lwc0";
+       goto ld;
+    case M_LWC1_AB:
+       s = "lwc1";
+       goto ld;
+    case M_LWC2_AB:
+       s = "lwc2";
+       goto ld;
+    case M_LWC3_AB:
+       s = "lwc3";
+       goto ld;
+    case M_LWL_AB:
+       s = "lwl";
+       goto ld;
+    case M_LWR_AB:
+       s = "lwr";
+    ld:
+       if (breg == treg) {
+               tempreg = AT;
+               used_at = 1;
+       } else {
+               tempreg = treg;
+               used_at = 0;
+       }
+       goto ld_st;
+    case M_SB_AB:
+       s = "sb";
+       goto st;
+    case M_SH_AB:
+       s = "sh";
+       goto st;
+    case M_SW_AB:
+       s = "sw";
+       goto st;
+    case M_SWC0_AB:
+       s = "swc0";
+       goto st;
+    case M_SWC1_AB:
+       s = "swc1";
+       goto st;
+    case M_SWC2_AB:
+       s = "swc2";
+       goto st;
+    case M_SWC3_AB:
+       s = "swc3";
+       goto st;
+    case M_SWL_AB:
+       s = "swl";
+       goto st;
+    case M_SWR_AB:
+       s = "swr";
+    st:
+       tempreg = AT;
+       used_at = 1;
+    ld_st:
+       macro_build_lui(&icnt, &offset_expr, tempreg);
+       if (breg != 0)
+           macro_build(&icnt, NULL, "addu", "d,v,t", tempreg, tempreg, breg);
+       macro_build(&icnt, &offset_expr, s,
+           mask == M_LWC1_AB || mask == M_SWC1_AB ? "T,o(b)" : "t,o(b)",
+           treg, tempreg);
+       if (used_at)
+               break;
+       return;
+
+    case M_LI:
+       load_register(&icnt, ip, treg, &imm_expr);
+       return;
+
+    case M_LI_D:
+       /*
+       0x400370 <main>:        lui $at,%hi(foo)
+       0x400374 <main+4>:      lw $v0,%lo(foo)($at)
+       0x400378 <main+8>:      lw $v1,%lo(foo+4)($at)
+                               .data
+                <foo>:
+                               .float 3.133435
+       */
+       macro_build_lui(&icnt, &offset_expr, AT);
+       macro_build(&icnt, &offset_expr, "lw", "t,o(b)", treg, AT);
+       offset_expr.X_add_number = 4;
+       macro_build(&icnt, &offset_expr, "lw", "t,o(b)", treg+1, AT);
+       break;
+
+    case M_LI_DD:
+       /*
+       0x4003a0 <main>:        lwc1 $f0,-32752($gp)
+       0x4003a4 <main+4>:      lwc1 $f1,-32748($gp)
+       0x4003a8 <main+8>:      nop
+       */
+       sreg = (ip->insn_opcode >> 11) & 0x1f;  /* Fs reg */
+       macro_build(&icnt, &offset_expr, "lwc1", "T,o(b)", treg, AT);
+       offset_expr.X_add_number = 4;
+       macro_build(&icnt, &offset_expr, "lwc1", "T,o(b)", treg+1, AT);
+       break;
+
+    case M_L_DOB:
+       save_reorder_condition = mips_noreorder;
+       mips_noreorder = 1;
+       macro_build(&icnt, &offset_expr, "lwc1", "T,o(b)", treg, breg);
+       /* unecessary implicit nop */
+       mips_noreorder = save_reorder_condition;
+       offset_expr.X_add_number += 4;
+       macro_build(&icnt, &offset_expr, "lwc1", "T,o(b)", treg+1, breg);
+       return;
+
+    case M_L_DAB:
+       /*
+        * The MIPS assembler seems to check for X_add_number not
+        * being double aligned and generating:
+        *      lui     at,%hi(foo+1)
+        *      addu    at,at,v1
+        *      addiu   at,at,%lo(foo+1)
+        *      lwc1    f2,0(at)
+        *      lwc1    f3,4(at)
+        * But, the resulting address is the same after relocation so why
+        * generate the extra instruction?
+        */
+       macro_build_lui(&icnt, &offset_expr, AT);
+       if (breg != 0)
+           macro_build(&icnt, NULL, "addu", "d,v,t", AT, AT, breg);
+       save_reorder_condition = mips_noreorder;
+       mips_noreorder = 1;
+       macro_build(&icnt, &offset_expr, "lwc1", "T,o(b)", treg, AT);
+       /* unecessary implicit nop */
+       mips_noreorder = save_reorder_condition;
+       offset_expr.X_add_number += 4;
+       macro_build(&icnt, &offset_expr, "lwc1", "T,o(b)", treg+1, AT);
+       break;
+
+    case M_LD_OB:
+       s = "lw";
+       goto sd_ob;
+    case M_SD_OB:
+       s = "sw";
+    sd_ob:
+       macro_build(&icnt, &offset_expr, s, "t,o(b)", treg, breg);
+       offset_expr.X_add_number = 4;
+       macro_build(&icnt, &offset_expr, s, "t,o(b)", treg+1, breg);
+       return;
+
+    case M_LD_AB:
+       s = "lw";
+       if (breg == treg) {
+               tempreg = AT;
+               used_at = 1;
+       } else {
+               tempreg = treg;
+               used_at = 0;
+       }
+       goto sd_ab;
+    case M_SD_AB:
+       s = "sw";
+       tempreg = AT;
+       used_at = 1;
+    sd_ab:
+       macro_build_lui(&icnt, &offset_expr, tempreg);
+       if (breg != 0)
+           macro_build(&icnt, NULL, "addu", "d,v,t", tempreg, tempreg, breg);
+       macro_build(&icnt, &offset_expr, s, "t,o(b)", treg, tempreg);
+       offset_expr.X_add_number += 4;
+       macro_build(&icnt, &offset_expr, s, "t,o(b)", treg+1, tempreg);
+       if (used_at)
+               break;
+       return;
+
+    case M_MUL:
+       macro_build(&icnt, NULL, "multu", "s,t", sreg, treg);
+       macro_build(&icnt, NULL, "mflo", "d", dreg);
+       /* two implicit nop's required for mflo */
+       return;
+
+    case M_MUL_I:
+       /*
+        * The mips assembler some times generates shifts and adds.
+         * Im not trying to be that fancy. GCC should do this for us
+         * anyway.
+         */
+       load_register(&icnt, ip, AT, &imm_expr);
+       macro_build(&icnt, NULL, "mult", "s,t", sreg, AT);
+       macro_build(&icnt, NULL, "mflo", "d", dreg);
+       /* two implicit nop's required for mflo */
+       break;
+
+    case M_ROL:
+       macro_build(&icnt, NULL, "subu", "d,v,t", AT, 0, treg);
+       macro_build(&icnt, NULL, "srlv", "d,t,s", AT, sreg, AT);
+       macro_build(&icnt, NULL, "sllv", "d,t,s", dreg, sreg, treg);
+       macro_build(&icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+       break;
+
+    case M_ROL_I:
+       macro_build(&icnt, NULL, "sll", "d,w,<", AT, sreg,
+           imm_expr.X_add_number & 0x1f);
+       macro_build(&icnt, NULL, "srl", "d,w,<", dreg, sreg,
+           (0 - imm_expr.X_add_number) & 0x1f);
+       macro_build(&icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+       break;
+
+    case M_ROR:
+       macro_build(&icnt, NULL, "subu", "d,v,t", AT, 0, treg);
+       macro_build(&icnt, NULL, "sllv", "d,t,s", AT, sreg, AT);
+       macro_build(&icnt, NULL, "srlv", "d,t,s", dreg, sreg, treg);
+       macro_build(&icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+       break;
+
+    case M_ROR_I:
+       macro_build(&icnt, NULL, "srl", "d,w,<", AT, sreg,
+           imm_expr.X_add_number & 0x1f);
+       macro_build(&icnt, NULL, "sll", "d,w,<", dreg, sreg,
+           (0 - imm_expr.X_add_number) & 0x1f);
+       macro_build(&icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+       break;
+
+    case M_S_DOB:
+       macro_build(&icnt, &offset_expr, "swc1", "T,o(b)", treg, breg);
+       offset_expr.X_add_number += 4;
+       macro_build(&icnt, &offset_expr, "swc1", "T,o(b)", treg+1, breg);
+       return;
+
+    case M_S_DAB:
+       macro_build_lui(&icnt, &offset_expr, AT);
+       if (breg != 0)
+           macro_build(&icnt, NULL, "addu", "d,v,t", AT, AT, breg);
+       macro_build(&icnt, &offset_expr, "swc1", "T,o(b)", treg, AT);
+       offset_expr.X_add_number += 4;
+       macro_build(&icnt, &offset_expr, "swc1", "T,o(b)", treg+1, AT);
+       break;
+
+    case M_SEQ:
+       if (sreg == 0)
+           macro_build(&icnt, &expr1, "sltiu", "t,r,j", dreg, treg);
+       else if (treg == 0)
+           macro_build(&icnt, &expr1, "sltiu", "t,r,j", dreg, sreg);
+       else {
+           macro_build(&icnt, NULL, "xor", "d,v,t", dreg, sreg, treg);
+           macro_build(&icnt, &expr1, "sltiu", "t,r,j", dreg, dreg);
+       }
+       return;
+
+    case M_SEQ_I:
+       if (imm_expr.X_add_number == 0) {
+           macro_build(&icnt, &expr1, "sltiu", "t,r,j", dreg, sreg);
+           return;
+       }
+       if (sreg == 0) {
+           /* result is always false */
+           macro_build(&icnt, NULL, "move", "d,s", dreg, 0);
+           return;
+       }
+       switch (imm_expr.X_add_number & 0xffff8000) {
+       case 0:
+       case 0x8000:
+           macro_build(&icnt, &imm_expr, "xori", "t,r,i", dreg, sreg);
+           used_at = 0;
+           break;
+
+       case 0xffff8000:
+           if (imm_expr.X_add_number != -32768) {
+               imm_expr.X_add_number = -imm_expr.X_add_number;
+               macro_build(&icnt, &imm_expr, "addiu", "t,r,j", dreg, sreg);
+               used_at = 0;
+               break;
+           }
+           /* FALLTHROUGH */
+
+       default:
+           macro_build_lui(&icnt, &imm_expr, AT);
+           if (imm_expr.X_add_number & 0xffff)
+               macro_build(&icnt, &imm_expr, "addiu", "t,r,j", AT, AT);
+           macro_build(&icnt, NULL, "xor", "d,v,t", dreg, sreg, AT);
+           used_at = 1;
+       }
+       macro_build(&icnt, &expr1, "sltiu", "t,r,j", dreg, dreg);
+       if (used_at)
+               break;
+       return;
+
+    case M_SGE:                /* sreg >= treg <==> not (sreg < treg) */
+       s = "slt";
+       goto sge;
+    case M_SGEU:
+       s = "sltu";
+    sge:
+       macro_build(&icnt, NULL, s, "d,v,t", dreg, sreg, treg);
+       macro_build(&icnt, &expr1, "xori", "t,r,i", dreg, dreg);
+       return;
+
+    case M_SGE_I:      /* sreg >= I <==> not (sreg < I) */
+    case M_SGEU_I:
+       if (imm_expr.X_add_number < 32768 && imm_expr.X_add_number > -32769) {
+           macro_build(&icnt, &expr1,
+               mask == M_SGE_I ? "slti" : "sltiu", "t,r,j", dreg, sreg);
+           used_at = 0;
+       } else {
+           load_register(&icnt, ip, AT, &imm_expr);
+           macro_build(&icnt, NULL,
+               mask == M_SGE_I ? "slt" : "sltu", "d,v,t", dreg, sreg, AT);
+           used_at = 1;
+       }
+       macro_build(&icnt, &expr1, "xori", "t,r,i", dreg, dreg);
+       if (used_at)
+               break;
+       return;
+
+    case M_SGT:                /* sreg > treg  <==>  treg < sreg */
+       s = "slt";
+       goto sgt;
+    case M_SGTU:
+       s = "sltu";
+    sgt:
+       macro_build(&icnt, NULL, s, "d,v,t", dreg, treg, sreg);
+       return;
+
+    case M_SGT_I:      /* sreg > I  <==>  I < sreg */
+       s = "slt";
+       goto sgti;
+    case M_SGTU_I:
+       s = "sltu";
+    sgti:
+       load_register(&icnt, ip, AT, &imm_expr);
+       macro_build(&icnt, NULL, s, "d,v,t", dreg, AT, sreg);
+       break;
+
+    case M_SLE:        /* sreg <= treg  <==>  treg >= sreg  <==>  not (treg < sreg) */
+       s = "slt";
+       goto sle;
+    case M_SLEU:
+       s = "sltu";
+    sle:
+       macro_build(&icnt, NULL, s, "d,v,t", dreg, treg, sreg);
+       macro_build(&icnt, &imm_expr, "xori", "t,r,i", dreg, dreg);
+       return;
+
+    case M_SLE_I:  /* sreg <= I <==> I >= sreg <==> not (I < sreg) */
+       s = "slt";
+       goto slei;
+    case M_SLEU_I:
+       s = "sltu";
+    slei:
+       load_register(&icnt, ip, AT, &imm_expr);
+       macro_build(&icnt, NULL, s, "d,v,t", dreg, AT, sreg);
+       macro_build(&icnt, &offset_expr, "xori", "t,r,i", dreg, dreg);
+       break;
+
+    case M_SLT_I:
+       if (imm_expr.X_add_number < 32768 && imm_expr.X_add_number > -32769) {
+           macro_build(&icnt, &imm_expr, "slti", "t,r,j", dreg, sreg);
+           return;
+       }
+       load_register(&icnt, ip, AT, &imm_expr);
+       macro_build(&icnt, NULL, "slt", "d,v,t", dreg, sreg, AT);
+       break;
+
+    case M_SLTU_I:
+       if (imm_expr.X_add_number < 32768 && imm_expr.X_add_number > -32769) {
+           macro_build(&icnt, &imm_expr, "sltiu", "t,r,j", dreg, sreg);
+           return;
+       }
+       load_register(&icnt, ip, AT, &imm_expr);
+       macro_build(&icnt, NULL, "sltu", "d,v,t", dreg, sreg, AT);
+       break;
+
+    case M_SNE:
+       if (sreg == 0)
+           macro_build(&icnt, NULL, "sltu", "d,v,t", dreg, 0, treg);
+       else if (treg == 0)
+           macro_build(&icnt, NULL, "sltu", "d,v,t", dreg, 0, sreg);
+       else {
+           macro_build(&icnt, NULL, "xor", "d,v,t", dreg, sreg, treg);
+           macro_build(&icnt, NULL, "sltu", "d,v,t", dreg, 0, dreg);
+       }
+       return;
+
+    case M_SNE_I:
+       if (imm_expr.X_add_number == 0) {
+           macro_build(&icnt, NULL, "sltu", "d,v,t", dreg, 0, sreg);
+           return;
+       }
+       if (sreg == 0) {
+           /* result is always true */
+           macro_build(&icnt, &expr1, "addiu", "t,r,j", dreg, 0);
+           return;
+       }
+       switch (imm_expr.X_add_number & 0xffff8000) {
+       case 0:
+       case 0x8000:
+           macro_build(&icnt, &imm_expr, "xori", "t,r,i", dreg, sreg);
+           used_at = 0;
+           break;
+
+       case 0xffff8000:
+           if (imm_expr.X_add_number != -32768) {
+               imm_expr.X_add_number = -imm_expr.X_add_number;
+               macro_build(&icnt, &imm_expr, "addiu", "t,r,j", dreg, sreg);
+               used_at = 0;
+               break;
+           }
+           /* FALLTHROUGH */
+
+       default:
+           macro_build_lui(&icnt, &imm_expr, AT);
+           if (imm_expr.X_add_number & 0xffff)
+               macro_build(&icnt, &imm_expr, "addiu", "t,r,j", AT, AT);
+           macro_build(&icnt, NULL, "xor", "d,v,t", dreg, sreg, AT);
+           used_at = 1;
+       }
+       macro_build(&icnt, NULL, "sltu", "d,v,t", dreg, 0, dreg);
+       if (used_at)
+               break;
+       return;
+
+    case M_SUB_I:
+       if (imm_expr.X_add_number < 32768 && imm_expr.X_add_number > -32768) {
+           imm_expr.X_add_number = -imm_expr.X_add_number;
+           macro_build(&icnt, &imm_expr, "addi", "t,r,j", dreg, sreg);
+           return;
+       }
+       load_register(&icnt, ip, AT, &imm_expr);
+       macro_build(&icnt, NULL, "sub", "d,v,t", dreg, sreg, AT);
+       break;
+
+    case M_SUBU_I:
+       if (imm_expr.X_add_number < 32768 && imm_expr.X_add_number > -32768) {
+           imm_expr.X_add_number = -imm_expr.X_add_number;
+           macro_build(&icnt, &imm_expr, "addiu", "t,r,j", dreg, sreg);
+           return;
+       }
+       load_register(&icnt, ip, AT, &imm_expr);
+       macro_build(&icnt, NULL, "subu", "d,v,t", dreg, sreg, AT);
+       break;
+
+    case M_TRUNCWD:
+    case M_TRUNCWS:
+       sreg = (ip->insn_opcode >> 11) & 0x1f;  /* floating reg */
+       dreg = (ip->insn_opcode >> 06) & 0x1f;  /* floating reg */
+
+       /*
+        * Is the double cfc1 instruction a bug in the mips assembler;
+        * or is there a reason for it?
+         */
+       save_reorder_condition = mips_noreorder;
+       mips_noreorder = 1;
+       macro_build(&icnt, NULL, "cfc1", "t,d", treg, 31);
+       macro_build(&icnt, NULL, "cfc1", "t,d", treg, 31);
+       macro_build(&icnt, NULL, "nop", "");
+       expr1.X_add_number = 3;
+       macro_build(&icnt, &expr1, "ori", "t,r,i", AT, treg);
+       expr1.X_add_number = 2;
+       macro_build(&icnt, &expr1, "xori", "t,r,i", AT, AT);
+       macro_build(&icnt, NULL, "ctc1", "t,d", AT, 31);
+       macro_build(&icnt, NULL, "nop", "");
+       macro_build(&icnt, NULL,
+           mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S", dreg, sreg);
+       macro_build(&icnt, NULL, "ctc1", "t,d", treg, 31);
+       macro_build(&icnt, NULL, "nop", "");
+       mips_noreorder = save_reorder_condition;
+       break;
+
+    case M_ULH:
+       s = "lb";
+       goto ulh;
+    case M_ULHU:
+       s = "lbu";
+    ulh:
+       /* avoid load delay */
+       offset_expr.X_add_number += 1;
+       macro_build(&icnt, &offset_expr, s, "t,o(b)", treg, breg);
+       offset_expr.X_add_number -= 1;
+       macro_build(&icnt, &offset_expr, "lbu", "t,o(b)", AT, breg);
+       macro_build(&icnt, NULL, "sll", "d,w,<", treg, treg, 8);
+       macro_build(&icnt,NULL, "or", "d,v,t", treg, treg, AT);
+       break;
+
+    case M_ULW:
+       /* does this work on a big endian machine? */
+       offset_expr.X_add_number += 3;
+       macro_build(&icnt, &offset_expr, "lwl", "t,o(b)", treg, breg);
+       offset_expr.X_add_number -= 3;
+       macro_build(&icnt, &offset_expr, "lwr", "t,o(b)", treg, breg);
+       return;
+
+    case M_ULH_A:
+    case M_ULHU_A:
+    case M_ULW_A:
+       if (offset_expr.X_seg == &bfd_abs_section)
+           load_register(&icnt, ip, AT, &offset_expr);
+       else {
+           macro_build_lui(&icnt, &offset_expr, AT);
+           macro_build(&icnt, &offset_expr, "addiu", "t,r,j", AT, AT);
+       }
+       if (mask == M_ULW_A) {
+           expr1.X_add_number = 3;
+           macro_build(&icnt, &expr1, "lwl", "t,o(b)", treg, AT);
+           imm_expr.X_add_number = 0;
+           macro_build(&icnt, &expr1, "lwr", "t,o(b)", treg, AT);
+       } else {
+           macro_build(&icnt, &expr1,
+               mask == M_ULH_A ? "lb" : "lbu", "t,o(b)", treg, AT);
+           imm_expr.X_add_number = 0;
+           macro_build(&icnt, &expr1, "lbu", "t,o(b)", AT, AT);
+           macro_build(&icnt, NULL, "sll", "d,w,<", treg, treg, 8);
+           macro_build(&icnt, NULL, "or", "d,v,t", treg, treg, AT);
+       }
+       break;
+
+    case M_USH:
+       macro_build(&icnt, &offset_expr, "sb", "t,o(b)", treg, breg);
+       macro_build(&icnt, NULL, "srl", "d,w,<", AT, treg, 8);
+       offset_expr.X_add_number += 1;
+       macro_build(&icnt, &offset_expr, "sb", "t,o(b)", AT, breg);
+       break;
+
+    case M_USW:
+       offset_expr.X_add_number += 3;
+       macro_build(&icnt, &offset_expr, "swl", "t,o(b)", treg, breg);
+       offset_expr.X_add_number -= 3;
+       macro_build(&icnt, &offset_expr, "swr", "t,o(b)", treg, breg);
+       return;
+
+    case M_USH_A:
+    case M_USW_A:
+       if (offset_expr.X_seg == &bfd_abs_section)
+           load_register(&icnt, ip, AT, &offset_expr);
+       else {
+           macro_build_lui(&icnt, &offset_expr, AT);
+           macro_build(&icnt, &offset_expr, "addiu", "t,r,j", AT, AT);
+       }
+       if (mask == M_USW_A) {
+           expr1.X_add_number = 3;
+           macro_build(&icnt, &expr1, "swl", "t,o(b)", treg, AT);
+           expr1.X_add_number = 0;
+           macro_build(&icnt, &expr1, "swr", "t,o(b)", treg, AT);
+       } else {
+           expr1.X_add_number = 0;
+           macro_build(&icnt, &expr1, "sb", "t,o(b)", treg, AT);
+           macro_build(&icnt, NULL, "srl", "d,w,<", treg, treg, 8);
+           expr1.X_add_number = 1;
+           macro_build(&icnt, &expr1, "sb", "t,o(b)", treg, AT);
+           expr1.X_add_number = 0;
+           macro_build(&icnt, &expr1, "lbu", "t,o(b)", AT, AT);
+           macro_build(&icnt, NULL, "sll", "d,w,<", treg, treg, 8);
+           macro_build(&icnt, NULL, "or", "d,v,t", treg, treg, AT);
+       }
+       break;
+
+    default:
+       as_bad("Macro %s not implemented yet", ip->insn_mo->name);
+    }
+    if (mips_noat)
+       as_warn("Macro used $at after \".set noat\"");
+}
+
+
+/*
+This routine assembles an instruction into its binary format.  As a side
+effect it sets one of the global variables imm_reloc or offset_reloc to the
+type of relocation to do if one of the operands is an address expression.
+*/
+static void
+mips_ip (str, ip)
+     char *str;
+     struct mips_cl_insn *ip;
+{
+    char                *s;
+    const char          *args;
+    char                c;
+    struct mips_opcode  *insn;
+    char                *argsStart;
+    unsigned int       regno;
+    unsigned int       lastregno = 0;
+    char                *s_reset;
+
+    insn_error = NULL;
+
+    for (s = str; islower(*s) || (*s >= '0' && *s <= '3') || *s == '.'; ++s)
+       continue;
+    switch (*s) {
+
+    case '\0':
+       break;
+
+    case ' ':
+       *s++ = '\0';
+       break;
+
+    default:
+       as_warn("Unknown opcode: `%s'", str);
+       exit(1);
+    }
+    if ((insn = (struct mips_opcode *) hash_find(op_hash, str)) == NULL) {
+       as_warn("`%s' not in hash table.", str);
+       insn_error = "ERROR: Unrecognized opcode";
+       return;
+    }
+    argsStart = s;
+    for (;;) {
+       assert(strcmp(insn->name, str) == 0);
+       ip->insn_mo = insn;
+       ip->insn_opcode = insn->match;
+       for (args = insn->args; ; ++args) {
+           if (*s == ' ')
+               ++s;
+           switch (*args) {
+
+           case '\0':  /* end of args */
+               if (*s == '\0')
+                   return;
+               break;
+
+           case ',':
+               if (*s++ == *args)
+                   continue;
+               s--;
+               switch (*++args) {
+               case 'r':
+               case 'v':
+                   ip->insn_opcode |= lastregno << 21;
+                   continue;
+
+               case 'w':
+               case 'W':
+                   ip->insn_opcode |= lastregno << 16;
+                   continue;
+
+               case 'V':
+                   ip->insn_opcode |= lastregno << 11;
+                   continue;
+               }
+               break;
+
+           case '(':
+               /* handle optional base register.
+                  Either the base register is omitted or
+                  we must have a left paren. */
+               /* this is dependent on the next operand specifier
+                  is a 'b' for base register */
+               assert(args[1] == 'b');
+               if (*s == '\0')
+                   return;
+
+           case ')':   /* these must match exactly */
+               if (*s++ == *args)
+                   continue;
+               break;
+
+           case '<':   /* must be at least one digit */
+               /*
+                * According to the manual, if the shift amount is greater
+                 * than 31 or less than 0 the the shift amount should be
+                * mod 32. In reality the mips assembler issues an error.
+                * We issue a warning and do the mod.
+                */
+               my_getExpression(&imm_expr, s);
+               check_absolute_expr(ip, &imm_expr);
+               if ((unsigned long) imm_expr.X_add_number > 31) {
+                   as_warn("Improper shift amount (%d)",
+                       imm_expr.X_add_number);
+                   imm_expr.X_add_number = imm_expr.X_add_number % 32;
+               }
+               ip->insn_opcode |= imm_expr.X_add_number << 6;
+               imm_expr.X_seg = absent_section;
+               s = expr_end;
+               continue;
+
+           case 'c':       /* break code */
+               my_getExpression(&imm_expr, s);
+               check_absolute_expr(ip, &imm_expr);
+               if ((unsigned)imm_expr.X_add_number > 1023)
+                   as_warn("Illegal break code (%d)", imm_expr.X_add_number);
+               ip->insn_opcode |= imm_expr.X_add_number << 16;
+               imm_expr.X_seg = absent_section;
+               s = expr_end;
+               continue;
+
+           case 'b':       /* base register */
+           case 'd':       /* destination register */
+           case 's':       /* source register */
+           case 't':       /* target register */
+           case 'r':       /* both target and source */
+           case 'v':       /* both dest and source */
+           case 'w':       /* both dest and target */
+               s_reset = s;
+               if (s[0] == '$') {
+                   if (isdigit(s[1])) {
+                       ++s;
+                       regno = 0;
+                       do {
+                           regno *= 10;
+                           regno += *s - '0';
+                           ++s;
+                       } while (isdigit(*s));
+                   } else if (s[1] == 'f' && s[2] == 'p') {
+                       s += 3;
+                       regno = 30;
+                   } else if (s[1] == 's' && s[2] == 'p') {
+                       s += 3;
+                       regno = 29;
+                   } else if (s[1] == 'g' && s[2] == 'p') {
+                       s += 3;
+                       regno = 28;
+                   } else if (s[1] == 'a' && s[2] == 't') {
+                       s += 3;
+                       regno = 1;
+                   } else
+                       goto notreg;
+                   if (regno > 31)
+                       as_bad("Invalid register number (%d)", regno);
+                   if (regno == AT && !mips_noat)
+                       as_warn("Used $at without \".set noat\"");
+                   c = *args;
+                   if (*s == ' ')
+                       s++;
+                   if (args[1] != *s) {
+                       if (c == 'r' || c == 'v' || c == 'w') {
+                           regno = lastregno;
+                           s = s_reset;
+                           args++;
+                       }
+                   }
+                   switch (c) {
+                   case 'r':
+                   case 's':
+                   case 'v':
+                   case 'b':
+                       ip->insn_opcode |= regno << 21;
+                       break;
+                   case 'd':
+                       ip->insn_opcode |= regno << 11;
+                       break;
+                   case 'w':
+                   case 't':
+                       ip->insn_opcode |= regno << 16;
+                   }
+                   lastregno = regno;
+                   continue;
+               }
+           notreg:
+               switch (*args++) {
+               case 'r':
+               case 'v':
+                   ip->insn_opcode |= lastregno << 21;
+                   continue;
+               case 'w':
+                   ip->insn_opcode |= lastregno << 16;
+                   continue;
+               }
+               break;
+
+           case 'D':       /* floating point destination register */
+           case 'S':       /* floating point source register */
+           case 'T':       /* floating point target register */
+           case 'V':
+           case 'W':
+               s_reset = s;
+               if (s[0] == '$' && s[1] == 'f' && isdigit(s[2])) {
+                   s += 2;
+                   regno = 0;
+                   do {
+                       regno *= 10;
+                       regno += *s - '0';
+                       ++s;
+                   } while (isdigit(*s));
+
+                   if (regno > 31)
+                       as_bad("Invalid float register number (%d)", regno);
+
+                   if ((regno & 1) &&
+                        !(strcmp(str, "mtc1") == 0 ||
+                        strcmp(str, "mfc1") == 0 ||
+                        strcmp(str, "lwc1") == 0 ||
+                        strcmp(str, "swc1") == 0))
+                           as_warn("Float register should be even, was %d",
+                               regno);
+
+                   c = *args;
+                   if (*s == ' ')
+                       s++;
+                   if (args[1] != *s) {
+                       if (c == 'V' || c == 'W') {
+                           regno = lastregno;
+                           s = s_reset;
+                           args++;
+                       }
+                   }
+                   switch (c) {
+                   case 'D':
+                       ip->insn_opcode |= regno <<  6;
+                       break;
+                   case 'V':
+                   case 'S':
+                       ip->insn_opcode |= regno << 11;
+                       break;
+                   case 'W':
+                   case 'T':
+                       ip->insn_opcode |= regno << 16;
+                   }
+                   lastregno = regno;
+                   continue;
+               }
+               switch (*args++) {
+               case 'V':
+                   ip->insn_opcode |= lastregno << 11;
+                   continue;
+               case 'W':
+                   ip->insn_opcode |= lastregno << 16;
+                   continue;
+               }
+               break;
+
+           case 'I':
+               my_getExpression(&imm_expr, s);
+               check_absolute_expr(ip, &imm_expr);
+               s = expr_end;
+               continue;
+
+           case 'A':
+               my_getExpression(&offset_expr, s);
+               imm_reloc = BFD_RELOC_32;
+               s = expr_end;
+               continue;
+
+           case 'F':
+               as_bad("Floating point constants only implemented for pseudo ops.");
+               continue;
+
+           case 'i':       /* 16 bit unsigned immediate */
+           case 'j':       /* 16 bit signed immediate */
+               imm_reloc = BFD_RELOC_LO16;
+               c = my_getSmallExpression(&imm_expr, s);
+               if (c) {
+                   if (c != 'l') {
+                       if (imm_expr.X_seg == &bfd_abs_section)
+                           imm_expr.X_add_number =
+                               (imm_expr.X_add_number >> 16) & 0xffff;
+                       else if (c == 'h')
+                           imm_reloc = BFD_RELOC_HI16_S;
+                       else
+                           imm_reloc = BFD_RELOC_HI16;
+                   }
+               } else
+                   check_absolute_expr(ip, &imm_expr);
+               if (*args == 'i') {
+                   if ((unsigned long) imm_expr.X_add_number > 65535)
+                       as_bad("16 bit expression not in range 0..65535");
+               } else {
+                   if (imm_expr.X_add_number < -32768 ||
+                     imm_expr.X_add_number > 32767)
+                       as_bad("16 bit expression not in range -32768..32767");
+               }
+               s = expr_end;
+               continue;
+
+           case 'o':       /* 16 bit offset */
+               c = my_getSmallExpression(&offset_expr, s);
+               /*
+                * If this value won't fit into a 16 bit offset, then
+                * go find a macro that will generate the 32 bit offset
+                * code pattern.
+                */
+               if ((offset_expr.X_add_symbol
+                    && offset_expr.X_seg != &bfd_abs_section)
+                   || offset_expr.X_subtract_symbol
+                   || offset_expr.X_add_number > 32767
+                   || offset_expr.X_add_number < -32768)
+                       break;
+
+               offset_reloc = BFD_RELOC_LO16;
+               if (c == 'h' || c == 'H')
+                   offset_expr.X_add_number =
+                       (offset_expr.X_add_number >> 16) & 0xffff;
+               s = expr_end;
+               continue;
+
+           case 'p':       /* pc relative offset */
+               offset_reloc = BFD_RELOC_16_PCREL_S2;
+               my_getExpression(&offset_expr, s);
+               s = expr_end;
+               continue;
+
+           case 'u':       /* upper 16 bits */
+               c = my_getSmallExpression(&imm_expr, s);
+               if ((unsigned long)imm_expr.X_add_number > 65535)
+                   as_bad("lui expression not in range 0..65535");
+               imm_reloc = BFD_RELOC_LO16;
+               if (c) {
+                   if (c != 'l') {
+                       if (imm_expr.X_seg == &bfd_abs_section)
+                           imm_expr.X_add_number =
+                               (imm_expr.X_add_number >> 16) & 0xffff;
+                       else if (c == 'h')
+                           imm_reloc = BFD_RELOC_HI16_S;
+                       else
+                           imm_reloc = BFD_RELOC_HI16;
+                   }
+               }
+               s = expr_end;
+               continue;
+
+           case 'a':       /* 26 bit address */
+               my_getExpression(&offset_expr, s);
+               s = expr_end;
+               offset_reloc = BFD_RELOC_MIPS_JMP;
+               continue;
+
+           default:
+               fprintf(stderr, "bad char = '%c'\n", *args);
+               internalError();
+           }
+           break;
+       }
+       /* Args don't match.  */
+       if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
+           !strcmp(insn->name, insn[1].name)) {
+           ++insn;
+           s = argsStart;
+           continue;
+       }
+       insn_error = "ERROR: Illegal operands";
+       return;
+    }
+}
+
+#define LP '('
+#define RP ')'
+
+static int
+my_getSmallExpression (ep, str)
+    expressionS *ep;
+    char *str;
+{
+    char *sp;
+    int c = 0;
+
+    if (*str == ' ')
+       str++;
+    if (*str == LP
+       || (*str == '%' &&
+           ((str[1] == 'h' && str[2] == 'i')
+            || (str[1] == 'H' && str[2] == 'I')
+            || (str[1] == 'l' && str[2] == 'o'))
+           && str[3] == LP)) {
+       if (*str == LP)
+           c = 0;
+       else {
+           c = str[1];
+           str += 3;
+       }
+
+       /*
+        * A small expression may be followed by a base register.
+        * Scan to the end of this operand, and then back over a possible
+        * base register.  Then scan the small expression up to that
+        * point.  (Based on code in sparc.c...)
+        */
+       for (sp = str; *sp && *sp != ','; sp++)
+           ;
+       if (sp - 4 >= str && sp[-1] == RP) {
+           if (isdigit(sp[-2])) {
+               for (sp -= 3; sp >= str && isdigit(*sp); sp--)
+                   ;
+               if (*sp == '$' && sp > str && sp[-1] == LP) {
+                   sp--;
+                   goto do_it;
+               }
+           } else if (sp - 5 >= str
+                      && sp[-5] == LP
+                      && sp[-4] == '$'
+                      && ((sp[-3] == 'f' && sp[-2] == 'p')
+                          || (sp[-3] == 's' && sp[-2] == 'p')
+                          || (sp[-3] == 'g' && sp[-2] == 'p')
+                          || (sp[-3] == 'a' && sp[-2] == 't'))) {
+               sp -= 5;
+           do_it:
+               if (sp == str) {
+                   /* no expression means zero offset */
+                   if (c) {
+                       /* %xx(reg) is an error */
+                       ep->X_seg = absent_section;
+                       expr_end = str - 3;
+                   } else {
+                       ep->X_seg = &bfd_abs_section;
+                       expr_end = sp;
+                   }
+                   ep->X_add_symbol = NULL;
+                   ep->X_subtract_symbol = NULL;
+                   ep->X_add_number = 0;
+               } else {
+                   *sp = '\0';
+                   my_getExpression(ep, str);
+                   *sp = LP;
+               }
+               return c;
+           }
+       }
+    }
+    my_getExpression(ep, str);
+    return c;                  /* => %hi or %lo encountered */
+}
+
+static void
+my_getExpression (ep, str)
+    expressionS *ep;
+    char *str;
+{
+    char *save_in;
+    asection *seg;
+
+    save_in = input_line_pointer;
+    input_line_pointer = str;
+    seg = expression(ep);
+    expr_end = input_line_pointer;
+    input_line_pointer = save_in;
+}
+
+char *
+md_atof (type,litP,sizeP)
+     char type;
+     char *litP;
+     int *sizeP;
+{
+    internalError();
+    return NULL;
+}
+
+void
+md_number_to_chars (buf, val, n)
+     char *buf;
+     long val;
+     int n;
+{
+
+    switch (byte_order) {
+    case LITTLE_ENDIAN:
+       switch (n) {
+
+       case 4:
+           *buf++ = val;
+           *buf++ = val >> 8;
+           *buf++ = val >> 16;
+           *buf   = val >> 24;
+           return;
+
+        case 2:
+           *buf++ = val;
+           *buf   = val >> 8;
+           return;
+
+       case 1:
+           *buf   = val;
+           return;
+
+       default:
+           internalError();
+       }
+
+    case BIG_ENDIAN:
+
+       switch (n) {
+
+       case 4:
+           *buf++ = val >> 24;
+           *buf++ = val >> 16;
+        case 2:
+           *buf++ = val >> 8;
+       case 1:
+           *buf = val;
+           return;
+
+       default:
+           internalError();
+       }
+
+    default:
+       internalError();
+    }
+}
+
+int
+md_parse_option (argP, cntP, vecP)
+     char **argP;
+     int *cntP;
+     char ***vecP;
+{
+    /* Accept -nocpp but ignore it. */
+    if (!strcmp(*argP, "nocpp")) {
+       *argP += 5;
+       return 1;
+    }
+    return 1; /* pretend you parsed the character */
+}
+
+long
+md_pcrel_from (fixP)
+     fixS *fixP;
+{
+    /* return the address of the delay slot */
+    return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+int
+md_apply_fix (fixP, valueP)
+     fixS *fixP;
+     long *valueP;
+{
+    unsigned char *buf;
+    long insn, value;
+
+    assert(fixP->fx_size == 4);
+
+    value = *valueP;
+    fixP->fx_addnumber = value;        /* Remember value for tc_gen_reloc */
+
+    switch (fixP->fx_r_type) {
+    case BFD_RELOC_32:
+    case BFD_RELOC_MIPS_JMP:
+    case BFD_RELOC_HI16:
+    case BFD_RELOC_HI16_S:
+    case BFD_RELOC_LO16:
+       /* Nothing needed to do. The value comes from the reloc entry */
+       return 1;
+
+    case BFD_RELOC_16_PCREL_S2:
+       /*
+        * We need to save the bits in the instruction since fixup_segment()
+        * might be deleting the relocation entry (i.e., a branch within
+        * the current segment).
+        */
+       if (value & 0x3)
+           as_warn("Branch to odd address (%x)", value);
+       value >>= 2;
+       if ((value & ~0xFFFF) && (value & ~0xFFFF) != (-1 & ~0xFFFF))
+           as_bad("Relocation overflow");
+
+       /* update old instruction data */
+       buf = (unsigned char *)(fixP->fx_where + fixP->fx_frag->fr_literal);
+       switch (byte_order) {
+       case LITTLE_ENDIAN:
+           insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+           break;
+
+       case BIG_ENDIAN:
+           insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+           break;
+
+       default:
+           internalError();
+           return 0;
+       }
+       insn |= value & 0xFFFF;
+       md_number_to_chars(buf, insn, 4);
+       break;
+
+    default:
+       internalError();
+    }
+    return 1;
+}
+
+#if 0
+void
+printInsn(oc)
+    unsigned long oc;
+{
+    const struct mips_opcode *p;
+    int treg, sreg, dreg, shamt;
+    short imm;
+    const char *args;
+    int i;
+
+    for (i = 0; i < NUMOPCODES; ++i) {
+       p = &mips_opcodes[i];
+       if (((oc & p->mask) == p->match) && (p->pinfo != INSN_MACRO)) {
+           printf("%08lx %s\t", oc, p->name);
+           treg  = (oc >> 16) & 0x1f;
+           sreg  = (oc >> 21) & 0x1f;
+           dreg  = (oc >> 11) & 0x1f;
+           shamt = (oc >>  6) & 0x1f;
+           imm   = oc;
+           for (args = p->args; ; ++args) {
+               switch (*args) {
+
+               case '\0':
+                   printf("\n");
+                   break;
+
+               case ',':
+               case '(':
+               case ')':
+                   printf("%c", *args);
+                   continue;
+
+               case 'r':
+                   assert(treg == sreg);
+                   printf("$%d,$%d", treg, sreg);
+                   continue;
+
+               case 'd':
+                   printf("$%d", dreg);
+                   continue;
+
+               case 't':
+                   printf("$%d", treg);
+                   continue;
+
+               case 'b':
+               case 's':
+                   printf("$%d", sreg);
+                   continue;
+
+               case 'a':
+                   printf("0x%08lx", oc & 0x1ffffff);
+                   continue;
+
+               case 'i':
+               case 'j':
+               case 'o':
+               case 'u':
+                   printf("%d", imm);
+                   continue;
+
+               case '<':
+                   printf("$%d", shamt);
+                   continue;
+
+               default:
+                   internalError();
+               }
+               break;
+           }
+           return;
+       }
+    }
+    printf("%08lx  UNDEFINED\n", oc);
+}
+#endif
+
+static symbolS *
+get_symbol ()
+{
+    int c;
+    char *name;
+    symbolS *p;
+
+    name = input_line_pointer;
+    c = get_symbol_end();
+    p = (symbolS *) symbol_find_or_make(name);
+    *input_line_pointer = c;
+    return p;
+}
+
+static long
+get_optional_absolute_expression ()
+{
+    expressionS        exp;
+    asection *s;
+
+    s = expression(&exp);
+    if (!(s == &bfd_abs_section || s == big_section || s == absent_section)) {
+       as_bad("Bad Absolute Expression.");
+    }
+    return exp.X_add_number;
+}
+
+static void
+s_align (x)
+     int x;
+{
+    register int temp;
+    register long temp_fill;
+    long max_alignment = 15;
+
+    /*
+
+    o  Note that the assembler pulls down any immediately preceeding label
+       to the aligned address.
+    o  It's not documented but auto alignment is reinstated by
+       a .align pseudo instruction.
+    o  Note also that after auto alignment is turned off the mips assembler
+       issues an error on attempt to assemble an improperly aligned data item.
+       We don't.
+
+    */
+
+    temp = get_absolute_expression ();
+    if (temp > max_alignment)
+      as_bad("Alignment too large: %d. assumed.", temp = max_alignment);
+    else if (temp < 0) {
+       as_warn("Alignment negative: 0 assumed.");
+       temp = 0;
+    }
+    if (*input_line_pointer == ',') {
+       input_line_pointer ++;
+       temp_fill = get_absolute_expression ();
+    } else
+      temp_fill = 0;
+    if (temp) {
+       auto_align = 1;
+       if (!need_pass_2)
+         frag_align (temp, (int)temp_fill);
+    } else {
+       auto_align = 0;
+    }
+
+    record_alignment(now_seg, temp);
+
+    demand_empty_rest_of_line();
+}
+
+static void
+s_change_sec (sec)
+     int sec;
+{
+    switch (sec) {
+    case 't':
+       s_text();
+       break;
+    case 'r':
+    case 'd':
+       s_data();
+       break;
+    case 'b':
+#ifdef BFD_ASSEMBLER
+       subseg_set (bss_section, (subsegT) get_absolute_expression ());
+#else
+       subseg_new (bss_section, (subsegT) get_absolute_expression ());
+#endif
+       demand_empty_rest_of_line ();
+       break;
+    default:
+       as_bad("Global pointers not supported; recompile -G 0");
+       return;
+    }
+    auto_align = 1;
+}
+
+static void
+s_cons (log_size)
+     int log_size;
+{
+
+    if (log_size > 0 && auto_align)
+        frag_align(log_size, 0);
+    cons(1 << log_size);
+}
+
+static void
+s_err (x)
+     int x;
+{
+    as_fatal("Encountered `.err', aborting assembly");
+}
+
+static void
+s_extern (x)
+     int x;
+{
+    long size;
+    symbolS *symbolP;
+
+    symbolP = get_symbol();
+    if (*input_line_pointer == ',')
+       input_line_pointer++;
+    size = get_optional_absolute_expression();
+    S_SET_VALUE(symbolP, size);
+}
+
+static void
+s_float_cons (is_double)
+     int is_double;
+{
+    char *f;
+    short words[4];
+    int error_code, repeat;
+    extern FLONUM_TYPE generic_floating_point_number;
+
+    if (auto_align)
+      if (is_double)
+       frag_align(3, 0);
+      else
+       frag_align(2, 0);
+
+    SKIP_WHITESPACE ();
+    if (! is_end_of_line [(unsigned char) *input_line_pointer]) {
+       do {
+           error_code = atof_generic(&input_line_pointer, ".", EXP_CHARS,
+                                     &generic_floating_point_number);
+           if (error_code) {
+               if (error_code == ERROR_EXPONENT_OVERFLOW)
+                 as_warn("Bad floating-point constant: exponent overflow");
+               else
+                 as_warn("Bad floating-point constant: unknown error code=%d.", error_code);
+           }
+
+           if (is_double) {
+               gen_to_words((LITTLENUM_TYPE *)words,
+                            4 /* precision */,
+                            11 /* exponent_bits */ );
+           } else {
+               gen_to_words((LITTLENUM_TYPE *)words,
+                            2 /* precision */,
+                            8 /* exponent_bits */ );
+           }
+           if (*input_line_pointer == ':') {
+               input_line_pointer++;
+               repeat = get_absolute_expression();
+           } else {
+               repeat = 1;
+           }
+           if (is_double) {
+               f = frag_more(repeat * 8);
+               for (;repeat--; f += 8) {
+                   md_number_to_chars(f+6, words[0], 2);
+                   md_number_to_chars(f+4, words[1], 2);
+                   md_number_to_chars(f+2, words[2], 2);
+                   md_number_to_chars(f,   words[3], 2);
+               }
+           } else {
+               f = frag_more(repeat * 4);
+               for (;repeat--; f += 4) {
+                   md_number_to_chars(f+2, words[0], 2);
+                   md_number_to_chars(f,   words[1], 2);
+               }
+           }
+           SKIP_WHITESPACE();
+           if (*input_line_pointer != ',') break;
+           input_line_pointer++;
+           SKIP_WHITESPACE();
+       } while (1);
+    }
+    demand_empty_rest_of_line();
+}
+
+static void
+s_option (x)
+     int x;
+{
+    if (strcmp(input_line_pointer, "O1") != 0
+           && strcmp(input_line_pointer, "O2") != 0)
+        as_warn("Unrecognized option");
+    demand_empty_rest_of_line();
+}
+
+static void
+s_mipsset (x)
+     int x;
+{
+    char *name = input_line_pointer, ch;
+
+    while (!is_end_of_line[(unsigned char) *input_line_pointer])
+       input_line_pointer++;
+    ch = *input_line_pointer;
+    *input_line_pointer = '\0';
+
+    if (strcmp(name, "reorder") == 0) {
+       mips_noreorder = 0;
+    } else if (strcmp(name, "noreorder") == 0) {
+       mips_noreorder = 1;
+    } else if (strcmp(name, "at") == 0) {
+       mips_noat = 0;
+    } else if (strcmp(name, "noat") == 0) {
+       mips_noat = 1;
+    } else if (strcmp(name, "macro") == 0) {
+       mips_warn_about_macros = 0;
+    } else if (strcmp(name, "nomacro") == 0) {
+       if (mips_noreorder == 0)
+           as_bad("`noreorder' must be set before `nomacro'");
+       mips_warn_about_macros = 1;
+    } else if (strcmp(name, "move") == 0 || strcmp(name, "novolatile") == 0) {
+       mips_nomove = 0;
+    } else if (strcmp(name, "nomove") == 0 || strcmp(name, "volatile") == 0) {
+       mips_nomove = 1;
+    } else if (strcmp(name, "bopt") == 0) {
+       mips_nobopt = 0;
+    } else if (strcmp(name, "nobopt") == 0) {
+       mips_nobopt = 1;
+    } else {
+       as_warn("Tried to set unrecognized symbol: %s\n", name);
+    }
+    *input_line_pointer = ch;
+    demand_empty_rest_of_line();
+}
+
+int
+tc_get_register ()
+{
+  int reg;
+
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer++ != '$')
+    {
+      as_warn ("expected `$'");
+      return 0;
+    }
+  if (isdigit ((unsigned char) *input_line_pointer))
+    {
+      reg = get_absolute_expression ();
+      if (reg < 0 || reg >= 32)
+       {
+         as_warn ("Bad register number");
+         reg = 0;
+       }
+    }
+  else
+    {
+      if (strncmp (input_line_pointer, "fp", 2) == 0)
+       reg = 30;
+      else if (strncmp (input_line_pointer, "sp", 2) == 0)
+       reg = 29;
+      else if (strncmp (input_line_pointer, "gp", 2) == 0)
+       reg = 28;
+      else if (strncmp (input_line_pointer, "at", 2) == 0)
+       reg = 1;
+      else
+       {
+         as_warn ("Unrecognized register name");
+         return 0;
+       }
+      input_line_pointer += 2;
+    }
+  return reg;
+}
+
+/*
+ * Translate internal representation of relocation info to BFD target format.
+ */
+arelent *
+tc_gen_reloc (section, fixp)
+     asection *section;
+     fixS *fixp;
+{
+  arelent *reloc;
+
+  reloc = (arelent *) xmalloc (sizeof (arelent));
+  assert (reloc != 0);
+
+  reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+  if (fixp->fx_pcrel == 0)
+    reloc->addend = fixp->fx_addnumber;
+  else
+#ifdef OBJ_ELF
+    reloc->addend = 0;
+#else
+    reloc->addend = - reloc->address;
+#endif
+  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+  assert (reloc->howto != 0);
+
+#ifdef OBJ_ECOFF
+  /* FIXME: This does the right thing, but it's confusing.  There
+     should be a more coherent approach, but I don't know what it
+     would be.  */
+  reloc->addend -=
+    bfd_get_section_vma (stdoutput,
+                        bfd_get_section (fixp->fx_addsy->bsym));
+#endif
+
+  return reloc;
+}
+
+/* should never be called */
+long
+md_section_align(seg, addr)
+    asection *seg;
+    long addr;
+{
+    int align = bfd_get_section_alignment(stdoutput, seg);
+
+    return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+int
+md_estimate_size_before_relax(fragP, segtype)
+    fragS *fragP;
+    asection *segtype;
+{
+    as_fatal("md_estimate_size_before_relax");
+    return(1);
+} /* md_estimate_size_before_relax() */
+\f
+#ifndef OBJ_ECOFF
+
+/* These functions should really be defined by the object file format,
+   since they are related to debugging information.  However, this
+   code has to work for the a.out format, which does not define them,
+   so we provide simple versions here.  These don't actually generate
+   any debugging information, but they do simple checking and someday
+   somebody may make them useful.  */
+
+typedef struct loc {
+    struct loc *loc_next;
+    unsigned long loc_fileno;
+    unsigned long loc_lineno;
+    unsigned long loc_offset;
+    unsigned short loc_delta;
+    unsigned short loc_count;
+#if 0
+    fragS       *loc_frag;
+#endif
+} locS;
+
+typedef struct proc {
+    struct proc *proc_next;
+    struct symbol *proc_isym;
+    struct symbol *proc_end;
+    unsigned long proc_reg_mask;
+    unsigned long proc_reg_offset;
+    unsigned long proc_fpreg_mask;
+    unsigned long proc_fpreg_offset;
+    unsigned long proc_frameoffset;
+    unsigned long proc_framereg;
+    unsigned long proc_pcreg;
+    locS *proc_iline;
+    struct file *proc_file;
+    int proc_index;
+} procS;
+
+typedef struct file {
+    struct file *file_next;
+    unsigned long file_fileno;
+    struct symbol *file_symbol;
+    struct symbol *file_end;
+    struct proc *file_proc;
+    int file_numprocs;
+} fileS;
+
+static struct obstack proc_frags;
+static procS *proc_lastP;
+static procS *proc_rootP;
+static int numprocs;
+
+static void
+md_obj_begin ()
+{
+    obstack_begin(&proc_frags, 0x2000);
+}
+
+static void
+md_obj_end ()
+{
+  /* check for premature end, nesting errors, etc */
+  if (proc_lastP && proc_lastP->proc_end == NULL)
+    as_warn("missing `.end' at end of assembly");
+}
+
+extern char hex_value[];
+
+static long
+get_number ()
+{
+    int negative = 0;
+    long val = 0;
+
+    if (*input_line_pointer == '-') {
+       ++input_line_pointer;
+       negative = 1;
+    }
+    if (!isdigit(*input_line_pointer))
+        as_bad("Expected simple number.");
+    if (input_line_pointer[0] == '0') {
+       if (input_line_pointer[1] == 'x') {
+           input_line_pointer += 2;
+           while (isxdigit(*input_line_pointer)) {
+               val <<= 4;
+               val |= hex_value[(int) *input_line_pointer++];
+           }
+           return negative ? -val : val;
+       } else {
+           ++input_line_pointer;
+           while (isdigit(*input_line_pointer)) {
+               val <<= 3;
+               val |= *input_line_pointer++ - '0';
+           }
+           return negative ? -val : val;
+       }
+    }
+    if (!isdigit(*input_line_pointer)) {
+       printf(" *input_line_pointer == '%c' 0x%02x\n",
+           *input_line_pointer, *input_line_pointer);
+       as_warn("Invalid number");
+       return -1;
+    }
+    while (isdigit(*input_line_pointer)) {
+       val *= 10;
+       val += *input_line_pointer++ - '0';
+    }
+    return negative ? -val : val;
+}
+
+/* The .file directive; just like the usual .file directive, but there
+   is an initial number which is the ECOFF file index.  */
+
+static void
+s_file (x)
+     int x;
+{
+    int line;
+
+    line = get_number();
+    s_app_file();
+}
+
+
+/* The .end directive.  */
+
+static void
+s_mipsend (x)
+     int x;
+{
+    symbolS *p;
+
+    if (!is_end_of_line[(unsigned char) *input_line_pointer]) {
+       p = get_symbol();
+       demand_empty_rest_of_line();
+    } else
+       p = NULL;
+    if (now_seg != text_section)
+       as_warn(".end not in text section");
+    if (!proc_lastP) {
+        as_warn(".end and no .ent seen yet.");
+       return;
+    }
+
+    if (p != NULL) {
+       assert(S_GET_NAME(p));
+       if (strcmp(S_GET_NAME(p), S_GET_NAME(proc_lastP->proc_isym)))
+           as_warn(".end symbol does not match .ent symbol.");
+    }
+
+    proc_lastP->proc_end = (symbolS *) 1;
+}
+
+/* The .aent and .ent directives.  */
+
+static void
+s_ent (aent)
+     int aent;
+{
+    int number = 0;
+    procS *procP;
+    symbolS *symbolP;
+
+    symbolP = get_symbol();
+    if (*input_line_pointer == ',')
+       input_line_pointer++;
+    if (isdigit(*input_line_pointer) || *input_line_pointer == '-')
+       number = get_number();
+    if (now_seg != text_section)
+        as_warn(".ent or .aent not in text section.");
+
+    if (!aent && proc_lastP && proc_lastP->proc_end == NULL)
+       as_warn("missing `.end'");
+
+    if (!aent) {
+       procP = (procS *) obstack_alloc(&proc_frags, sizeof(*procP));
+       procP->proc_isym = symbolP;
+       procP->proc_reg_mask = 0;
+       procP->proc_reg_offset = 0;
+       procP->proc_fpreg_mask = 0;
+       procP->proc_fpreg_offset = 0;
+       procP->proc_frameoffset = 0;
+       procP->proc_framereg = 0;
+       procP->proc_pcreg = 0;
+       procP->proc_end = NULL;
+       procP->proc_next = NULL;
+       if (proc_lastP)
+           proc_lastP->proc_next = procP;
+       else
+           proc_rootP = procP;
+       proc_lastP = procP;
+       numprocs++;
+    }
+    demand_empty_rest_of_line();
+}
+
+/* The .frame directive.  */
+
+static void
+s_frame (x)
+    int x;
+{
+#if 0
+    char str[100];
+    symbolS *symP;
+    int frame_reg;
+    int frame_off;
+    int pcreg;
+
+    frame_reg = tc_get_register();
+    if (*input_line_pointer == ',')
+       input_line_pointer++;
+    frame_off = get_optional_absolute_expression();
+    if (*input_line_pointer == ',')
+       input_line_pointer++;
+    pcreg = tc_get_register();
+
+    /* bob third eye */
+    assert(proc_rootP);
+    proc_rootP->proc_framereg = frame_reg;
+    proc_rootP->proc_frameoffset = frame_off;
+    proc_rootP->proc_pcreg = pcreg;
+    /* bob macho .frame */
+
+    /* We don't have to write out a frame stab for unoptimized code. */
+    if (!(frame_reg == 30 && frame_off == 0)) {
+       if (!proc_lastP)
+           as_warn("No .ent for .frame to use." );
+       (void) sprintf(str, "R%d;%d", frame_reg, frame_off );
+       symP = symbol_new(str, N_VFP, 0, frag_now );
+       S_SET_TYPE(symP, N_RMASK);
+       S_SET_OTHER(symP, 0);
+       S_SET_DESC(symP, 0);
+       symP->sy_forward = proc_lastP->proc_isym;
+       /* bob perhaps I should have used pseudo set */
+    }
+    demand_empty_rest_of_line();
+#endif
+}
+
+/* The .fmask and .mask directives.  */
+
+static void
+s_mask (reg_type)
+     char reg_type;
+{
+#if 0
+    char str[100], *strP;
+    symbolS *symP;
+    int  i;
+    unsigned int mask;
+    int off;
+
+    mask = get_number();
+    if (*input_line_pointer == ',')
+       input_line_pointer++;
+    off = get_absolute_expression();
+
+    /* bob only for coff */
+    assert(proc_rootP);
+    if (reg_type == 'F' ) {
+       proc_rootP->proc_fpreg_mask = mask;
+       proc_rootP->proc_fpreg_offset = off;
+    } else {
+       proc_rootP->proc_reg_mask = mask;
+       proc_rootP->proc_reg_offset = off;
+    }
+
+    /* bob macho .mask + .fmask */
+
+    /* We don't have to write out a mask stab if no saved regs. */
+    if (!(mask == 0)) {
+       if (!proc_lastP)
+           as_warn("No .ent for .mask to use." );
+       strP = str;
+       for (i=0; i<32; i++ ) {
+         if (mask%2) {
+            sprintf(strP, "%c%d,", reg_type, i );
+            strP += strlen(strP);
+         }
+         mask /= 2;
+        }
+       sprintf(strP, ";%d,", off );
+       symP = symbol_new(str, N_RMASK, 0, frag_now);
+       S_SET_TYPE(symP, N_RMASK);
+       S_SET_OTHER(symP, 0);
+       S_SET_DESC(symP, 0);
+       symP->sy_forward = proc_lastP->proc_isym;
+       /* bob perhaps I should have used pseudo set */
+    }
+#endif
+}
+
+/* The .loc directive.  */
+
+static void
+s_loc (x)
+     int x;
+{
+#if 0
+    symbolS * symbolP;
+    int lineno;
+    int addroff;
+
+    assert(now_seg == text_section);
+
+    lineno  = get_number();
+    addroff = obstack_next_free(&frags) - frag_now->fr_literal;
+
+    symbolP = symbol_new ("", N_SLINE, addroff, frag_now);
+    S_SET_TYPE(symbolP, N_SLINE);
+    S_SET_OTHER(symbolP, 0);
+    S_SET_DESC(symbolP, lineno);
+    symbolP->sy_segment = now_seg;
+#endif
+}
+
+#endif /* ! defined (OBJ_ECOFF) */
+
index d6781dd6f4b8a014bb44dec20267e986eace9b3f..2c7971095e8d2ce3cd3745c07732df9c7781e5ec 100644 (file)
@@ -1,3 +1,3 @@
-TARG_CPU_DEPENDENTS=$(srcdir)/../opcodes/z8k.h
+TARG_CPU_DEPENDENTS=$(srcdir)/../opcodes/z8k-opc.h
 LOCAL_LOADLIBES=../bfd/libbfd.a
 TDEFINES=-DBFD_HEADERS -DMANY_SEGMENTS -DBFD -DSINGLE_QUOTE_STRINGS
index 0c550b991740a313b73b418506f1bea74483296e..17430b5abf878cfb6fd7cc8cf104368fd9473781 100644 (file)
@@ -400,40 +400,82 @@ write_contents (abfd, sec, xxx)
 
   fixup_segment (seginfo->fix_root, sec);
 
+  n = 0;
   for (i = 0, fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
-    if (fixp->fx_addsy)
-      {
-       symbolS *sym = fixp->fx_addsy;
-       asection *sec = sym->bsym->section;
-       if (sec == &bfd_und_section
-           || sec == &bfd_abs_section
-           || sec == &bfd_com_section)
-         continue;
-       if (sym->bsym == sec->symbol)
-         continue;
-       /* If the section symbol isn't going to be output, the relocs
-          at least should still work.  If not, figure out what to do
-          when we run into that case.  */
-       fixp->fx_offset += S_GET_VALUE (sym);
-       fixp->fx_addsy = symbol_find (sec->name);
-       if (!fixp->fx_addsy)
-         {
-           fixp->fx_addsy = symbol_make (sec->name);
-           fixp->fx_addsy->bsym = sec->symbol;
-         }
-      }
+    {
+      n++;
+      if (fixp->fx_addsy)
+       {
+         symbolS *sym = fixp->fx_addsy;
+         asection *sec = sym->bsym->section;
+         if (sec == &bfd_und_section
+             || sec == &bfd_abs_section
+             || sec == &bfd_com_section)
+           continue;
+         if (sym->bsym == sec->symbol)
+           continue;
+         /* If the section symbol isn't going to be output, the relocs
+            at least should still work.  If not, figure out what to do
+            when we run into that case.  */
+         fixp->fx_offset += S_GET_VALUE (sym);
+         fixp->fx_addsy = symbol_find (sec->name);
+         if (!fixp->fx_addsy)
+           {
+             fixp->fx_addsy = symbol_make (sec->name);
+             fixp->fx_addsy->bsym = sec->symbol;
+           }
+       }
+    }
 
   /* Force calculations (size, vma) to get done.  */
   bfd_set_section_contents (stdoutput, sec, "", 0, 0);
 
   /* Set up reloc information as well.  */
-  n = 0;
-  for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
-    n++;
   relocs = (arelent **) bfd_alloc_by_size_t (stdoutput,
                                             n * sizeof (arelent *));
 
-  for (frags = seginfo->frchainP->frch_root, fixp = seginfo->fix_root, i = 0;
+  i = 0;
+  for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next)
+    {
+      arelent *reloc;
+      extern arelent *tc_gen_reloc ();
+      char *data;
+      bfd_reloc_status_type s;
+
+      if (fixp->fx_addsy == 0)
+       {
+         /* @@ Need some other flag to indicate which have already
+            been performed...  */
+         n--;
+         continue;
+       }
+      reloc = tc_gen_reloc (sec, fixp);
+      if (!reloc)
+       {
+         n--;
+         continue;
+       }
+      data = fixp->fx_frag->fr_literal + fixp->fx_where;
+      if (fixp->fx_where + 4
+         > fixp->fx_frag->fr_fix + fixp->fx_frag->fr_offset)
+       abort ();
+      s = bfd_perform_relocation (stdoutput, reloc, data - reloc->address,
+                                 sec, stdoutput);
+      switch (s)
+       {
+       case bfd_reloc_ok:
+         break;
+       default:
+         as_fatal ("bad return from bfd_perform_relocation");
+       }
+      relocs[i++] = reloc;
+    }
+      
+  if (n)
+    bfd_set_reloc (stdoutput, sec, relocs, n);
+
+  /* Write out the frags.  */
+  for (frags = seginfo->frchainP->frch_root;
        frags;
        frags = frags->fr_next)
     {
@@ -443,44 +485,6 @@ write_contents (abfd, sec, xxx)
       long count;
 
       assert (frags->fr_type == rs_fill);
-      while (fixp
-            && fixp->fx_frag == frags)
-       {
-         arelent *reloc;
-         extern arelent *tc_gen_reloc ();
-         char *data;
-         static bfd_reloc_status_type s;
-
-         if (fixp->fx_addsy == 0)
-           {
-             /* @@ Need some other flag to indicate which have already
-                been performed...  */
-             n--;
-             goto next;
-           }
-         reloc = tc_gen_reloc (sec, fixp);
-         if (!reloc)
-           {
-             n--;
-             goto next;
-           }
-         data = frags->fr_literal + fixp->fx_where;
-         if (fixp->fx_where + 4 > frags->fr_fix + frags->fr_offset)
-           abort ();
-         s = bfd_perform_relocation (stdoutput, reloc, data - reloc->address,
-                                     sec, stdoutput);
-         switch (s)
-           {
-           case bfd_reloc_ok:
-             break;
-           default:
-             printf ("bad s value\n");
-             abort ();
-           }
-         relocs[i++] = reloc;
-       next:
-         fixp = fixp->fx_next;
-       }
       if (frags->fr_fix)
        {
          x = bfd_set_section_contents (stdoutput, sec,
@@ -502,12 +506,6 @@ write_contents (abfd, sec, xxx)
            offset += fill_size;
          }
     }
-  /* Did we miss any relocs?  */
-  if (fixp != 0)
-    abort ();
-
-  if (n)
-    bfd_set_reloc (stdoutput, sec, relocs, n);
 }
 #endif