1  /* tc-pj.c -- Assemble code for Pico Java
       2     Copyright (C) 1999-2023 Free Software Foundation, Inc.
       3  
       4     This file is part of GAS, the GNU Assembler.
       5  
       6     GAS is free software; you can redistribute it and/or modify
       7     it under the terms of the GNU General Public License as published by
       8     the Free Software Foundation; either version 3, or (at your option)
       9     any later version.
      10  
      11     GAS is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU General Public License for more details.
      15  
      16     You should have received a copy of the GNU General Public License
      17     along with GAS; see the file COPYING.  If not, write to
      18     the Free Software Foundation, 51 Franklin Street - Fifth Floor,
      19     Boston, MA 02110-1301, USA.  */
      20  
      21  /* Contributed by Steve Chamberlain of Transmeta <sac@pobox.com>.  */
      22  
      23  #include "as.h"
      24  #include "safe-ctype.h"
      25  #include "opcode/pj.h"
      26  
      27  extern const pj_opc_info_t pj_opc_info[512];
      28  
      29  const char comment_chars[]        = "!/";
      30  const char line_separator_chars[] = ";";
      31  const char line_comment_chars[]   = "/!#";
      32  
      33  static int pending_reloc;
      34  static htab_t opcode_hash_control;
      35  
      36  static void
      37  little (int ignore ATTRIBUTE_UNUSED)
      38  {
      39    target_big_endian = 0;
      40  }
      41  
      42  static void
      43  big (int ignore ATTRIBUTE_UNUSED)
      44  {
      45    target_big_endian = 1;
      46  }
      47  
      48  const pseudo_typeS md_pseudo_table[] =
      49  {
      50    {"ml",    little, 0},
      51    {"mb",    big,    0},
      52    {0, 0, 0}
      53  };
      54  
      55  const char FLT_CHARS[] = "rRsSfFdDxXpP";
      56  const char EXP_CHARS[] = "eE";
      57  
      58  void
      59  md_operand (expressionS *op)
      60  {
      61    if (startswith (input_line_pointer, "%hi16"))
      62      {
      63        if (pending_reloc)
      64  	as_bad (_("confusing relocation expressions"));
      65        pending_reloc = BFD_RELOC_PJ_CODE_HI16;
      66        input_line_pointer += 5;
      67        expression (op);
      68      }
      69  
      70    if (startswith (input_line_pointer, "%lo16"))
      71      {
      72        if (pending_reloc)
      73  	as_bad (_("confusing relocation expressions"));
      74        pending_reloc = BFD_RELOC_PJ_CODE_LO16;
      75        input_line_pointer += 5;
      76        expression (op);
      77      }
      78  }
      79  
      80  /* Parse an expression and then restore the input line pointer.  */
      81  
      82  static char *
      83  parse_exp_save_ilp (char *s, expressionS *op)
      84  {
      85    char *save = input_line_pointer;
      86  
      87    input_line_pointer = s;
      88    expression (op);
      89    s = input_line_pointer;
      90    input_line_pointer = save;
      91    return s;
      92  }
      93  
      94  /* This is called by emit_expr via TC_CONS_FIX_NEW when creating a
      95     reloc for a cons.  We could use the definition there, except that
      96     we want to handle magic pending reloc expressions specially.  */
      97  
      98  void
      99  pj_cons_fix_new_pj (fragS *frag, int where, int nbytes, expressionS *exp,
     100  		    bfd_reloc_code_real_type r ATTRIBUTE_UNUSED)
     101  {
     102    static int rv[5][2] =
     103    { { 0, 0 },
     104      { BFD_RELOC_8, BFD_RELOC_8 },
     105      { BFD_RELOC_PJ_CODE_DIR16, BFD_RELOC_16 },
     106      { 0, 0 },
     107      { BFD_RELOC_PJ_CODE_DIR32, BFD_RELOC_32 }};
     108  
     109    fix_new_exp (frag, where, nbytes, exp, 0,
     110  	       pending_reloc ? pending_reloc
     111  	       : rv[nbytes][(now_seg->flags & SEC_CODE) ? 0 : 1]);
     112  
     113    pending_reloc = 0;
     114  }
     115  
     116  /* Turn a reloc description character from the pj-opc.h table into
     117     code which BFD can handle.  */
     118  
     119  static int
     120  c_to_r (int x)
     121  {
     122    switch (x)
     123      {
     124      case O_R8:
     125        return BFD_RELOC_8_PCREL;
     126      case O_U8:
     127      case O_8:
     128        return BFD_RELOC_8;
     129      case O_R16:
     130        return BFD_RELOC_PJ_CODE_REL16;
     131      case O_U16:
     132      case O_16:
     133        return BFD_RELOC_PJ_CODE_DIR16;
     134      case O_R32:
     135        return BFD_RELOC_PJ_CODE_REL32;
     136      case O_32:
     137        return BFD_RELOC_PJ_CODE_DIR32;
     138      }
     139    abort ();
     140    return 0;
     141  }
     142  
     143  /* Handler for the ipush fake opcode,
     144     turns ipush <foo> into sipush lo16<foo>, sethi hi16<foo>.  */
     145  
     146  static void
     147  ipush_code (pj_opc_info_t *opcode ATTRIBUTE_UNUSED, char *str)
     148  {
     149    char *b = frag_more (6);
     150    expressionS arg;
     151  
     152    b[0] = 0x11;
     153    b[3] = 0xed;
     154    parse_exp_save_ilp (str + 1, &arg);
     155    if (pending_reloc)
     156      {
     157        as_bad (_("can't have relocation for ipush"));
     158        pending_reloc = 0;
     159      }
     160  
     161    fix_new_exp (frag_now, b - frag_now->fr_literal + 1, 2,
     162  	       &arg, 0, BFD_RELOC_PJ_CODE_DIR16);
     163    fix_new_exp (frag_now, b - frag_now->fr_literal + 4, 2,
     164  	       &arg, 0, BFD_RELOC_PJ_CODE_HI16);
     165  }
     166  
     167  /* Insert names into the opcode table which are really mini macros,
     168     not opcodes.  The fakeness is indicated with an opcode of -1.  */
     169  
     170  static void
     171  fake_opcode (const char *name,
     172  	     void (*func) (struct pj_opc_info_t *, char *))
     173  {
     174    pj_opc_info_t * fake = XNEW (pj_opc_info_t);
     175  
     176    fake->opcode = -1;
     177    fake->opcode_next = -1;
     178    fake->u.func = func;
     179    str_hash_insert (opcode_hash_control, name, fake, 0);
     180  }
     181  
     182  /* Enter another entry into the opcode hash table so the same opcode
     183     can have another name.  */
     184  
     185  static void
     186  alias (const char *new_name, const char *old)
     187  {
     188    str_hash_insert (opcode_hash_control, new_name,
     189  		   str_hash_find (opcode_hash_control, old), 0);
     190  }
     191  
     192  /* This function is called once, at assembler startup time.  It sets
     193     up the hash table with all the opcodes in it, and also initializes
     194     some aliases for compatibility with other assemblers.  */
     195  
     196  void
     197  md_begin (void)
     198  {
     199    const pj_opc_info_t *opcode;
     200    opcode_hash_control = str_htab_create ();
     201  
     202    /* Insert names into hash table.  */
     203    for (opcode = pj_opc_info; opcode->u.name; opcode++)
     204      str_hash_insert (opcode_hash_control, opcode->u.name, opcode, 0);
     205  
     206    /* Insert the only fake opcode.  */
     207    fake_opcode ("ipush", ipush_code);
     208  
     209    /* Add some aliases for opcode names.  */
     210    alias ("ifeq_s", "ifeq");
     211    alias ("ifne_s", "ifne");
     212    alias ("if_icmpge_s", "if_icmpge");
     213    alias ("if_icmpne_s", "if_icmpne");
     214    alias ("if_icmpeq_s", "if_icmpeq");
     215    alias ("if_icmpgt_s", "if_icmpgt");
     216    alias ("goto_s", "goto");
     217  
     218    bfd_set_arch_mach (stdoutput, TARGET_ARCH, 0);
     219  }
     220  
     221  /* This is the guts of the machine-dependent assembler.  STR points to
     222     a machine dependent instruction.  This function is supposed to emit
     223     the frags/bytes it assembles to.  */
     224  
     225  void
     226  md_assemble (char *str)
     227  {
     228    char *op_start;
     229    char *op_end;
     230  
     231    pj_opc_info_t *opcode;
     232    char *output;
     233    int idx = 0;
     234    char pend;
     235  
     236    int nlen = 0;
     237  
     238    /* Drop leading whitespace.  */
     239    while (*str == ' ')
     240      str++;
     241  
     242    /* Find the op code end.  */
     243    op_start = str;
     244    for (op_end = str;
     245         *op_end && !is_end_of_line[*op_end & 0xff] && *op_end != ' ';
     246         op_end++)
     247      nlen++;
     248  
     249    pend = *op_end;
     250    *op_end = 0;
     251  
     252    if (nlen == 0)
     253      as_bad (_("can't find opcode "));
     254  
     255    opcode = (pj_opc_info_t *) str_hash_find (opcode_hash_control, op_start);
     256    *op_end = pend;
     257  
     258    if (opcode == NULL)
     259      {
     260        as_bad (_("unknown opcode %s"), op_start);
     261        return;
     262      }
     263  
     264    dwarf2_emit_insn (0);
     265    if (opcode->opcode == -1)
     266      {
     267        /* It's a fake opcode.  Dig out the args and pretend that was
     268           what we were passed.  */
     269        (*opcode->u.func) (opcode, op_end);
     270      }
     271    else
     272      {
     273        unsigned int an;
     274  
     275        output = frag_more (opcode->len);
     276        output[idx++] = opcode->opcode;
     277  
     278        if (opcode->opcode_next != -1)
     279  	output[idx++] = opcode->opcode_next;
     280  
     281        for (an = 0; an < ARRAY_SIZE (opcode->arg) && opcode->arg[an]; an++)
     282  	{
     283  	  expressionS arg;
     284  
     285  	  if (*op_end == ',' && an != 0)
     286  	    op_end++;
     287  
     288  	  if (*op_end == 0)
     289  	    as_bad (_("expected expression"));
     290  
     291  	  op_end = parse_exp_save_ilp (op_end, &arg);
     292  
     293  	  fix_new_exp (frag_now,
     294  		       output - frag_now->fr_literal + idx,
     295  		       ASIZE (opcode->arg[an]),
     296  		       &arg,
     297  		       PCREL (opcode->arg[an]),
     298  		       pending_reloc ? pending_reloc : c_to_r (opcode->arg[an]));
     299  
     300  	  idx += ASIZE (opcode->arg[an]);
     301  	  pending_reloc = 0;
     302  	}
     303  
     304        while (ISSPACE (*op_end))
     305  	op_end++;
     306  
     307        if (*op_end != 0)
     308  	as_warn (_("extra stuff on line ignored"));
     309  
     310      }
     311  
     312    if (pending_reloc)
     313      as_bad (_("Something forgot to clean up\n"));
     314  }
     315  
     316  const char *
     317  md_atof (int type, char *litP, int *sizeP)
     318  {
     319    return ieee_md_atof (type, litP, sizeP, target_big_endian);
     320  }
     321  
     322  const char *md_shortopts = "";
     323  
     324  struct option md_longopts[] =
     325  {
     326  #define OPTION_LITTLE (OPTION_MD_BASE)
     327  #define OPTION_BIG    (OPTION_LITTLE + 1)
     328  
     329    {"little", no_argument, NULL, OPTION_LITTLE},
     330    {"big", no_argument, NULL, OPTION_BIG},
     331    {NULL, no_argument, NULL, 0}
     332  };
     333  size_t md_longopts_size = sizeof (md_longopts);
     334  
     335  int
     336  md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED)
     337  {
     338    switch (c)
     339      {
     340      case OPTION_LITTLE:
     341        little (0);
     342        break;
     343      case OPTION_BIG:
     344        big (0);
     345        break;
     346      default:
     347        return 0;
     348      }
     349    return 1;
     350  }
     351  
     352  void
     353  md_show_usage (FILE *stream)
     354  {
     355    fprintf (stream, _("\
     356  PJ options:\n\
     357  -little			generate little endian code\n\
     358  -big			generate big endian code\n"));
     359  }
     360  
     361  /* Apply a fixup to the object file.  */
     362  
     363  void
     364  md_apply_fix (fixS *fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED)
     365  {
     366    char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
     367    long val = *valP;
     368    long max, min;
     369  
     370    max = min = 0;
     371    switch (fixP->fx_r_type)
     372      {
     373      case BFD_RELOC_VTABLE_INHERIT:
     374      case BFD_RELOC_VTABLE_ENTRY:
     375        fixP->fx_done = 0;
     376        return;
     377  
     378      case BFD_RELOC_PJ_CODE_REL16:
     379        if (val < -0x8000 || val >= 0x7fff)
     380  	as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
     381        buf[0] |= (val >> 8) & 0xff;
     382        buf[1] = val & 0xff;
     383        break;
     384  
     385      case BFD_RELOC_PJ_CODE_HI16:
     386        *buf++ = val >> 24;
     387        *buf++ = val >> 16;
     388        fixP->fx_addnumber = val & 0xffff;
     389        break;
     390  
     391      case BFD_RELOC_PJ_CODE_DIR16:
     392      case BFD_RELOC_PJ_CODE_LO16:
     393        *buf++ = val >> 8;
     394        *buf++ = val >> 0;
     395  
     396        max = 0xffff;
     397        min = -0xffff;
     398        break;
     399  
     400      case BFD_RELOC_8:
     401        max = 0xff;
     402        min = -0xff;
     403        *buf++ = val;
     404        break;
     405  
     406      case BFD_RELOC_PJ_CODE_DIR32:
     407      case BFD_RELOC_PJ_CODE_REL32:
     408        *buf++ = val >> 24;
     409        *buf++ = val >> 16;
     410        *buf++ = val >> 8;
     411        *buf++ = val >> 0;
     412        break;
     413  
     414      case BFD_RELOC_32:
     415        if (target_big_endian)
     416  	{
     417  	  *buf++ = val >> 24;
     418  	  *buf++ = val >> 16;
     419  	  *buf++ = val >> 8;
     420  	  *buf++ = val >> 0;
     421  	}
     422        else
     423  	{
     424  	  *buf++ = val >> 0;
     425  	  *buf++ = val >> 8;
     426  	  *buf++ = val >> 16;
     427  	  *buf++ = val >> 24;
     428  	}
     429        break;
     430  
     431      case BFD_RELOC_16:
     432        if (target_big_endian)
     433  	{
     434  	  *buf++ = val >> 8;
     435  	  *buf++ = val >> 0;
     436  	}
     437        else
     438  	{
     439  	  *buf++ = val >> 0;
     440  	  *buf++ = val >> 8;
     441  	}
     442        break;
     443  
     444      default:
     445        abort ();
     446      }
     447  
     448    if (max != 0 && (val < min || val > max))
     449      as_bad_where (fixP->fx_file, fixP->fx_line, _("offset out of range"));
     450  
     451    if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
     452      fixP->fx_done = 1;
     453  }
     454  
     455  /* Put number into target byte order.  Always put values in an
     456     executable section into big endian order.  */
     457  
     458  void
     459  md_number_to_chars (char *ptr, valueT use, int nbytes)
     460  {
     461    if (target_big_endian || now_seg->flags & SEC_CODE)
     462      number_to_chars_bigendian (ptr, use, nbytes);
     463    else
     464      number_to_chars_littleendian (ptr, use, nbytes);
     465  }
     466  
     467  /* Translate internal representation of relocation info to BFD target
     468     format.  */
     469  
     470  arelent *
     471  tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
     472  {
     473    arelent *rel;
     474    bfd_reloc_code_real_type r_type;
     475  
     476    rel = XNEW (arelent);
     477    rel->sym_ptr_ptr = XNEW (asymbol *);
     478    *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
     479    rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
     480  
     481    r_type = fixp->fx_r_type;
     482    rel->addend = fixp->fx_addnumber;
     483    rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
     484  
     485    if (rel->howto == NULL)
     486      {
     487        as_bad_where (fixp->fx_file, fixp->fx_line,
     488  		    _("Cannot represent relocation type %s"),
     489  		    bfd_get_reloc_code_name (r_type));
     490        /* Set howto to a garbage value so that we can keep going.  */
     491        rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
     492        gas_assert (rel->howto != NULL);
     493      }
     494  
     495    return rel;
     496  }