/* * Assembler for MIPS32. * The syntax is compatible with GCC output. * * Copyright (C) 2011-2012 Serge Vakulenko, * * 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 # include #else # include #endif #include #include #include #include #include #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; in_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> 19); } return hash; } void hashinit () { register int i, h; register const struct optable *p; for (i=0; iname; p++) { h = hash_rot13 (p->name) & (HCMDSZ-1); while (hashctab[h] != -1) if (--h < 0) h += HCMDSZ; hashctab[h] = p - optable; } for (i=0; iname; 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='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= 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; pvalue > 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; iflags & 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; iflags & 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