/* m88k.c -- Assembler for the Motorola 88000 Contributed by Devon Bowen of Buffalo University and Torbjorn Granlund of the Swedish Institute of Computer Science. Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. GAS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GAS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GAS; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "as.h" #include "safe-ctype.h" #include "subsegs.h" #include "m88k-opcode.h" #if defined (OBJ_ELF) #include "elf/m88k.h" #endif #define RELOC_LO16 BFD_RELOC_LO16 #define RELOC_HI16 BFD_RELOC_HI16 #define RELOC_PC16 BFD_RELOC_18_PCREL_S2 #define RELOC_PC26 BFD_RELOC_28_PCREL_S2 #define RELOC_32 BFD_RELOC_32 #define NO_RELOC BFD_RELOC_NONE struct field_val_assoc { char *name; unsigned val; }; struct field_val_assoc m88100_cr_regs[] = { {"PID", 0}, {"PSR", 1}, {"EPSR", 2}, {"SSBR", 3}, {"SXIP", 4}, {"SNIP", 5}, {"SFIP", 6}, {"VBR", 7}, {"DMT0", 8}, {"DMD0", 9}, {"DMA0", 10}, {"DMT1", 11}, {"DMD1", 12}, {"DMA1", 13}, {"DMT2", 14}, {"DMD2", 15}, {"DMA2", 16}, {"SR0", 17}, {"SR1", 18}, {"SR2", 19}, {"SR3", 20}, {NULL, 0}, }; struct field_val_assoc m88110_cr_regs[] = { {"PID", 0}, {"PSR", 1}, {"EPSR", 2}, {"EXIP", 4}, {"ENIP", 5}, {"VBR", 7}, {"SRX", 16}, {"SR0", 17}, {"SR1", 18}, {"SR2", 19}, {"SR3", 20}, {"ICMD", 25}, {"ICTL", 26}, {"ISAR", 27}, {"ISAP", 28}, {"IUAP", 29}, {"IIR", 30}, {"IBP", 31}, {"IPPU", 32}, {"IPPL", 33}, {"ISR", 34}, {"ILAR", 35}, {"IPAR", 36}, {"DCMD", 40}, {"DCTL", 41}, {"DSAR", 42}, {"DSAP", 43}, {"DUAP", 44}, {"DIR", 45}, {"DBP", 46}, {"DPPU", 47}, {"DPPL", 48}, {"DSR", 49}, {"DLAR", 50}, {"DPAR", 51}, {NULL, 0}, }; struct field_val_assoc fcr_regs[] = { {"FPECR", 0}, {"FPHS1", 1}, {"FPLS1", 2}, {"FPHS2", 3}, {"FPLS2", 4}, {"FPPT", 5}, {"FPRH", 6}, {"FPRL", 7}, {"FPIT", 8}, {"FPSR", 62}, {"FPCR", 63}, {NULL, 0}, }; struct field_val_assoc cmpslot[] = { /* Integer Floating point */ {"nc", 0}, {"cp", 1}, {"eq", 2}, {"ne", 3}, {"gt", 4}, {"le", 5}, {"lt", 6}, {"ge", 7}, {"hi", 8}, {"ou", 8}, {"ls", 9}, {"ib", 9}, {"lo", 10}, {"in", 10}, {"hs", 11}, {"ob", 11}, {"be", 12}, {"ue", 12}, {"nb", 13}, {"lg", 13}, {"he", 14}, {"ug", 14}, {"nh", 15}, {"ule", 15}, {"ul", 16}, {"uge", 17}, {NULL, 0}, }; struct field_val_assoc cndmsk[] = { {"gt0", 1}, {"eq0", 2}, {"ge0", 3}, {"lt0", 12}, {"ne0", 13}, {"le0", 14}, {NULL, 0}, }; struct m88k_insn { unsigned long opcode; expressionS exp; enum m88k_reloc_type reloc; }; static char *get_bf (char *param, unsigned *valp); static char *get_cmp (char *param, unsigned *valp); static char *get_cnd (char *param, unsigned *valp); static char *get_bf2 (char *param, int bc); static char *get_bf_offset_expression (char *param, unsigned *offsetp); static char *get_cr (char *param, unsigned *regnop); static char *get_fcr (char *param, unsigned *regnop); static char *get_imm16 (char *param, struct m88k_insn *insn); static char *get_o6 (char *param, unsigned *valp); static char *match_name (char *, struct field_val_assoc *, unsigned *); static char *get_reg (char *param, unsigned *regnop, unsigned int reg_prefix); static char *get_vec9 (char *param, unsigned *valp); static char *getval (char *param, unsigned int *valp); static char *get_pcr (char *param, struct m88k_insn *insn, enum m88k_reloc_type reloc); static int calcop (struct m88k_opcode *format, char *param, struct m88k_insn *insn); static void s_m88k_88110 (int); static struct hash_control *op_hash = NULL; /* Current cpu (either 88100 or 88110, or 0 if unspecified). Defaults to zero, overriden with -m options or assembler pseudo-ops. */ static int current_cpu = 0; /* These chars start a comment anywhere in a source file (except inside another comment. */ #if defined(OBJ_ELF) const char comment_chars[] = "|"; #elif defined(OBJ_AOUT) const char comment_chars[] = "|#"; #else const char comment_chars[] = ";"; #endif /* These chars only start a comment at the beginning of a line. */ #if defined(OBJ_AOUT) const char line_comment_chars[] = ";"; #else const char line_comment_chars[] = "#"; #endif #if defined(OBJ_ELF) const char line_separator_chars[] = ";"; #else const char line_separator_chars[] = ""; #endif /* Chars that can be used to separate mant from exp in floating point nums */ const char EXP_CHARS[] = "eE"; /* Chars that mean this number is a floating point constant */ /* as in 0f123.456 */ /* or 0H1.234E-12 (see exp chars above) */ const char FLT_CHARS[] = "dDfF"; const pseudo_typeS md_pseudo_table[] = { #ifndef OBJ_ELF {"align", s_align_bytes, 4}, #else /* handled with s_align_ptwo in read.c potable[] */ #endif {"bss", s_lcomm, 1}, {"def", s_set, 0}, {"half", cons, 2}, {"requires_88110", s_m88k_88110, 0}, {"sbss", s_lcomm, 1}, #if !defined(OBJ_ELF) || !defined(TE_OpenBSD) /* i.e. NO_PSEUDO_DOT == 1 */ /* Force set to be treated as an instruction. */ {"set", NULL, 0}, {".set", s_set, 0}, #endif {"uahalf", cons, 2}, {"uaword", cons, 4}, {"word", cons, 4}, /* override potable[] which has word == short */ {NULL, NULL, 0} }; static void s_m88k_88110(int i ATTRIBUTE_UNUSED) { current_cpu = 88110; } void md_begin (void) { const char *retval = NULL; unsigned int i = 0; /* Initialize hash table. */ op_hash = hash_new (); while (*m88k_opcodes[i].name) { char *name = m88k_opcodes[i].name; /* Hash each mnemonic and record its position. */ retval = hash_insert (op_hash, name, &m88k_opcodes[i]); if (retval != NULL) as_fatal (_("Can't hash instruction '%s':%s"), m88k_opcodes[i].name, retval); /* Skip to next unique mnemonic or end of list. */ for (i++; !strcmp (m88k_opcodes[i].name, name); i++) ; } #ifdef OBJ_ELF record_alignment (text_section, 2); record_alignment (data_section, 2); record_alignment (bss_section, 2); bfd_set_private_flags (stdoutput, 0); #endif } const char *md_shortopts = "m:"; struct option md_longopts[] = { {NULL, no_argument, NULL, 0} }; size_t md_longopts_size = sizeof (md_longopts); int md_parse_option (int c, char *arg) { switch (c) { case 'm': if (strcmp (arg, "88100") == 0) current_cpu = 88100; else if (strcmp (arg, "88110") == 0) current_cpu = 88110; else as_bad (_("Option `%s' is not recognized."), arg); break; default: return 0; } return 1; } void md_show_usage (FILE *stream) { fputs (_("\ M88k options:\n\ -m88100 | -m88110 select processor type\n"), stream); } #ifdef OBJ_ELF enum m88k_pic_reloc_type { pic_reloc_none, pic_reloc_abdiff, pic_reloc_gotrel, pic_reloc_plt }; static bfd_reloc_code_real_type m88k_get_reloc_code(struct m88k_insn *insn) { switch (insn->exp.X_md) { default: case pic_reloc_none: return insn->reloc; case pic_reloc_abdiff: if (insn->reloc == BFD_RELOC_LO16) return BFD_RELOC_LO16_BASEREL; if (insn->reloc == BFD_RELOC_HI16) return BFD_RELOC_HI16_BASEREL; break; case pic_reloc_gotrel: if (insn->reloc == BFD_RELOC_LO16) return BFD_RELOC_LO16_GOTOFF; if (insn->reloc == BFD_RELOC_HI16) return BFD_RELOC_HI16_GOTOFF; break; case pic_reloc_plt: if (insn->reloc == BFD_RELOC_32) return BFD_RELOC_32_PLTOFF; if (insn->reloc == BFD_RELOC_28_PCREL_S2) return BFD_RELOC_32_PLT_PCREL; break; } as_bad ("Can't process pic type %d relocation type %d", insn->exp.X_md, insn->reloc); return BFD_RELOC_NONE; } #else #define m88k_get_reloc_code(insn) (insn).reloc #endif void md_assemble (char *op) { char *param, *thisfrag; char c; struct m88k_opcode *format; struct m88k_insn insn; fixS *fixP; assert (op); /* Skip over instruction to find parameters. */ for (param = op; *param != 0 && !ISSPACE (*param); param++) ; c = *param; *param++ = '\0'; /* Try to find the instruction in the hash table. */ /* XXX will not match XRF flavours of 88100 instructions on 88110 */ if ((format = (struct m88k_opcode *) hash_find (op_hash, op)) == NULL) { as_bad (_("Invalid mnemonic '%s'"), op); return; } /* Try parsing this instruction into insn. */ insn.exp.X_add_symbol = 0; insn.exp.X_op_symbol = 0; insn.exp.X_add_number = 0; insn.exp.X_op = O_illegal; insn.exp.X_md = pic_reloc_none; insn.reloc = NO_RELOC; while (!calcop (format, param, &insn)) { /* If it doesn't parse try the next instruction. */ if (!strcmp (format[0].name, format[1].name)) format++; else { as_fatal (_("Parameter syntax error")); return; } } /* Grow the current frag and plop in the opcode. */ thisfrag = frag_more (4); md_number_to_chars (thisfrag, insn.opcode, 4); /* If this instruction requires labels mark it for later. */ switch (insn.reloc) { case NO_RELOC: break; case RELOC_LO16: case RELOC_HI16: fixP = fix_new_exp (frag_now, thisfrag - frag_now->fr_literal + 2, 2, &insn.exp, 0, m88k_get_reloc_code(&insn)); fixP->fx_no_overflow = 1; break; #ifdef M88KCOFF case RELOC_IW16: fix_new_exp (frag_now, thisfrag - frag_now->fr_literal, 4, &insn.exp, 0, m88k_get_reloc_code(&insn)); break; #endif case RELOC_PC16: #ifdef OBJ_ELF fix_new_exp (frag_now, thisfrag - frag_now->fr_literal , 4, &insn.exp, 1, m88k_get_reloc_code(&insn)); #else fix_new_exp (frag_now, thisfrag - frag_now->fr_literal + 2, 2, &insn.exp, 1, m88k_get_reloc_code(&insn)); #endif break; case RELOC_PC26: fix_new_exp (frag_now, thisfrag - frag_now->fr_literal, 4, &insn.exp, 1, m88k_get_reloc_code(&insn)); break; case RELOC_32: fix_new_exp (frag_now, thisfrag - frag_now->fr_literal, 4, &insn.exp, 0, m88k_get_reloc_code(&insn)); break; default: as_fatal (_("Unknown relocation type")); break; } } static int calcop (struct m88k_opcode *format, char *param, struct m88k_insn *insn) { char *fmt = format->op_spec; int f; unsigned val; unsigned opcode; unsigned int reg_prefix = 'r'; insn->opcode = format->opcode; opcode = 0; /* * Instructions which have no arguments (such as rte) will get * correctly reported only if param == "", although there could be * whitespace following the instruction. * Rather than eating whitespace here, let's assume everything is * fine. If there were non-wanted arguments, they will be parsed as * an incorrect opcode at the offending line, so that's not too bad. * -- miod */ if (*fmt == '\0') return 1; for (;;) { if (param == NULL) return 0; f = *fmt++; switch (f) { case 0: insn->opcode |= opcode; return (*param == 0 || *param == '\n'); default: if (f != *param++) return 0; break; case 'd': param = get_reg (param, &val, reg_prefix); reg_prefix = 'r'; opcode |= val << 21; break; case 'o': param = get_o6 (param, &val); opcode |= ((val >> 2) << 7); break; case 'x': reg_prefix = 'x'; break; case '1': param = get_reg (param, &val, reg_prefix); reg_prefix = 'r'; opcode |= val << 16; break; case '2': param = get_reg (param, &val, reg_prefix); reg_prefix = 'r'; opcode |= val; break; case '3': param = get_reg (param, &val, 'r'); opcode |= (val << 16) | val; break; case 'I': param = get_imm16 (param, insn); break; case 'b': param = get_bf (param, &val); opcode |= val; break; case 'p': param = get_pcr (param, insn, RELOC_PC16); break; case 'P': param = get_pcr (param, insn, RELOC_PC26); break; case 'B': param = get_cmp (param, &val); opcode |= val; break; case 'M': param = get_cnd (param, &val); opcode |= val; break; case 'c': param = get_cr (param, &val); opcode |= val << 5; break; case 'f': param = get_fcr (param, &val); opcode |= val << 5; break; case 'V': param = get_vec9 (param, &val); opcode |= val; break; case '?': /* Having this here repeats the warning sometimes. But can't we stand that? */ as_warn (_("Use of obsolete instruction")); break; } } } static char * match_name (char *param, struct field_val_assoc *assoc_tab, unsigned *valp) { int i; char *name; int name_len; for (i = 0;; i++) { name = assoc_tab[i].name; if (name == NULL) return NULL; name_len = strlen (name); if (!strncmp (param, name, name_len)) { *valp = assoc_tab[i].val; return param + name_len; } } } static char * get_reg (char *param, unsigned *regnop, unsigned int reg_prefix) { unsigned c; unsigned regno; #ifdef REGISTER_PREFIX c = *param++; if (c != REGISTER_PREFIX) return NULL; #endif c = *param++; if (c == reg_prefix) { regno = *param++ - '0'; if (regno < 10) { if (regno == 0) { *regnop = 0; return param; } c = *param - '0'; if (c < 10) { regno = regno * 10 + c; if (c < 32) { *regnop = regno; return param + 1; } } else { *regnop = regno; return param; } } return NULL; } else if (c == 's' && param[0] == 'p') { *regnop = 31; return param + 1; } return NULL; } static char * get_imm16 (char *param, struct m88k_insn *insn) { enum m88k_reloc_type reloc = NO_RELOC; unsigned int val; char *save_ptr; #ifdef REGISTER_PREFIX int found_prefix = 0; #endif #ifdef REGISTER_PREFIX if (*param == REGISTER_PREFIX) { param++; found_prefix = 1; } #endif if (!strncmp (param, "hi16", 4) && !ISALNUM (param[4])) { reloc = RELOC_HI16; param += 4; } else if (!strncmp (param, "lo16", 4) && !ISALNUM (param[4])) { reloc = RELOC_LO16; param += 4; } #ifdef M88KCOFF else if (!strncmp (param, "iw16", 4) && !ISALNUM (param[4])) { reloc = RELOC_IW16; param += 4; } #endif #ifdef REGISTER_PREFIX if (found_prefix && reloc == NO_RELOC) return NULL; #endif save_ptr = input_line_pointer; input_line_pointer = param; expression (&insn->exp); param = input_line_pointer; input_line_pointer = save_ptr; val = insn->exp.X_add_number; if (insn->exp.X_op == O_constant) { /* Insert the value now, and reset reloc to NO_RELOC. */ if (reloc == NO_RELOC) { /* Warn about too big expressions if not surrounded by xx16. */ if (val > 0xffff) as_warn (_("Expression truncated to 16 bits")); } if (reloc == RELOC_HI16) val >>= 16; insn->opcode |= val & 0xffff; reloc = NO_RELOC; } else if (reloc == NO_RELOC) /* We accept a symbol even without lo16, hi16, etc, and assume lo16 was intended. */ reloc = RELOC_LO16; insn->reloc = reloc; return param; } static char * get_pcr (char *param, struct m88k_insn *insn, enum m88k_reloc_type reloc) { char *saveptr, *saveparam; saveptr = input_line_pointer; input_line_pointer = param; expression (&insn->exp); saveparam = input_line_pointer; input_line_pointer = saveptr; /* Botch: We should relocate now if O_constant. */ insn->reloc = reloc; return saveparam; } static char * get_cmp (char *param, unsigned *valp) { unsigned int val; char *save_ptr; save_ptr = param; #ifdef REGISTER_PREFIX /* SVR4 compiler prefixes condition codes with the register prefix */ if (*param == REGISTER_PREFIX) param++; #endif param = match_name (param, cmpslot, valp); val = *valp; if (param == NULL) { param = save_ptr; save_ptr = input_line_pointer; input_line_pointer = param; val = get_absolute_expression (); param = input_line_pointer; input_line_pointer = save_ptr; if (val >= 32) { as_warn (_("Expression truncated to 5 bits")); val %= 32; } } *valp = val << 21; return param; } static char * get_cnd (char *param, unsigned *valp) { unsigned int val; if (ISDIGIT (*param)) { param = getval (param, &val); if (val >= 32) { as_warn (_("Expression truncated to 5 bits")); val %= 32; } } else { #ifdef REGISTER_PREFIX /* SVR4 compiler prefixes condition codes with the register prefix */ if (*param == REGISTER_PREFIX) param++; #endif param[0] = TOLOWER (param[0]); param[1] = TOLOWER (param[1]); param = match_name (param, cndmsk, valp); if (param == NULL) return NULL; val = *valp; } *valp = val << 21; return param; } static char * get_bf2 (char *param, int bc) { int depth = 0; int c; for (;;) { c = *param; if (c == 0) return param; else if (c == '(') depth++; else if (c == ')') depth--; else if (c == bc && depth <= 0) return param; param++; } } static char * get_bf_offset_expression (char *param, unsigned *offsetp) { unsigned offset; #ifdef REGISTER_PREFIX /* SVR4 compiler prefixes condition codes with the register prefix */ if (*param == REGISTER_PREFIX && ISALPHA (param[1])) param++; #endif if (ISALPHA (param[0])) { param[0] = TOLOWER (param[0]); param[1] = TOLOWER (param[1]); param = match_name (param, cmpslot, offsetp); return param; } else { input_line_pointer = param; offset = get_absolute_expression (); param = input_line_pointer; } *offsetp = offset; return param; } static char * get_bf (char *param, unsigned *valp) { unsigned offset = 0; unsigned width = 0; char *xp; char *save_ptr; xp = get_bf2 (param, '<'); save_ptr = input_line_pointer; input_line_pointer = param; if (*xp == 0) { /* We did not find '<'. We have an offset (width implicitly 32). */ param = get_bf_offset_expression (param, &offset); input_line_pointer = save_ptr; if (param == NULL) return NULL; } else { *xp++ = 0; /* Overwrite the '<' */ param = get_bf2 (xp, '>'); if (*param == 0) return NULL; *param++ = 0; /* Overwrite the '>' */ width = get_absolute_expression (); xp = get_bf_offset_expression (xp, &offset); input_line_pointer = save_ptr; if (xp + 1 != param) return NULL; } *valp = ((width % 32) << 5) | (offset % 32); return param; } static char * get_cr (char *param, unsigned *regnop) { unsigned regno; unsigned c; #ifdef REGISTER_PREFIX if (*param++ != REGISTER_PREFIX) return NULL; #endif if (!strncmp (param, "cr", 2)) { param += 2; regno = *param++ - '0'; if (regno < 10) { if (regno == 0) { *regnop = 0; return param; } c = *param - '0'; if (c < 10) { regno = regno * 10 + c; if (c < 64) { *regnop = regno; return param + 1; } } else { *regnop = regno; return param; } } return NULL; } param = match_name (param, current_cpu == 88110 ? m88110_cr_regs : m88100_cr_regs, regnop); return param; } static char * get_fcr (char *param, unsigned *regnop) { unsigned regno; unsigned c; #ifdef REGISTER_PREFIX if (*param++ != REGISTER_PREFIX) return NULL; #endif if (!strncmp (param, "fcr", 3)) { param += 3; regno = *param++ - '0'; if (regno < 10) { if (regno == 0) { *regnop = 0; return param; } c = *param - '0'; if (c < 10) { regno = regno * 10 + c; if (c < 64) { *regnop = regno; return param + 1; } } else { *regnop = regno; return param; } } return NULL; } param = match_name (param, fcr_regs, regnop); return param; } static char * get_vec9 (char *param, unsigned *valp) { unsigned val; char *save_ptr; save_ptr = input_line_pointer; input_line_pointer = param; val = get_absolute_expression (); param = input_line_pointer; input_line_pointer = save_ptr; if (val >= 1 << 9) as_warn (_("Expression truncated to 9 bits")); *valp = val % (1 << 9); return param; } static char * get_o6 (char *param, unsigned *valp) { unsigned val; char *save_ptr; save_ptr = input_line_pointer; input_line_pointer = param; val = get_absolute_expression (); param = input_line_pointer; input_line_pointer = save_ptr; if (val & 0x3) as_warn (_("Removed lower 2 bits of expression")); *valp = val; return (param); } #define hexval(z) \ (ISDIGIT (z) ? (z) - '0' : \ ISLOWER (z) ? (z) - 'a' + 10 : \ ISUPPER (z) ? (z) - 'A' + 10 : (unsigned) -1) static char * getval (char *param, unsigned int *valp) { unsigned int val = 0; unsigned int c; c = *param++; if (c == '0') { c = *param++; if (c == 'x' || c == 'X') { c = *param++; c = hexval (c); while (c < 16) { val = val * 16 + c; c = *param++; c = hexval (c); } } else { c -= '0'; while (c < 8) { val = val * 8 + c; c = *param++ - '0'; } } } else { c -= '0'; while (c < 10) { val = val * 10 + c; c = *param++ - '0'; } } *valp = val; return param - 1; } void md_number_to_chars (char *buf, valueT val, int nbytes) { number_to_chars_bigendian (buf, val, nbytes); } #define MAX_LITTLENUMS 6 /* Turn a string in input_line_pointer into a floating point constant of type type, and store the appropriate bytes in *litP. The number of LITTLENUMS emitted is stored in *sizeP . An error message is returned, or NULL on OK. */ char * md_atof (int type, char *litP, int *sizeP) { int prec; LITTLENUM_TYPE words[MAX_LITTLENUMS]; LITTLENUM_TYPE *wordP; char *t; switch (type) { case 'f': case 'F': case 's': case 'S': prec = 2; break; case 'd': case 'D': case 'r': case 'R': prec = 4; break; case 'x': case 'X': prec = 6; break; case 'p': case 'P': prec = 6; break; default: *sizeP = 0; return _("Bad call to MD_ATOF()"); } t = atof_ieee (input_line_pointer, type, words); if (t) input_line_pointer = t; *sizeP = prec * sizeof (LITTLENUM_TYPE); for (wordP = words; prec--;) { md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE)); litP += sizeof (LITTLENUM_TYPE); } return 0; } int md_short_jump_size = 4; int md_long_jump_size = 4; void md_create_short_jump (char *ptr, addressT from_addr ATTRIBUTE_UNUSED, addressT to_addr ATTRIBUTE_UNUSED, fragS *frag, symbolS *to_symbol) { /* Since all instructions have the same width, it does not make sense to try and abuse a conditional instruction to get a short displacement (such as bb1 0, %r0, address). */ md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol); } void md_create_long_jump (char *ptr, addressT from_addr ATTRIBUTE_UNUSED, addressT to_addr ATTRIBUTE_UNUSED, fragS *frag, symbolS *to_symbol) { ptr[0] = (char) 0xc0; /* br to_addr */ ptr[1] = 0x00; ptr[2] = 0x00; ptr[3] = 0x00; fix_new (frag, ptr - frag->fr_literal, 4, to_symbol, (offsetT) 0, 0, RELOC_PC26); } int md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED, segT segment_type ATTRIBUTE_UNUSED) { as_fatal (_("Relaxation should never occur")); return (-1); } #ifdef M88KCOFF /* These functions are needed if we are linking with obj-coffbfd.c. That file may be replaced by a more BFD oriented version at some point. If that happens, these functions should be reexamined. Ian Lance Taylor, Cygnus Support, 13 July 1993. */ /* Given a fixS structure (created by a call to fix_new, above), return the BFD relocation type to use for it. */ short tc_coff_fix2rtype (fixS *fixp) { switch (fixp->fx_r_type) { case RELOC_LO16: return R_LVRT16; case RELOC_HI16: return R_HVRT16; case RELOC_PC16: return R_PCR16L; case RELOC_PC26: return R_PCR26L; case RELOC_32: return R_VRT32; case RELOC_IW16: return R_VRT16; default: abort (); } } /* Apply a fixS to the object file. Since COFF does not use addends in relocs, the addend is actually stored directly in the object file itself. */ void md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) { long val = * (long *) valP; char *buf; buf = fixP->fx_frag->fr_literal + fixP->fx_where; fixP->fx_addnumber = val; fixP->fx_offset = 0; switch (fixP->fx_r_type) { case RELOC_IW16: fixP->fx_offset = val >> 16; buf[2] = val >> 8; buf[3] = val; break; case RELOC_LO16: fixP->fx_offset = val >> 16; buf[0] = val >> 8; buf[1] = val; break; case RELOC_HI16: buf[0] = val >> 24; buf[1] = val >> 16; break; case RELOC_PC16: buf[0] = val >> 10; buf[1] = val >> 2; break; case RELOC_PC26: buf[0] |= (val >> 26) & 0x03; buf[1] = val >> 18; buf[2] = val >> 10; buf[3] = val >> 2; break; case RELOC_32: buf[0] = val >> 24; buf[1] = val >> 16; buf[2] = val >> 8; buf[3] = val; break; default: abort (); } if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0) fixP->fx_done = 1; } #endif /* M88KCOFF */ /* Fill in rs_align_code fragments. */ void m88k_handle_align (fragS *fragp) { static const unsigned char nop_pattern[] = { 0xf4, 0x00, 0x58, 0x00 }; int bytes; char *p; if (fragp->fr_type != rs_align_code) return; bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; p = fragp->fr_literal + fragp->fr_fix; if (bytes & 3) { int fix = bytes & 3; memset (p, 0, fix); p += fix; bytes -= fix; fragp->fr_fix += fix; } memcpy (p, nop_pattern, 4); fragp->fr_var = 4; } /* Where a PC relative offset is calculated from. On the m88k they are calculated from just after the instruction. */ long md_pcrel_from (fixS *fixp) { switch (fixp->fx_r_type) { case RELOC_PC16: #ifdef OBJ_ELF /* FALLTHROUGH */ #else return fixp->fx_frag->fr_address + fixp->fx_where - 2; #endif case RELOC_PC26: #ifdef OBJ_ELF case BFD_RELOC_32_PLT_PCREL: #endif return fixp->fx_frag->fr_address + fixp->fx_where; default: abort (); } /*NOTREACHED*/ } #ifdef OBJ_ELF valueT md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size) { return size; } /* Generate the BFD reloc to be stuck in the object file from the fixup used internally in the assembler. */ arelent * tc_gen_reloc (asection *sec ATTRIBUTE_UNUSED, fixS *fixp) { arelent *reloc; bfd_reloc_code_real_type code; reloc = (arelent *) xmalloc (sizeof (arelent)); reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; /* Make sure none of our internal relocations make it this far. They'd better have been fully resolved by this point. */ assert ((int) fixp->fx_r_type > 0); code = fixp->fx_r_type; reloc->howto = bfd_reloc_type_lookup (stdoutput, code); if (reloc->howto == NULL) { as_bad_where (fixp->fx_file, fixp->fx_line, _("cannot represent `%s' relocation in object file"), bfd_get_reloc_code_name (code)); return NULL; } if (!fixp->fx_pcrel != !reloc->howto->pc_relative) { as_fatal (_("internal error? cannot generate `%s' relocation"), bfd_get_reloc_code_name (code)); } assert (!fixp->fx_pcrel == !reloc->howto->pc_relative); reloc->addend = fixp->fx_offset; return reloc; } /* Apply a fixS to the object file. This is called for all the fixups we generated by the call to fix_new_exp, above. In the call above we used a reloc code which was the largest legal reloc code plus the operand index. Here we undo that to recover the operand index. At this point all symbol values should be fully resolved, and we attempt to completely resolve the reloc. If we can not do that, we determine the correct reloc code and put it back in the fixup. This is the ELF version. */ void md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) { valueT val = * (valueT *) valP; char *buf; long insn; buf = fixP->fx_frag->fr_literal + fixP->fx_where; if (fixP->fx_subsy != NULL) as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex")); if (fixP->fx_addsy) { #if 0 /* can't empty 26-bit relocation values with memset() */ if (fixP->fx_r_type == BFD_RELOC_28_PCREL_S2) { insn = bfd_getb32 ((unsigned char *) buf); insn &= ~0x03ffffff; bfd_putb32(insn, buf); } else memset(buf, 0, fixP->fx_size); #endif if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT && !S_IS_DEFINED (fixP->fx_addsy) && !S_IS_WEAK (fixP->fx_addsy)) S_SET_WEAK (fixP->fx_addsy); return; } switch (fixP->fx_r_type) { case BFD_RELOC_VTABLE_INHERIT: case BFD_RELOC_VTABLE_ENTRY: return; case BFD_RELOC_HI16_BASEREL: case BFD_RELOC_LO16_BASEREL: case BFD_RELOC_HI16_GOTOFF: case BFD_RELOC_LO16_GOTOFF: case BFD_RELOC_32_PLTOFF: return; case BFD_RELOC_LO16: case BFD_RELOC_HI16: if (fixP->fx_pcrel) abort (); buf[0] = val >> 8; buf[1] = val; break; case BFD_RELOC_18_PCREL_S2: if ((val & 0x03) != 0) as_bad_where (fixP->fx_file, fixP->fx_line, "Branch to unaligned address (%lx)", (long)val); buf[2] = val >> 10; buf[3] = val >> 2; break; case BFD_RELOC_32_PLT_PCREL: case BFD_RELOC_28_PCREL_S2: if ((val & 0x03) != 0) as_bad_where (fixP->fx_file, fixP->fx_line, "Branch to unaligned address (%lx)", (long)val); buf[0] |= (val >> 26) & 0x03; buf[1] = val >> 18; buf[2] = val >> 10; buf[3] = val >> 2; break; case BFD_RELOC_32: insn = val; bfd_putb32(insn, buf); break; default: abort (); } if (/* fixP->fx_addsy == NULL && */ fixP->fx_pcrel == 0) fixP->fx_done = 1; } /* Set the ELF specific flags. */ void m88k_elf_final_processing (void) { if (current_cpu == 88110) elf_elfheader (stdoutput)->e_flags |= EF_M88110; } inline static char * m88k_end_of_name (char *suffix, const char *pattern, size_t patlen) { if (strncmp (suffix, pattern, patlen) == 0 && ! is_part_of_name (suffix[patlen])) return suffix + patlen; return NULL; } int m88k_parse_name (const char *name, expressionS *expressionP, char *nextcharP) { char *next = input_line_pointer; char *next_end; enum m88k_pic_reloc_type reloc_type = pic_reloc_none; symbolS *symbolP; segT segment; if (*nextcharP != '#') return 0; if ((next_end = m88k_end_of_name (next + 1, "abdiff", 6)) != NULL) { reloc_type = pic_reloc_abdiff; } else if ((next_end = m88k_end_of_name (next + 1, "got_rel", 7)) != NULL) { reloc_type = pic_reloc_gotrel; } else if ((next_end = m88k_end_of_name (next + 1, "plt", 3)) != NULL) { reloc_type = pic_reloc_plt; } else return 0; symbolP = symbol_find_or_make (name); segment = S_GET_SEGMENT (symbolP); if (segment == absolute_section) { expressionP->X_op = O_constant; expressionP->X_add_number = S_GET_VALUE (symbolP); } else if (segment == reg_section) { expressionP->X_op = O_register; expressionP->X_add_number = S_GET_VALUE (symbolP); } else { expressionP->X_op = O_symbol; expressionP->X_add_symbol = symbolP; expressionP->X_add_number = 0; } expressionP->X_md = reloc_type; *input_line_pointer = *nextcharP; input_line_pointer = next_end; *nextcharP = *input_line_pointer; *input_line_pointer = '\0'; return 1; } int m88k_fix_adjustable (fixS *fix) { return (fix->fx_r_type != BFD_RELOC_LO16_GOTOFF && fix->fx_r_type != BFD_RELOC_HI16_GOTOFF && fix->fx_r_type != BFD_RELOC_VTABLE_INHERIT && fix->fx_r_type != BFD_RELOC_VTABLE_ENTRY && (fix->fx_pcrel || (fix->fx_subsy != NULL && (S_GET_SEGMENT (fix->fx_subsy) == S_GET_SEGMENT (fix->fx_addsy))) || S_IS_LOCAL (fix->fx_addsy))); } #endif /* OBJ_ELF */ #ifdef OBJ_AOUT /* Round up a section size to the appropriate boundary. */ valueT md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size) { /* For a.out, force the section size to be aligned. If we don't do this, BFD will align it for us, but it will not write out the final bytes of the section. This may be a bug in BFD, but it is easier to fix it here since that is how the other a.out targets work. */ int align; align = bfd_get_section_alignment (stdoutput, segment); valueT mask = ((valueT) 1 << align) - 1; return (size + mask) & ~mask; } const int md_reloc_size = 12; /* sizeof(struct relocation_info); */ void tc_aout_fix_to_chars (char *where, fixS *fixP, relax_addressT segment_address_in_file) { long r_symbolnum; long r_addend = 0; long r_address; know (fixP->fx_addsy != NULL); r_address = fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file; md_number_to_chars (where, r_address, 4); r_symbolnum = (S_IS_DEFINED (fixP->fx_addsy) ? S_GET_TYPE (fixP->fx_addsy) : fixP->fx_addsy->sy_number); where[4] = (r_symbolnum >> 16) & 0x0ff; where[5] = (r_symbolnum >> 8) & 0x0ff; where[6] = r_symbolnum & 0x0ff; where[7] = ((((!S_IS_DEFINED (fixP->fx_addsy)) << 7) & 0x80) | (0 & 0x70) | (fixP->fx_r_type & 0xf)); if (fixP->fx_addsy->sy_frag) { r_addend = fixP->fx_addsy->sy_frag->fr_address; } if (fixP->fx_pcrel) { r_addend -= r_address; } else { r_addend = fixP->fx_addnumber; } md_number_to_chars(&where[8], r_addend, 4); } void tc_headers_hook (object_headers *headers) { #if defined(TE_NetBSD) || defined(TE_OpenBSD) N_SET_INFO(headers->header, OMAGIC, M_88K_OPENBSD, 0); headers->header.a_info = htonl(headers->header.a_info); #endif } #endif /* OBJ_AOUT */