Files
retrobsd/src/cmd/as/as.c
2014-04-18 01:12:49 -07:00

2719 lines
81 KiB
C

/*
* Assembler for MIPS32.
* The syntax is compatible with GCC output.
*
* Copyright (C) 2011-2012 Serge Vakulenko, <serge@vak.ru>
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name of the author not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
*
* The author disclaim all warranties with regard to this
* software, including all implied warranties of merchantability
* and fitness. In no event shall the author be liable for any
* special, indirect or consequential damages or any damages
* whatsoever resulting from loss of use, data or profits, whether
* in an action of contract, negligence or other tortious action,
* arising out of or in connection with the use or performance of
* this software.
*/
#ifdef CROSS
# include <stdio.h>
# include <nlist.h>
#else
# include <stdio.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <a.out.h>
#define WORDSZ 4 /* word size in bytes */
/*
* Locals beginning with L or dot are stripped off by -X flag.
*/
#define IS_LOCAL(s) ((s)->n_name[0] == 'L' || (s)->n_name[0] == '.')
/*
* Types of lexemes.
*/
enum {
LEOF = 1, /* end of file */
LEOL, /* end of line */
LNAME, /* identifier */
LREG, /* machine register */
LNUM, /* integer number */
LLSHIFT, /* << */
LRSHIFT, /* >> */
LASCII, /* .ascii */
LBSS, /* .bss */
LCOMM, /* .comm */
LDATA, /* .data */
LGLOBL, /* .globl */
LHALF, /* .half */
LSTRNG, /* .strng */
LRDATA, /* .rdata */
LTEXT, /* .text */
LEQU, /* .equ */
LWORD, /* .word */
LBYTE, /* .byte */
LSPACE, /* .space */
LFILE, /* .file */
LSECTION, /* .section */
LSYMTYPE, /* @type of symbol */
LSECTYPE, /* @type of section */
LPREVIOUS, /* .previous */
LGNUATTR, /* .gnu_attribute */
LALIGN, /* .align */
LSET, /* .set */
LENT, /* .ent */
LTYPE, /* .type */
LFRAME, /* .frame */
LMASK, /* .mask */
LFMASK, /* .fmask */
LEND, /* .end */
LSIZE, /* .size */
LIDENT, /* .ident */
LWEAK, /* .weak */
LLOCAL, /* .local */
};
/*
* Segment ids.
*/
enum {
STEXT,
SDATA,
SSTRNG,
SBSS,
SEXT,
SABS, /* special case for getexpr() */
};
/*
* Sizes of tables.
* Hash sizes should be powers of 2!
*/
#define HASHSZ 1024 /* symbol name hash table size */
#define HCMDSZ 256 /* instruction hash table size */
#define STSIZE (HASHSZ*9/10) /* symbol name table size */
#define MAXRLAB 200 /* max relative (digit) labels */
/*
* On second pass, hashtab[] is not needed.
* We use it under name newindex[] to reindex symbol references
* when -x or -X options are enabled.
*/
#define newindex hashtab
/*
* Convert segment id to symbol type.
*/
const int segmtype [] = {
N_TEXT, /* STEXT */
N_DATA, /* SDATA */
N_STRNG, /* SSTRNG */
N_BSS, /* SBSS */
N_UNDF, /* SEXT */
N_ABS, /* SABS */
};
/*
* Convert segment id to relocation type.
*/
const int segmrel [] = {
RTEXT, /* STEXT */
RDATA, /* SDATA */
RSTRNG, /* SSTRNG */
RBSS, /* SBSS */
REXT, /* SEXT */
RABS, /* SABS */
};
/*
* Convert symbol type to segment id.
*/
const int typesegm [] = {
SEXT, /* N_UNDF */
SABS, /* N_ABS */
STEXT, /* N_TEXT */
SDATA, /* N_DATA */
SBSS, /* N_BSS */
SSTRNG, /* N_STRNG */
};
/*
* Table of local (numeric) labels.
*/
struct labeltab {
int num;
int value;
};
#define RLAB_OFFSET (1 << 23) /* index offset of relative label */
#define RLAB_MAXVAL 1000000 /* max value of relative label */
/*
* Main opcode table.
*/
struct optable {
unsigned opcode; /* instruction code */
const char *name; /* instruction name */
unsigned type; /* flags */
void (*func) (unsigned, struct reloc*); /* handler for pseudo-instructions */
};
/*
* Flags of instruction formats.
*/
#define FRD1 (1 << 0) /* rd, ... */
#define FRD2 (1 << 1) /* .., rd, ... */
#define FRT1 (1 << 2) /* rt, ... */
#define FRT2 (1 << 3) /* .., rt, ... */
#define FRT3 (1 << 4) /* .., .., rt */
#define FRS1 (1 << 5) /* rs, ... */
#define FRS2 (1 << 6) /* .., rs, ... */
#define FRS3 (1 << 7) /* .., .., rs */
#define FRSB (1 << 8) /* ... (rs) */
#define FCODE (1 << 9) /* immediate shifted <<6 */
#define FDSLOT (1 << 10) /* have delay slot */
#define FOFF16 (1 << 11) /* 16-bit relocatable offset */
#define FHIGH16 (1 << 12) /* high 16-bit relocatable offset */
#define FOFF18 (1 << 13) /* 18-bit PC-relative relocatable offset shifted >>2 */
#define FAOFF18 (1 << 14) /* 18-bit PC-relative relocatable offset shifted >>2 */
#define FAOFF28 (1 << 15) /* 28-bit absolute relocatable offset shifted >>2 */
#define FSA (1 << 16) /* 5-bit shift amount */
#define FSEL (1 << 17) /* optional 3-bit COP0 register select */
#define FSIZE (1 << 18) /* bit field size */
#define FMSB (1 << 19) /* bit field msb */
#define FRTD (1 << 20) /* set rt to rd number */
#define FCODE16 (1 << 21) /* immediate shifted <<16 */
#define FMOD (1 << 22) /* modifies the first register */
/*
* Implement pseudo-instructions.
* TODO: bge, bgeu, bgt, bgtu, ble, bleu, blt, bltu
*/
void emit_li (unsigned, struct reloc*);
void emit_la (unsigned, struct reloc*);
const struct optable optable [] = {
{ 0x00000020, "add", FRD1 | FRS2 | FRT3 | FMOD },
{ 0x20000000, "addi", FRT1 | FRS2 | FOFF16 | FMOD },
{ 0x24000000, "addiu", FRT1 | FRS2 | FOFF16 | FMOD },
{ 0x00000021, "addu", FRD1 | FRS2 | FRT3 | FMOD },
{ 0x00000024, "and", FRD1 | FRS2 | FRT3 | FMOD },
{ 0x30000000, "andi", FRT1 | FRS2 | FOFF16 | FMOD },
{ 0x10000000, "b", FAOFF18 | FDSLOT },
{ 0x04110000, "bal", FAOFF18 | FDSLOT },
{ 0x10000000, "beq", FRS1 | FRT2 | FOFF18 | FDSLOT },
{ 0x50000000, "beql", FRS1 | FRT2 | FOFF18 | FDSLOT },
{ 0x50000000, "beqz", FRS1 | FOFF18 | FDSLOT },
{ 0x04010000, "bgez", FRS1 | FOFF18 | FDSLOT },
{ 0x04110000, "bgezal", FRS1 | FOFF18 | FDSLOT },
{ 0x04130000, "bgezall", FRS1 | FOFF18 | FDSLOT },
{ 0x04030000, "bgezl", FRS1 | FOFF18 | FDSLOT },
{ 0x1c000000, "bgtz", FRS1 | FOFF18 | FDSLOT },
{ 0x5c000000, "bgtzl", FRS1 | FOFF18 | FDSLOT },
{ 0x18000000, "blez", FRS1 | FOFF18 | FDSLOT },
{ 0x58000000, "blezl", FRS1 | FOFF18 | FDSLOT },
{ 0x04000000, "bltz", FRS1 | FOFF18 | FDSLOT },
{ 0x04100000, "bltzal", FRS1 | FOFF18 | FDSLOT },
{ 0x04120000, "bltzall", FRS1 | FOFF18 | FDSLOT },
{ 0x04020000, "bltzl", FRS1 | FOFF18 | FDSLOT },
{ 0x14000000, "bne", FRS1 | FRT2 | FOFF18 | FDSLOT },
{ 0x54000000, "bnel", FRS1 | FRT2 | FOFF18 | FDSLOT },
{ 0x54000000, "bnez", FRS1 | FOFF18 | FDSLOT },
{ 0x0000000d, "break", FCODE16 },
{ 0x70000021, "clo", FRD1 | FRS2 | FRTD | FMOD },
{ 0x70000020, "clz", FRD1 | FRS2 | FRTD | FMOD },
{ 0x4200001f, "deret", 0 },
{ 0x41606000, "di", FRT1 | FMOD },
{ 0x0000001a, "div", FRS1 | FRT2 },
{ 0x0000001b, "divu", FRS1 | FRT2 },
{ 0x000000c0, "ehb", 0 },
{ 0x41606020, "ei", FRT1 | FMOD },
{ 0x42000018, "eret", 0 },
{ 0x7c000000, "ext", FRT1 | FRS2 | FSA | FSIZE | FMOD },
{ 0x7c000004, "ins", FRT1 | FRS2 | FSA | FMSB | FMOD },
{ 0x08000000, "j", FAOFF28 | FDSLOT },
{ 0x0c000000, "jal", FAOFF28 | FDSLOT },
{ 0x00000009, "jalr", FRD1 | FRS2 | FDSLOT },
{ 0x00000409, "jalr.hb", FRD1 | FRS2 | FDSLOT },
{ 0x00000008, "jr", FRS1 | FDSLOT },
{ 0x00000408, "jr.hb", FRS1 | FDSLOT },
{ 0, "la", FRT1 | FMOD, emit_la },
{ 0x80000000, "lb", FRT1 | FOFF16 | FRSB | FMOD },
{ 0x90000000, "lbu", FRT1 | FOFF16 | FRSB | FMOD },
{ 0x84000000, "lh", FRT1 | FOFF16 | FRSB | FMOD },
{ 0x94000000, "lhu", FRT1 | FOFF16 | FRSB | FMOD },
{ 0, "li", FRT1 | FMOD, emit_li },
{ 0xc0000000, "ll", FRT1 | FOFF16 | FRSB | FMOD },
{ 0x3c000000, "lui", FRT1 | FHIGH16 | FMOD },
{ 0x8c000000, "lw", FRT1 | FOFF16 | FRSB | FMOD },
{ 0x88000000, "lwl", FRT1 | FOFF16 | FRSB | FMOD },
{ 0x98000000, "lwr", FRT1 | FOFF16 | FRSB | FMOD },
{ 0x70000000, "madd", FRS1 | FRT2 | FMOD },
{ 0x70000001, "maddu", FRS1 | FRT2 | FMOD },
{ 0x40000000, "mfc0", FRT1 | FRD2 | FSEL | FMOD },
{ 0x00000010, "mfhi", FRD1 | FMOD },
{ 0x00000012, "mflo", FRD1 | FMOD },
{ 0x00000021, "move", FRD1 | FRS2 | FMOD }, // addu
{ 0x0000000b, "movn", FRD1 | FRS2 | FRT3 | FMOD },
{ 0x0000000a, "movz", FRD1 | FRS2 | FRT3 | FMOD },
{ 0x70000004, "msub", FRS1 | FRT2 | FMOD },
{ 0x70000005, "msubu", FRS1 | FRT2 | FMOD },
{ 0x40800000, "mtc0", FRT1 | FRD2 | FSEL },
{ 0x00000011, "mthi", FRS1 },
{ 0x00000013, "mtlo", FRS1 },
{ 0x70000002, "mul", FRD1 | FRS2 | FRT3 | FMOD },
{ 0x00000018, "mult", FRS1 | FRT2 },
{ 0x00000019, "multu", FRS1 | FRT2 },
{ 0x00000000, "nop", 0 },
{ 0x00000027, "nor", FRD1 | FRS2 | FRT3 | FMOD },
{ 0x00000025, "or", FRD1 | FRS2 | FRT3 | FMOD },
{ 0x34000000, "ori", FRT1 | FRS2 | FOFF16 | FMOD },
{ 0x7c00003b, "rdhwr", FRT1 | FRD2 | FMOD },
{ 0x41400000, "rdpgpr", FRD1 | FRT2 | FMOD },
{ 0x00200002, "ror", FRD1 | FRT2 | FSA | FMOD },
{ 0x00000046, "rorv", FRD1 | FRT2 | FRS3 | FMOD },
{ 0xa0000000, "sb", FRT1 | FOFF16 | FRSB },
{ 0xe0000000, "sc", FRT1 | FOFF16 | FRSB },
{ 0x7000003f, "sdbbp", FCODE },
{ 0x7c000420, "seb", FRD1 | FRT2 },
{ 0x7c000620, "seh", FRD1 | FRT2 },
{ 0xa4000000, "sh", FRT1 | FOFF16 | FRSB },
{ 0x00000000, "sll", FRD1 | FRT2 | FSA | FMOD },
{ 0x00000004, "sllv", FRD1 | FRT2 | FRS3 | FMOD },
{ 0x0000002a, "slt", FRD1 | FRS2 | FRT3 | FMOD },
{ 0x28000000, "slti", FRT1 | FRS2 | FOFF16 | FMOD },
{ 0x2c000000, "sltiu", FRT1 | FRS2 | FOFF16 | FMOD },
{ 0x0000002b, "sltu", FRD1 | FRS2 | FRT3 | FMOD },
{ 0x00000003, "sra", FRD1 | FRT2 | FSA | FMOD },
{ 0x00000007, "srav", FRD1 | FRT2 | FRS3 | FMOD },
{ 0x00000002, "srl", FRD1 | FRT2 | FSA | FMOD },
{ 0x00000006, "srlv", FRD1 | FRT2 | FRS3 | FMOD },
{ 0x00000040, "ssnop", 0 },
{ 0x00000022, "sub", FRD1 | FRS2 | FRT3 | FMOD },
{ 0x00000023, "subu", FRD1 | FRS2 | FRT3 | FMOD },
{ 0xac000000, "sw", FRT1 | FOFF16 | FRSB },
{ 0xa8000000, "swl", FRT1 | FOFF16 | FRSB },
{ 0xb8000000, "swr", FRT1 | FOFF16 | FRSB },
{ 0x0000000f, "sync", FCODE },
{ 0x0000000c, "syscall", FCODE },
{ 0x00000034, "teq", FRS1 | FRT2 | FCODE },
{ 0x040c0000, "teqi", FRS1 | FOFF16 },
{ 0x00000030, "tge", FRS1 | FRT2 | FCODE },
{ 0x04080000, "tgei", FRS1 | FOFF16 },
{ 0x04090000, "tgeiu", FRS1 | FOFF16 },
{ 0x00000031, "tgeu", FRS1 | FRT2 | FCODE },
{ 0x00000032, "tlt", FRS1 | FRT2 | FCODE },
{ 0x040a0000, "tlti", FRS1 | FOFF16 },
{ 0x040b0000, "tltiu", FRS1 | FOFF16 },
{ 0x00000033, "tltu", FRS1 | FRT2 | FCODE },
{ 0x00000036, "tne", FRS1 | FRT2 | FCODE },
{ 0x040e0000, "tnei", FRS1 | FOFF16 },
{ 0x42000020, "wait", FCODE },
{ 0x41c00000, "wrpgpr", FRD1 | FRT2 },
{ 0x7c0000a0, "wsbh", FRD1 | FRT2 | FMOD },
{ 0x00000026, "xor", FRD1 | FRS2 | FRT3 | FMOD },
{ 0x38000000, "xori", FRT1 | FRS2 | FOFF16 | FMOD },
{ 0, 0, 0 },
};
/*
* Character classes.
*/
#define ISHEX(c) (ctype[(c)&0377] & 1)
#define ISOCTAL(c) (ctype[(c)&0377] & 2)
#define ISDIGIT(c) (ctype[(c)&0377] & 4)
#define ISLETTER(c) (ctype[(c)&0377] & 8)
const char ctype [256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,8,0,0,0,0,0,0,0,0,0,8,0,7,7,7,7,7,7,7,7,5,5,0,0,0,0,0,0,
8,9,9,9,9,9,9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,0,0,0,0,8,
0,9,9,9,9,9,9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,0,
};
FILE *sfile [SABS], *rfile [SABS];
unsigned count [SABS];
int segm;
char *infile, *outfile = "a.out";
char tfilename[] = "/tmp/asXXXXXX";
int line; /* Source line number */
int xflags, Xflag, uflag;
int stlength; /* Symbol table size in bytes */
int stalign; /* Symbol table alignment */
unsigned tbase, dbase, adbase, bbase;
struct nlist stab [STSIZE];
int stabfree;
char space [STSIZE*8]; /* Area for symbol names */
int lastfree; /* Free space offset */
char name [256];
unsigned intval;
int extref;
int blexflag, backlex, blextype;
short hashtab [HASHSZ], hashctab [HCMDSZ];
struct labeltab labeltab [MAXRLAB]; /* relative labels */
int nlabels;
int mode_reorder = 1; /* .set reorder option (default) */
int mode_macro; /* .set macro option */
int mode_mips16; /* .set mips16 option */
int mode_micromips; /* .set micromips option */
int mode_at; /* .set at option */
int reorder_full; /* instruction buffered for reorder */
unsigned reorder_word; /* buffered instruction... */
unsigned reorder_clobber; /* ...modified this register */
struct reloc reorder_rel; /* buffered relocation */
struct reloc relabs = { RABS }; /* absolute relocation */
int expr_flags; /* flags set by getexpr */
#define EXPR_GPREL 1 /* gp relative relocation */
#define EXPR_HI 2 /* %hi function */
#define EXPR_LO 4 /* %lo function */
/* Forward declarations. */
unsigned getexpr (int *s);
/*
* Fatal error message.
*/
void uerror (char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
fprintf (stderr, "as: ");
if (infile)
fprintf (stderr, "%s, ", infile);
if (line)
fprintf (stderr, "%d: ", line);
vfprintf (stderr, fmt, ap);
va_end (ap);
fprintf (stderr, "\n");
exit (1);
}
/*
* Read a 4-byte word from the file.
* Little-endian.
*/
unsigned fgetword (f)
FILE *f;
{
unsigned int w;
if (fread (&w, sizeof(w), 1, f) != 1)
return 0;
return w;
}
/*
* Write a 4-byte word to the file.
* Little-endian.
*/
void fputword (w, f)
unsigned int w;
FILE *f;
{
fwrite (&w, sizeof(w), 1, f);
}
/*
* Read a relocation record: 1 to 6 bytes.
*/
void fgetrel (f, r)
register FILE *f;
register struct reloc *r;
{
r->flags = getc (f);
if ((r->flags & RSMASK) == REXT) {
r->index = getc (f);
r->index |= getc (f) << 8;
r->index |= getc (f) << 16;
}
if ((r->flags & RFMASK) == RHIGH16 ||
(r->flags & RFMASK) == RHIGH16S)
{
r->offset = getc (f);
r->offset |= getc (f) << 8;
}
}
/*
* Emit a relocation record: 1 to 6 bytes.
* Return a written length.
*/
unsigned fputrel (r, f)
register struct reloc *r;
register FILE *f;
{
register unsigned nbytes = 1;
putc (r->flags, f);
if ((r->flags & RSMASK) == REXT) {
putc (r->index, f);
putc (r->index >> 8, f);
putc (r->index >> 16, f);
nbytes += 3;
}
if ((r->flags & RFMASK) == RHIGH16 ||
(r->flags & RFMASK) == RHIGH16S)
{
putc (r->offset, f);
putc (r->offset >> 8, f);
nbytes += 2;
}
return nbytes;
}
/*
* Write the a.out header to the file.
* Little-endian.
*/
void fputhdr (filhdr, coutb)
register struct exec *filhdr;
register FILE *coutb;
{
fputword (filhdr->a_magic, coutb);
fputword (filhdr->a_text, coutb);
fputword (filhdr->a_data, coutb);
fputword (filhdr->a_bss, coutb);
fputword (filhdr->a_reltext, coutb);
fputword (filhdr->a_reldata, coutb);
fputword (filhdr->a_syms, coutb);
fputword (filhdr->a_entry, coutb);
}
/*
* Emit the nlist record for the symbol.
*/
void fputsym (s, file)
register struct nlist *s;
register FILE *file;
{
register int i;
putc (s->n_len, file);
putc (s->n_type & ~N_LOC, file);
fputword (s->n_value, file);
for (i=0; i<s->n_len; i++)
putc (s->n_name[i], file);
}
/*
* Create temporary files for STEXT, SDATA and SSTRNG segments.
*/
void startup ()
{
register int i;
mktemp (tfilename);
for (i=STEXT; i<SBSS; i++) {
sfile [i] = fopen (tfilename, "w+");
if (! sfile [i])
uerror ("cannot open %s", tfilename);
unlink (tfilename);
rfile [i] = fopen (tfilename, "w+");
if (! rfile [i])
uerror ("cannot open %s", tfilename);
unlink (tfilename);
}
line = 1;
}
/*
* Suboptimal 32-bit hash function.
* Copyright (C) 2006 Serge Vakulenko.
*/
unsigned hash_rot13 (s)
register const char *s;
{
register unsigned hash, c;
hash = 0;
while ((c = (unsigned char) *s++) != 0) {
hash += c;
hash -= (hash << 13) | (hash >> 19);
}
return hash;
}
void hashinit ()
{
register int i, h;
register const struct optable *p;
for (i=0; i<HCMDSZ; i++)
hashctab[i] = -1;
for (p=optable; p->name; p++) {
h = hash_rot13 (p->name) & (HCMDSZ-1);
while (hashctab[h] != -1)
if (--h < 0)
h += HCMDSZ;
hashctab[h] = p - optable;
}
for (i=0; i<HASHSZ; i++)
hashtab[i] = -1;
}
int hexdig (c)
register int c;
{
if (c <= '9')
return (c - '0');
else if (c <= 'F')
return (c - 'A' + 10);
else
return (c - 'a' + 10);
}
/*
* Get hexadecimal number 0xZZZ
*/
void gethnum ()
{
register int c;
register char *cp;
c = getchar ();
for (cp=name; ISHEX(c); c=getchar())
*cp++ = hexdig (c);
ungetc (c, stdin);
intval = 0;
for (c=0; c<32; c+=4) {
if (--cp < name)
return;
intval |= *cp << c;
}
}
/*
* Get a number.
* 1234 - decimal
* 01234 - octal
*/
void getnum (c)
register int c;
{
register char *cp;
int leadingzero;
leadingzero = (c=='0');
for (cp=name; ISDIGIT(c); c=getchar())
*cp++ = hexdig (c);
ungetc (c, stdin);
intval = 0;
if (leadingzero) {
/* Octal. */
for (c=0; c<=27; c+=3) {
if (--cp < name)
return;
intval |= *cp << c;
}
if (--cp < name)
return;
intval |= *cp << 30;
return;
}
/* Decimal. */
for (c=1; ; c*=10) {
if (--cp < name)
return;
intval += *cp * c;
}
}
void getname (c)
register int c;
{
register char *cp;
for (cp=name; ISLETTER (c) || ISDIGIT (c); c=getchar())
*cp++ = c;
*cp = 0;
ungetc (c, stdin);
}
int looktype ()
{
switch (name [1]) {
case 'c':
if (! strcmp ("@common", name)) return (LSYMTYPE);
break;
case 'f':
if (! strcmp ("@fini_array", name)) return (LSECTYPE);
if (! strcmp ("@function", name)) return (LSYMTYPE);
break;
case 'g':
if (! strcmp ("@gnu_indirect_function", name)) return (LSYMTYPE);
if (! strcmp ("@gnu_unique_object", name)) return (LSYMTYPE);
break;
case 'i':
if (! strcmp ("@init_array", name)) return (LSECTYPE);
break;
case 'n':
if (! strcmp ("@nobits", name)) return (LSECTYPE);
if (! strcmp ("@note", name)) return (LSECTYPE);
if (! strcmp ("@notype", name)) return (LSYMTYPE);
break;
case 'o':
if (! strcmp ("@object", name)) return (LSYMTYPE);
break;
case 'p':
if (! strcmp ("@progbits", name)) return (LSECTYPE);
if (! strcmp ("@preinit_array", name)) return (LSECTYPE);
break;
case 't':
if (! strcmp ("@tls_object", name)) return (LSYMTYPE);
break;
}
return (-1);
}
int lookacmd ()
{
switch (name [1]) {
case 'a':
if (! strcmp (".ascii", name)) return (LASCII);
if (! strcmp (".align", name)) return (LALIGN);
break;
case 'b':
if (! strcmp (".bss", name)) return (LBSS);
if (! strcmp (".byte", name)) return (LBYTE);
break;
case 'c':
if (! strcmp (".comm", name)) return (LCOMM);
break;
case 'd':
if (! strcmp (".data", name)) return (LDATA);
break;
case 'e':
if (! strcmp (".equ", name)) return (LEQU);
if (! strcmp (".end", name)) return (LEND);
if (! strcmp (".ent", name)) return (LENT);
break;
case 'f':
if (! strcmp (".file", name)) return (LFILE);
if (! strcmp (".fmask", name)) return (LFMASK);
if (! strcmp (".frame", name)) return (LFRAME);
break;
case 'g':
if (! strcmp (".globl", name)) return (LGLOBL);
if (! strcmp (".gnu_attribute", name)) return (LGNUATTR);
break;
case 'h':
if (! strcmp (".half", name)) return (LHALF);
break;
case 'i':
if (! strcmp (".ident", name)) return (LIDENT);
break;
case 'l':
if (! strcmp (".local", name)) return (LLOCAL);
break;
case 'm':
if (! strcmp (".mask", name)) return (LMASK);
break;
case 'p':
if (! strcmp (".previous", name)) return (LPREVIOUS);
break;
case 'r':
if (! strcmp (".rdata", name)) return (LRDATA);
break;
case 's':
if (! strcmp (".section", name)) return (LSECTION);
if (! strcmp (".set", name)) return (LSET);
if (! strcmp (".size", name)) return (LSIZE);
if (! strcmp (".space", name)) return (LSPACE);
if (! strcmp (".strng", name)) return (LSTRNG);
break;
case 't':
if (! strcmp (".text", name)) return (LTEXT);
if (! strcmp (".type", name)) return (LTYPE);
break;
case 'w':
if (! strcmp (".word", name)) return (LWORD);
if (! strcmp (".weak", name)) return (LWEAK);
break;
}
return (-1);
}
/*
* Change a segment based on a section name.
*/
void setsection ()
{
struct {
const char *name;
int len;
int segm;
} const *p, map[] = {
{ ".text", 5, STEXT },
{ ".data", 5, SDATA },
{ ".sdata", 6, SDATA },
{ ".rodata", 7, SSTRNG },
{ ".bss", 4, SBSS },
{ ".sbss", 5, SBSS },
{ ".mdebug", 7, SSTRNG },
{ 0 },
};
for (p=map; p->name; p++) {
if (strncmp (name, p->name, p->len) == 0 &&
(p->name [p->len] == 0 ||
p->name [p->len] == '.'))
{
segm = p->segm;
return;
}
}
uerror ("bad .section name");
}
int lookreg ()
{
int val;
char *cp;
switch (name [1]) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
val = 0;
for (cp=name+1; ISDIGIT (*cp); cp++) {
val *= 10;
val += *cp - '0';
}
if (*cp != 0)
break;
return val;
case 'a':
if (name[3] == 0) {
switch (name[2]) {
case '0': return 4; /* $a0 */
case '1': return 5; /* $a1 */
case '2': return 6; /* $a2 */
case '3': return 7; /* $a3 */
case 't': return 1; /* $at */
}
}
break;
case 'f':
if (name[3] == 0 && name[2] == 'p')
return 30; /* $fp */
break;
case 'g':
if (name[3] == 0 && name[2] == 'p')
return 28; /* $gp */
break;
case 'k':
if (name[3] == 0) {
switch (name[2]) {
case '0': return 26; /* $k0 */
case '1': return 27; /* $k1 */
}
}
break;
case 'r':
if (name[3] == 0 && name[2] == 'a')
return 31; /* $ra */
break;
case 's':
if (name[3] == 0) {
switch (name[2]) {
case '0': return 16; /* $s0 */
case '1': return 17; /* $s1 */
case '2': return 18; /* $s2 */
case '3': return 19; /* $s3 */
case '4': return 20; /* $s4 */
case '5': return 21; /* $s5 */
case '6': return 22; /* $s6 */
case '7': return 23; /* $s7 */
case '8': return 30; /* $s8 */
case 'p': return 29; /* $sp */
}
}
break;
case 't':
if (name[3] == 0) {
switch (name[2]) {
case '0': return 8; /* $t0 */
case '1': return 9; /* $t1 */
case '2': return 10; /* $t2 */
case '3': return 11; /* $t3 */
case '4': return 12; /* $t4 */
case '5': return 13; /* $t5 */
case '6': return 14; /* $t6 */
case '7': return 15; /* $t7 */
case '8': return 24; /* $t8 */
case '9': return 25; /* $t9 */
}
}
break;
case 'v':
if (name[3] == 0) {
switch (name[2]) {
case '0': return 2; /* $v0 */
case '1': return 3; /* $v1 */
}
}
break;
case 'z':
if (! strcmp (name+2, "ero"))
return 0; /* $zero */
break;
}
return -1;
}
int lookcmd ()
{
register int i, h;
h = hash_rot13 (name) & (HCMDSZ-1);
while ((i = hashctab[h]) != -1) {
if (! strcmp (optable[i].name, name))
return (i);
if (--h < 0)
h += HCMDSZ;
}
return (-1);
}
char *alloc (len)
{
register int r;
r = lastfree;
lastfree += len;
if (lastfree > sizeof(space))
uerror ("out of memory");
return (space + r);
}
int lookname ()
{
register int i, h;
/* Search for symbol name. */
h = hash_rot13 (name) & (HASHSZ-1);
while ((i = hashtab[h]) != -1) {
if (! strcmp (stab[i].n_name, name))
return (i);
if (--h < 0)
h += HASHSZ;
}
/* Add a new symbol to table. */
if ((i = stabfree++) >= STSIZE)
uerror ("symbol table overflow");
stab[i].n_len = strlen (name);
stab[i].n_name = alloc (1 + stab[i].n_len);
strcpy (stab[i].n_name, name);
stab[i].n_value = 0;
stab[i].n_type = N_UNDF;
hashtab[h] = i;
return (i);
}
/*
* Read a lexical element, return it's type and store a value into *val.
* Returned type codes:
* LEOL - End of line. Value is a line number.
* LEOF - End of file.
* LNUM - Integer value (into intval), *val undefined.
* LNAME - Identifier. String value is in name[] array.
* LREG - Machine register. Value is a register number.
* LLSHIFT - << operator.
* LRSHIFT - >> operator.
* LASCII - .ascii assembler instruction.
* LBSS - .bss assembler instruction.
* LCOMM - .comm assembler instruction.
* LDATA - .data assembler instruction.
* LGLOBL - .globl assembler instruction.
* LHALF - .half assembler instruction.
* LSTRNG - .strng assembler instruction.
* LTEXT - .text assembler instruction.
* LEQU - .equ assembler instruction.
* LWORD - .word assembler instruction.
* LFILE - .file assembler instruction.
* LSECTION - .section assembler instruction.
*/
int getlex (pval)
register int *pval;
{
register int c;
if (blexflag) {
blexflag = 0;
*pval = blextype;
return (backlex);
}
for (;;) {
switch (c = getchar()) {
case '#':
skiptoeol: while ((c = getchar()) != '\n')
if (c == EOF)
return (LEOF);
case '\n':
++line;
c = getchar ();
if (c == '#')
goto skiptoeol;
ungetc (c, stdin);
case ';':
*pval = line;
return (LEOL);
case ' ':
case '\t':
continue;
case EOF:
return (LEOF);
case '\\':
c = getchar ();
if (c=='<')
return (LLSHIFT);
if (c=='>')
return (LRSHIFT);
ungetc (c, stdin);
return ('\\');
case '\'': case '%':
case '^': case '&': case '|': case '~':
case '+': case '-': case '*': case '/':
case '"': case ',': case '[': case ']':
case '(': case ')': case '{': case '}':
case '<': case '>': case '=': case ':':
return (c);
case '0':
if ((c = getchar ()) == 'x' || c=='X') {
gethnum ();
return (LNUM);
}
ungetc (c, stdin);
c = '0';
case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
case '8': case '9':
getnum (c);
return (LNUM);
default:
if (! ISLETTER (c))
uerror ("bad character: \\%o", c & 0377);
getname (c);
if (name[0] == '.') {
if (name[1] == 0)
return ('.');
*pval = lookacmd();
if (*pval != -1)
return (*pval);
}
if (name[0] == '$') {
*pval = lookreg();
if (*pval != -1)
return (LREG);
}
if (name[0] == '@') {
*pval = looktype();
if (*pval != -1)
return (*pval);
}
return (LNAME);
}
}
}
void ungetlex (val, type)
{
blexflag = 1;
backlex = val;
blextype = type;
}
int getterm ()
{
register int ty;
int cval, s;
switch (getlex (&cval)) {
default:
uerror ("operand missed");
case LNUM:
cval = getchar ();
if (cval == 'b' || cval == 'B')
extref = RLAB_OFFSET - intval;
else if (cval == 'f' || cval == 'F')
extref = RLAB_OFFSET + intval;
else {
/* Integer literal. */
ungetc (cval, stdin);
return (SABS);
}
/* Local label. */
if (intval >= RLAB_MAXVAL)
uerror ("too large relative label");
intval = 0;
return (SEXT);
case LNAME:
intval = 0;
cval = lookname();
ty = stab[cval].n_type & N_TYPE;
if (ty==N_UNDF || ty==N_COMM) {
extref = cval;
return (SEXT);
}
intval = stab[cval].n_value;
return (typesegm [ty]);
case '.':
intval = count[segm];
return (segm);
case '%':
if (getlex (&cval) != LNAME)
uerror ("bad %function name");
if (strcmp (name, "gp_rel") == 0) {
/* GP relative reverence. */
expr_flags |= EXPR_GPREL;
} else if (strcmp (name, "hi") == 0) {
expr_flags |= EXPR_HI;
} else if (strcmp (name, "lo") == 0) {
expr_flags |= EXPR_LO;
} else
uerror ("unknown function %s", name);
if (getlex (&cval) != '(')
uerror ("bad %s syntax", name);
/* fall through */
case '(':
getexpr (&s);
if (getlex (&cval) != ')')
uerror ("bad () syntax");
return (s);
}
}
/*
* Get an expression.
* Return a value, put a base segment id to *s.
* A copy of value is saved in intval.
*
* expression = [term] {op term}...
* term = LNAME | LNUM | "." | "(" expression ")"
* op = "+" | "-" | "&" | "|" | "^" | "~" | "<<" | ">>" | "/" | "*"
*/
unsigned getexpr (s)
register int *s;
{
register int clex;
int cval, s2;
unsigned rez;
/* look a first lexeme */
switch (clex = getlex (&cval)) {
default:
ungetlex (clex, cval);
rez = 0;
*s = SABS;
break;
case LNUM:
case LNAME:
case '.':
case '(':
case '%':
ungetlex (clex, cval);
*s = getterm ();
rez = intval;
break;
}
for (;;) {
switch (clex = getlex (&cval)) {
case '+':
s2 = getterm ();
if (*s == SABS)
*s = s2;
else if (s2 != SABS)
uerror ("too complex expression");
rez += intval;
break;
case '-':
s2 = getterm ();
if (s2 == *s && s2 != SEXT)
*s = SABS;
else if (s2 != SABS)
uerror ("too complex expression");
rez -= intval;
break;
case '&':
s2 = getterm ();
if (*s != SABS || s2 != SABS)
uerror ("too complex expression");
rez &= intval;
break;
case '|':
s2 = getterm ();
if (*s != SABS || s2 != SABS)
uerror ("too complex expression");
rez |= intval;
break;
case '^':
s2 = getterm ();
if (*s != SABS || s2 != SABS)
uerror ("too complex expression");
rez ^= intval;
break;
case '~':
s2 = getterm ();
if (*s != SABS || s2 != SABS)
uerror ("too complex expression");
rez ^= ~intval;
break;
case LLSHIFT: /* сдвиг влево */
s2 = getterm ();
if (*s != SABS || s2 != SABS)
uerror ("too complex expression");
rez <<= intval & 037;
break;
case LRSHIFT: /* сдвиг вправо */
s2 = getterm ();
if (*s != SABS || s2 != SABS)
uerror ("too complex expression");
rez >>= intval & 037;
break;
case '*':
s2 = getterm ();
if (*s != SABS || s2 != SABS)
uerror ("too complex expression");
rez *= intval;
break;
case '/':
s2 = getterm ();
if (*s != SABS || s2 != SABS)
uerror ("too complex expression");
if (intval == 0)
uerror ("division by zero");
rez /= intval;
break;
default:
ungetlex (clex, cval);
intval = rez;
return (rez);
}
}
/* NOTREACHED */
}
void reorder_flush ()
{
if (reorder_full) {
fputword (reorder_word, sfile[segm]);
fputrel (&reorder_rel, rfile[segm]);
reorder_full = 0;
}
}
/*
* Default emit function.
*/
void emitword (w, r, clobber_reg)
register unsigned w;
register struct reloc *r;
int clobber_reg;
{
if (mode_reorder && segm == STEXT) {
reorder_flush();
reorder_word = w;
reorder_rel = *r;
reorder_full = 1;
reorder_clobber = clobber_reg;
} else {
fputword (w, sfile[segm]);
fputrel (r, rfile[segm]);
}
count[segm] += WORDSZ;
}
/*
* LI pseudo instruction.
*/
void emit_li (opcode, relinfo)
register unsigned opcode;
register struct reloc *relinfo;
{
register unsigned value;
int cval, segment;
if (getlex (&cval) != ',')
uerror ("comma expected");
value = getexpr (&segment);
if (segment != SABS)
uerror ("absolute value required");
if (value <= 0xffff) {
/* ori d, $zero, value */
opcode |= 0x34000000 | value;
} else if (value >= -0x8000) {
/* addiu d, $zero, value */
opcode |= 0x24000000 | (value & 0xffff);
} else {
/* lui d, value[31:16]
* ori d, d, value[15:0]) */
emitword (opcode | 0x3c000000 | (value >> 16), &relabs, value >> 16);
opcode |= 0x34000000 | (opcode & 0x1f0000) << 5 | (value & 0xffff);
}
emitword (opcode, relinfo, value >> 16);
}
/*
* LA pseudo instruction.
*/
void emit_la (opcode, relinfo)
register unsigned opcode;
register struct reloc *relinfo;
{
register unsigned value, hi;
int cval, segment;
if (getlex (&cval) != ',')
uerror ("comma expected");
expr_flags = 0;
value = getexpr (&segment);
if (segment == SABS)
uerror ("relocatable value required");
relinfo->flags = segmrel [segment];
if (relinfo->flags == REXT)
relinfo->index = extref;
if (expr_flags & EXPR_GPREL)
relinfo->flags |= RGPREL;
/* lui d, %hi(value)
* addiu d, d, %lo(value) */
relinfo->flags |= RHIGH16S;
relinfo->offset = value & 0xffff;
hi = (value + 0x8000) >> 16;
emitword (opcode | 0x3c000000 | hi, relinfo, hi);
relinfo->flags &= ~RHIGH16S;
opcode |= 0x24000000 | (opcode & 0x1f0000) << 5 | (value & 0xffff);
emitword (opcode, relinfo, value >> 16);
}
/*
* Build and emit a machine instruction code.
*/
void makecmd (opcode, type, emitfunc)
unsigned opcode;
void (*emitfunc) (unsigned, struct reloc*);
{
register int clex;
register unsigned offset;
struct reloc relinfo;
int cval, segment, clobber_reg, negate_literal;
offset = 0;
relinfo.flags = RABS;
negate_literal = 0;
/*
* GCC can generate "j" instead of "jr".
* Need to detect it early.
*/
if (type == (FAOFF28 | FDSLOT)) {
clex = getlex (&cval);
ungetlex (clex, cval);
if (clex == LREG) {
if (opcode == 0x08000000) { /* j - replace by jr */
opcode = 0x00000008;
type = FRS1 | FDSLOT;
}
if (opcode == 0x0c000000) { /* jal - replace by jalr */
opcode = 0x00000009;
type = FRD1 | FRS2 | FDSLOT;
}
}
}
/*
* First register.
*/
cval = 0;
clobber_reg = 0;
if (type & FRD1) {
clex = getlex (&cval);
if (clex != LREG)
uerror ("bad rd register");
opcode |= cval << 11; /* rd, ... */
}
if (type & FRT1) {
clex = getlex (&cval);
if (clex != LREG)
uerror ("bad rt register");
opcode |= cval << 16; /* rt, ... */
}
if (type & FRS1) {
frs1: clex = getlex (&cval);
if (clex != LREG)
uerror ("bad rs register");
if (cval == 0 && (opcode == 0x0000001a || /* div */
opcode == 0x0000001b)) { /* divu */
/* Div instruction with three args.
* Treat it as a 2-arg variant. */
if (getlex (&cval) != ',')
uerror ("comma expected");
goto frs1;
}
opcode |= cval << 21; /* rs, ... */
}
if (type & FRTD) {
opcode |= cval << 16; /* rt equals rd */
}
if ((type & FMOD) && (type & (FRD1 | FRT1 | FRS1)))
clobber_reg = cval;
/*
* Second register.
*/
if (type & FRD2) {
if (getlex (&cval) != ',')
uerror ("comma expected");
clex = getlex (&cval);
if (clex != LREG)
uerror ("bad rd register");
opcode |= cval << 11; /* .., rd, ... */
}
if (type & FRT2) {
if (getlex (&cval) != ',')
uerror ("comma expected");
clex = getlex (&cval);
if (clex != LREG) {
if ((type & FRD1) && (type & FSA)) {
/* Second register operand omitted.
* Need to restore the missing operand. */
ungetlex (clex, cval);
cval = (opcode >> 11) & 31; /* get 1-st register */
opcode |= cval << 16; /* use 1-st reg as 2-nd */
goto fsa;
}
uerror ("bad rt register");
}
opcode |= cval << 16; /* .., rt, ... */
}
if (type & FRS2) {
clex = getlex (&cval);
if (clex != ',') {
if ((opcode & 0xfc00003f) != 0x00000009)
uerror ("comma expected");
/* Jalr with one argument.
* Treat as if the first argument is $31. */
ungetlex (clex, cval);
cval = (opcode >> 11) & 31; /* get 1-st register */
opcode |= cval << 21; /* use 1-st reg as 2-nd */
opcode |= 31 << 11; /* set 1-st reg to 31 */
clobber_reg = 31;
goto done3;
}
clex = getlex (&cval);
if (clex != LREG) {
if ((type & FRT1) && (type & FOFF16)) {
/* Second register operand omitted.
* Need to restore the missing operand. */
ungetlex (clex, cval);
cval = (opcode >> 16) & 31; /* get 1-st register */
opcode |= cval << 21; /* use 1-st reg as 2-nd */
goto foff16;
}
uerror ("bad rs register");
}
opcode |= cval << 21; /* .., rs, ... */
}
/*
* Third register.
*/
if (type & FRT3) {
clex = getlex (&cval);
if (clex != ',') {
/* Three-operand instruction used with two operands.
* Need to restore the missing operand. */
ungetlex (clex, cval);
cval = (opcode >> 21) & 31;
opcode &= ~(31 << 21); /* clear 2-nd register */
opcode |= ((opcode >> 11) & 31) << 21; /* use 1-st reg as 2-nd */
opcode |= cval << 16; /* add 3-rd register */
goto done3;
}
clex = getlex (&cval);
if (clex != LREG) {
if ((type & FRD1) && (type & FRS2)) {
/* Three-operand instruction used with literal operand.
* Convert it to immediate type. */
unsigned newop;
switch (opcode & 0xfc0007ff) {
case 0x00000020: newop = 0x20000000; break; // add -> addi
case 0x00000021: newop = 0x24000000; break; // addu -> addiu
case 0x00000024: newop = 0x30000000; break; // and -> andi
case 0x00000025: newop = 0x34000000; break; // or -> ori
case 0x0000002a: newop = 0x28000000; break; // slt -> slti
case 0x0000002b: newop = 0x2c000000; break; // sltu -> sltiu
case 0x00000022: newop = 0x20000000; // sub -> addi, negate
negate_literal = 1; break;
case 0x00000023: newop = 0x24000000; // subu -> addiu, negate
negate_literal = 1; break;
case 0x00000026: newop = 0x38000000; break; // xor -> xori
default:
uerror ("bad rt register");
return;
}
ungetlex (clex, cval);
cval = (opcode >> 11) & 31; /* get 1-st register */
newop |= cval << 16; /* set 1-st register */
newop |= opcode & (31 << 21); /* set 2-nd register */
opcode = newop;
type = FRT1 | FRS2 | FOFF16 | FMOD;
goto foff16;
}
uerror ("bad rt register");
}
opcode |= cval << 16; /* .., .., rt */
}
if (type & FRS3) {
clex = getlex (&cval);
if (clex != ',') {
/* Three-operand instruction used with two operands.
* Need to restore the missing operand. */
ungetlex (clex, cval);
cval = (opcode >> 16) & 31;
opcode &= ~(31 << 16); /* clear 2-nd register */
opcode |= ((opcode >> 11) & 31) << 16; /* use 1-st reg as 2-nd */
opcode |= cval << 21; /* add 3-rd register */
goto done3;
}
clex = getlex (&cval);
if (clex != LREG)
uerror ("bad rs register");
opcode |= cval << 21; /* .., .., rs */
}
done3:
/*
* Immediate argument.
*/
if (type & FSEL) {
/* optional COP0 register select */
clex = getlex (&cval);
if (clex == ',') {
offset = getexpr (&segment);
if (segment != SABS)
uerror ("absolute value required");
opcode |= offset & 7;
} else
ungetlex (clex, cval);
} else if (type & (FCODE | FCODE16 | FSA)) {
/* Non-relocatable offset */
if (type & FSA) {
if (getlex (&cval) != ',')
uerror ("comma expected");
clex = getlex (&cval);
if (clex == LREG && type == (FRD1 | FRT2 | FSA | FMOD)) {
/* Literal-operand shift instruction used with register operand.
* Convert it to 3-register type. */
unsigned newop;
switch (opcode & 0xffe0003f) {
case 0x00200002: newop = 0x00000046; break; // ror -> rorv
case 0x00000000: newop = 0x00000004; break; // sll -> sllv
case 0x00000003: newop = 0x00000007; break; // sra -> srav
case 0x00000002: newop = 0x00000006; break; // srl -> srlv
default:
uerror ("bad shift amount");
return;
}
newop |= opcode & (0x3ff << 11); /* set 1-st and 2-nd regs */
newop |= cval << 21; /* set 3-rd register */
opcode = newop;
type = FRD1 | FRT2 | FRS3 | FMOD;
goto done3;
}
ungetlex (clex, cval);
}
if ((type & FCODE) && (type & FRT2)) {
/* Optional code for trap instruction. */
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
goto done;
}
}
fsa: offset = getexpr (&segment);
if (segment != SABS)
uerror ("absolute value required");
switch (type & (FCODE | FCODE16 | FSA)) {
case FCODE: /* immediate shifted <<6 */
opcode |= offset << 6;
break;
case FCODE16: /* immediate shifted <<16 */
opcode |= offset << 16;
break;
case FSA: /* shift amount */
opcode |= (offset & 0x1f) << 6;
break;
}
} else if (type & (FOFF16 | FOFF18 | FAOFF18 | FAOFF28 | FHIGH16)) {
/* Relocatable offset */
if ((type & (FOFF16 | FOFF18 | FHIGH16)) && getlex (&cval) != ',')
uerror ("comma expected");
foff16: expr_flags = 0;
offset = getexpr (&segment);
relinfo.flags = segmrel [segment];
if (negate_literal) {
// Negate literal arg for sub and subu
offset = -offset;
if (relinfo.flags != RABS)
uerror ("cannot negate relocatable literal");
}
if (relinfo.flags == REXT)
relinfo.index = extref;
if (expr_flags & EXPR_GPREL)
relinfo.flags |= RGPREL;
switch (type & (FOFF16 | FOFF18 | FAOFF18 | FAOFF28 | FHIGH16)) {
case FOFF16: /* low 16-bit byte address */
opcode |= offset & 0xffff;
break;
case FHIGH16: /* high 16-bit byte address */
if (relinfo.flags != RABS) {
/* %hi function - assume signed offset */
relinfo.flags |= (expr_flags & EXPR_HI) ? RHIGH16S : RHIGH16;
relinfo.offset = offset & 0xffff;
offset += 0x8000;
}
opcode |= offset >> 16;
break;
case FOFF18: /* 18-bit PC-relative word address */
case FAOFF18:
if (segment == segm) {
offset -= count[segm] + 4;
relinfo.flags = RABS;
} else if (segment == SEXT) {
relinfo.flags |= RWORD16;
} else
uerror ("invalid segment %d", segment);
opcode |= (offset >> 2) & 0xffff;
break;
case FAOFF28: /* 28-bit word address */
opcode |= (offset >> 2) & 0x3ffffff;
relinfo.flags |= RWORD26;
break;
}
}
/*
* Last argument.
*/
if (type & FRSB) {
if (getlex (&cval) != '(')
uerror ("left par expected");
clex = getlex (&cval);
if (clex != LREG)
uerror ("bad rs register");
if (getlex (&cval) != ')')
uerror ("right par expected");
opcode |= cval << 21; /* ... (rs) */
}
if (type & FSIZE) {
if (getlex (&cval) != ',')
uerror ("comma expected");
offset = getexpr (&segment);
if (segment != SABS)
uerror ("absolute value required");
opcode |= ((offset - 1) & 0x1f) << 11; /* bit field size */
}
if (type & FMSB) {
if (getlex (&cval) != ',')
uerror ("comma expected");
offset += getexpr (&segment);
if (segment != SABS)
uerror ("absolute value required");
if (offset > 32)
offset = 32;
opcode |= ((offset - 1) & 0x1f) << 11; /* msb */
}
done:
/* Output resulting values. */
if (emitfunc) {
emitfunc (opcode, &relinfo);
} else if (mode_reorder && (type & FDSLOT) && segm == STEXT) {
/* Need a delay slot. */
if (reorder_full && reorder_clobber != 0) {
/* Analyse register dependency.
* Flush the instruction if needed. */
int rt = (opcode >> 16) & 31;
int rs = (opcode >> 21) & 31;
if (((type & (FRS1 | FRS2)) && rs == reorder_clobber) ||
((type & FRT2) && rt == reorder_clobber))
reorder_flush();
}
fputword (opcode, sfile[segm]);
fputrel (&relinfo, rfile[segm]);
if (reorder_full) {
/* Delay slot: insert a previous instruction. */
reorder_flush();
} else {
/* Insert NOP in delay slot. */
fputword (0, sfile[segm]);
fputrel (&relabs, rfile[segm]);
count[segm] += WORDSZ;
}
count[segm] += WORDSZ;
} else {
emitword (opcode, &relinfo, clobber_reg);
}
}
/*
* Increment the current segment by nbytes.
* Emit a needed amount of zeroes to sfile and rfile.
* Part of data have already been sent to rfile;
* length specified by 'done' argument.
*/
void add_space (nbytes, fill_data)
unsigned nbytes, fill_data;
{
unsigned c;
if (segm < SBSS) {
/* Emit data and relocation. */
for (c=0; c<nbytes; c++) {
count[segm]++;
if (fill_data)
fputc (0, sfile[segm]);
if (! (count[segm] & 3))
fputrel (&relabs, rfile[segm]);
}
} else
count[segm] += nbytes;
}
void makeascii ()
{
register int c, nbytes;
int cval;
c = getlex (&cval);
if (c != '"')
uerror ("no .ascii parameter");
nbytes = 0;
for (;;) {
c = getchar ();
switch (c) {
case EOF:
uerror ("EOF in text string");
case '"':
break;
case '\\':
c = getchar ();
switch (c) {
case EOF:
uerror ("EOF in text string");
case '\n':
continue;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
cval = c & 07;
c = getchar ();
if (c>='0' && c<='7') {
cval = (cval << 3) | (c & 7);
c = getchar ();
if (c>='0' && c<='7') {
cval = (cval << 3) | (c & 7);
} else
ungetc (c, stdin);
} else
ungetc (c, stdin);
c = cval;
break;
case 't':
c = '\t';
break;
case 'b':
c = '\b';
break;
case 'r':
c = '\r';
break;
case 'n':
c = '\n';
break;
case 'f':
c = '\f';
break;
}
default:
fputc (c, sfile[segm]);
nbytes++;
continue;
}
break;
}
add_space (nbytes, 0);
}
/*
* Skip a string from the input file.
*/
void skipstring ()
{
int c, cval;
c = getlex (&cval);
if (c != '"')
uerror ("no string parameter");
for (;;) {
c = getchar ();
switch (c) {
case EOF:
uerror ("EOF in text string");
case '"':
break;
case '\\':
c = getchar ();
switch (c) {
case EOF:
uerror ("EOF in text string");
case '\n':
continue;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
c = getchar ();
if (c>='0' && c<='7') {
c = getchar ();
if (c>='0' && c<='7') {
} else
ungetc (c, stdin);
} else
ungetc (c, stdin);
break;
}
default:
continue;
}
break;
}
}
/*
* Set assembler option.
*/
void setoption ()
{
const char *option = name;
int enable = 1;
if (option[0] == 'n' && option[1] == 'o') {
enable = 0;
option += 2;
}
if (! strcmp ("reorder", option)) {
/* reorder mode */
mode_reorder = enable;
if (! mode_reorder)
reorder_flush();
return;
}
if (! strcmp ("macro", option)) {
/* macro mode */
mode_macro = enable;
return;
}
if (! strcmp ("mips16", option)) {
/* mips16 mode */
mode_mips16 = enable;
return;
}
if (! strcmp ("micromips", option)) {
/* micromips mode */
mode_micromips = enable;
return;
}
if (! strcmp ("at", option)) {
/* at mode */
mode_at = enable;
return;
}
uerror ("unknown option %s", option);
}
/*
* Align the current segment.
*/
void align (align_bits)
{
unsigned nbytes, align_mask, c;
align_mask = (1 << align_bits) - 1;
nbytes = count[segm] & align_mask;
if (nbytes == 0)
return;
nbytes = align_mask + 1 - nbytes;
if (segm < SBSS) {
/* Emit data and relocation. */
for (c=0; c<nbytes; c++) {
count[segm]++;
fputc (0, sfile[segm]);
if (! (count[segm] & 3))
fputrel (&relabs, rfile[segm]);
}
} else
count[segm] += nbytes;
}
void pass1 ()
{
register int clex;
int cval, tval, csegm, nbytes;
register unsigned addr;
segm = STEXT;
for (;;) {
clex = getlex (&cval);
switch (clex) {
case LEOF:
reorder_flush();
segm = STEXT;
align (2);
segm = SDATA;
align (2);
segm = SSTRNG;
align (2);
segm = SBSS;
align (2);
return;
case LEOL:
continue;
case ':':
continue;
case '.':
if (getlex (&cval) != '=')
uerror ("bad instruction");
addr = getexpr (&csegm);
if (csegm != segm)
uerror ("bad count assignment");
if (addr < count[segm])
uerror ("negative count increment");
reorder_flush();
if (segm == SBSS)
count [segm] = addr;
else {
while (count[segm] < addr) {
emitword (0, &relabs, 0);
}
}
break;
case LNAME:
cval = lookcmd();
clex = getlex (&tval);
if (clex == ':') {
/* Label. */
reorder_flush();
cval = lookname();
stab[cval].n_value = count[segm];
stab[cval].n_type &= ~N_TYPE;
stab[cval].n_type |= segmtype [segm];
continue;
} else if (clex=='=' || clex==LEQU) {
/* Symbol definition. */
cval = lookname();
stab[cval].n_value = getexpr (&csegm);
if (csegm == SEXT)
uerror ("indirect equivalence");
stab[cval].n_type &= N_EXT;
stab[cval].n_type |= segmtype [csegm];
break;
} else if (clex==LCOMM) {
/* name .comm len */
cval = lookname();
if (stab[cval].n_type != N_UNDF &&
stab[cval].n_type != N_LOC &&
(stab[cval].n_type & N_TYPE) != N_COMM)
uerror ("name already defined");
if (stab[cval].n_type & N_LOC)
stab[cval].n_type = N_COMM;
else
stab[cval].n_type = N_EXT | N_COMM;
getexpr (&tval);
if (tval != SABS)
uerror ("bad .comm length");
stab[cval].n_value = intval;
break;
}
/* Machine instruction. */
if (cval < 0)
uerror ("bad instruction");
ungetlex (clex, tval);
align (2);
makecmd (optable[cval].opcode, optable[cval].type,
optable[cval].func);
break;
case LNUM:
/* Local label. */
if (nlabels >= MAXRLAB)
uerror ("too many digital labels");
reorder_flush();
labeltab[nlabels].num = intval;
labeltab[nlabels].value = count[segm];
++nlabels;
clex = getlex (&tval);
if (clex != ':')
uerror ("bad digital label");
continue;
case LTEXT:
segm = STEXT;
reorder_flush();
break;
case LDATA:
segm = SDATA;
break;
case LSTRNG:
case LRDATA:
segm = SSTRNG;
break;
case LBSS:
segm = SBSS;
break;
case LWORD:
reorder_flush();
align (2);
for (;;) {
struct reloc relinfo;
expr_flags = 0;
getexpr (&cval);
relinfo.flags = RBYTE32 | segmrel [cval];
if (cval == SEXT)
relinfo.index = extref;
if (expr_flags & EXPR_GPREL)
relinfo.flags |= RGPREL;
emitword (intval, &relinfo, 0);
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
break;
}
}
break;
case LBYTE:
reorder_flush();
nbytes = 0;
for (;;) {
getexpr (&cval);
fputc (intval, sfile[segm]);
nbytes++;
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
break;
}
}
add_space (nbytes, 0);
break;
case LHALF:
reorder_flush();
align (1);
nbytes = 0;
for (;;) {
getexpr (&cval);
fputc (intval, sfile[segm]);
fputc (intval >> 8, sfile[segm]);
nbytes += 2;
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
break;
}
}
add_space (nbytes, 0);
break;
case LSPACE:
/* .space num */
getexpr (&cval);
reorder_flush();
add_space (intval, 1);
break;
case LALIGN:
/* .align num */
if (getlex (&cval) != LNUM)
uerror ("bad parameter of .align");
reorder_flush();
align (intval);
break;
case LASCII:
reorder_flush();
makeascii ();
break;
case LGLOBL:
/* .globl name, ... */
for (;;) {
clex = getlex (&cval);
if (clex != LNAME)
uerror ("bad parameter of .globl");
cval = lookname();
if (stab[cval].n_type & N_LOC)
uerror ("local name redefined as global");
stab[cval].n_type |= N_EXT;
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
break;
}
}
break;
case LLOCAL:
/* .local name, ... */
for (;;) {
clex = getlex (&cval);
if (clex != LNAME)
uerror ("bad parameter of .local");
cval = lookname();
if (stab[cval].n_type & N_EXT)
uerror ("global name redefined as local");
stab[cval].n_type |= N_LOC;
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
break;
}
}
break;
case LWEAK:
/* .weak name */
for (;;) {
clex = getlex (&cval);
if (clex != LNAME)
uerror ("bad parameter of .weak");
cval = lookname();
stab[cval].n_type |= N_WEAK;
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
break;
}
}
break;
case LCOMM:
/* .comm name,len */
if (getlex (&cval) != LNAME)
uerror ("bad parameter of .comm");
cval = lookname();
if (stab[cval].n_type != N_UNDF &&
stab[cval].n_type != N_LOC &&
(stab[cval].n_type & N_TYPE) != N_COMM)
uerror ("name already defined");
if (stab[cval].n_type & N_LOC)
stab[cval].n_type = N_COMM;
else
stab[cval].n_type = N_EXT | N_COMM;
clex = getlex (&tval);
if (clex == ',') {
getexpr (&tval);
if (tval != SABS)
uerror ("bad length of .comm");
} else {
ungetlex (clex, cval);
intval = 1;
}
stab[cval].n_value = intval;
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
break;
}
getexpr (&tval);
if (tval != SABS)
uerror ("bad .comm alignment");
break;
case LFILE:
/* .file line filename */
if (getlex (&cval) != LNUM)
uerror ("bad parameter of .file");
skipstring();
break;
case LIDENT:
/* .ident string */
skipstring();
break;
case LSECTION:
/* .section name[,"flags"[,type[,entsize]]] */
clex = getlex (&cval);
if (clex != LNAME && clex != LBSS)
uerror ("bad name of .section");
setsection();
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
break;
}
skipstring();
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
break;
}
if (getlex (&cval) != LSECTYPE)
uerror ("bad type of .section");
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
break;
}
if (getlex (&cval) != LNUM)
uerror ("bad entry size of .section");
break;
case LPREVIOUS:
/* .previous - ignore */
break;
case LGNUATTR:
/* .gnu_attribute num[,num] */
if (getlex (&cval) != LNUM)
uerror ("bad parameter of .gnu_attribute");
clex = getlex (&cval);
if (clex != ',' || getlex (&cval) != LNUM)
uerror ("bad parameter of .gnu_attribute");
break;
case LSET:
/* .set option */
if (getlex (&cval) != LNAME)
uerror ("bad parameter of .set");
setoption();
break;
case LENT:
/* .ent name */
clex = getlex (&cval);
if (clex != LNAME)
uerror ("bad parameter of .ent");
cval = lookname();
break;
case LEND:
/* .end name */
clex = getlex (&cval);
if (clex != LNAME)
uerror ("bad parameter of .end");
cval = lookname();
break;
case LTYPE:
/* .type name,type */
if (getlex (&cval) != LNAME)
uerror ("bad name of .type");
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
break;
}
if (getlex (&cval) != LSYMTYPE)
uerror ("bad type of .type");
break;
case LFRAME:
/* .frame reg,num,reg */
if (getlex (&cval) != LREG)
uerror ("bad register of .frame");
clex = getlex (&cval);
if (clex != ',' || getlex (&cval) != LNUM)
uerror ("bad parameter of .frame");
clex = getlex (&cval);
if (clex != ',' || getlex (&cval) != LREG)
uerror ("bad register of .frame");
break;
case LMASK:
/* .mask mask,expr */
if (getlex (&cval) != LNUM)
uerror ("bad mask of .mask");
clex = getlex (&cval);
if (clex != ',')
uerror ("bad parameter of .mask");
getexpr (&cval);
if (cval != SABS)
uerror ("bad expression of .mask");
break;
case LFMASK:
/* .fmask mask,expr */
if (getlex (&cval) != LNUM)
uerror ("bad mask of .fmask");
clex = getlex (&cval);
if (clex != ',')
uerror ("bad parameter of .fmask");
getexpr (&cval);
if (cval != SABS)
uerror ("bad expression of .fmask");
break;
case LSIZE:
/* .size name,expr */
if (getlex (&cval) != LNAME)
uerror ("bad name of .size");
clex = getlex (&cval);
if (clex != ',') {
ungetlex (clex, cval);
break;
}
nbytes = getexpr (&csegm);
if (csegm != SABS)
uerror ("bad value of .size");
break;
default:
uerror ("bad syntax");
}
clex = getlex (&cval);
if (clex != LEOL) {
if (clex == LEOF)
return;
uerror ("bad instruction arguments");
}
}
}
/*
* Find the relative label address,
* by the reference address and the label number.
* Backward references have negative label numbers.
*/
int findlabel (int addr, int sym)
{
struct labeltab *p;
if (sym < 0) {
/* Backward reference. */
for (p=labeltab+nlabels-1; p>=labeltab; --p) {
if (p->value <= addr && p->num == -sym) {
return p->value;
}
}
uerror ("undefined label %db at address %d", -sym, addr);
} else {
/* Forward reference. */
for (p=labeltab; p<labeltab+nlabels; ++p) {
if (p->value > addr && p->num == sym) {
return p->value;
}
}
uerror ("undefined label %df at address %d", sym, addr);
}
return 0;
}
void middle ()
{
register int i, snum;
stlength = 0;
for (snum=0, i=0; i<stabfree; i++) {
/* Without -u option, undefined symbol is considered external */
if (stab[i].n_type == N_UNDF) {
if (uflag)
uerror ("name undefined", stab[i].n_name);
stab[i].n_type |= N_EXT;
}
if (xflags)
newindex[i] = snum;
if (! xflags || (stab[i].n_type & N_EXT) ||
(Xflag && ! IS_LOCAL(&stab[i])))
{
stlength += 2 + WORDSZ + stab[i].n_len;
snum++;
}
}
stalign = WORDSZ - stlength % WORDSZ;
stlength += stalign;
line = 0;
}
void makeheader (rtsize, rdsize)
{
struct exec hdr;
hdr.a_magic = RMAGIC;
hdr.a_text = count [STEXT];
hdr.a_data = count [SDATA] + count [SSTRNG];
hdr.a_bss = count [SBSS];
hdr.a_reltext = rtsize;
hdr.a_reldata = rdsize;
hdr.a_syms = stlength;
hdr.a_entry = 0;
fseek (stdout, 0, 0);
fputhdr (&hdr, stdout);
}
unsigned relocate (opcode, offset, relinfo)
register unsigned opcode, offset;
register struct reloc *relinfo;
{
switch (relinfo->flags & RFMASK) {
case RBYTE32: /* 32 bits of byte address */
opcode += offset;
break;
case RBYTE16: /* low 16 bits of byte address */
offset += opcode & 0xffff;
opcode &= ~0xffff;
opcode |= offset & 0xffff;
break;
case RHIGH16: /* high 16 bits of byte address */
offset += (opcode & 0xffff) << 16;
offset += relinfo->offset;
opcode &= ~0xffff;
opcode |= (offset >> 16) & 0xffff;
relinfo->offset = offset & 0xffff;
break;
case RHIGH16S: /* high 16 bits of byte address */
offset += (opcode & 0xffff) << 16;
offset += (signed short) relinfo->offset;
opcode &= ~0xffff;
opcode |= ((offset + 0x8000) >> 16) & 0xffff;
relinfo->offset = offset & 0xffff;
break;
case RWORD16: /* 16 bits of relative word address */
uerror ("bad relative relocation: opcode %08x, relinfo %02x", opcode, relinfo->flags);
break;
case RWORD26: /* 26 bits of word address */
offset += (opcode & 0x3ffffff) << 2;
opcode &= ~0x3ffffff;
opcode |= (offset >> 2) & 0x3ffffff;
break;
}
return (opcode);
}
unsigned makeword (opcode, relinfo, offset)
register unsigned opcode, offset;
register struct reloc *relinfo;
{
struct nlist *sym;
unsigned value;
switch (relinfo->flags & RSMASK) {
case RABS:
break;
case RTEXT:
opcode = relocate (opcode, tbase, relinfo);
break;
case RDATA:
opcode = relocate (opcode, dbase, relinfo);
break;
case RSTRNG:
opcode = relocate (opcode, adbase, relinfo);
break;
case RBSS:
opcode = relocate (opcode, bbase, relinfo);
break;
case REXT:
if (relinfo->index >= RLAB_OFFSET - RLAB_MAXVAL) {
/* Relative label.
* Change relocation to segment type. */
sym = 0;
value = findlabel (offset, relinfo->index - RLAB_OFFSET);
relinfo->flags &= RGPREL | RFMASK;
relinfo->flags |= segmrel[segm];
} else {
/* Symbol name. */
sym = &stab[relinfo->index];
if (sym->n_type == N_EXT+N_UNDF || sym->n_type == N_EXT+N_COMM)
return opcode;
value = sym->n_value;
}
switch (relinfo->flags & RFMASK) {
case RWORD16:
/* Relative word address.
* Change relocation to absolute. */
if (sym && (sym->n_type & N_TYPE) != segmtype[segm])
uerror ("%s: bad segment for relative relocation, offset %u",
sym->n_name, offset);
offset = value - offset - 4;
if (segm == SDATA)
offset -= dbase;
else if (segm == SSTRNG)
offset -= adbase;
offset += (opcode & 0xffff) << 2;
opcode &= ~0xffff;
opcode |= (offset >> 2) & 0xffff;
relinfo->flags = RABS;
return opcode;
case RHIGH16:
value += relinfo->offset;
break;
case RHIGH16S:
value += (signed short) relinfo->offset;
break;
}
opcode = relocate (opcode, value, relinfo);
break;
}
return opcode;
}
void pass2 ()
{
register int i;
register unsigned h;
tbase = 0;
dbase = tbase + count[STEXT];
adbase = dbase + count[SDATA];
bbase = adbase + count[SSTRNG];
/* Adjust indexes in symbol name */
for (i=0; i<stabfree; i++) {
switch (stab[i].n_type & N_TYPE) {
case N_UNDF:
case N_ABS:
break;
case N_TEXT:
stab[i].n_value += tbase;
break;
case N_DATA:
stab[i].n_value += dbase;
break;
case N_STRNG:
stab[i].n_value += adbase;
stab[i].n_type += N_DATA - N_STRNG;
break;
case N_BSS:
stab[i].n_value += bbase;
break;
}
}
fseek (stdout, sizeof(struct exec), 0);
for (segm=STEXT; segm<SBSS; segm++) {
/* Need to rewrite a relocation file. */
FILE *rfd = fopen (tfilename, "w+");
if (! rfd)
uerror ("cannot open %s", tfilename);
unlink (tfilename);
rewind (sfile [segm]);
rewind (rfile [segm]);
for (h=0; h<count[segm]; h+=WORDSZ) {
struct reloc relinfo;
unsigned word = fgetword (sfile[segm]);
fgetrel (rfile[segm], &relinfo);
word = makeword (word, &relinfo, h);
fputword (word, stdout);
fputrel (&relinfo, rfd);
}
fclose (rfile [segm]);
rfile [segm] = rfd;
}
}
/*
* Convert symbol type to relocation type.
*/
int typerel (t)
{
switch (t & N_TYPE) {
case N_ABS: return (RABS);
case N_TEXT: return (RTEXT);
case N_DATA: return (RDATA);
case N_BSS: return (RBSS);
case N_STRNG: return (RDATA);
case N_UNDF:
case N_COMM:
case N_FN:
default: return (0);
}
}
/*
* Relocate a relocation info word.
* Remap symbol indexes.
* Put string pseudo-section to data section.
*/
void relrel (relinfo)
register struct reloc *relinfo;
{
register unsigned type;
switch ((int) relinfo->flags & REXT) {
case RSTRNG:
relinfo->flags &= ~RSMASK;
relinfo->flags |= RDATA;
break;
case REXT:
type = stab[relinfo->index].n_type;
if (type == N_EXT+N_UNDF || type == N_EXT+N_COMM)
{
/* Reindexing */
if (xflags)
relinfo->index = newindex [relinfo->index];
} else {
relinfo->flags &= ~RSMASK;
relinfo->flags |= typerel (type);
}
break;
}
}
/*
* Emit a relocation info for a given segment.
* Copy it from scratch file to output.
* Return a size of relocation data in bytes.
*/
unsigned makereloc (s)
register int s;
{
register unsigned i, nbytes;
struct reloc relinfo;
if (count [s] <= 0)
return 0;
rewind (rfile [s]);
nbytes = 0;
for (i=0; i<count[s]; i+=WORDSZ) {
fgetrel (rfile[s], &relinfo);
relrel (&relinfo);
nbytes += fputrel (&relinfo, stdout);
}
while (nbytes % WORDSZ) {
putchar (0);
nbytes++;
}
return nbytes;
}
void makesymtab ()
{
register int i;
for (i=0; i<stabfree; i++) {
if (! xflags || (stab[i].n_type & N_EXT) ||
(Xflag && stab[i].n_name[0] != 'L'))
{
fputsym (&stab[i], stdout);
}
}
while (stalign--)
putchar (0);
}
void usage ()
{
fprintf (stderr, "Usage:\n");
fprintf (stderr, " as [-uxX] [-o outfile] [infile]\n");
fprintf (stderr, "Options:\n");
fprintf (stderr, " -o filename Set output file name, default stdout\n");
fprintf (stderr, " -u Treat undefined names as error\n");
fprintf (stderr, " -x Discard local symbols\n");
fprintf (stderr, " -X Discard locals starting with 'L' or '.'\n");
exit (1);
}
int main (argc, argv)
register char *argv[];
{
register int i;
register char *cp;
int ofile = 0;
unsigned rtsize, rdsize;
/*
* Parse options.
*/
for (i=1; i<argc; i++) {
switch (argv[i][0]) {
case '-':
for (cp=argv[i]+1; *cp; cp++) {
switch (*cp) {
case 'X': /* strip L* and .* locals */
Xflag++;
case 'x': /* strip local symbols */
xflags++;
break;
case 'u': /* treat undefines as error */
uflag++;
break;
case 'o': /* output file name */
if (ofile)
uerror ("too many -o flags");
ofile = 1;
if (cp [1]) {
/* -ofile */
outfile = cp+1;
while (*++cp);
--cp;
} else if (i+1 < argc)
/* -o file */
outfile = argv[++i];
break;
case 'v': /* verbose mode */
// TODO
break;
case 'g': /* debug mode */
// TODO
break;
case 'I': /* include dir */
// TODO
while (*++cp);
--cp;
break;
case 'O': /* optimization level */
// TODO
while (*++cp);
--cp;
break;
case '-': /* long option - skip */
while (*++cp);
--cp;
break;
case 'n': /* -no-xyz option - skip */
while (*++cp);
--cp;
break;
case 'm': /* -mips32r2, -mabi=32 - ignore */
while (*++cp);
--cp;
break;
case 'E': /* -EL, -EB - endianness */
if (cp[1] != 'L')
uerror ("only little endian is supported");
while (*++cp);
--cp;
break;
default:
fprintf (stderr, "Unknown option: %s\n", cp);
usage();
}
}
break;
default:
if (infile)
uerror ("too many input files");
infile = argv[i];
break;
}
}
if (! infile && isatty(0))
usage();
/*
* Setup input-output.
*/
if (infile && ! freopen (infile, "r", stdin))
uerror ("cannot open %s", infile);
if (! freopen (outfile, "w", stdout))
uerror ("cannot open %s", outfile);
startup (); /* Open temporary files */
hashinit (); /* Initialize hash tables */
pass1 (); /* First pass */
middle (); /* Prepare symbol table */
pass2 (); /* Second pass */
rtsize = makereloc (STEXT); /* Emit relocation info */
rdsize = makereloc (SDATA);
rdsize += makereloc (SSTRNG);
makesymtab (); /* Emit symbol table */
makeheader (rtsize, rdsize); /* Write a.out header */
return 0;
}