--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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) */
+