1/* tc-wasm32.c -- Assembler code for the wasm32 target.
2
3   Copyright (C) 2017-2024 Free Software Foundation, Inc.
4
5   This file is part of GAS, the GNU Assembler.
6
7   GAS is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3, or (at your option)
10   any later version.
11
12   GAS is distributed in the hope that it will be useful, but WITHOUT
13   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15   License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with GAS; see the file COPYING.  If not, write to the Free
19   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
20   02110-1301, USA.  */
21
22#include "as.h"
23#include "safe-ctype.h"
24#include "subsegs.h"
25#include "dwarf2dbg.h"
26#include "dw2gencfi.h"
27#include "elf/wasm32.h"
28#include <float.h>
29
30enum wasm_class
31{
32  wasm_typed,			/* a typed opcode: block, loop, or if */
33  wasm_special,			/* a special opcode: unreachable, nop, else,
34				   or end */
35  wasm_break,			/* "br" */
36  wasm_break_if,		/* "br_if" opcode */
37  wasm_break_table,		/* "br_table" opcode */
38  wasm_return,			/* "return" opcode */
39  wasm_call,			/* "call" opcode */
40  wasm_call_indirect,		/* "call_indirect" opcode */
41  wasm_get_local,		/* "get_local" and "get_global" */
42  wasm_set_local,		/* "set_local" and "set_global" */
43  wasm_tee_local,		/* "tee_local" */
44  wasm_drop,			/* "drop" */
45  wasm_constant_i32,		/* "i32.const" */
46  wasm_constant_i64,		/* "i64.const" */
47  wasm_constant_f32,		/* "f32.const" */
48  wasm_constant_f64,		/* "f64.const" */
49  wasm_unary,			/* unary operators */
50  wasm_binary,			/* binary operators */
51  wasm_conv,			/* conversion operators */
52  wasm_load,			/* load operators */
53  wasm_store,			/* store operators */
54  wasm_select,			/* "select" */
55  wasm_relational,		/* comparison operators, except for "eqz" */
56  wasm_eqz,			/* "eqz" */
57  wasm_current_memory,		/* "current_memory" */
58  wasm_grow_memory,		/* "grow_memory" */
59  wasm_signature		/* "signature", which isn't an opcode */
60};
61
62#define WASM_OPCODE(opcode, name, intype, outtype, class, signedness)   \
63  { name, wasm_ ## class, opcode },
64
65struct wasm32_opcode_s
66{
67  const char *name;
68  enum wasm_class clas;
69  unsigned char opcode;
70} wasm32_opcodes[] =
71{
72#include "opcode/wasm.h"
73  {
74  NULL, 0, 0}
75};
76
77const char comment_chars[] = ";#";
78const char line_comment_chars[] = ";#";
79const char line_separator_chars[] = "";
80
81const char *md_shortopts = "m:";
82
83const char EXP_CHARS[] = "eE";
84const char FLT_CHARS[] = "dD";
85
86/* The target specific pseudo-ops which we support.  */
87
88const pseudo_typeS md_pseudo_table[] =
89{
90  {NULL, NULL, 0}
91};
92
93/* Opcode hash table.  */
94
95static htab_t wasm32_hash;
96
97struct option md_longopts[] =
98{
99  {NULL, no_argument, NULL, 0}
100};
101
102size_t md_longopts_size = sizeof (md_longopts);
103
104/* No relaxation/no machine-dependent frags.  */
105
106int
107md_estimate_size_before_relax (fragS * fragp ATTRIBUTE_UNUSED,
108			       asection * seg ATTRIBUTE_UNUSED)
109{
110  abort ();
111  return 0;
112}
113
114void
115md_show_usage (FILE * stream)
116{
117  fprintf (stream, _("wasm32 assembler options:\n"));
118}
119
120/* No machine-dependent options.  */
121
122int
123md_parse_option (int c ATTRIBUTE_UNUSED, const char *arg ATTRIBUTE_UNUSED)
124{
125  return 0;
126}
127
128/* No machine-dependent symbols.  */
129
130symbolS *
131md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
132{
133  return NULL;
134}
135
136/* IEEE little-endian floats.  */
137
138const char *
139md_atof (int type, char *litP, int *sizeP)
140{
141  return ieee_md_atof (type, litP, sizeP, false);
142}
143
144/* No machine-dependent frags.  */
145
146void
147md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
148		 asection * sec ATTRIBUTE_UNUSED,
149		 fragS * fragP ATTRIBUTE_UNUSED)
150{
151  abort ();
152}
153
154/* Build opcode hash table, set some flags.  */
155
156void
157md_begin (void)
158{
159  struct wasm32_opcode_s *opcode;
160
161  wasm32_hash = str_htab_create ();
162
163  /* Insert unique names into hash table.  This hash table then
164     provides a quick index to the first opcode with a particular name
165     in the opcode table.  */
166  for (opcode = wasm32_opcodes; opcode->name; opcode++)
167    str_hash_insert (wasm32_hash, opcode->name, opcode, 0);
168
169  linkrelax = 0;
170  flag_sectname_subst = 1;
171  flag_no_comments = 0;
172  flag_keep_locals = 1;
173}
174
175/* Do the normal thing for md_section_align.  */
176
177valueT
178md_section_align (asection * seg, valueT addr)
179{
180  int align = bfd_section_alignment (seg);
181  return ((addr + (1 << align) - 1) & -(1 << align));
182}
183
184/* Apply a fixup, return TRUE if done (and no relocation is
185   needed).  */
186
187static bool
188apply_full_field_fix (fixS * fixP, char *buf, bfd_vma val, int size)
189{
190  if (fixP->fx_addsy != NULL || fixP->fx_pcrel)
191    {
192      fixP->fx_addnumber = val;
193      return false;
194    }
195
196  number_to_chars_littleendian (buf, val, size);
197  return true;
198}
199
200/* Apply a fixup (potentially PC-relative), set the fx_done flag if
201   done.  */
202
203void
204md_apply_fix (fixS * fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED)
205{
206  char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
207  long val = (long) *valP;
208
209  if (fixP->fx_pcrel)
210    {
211      switch (fixP->fx_r_type)
212	{
213	default:
214	  bfd_set_error (bfd_error_bad_value);
215	  return;
216
217	case BFD_RELOC_32:
218	  fixP->fx_r_type = BFD_RELOC_32_PCREL;
219	  return;
220	}
221    }
222
223  if (apply_full_field_fix (fixP, buf, val, fixP->fx_size))
224    fixP->fx_done = 1;
225}
226
227/* Skip whitespace.  */
228
229static inline char *
230skip_space (char *s)
231{
232  while (*s == ' ' || *s == '\t')
233    ++s;
234  return s;
235}
236
237/* Allow '/' in opcodes.  */
238
239static inline bool
240is_part_of_opcode (char c)
241{
242  return is_part_of_name (c) || (c == '/');
243}
244
245/* Extract an opcode.  */
246
247static char *
248extract_opcode (char *from, char *to, int limit)
249{
250  char *op_end;
251  int size = 0;
252
253  /* Drop leading whitespace.  */
254  from = skip_space (from);
255  *to = 0;
256
257  /* Find the op code end.  */
258  for (op_end = from; *op_end != 0 && is_part_of_opcode (*op_end);)
259    {
260      to[size++] = *op_end++;
261      if (size + 1 >= limit)
262	break;
263    }
264
265  to[size] = 0;
266  return op_end;
267}
268
269/* Produce an unsigned LEB128 integer padded to the right number of
270   bytes to store BITS bits, of value VALUE.  Uses FRAG_APPEND_1_CHAR
271   to write.  */
272
273static void
274wasm32_put_long_uleb128 (int bits, unsigned long value)
275{
276  unsigned char c;
277  int i = 0;
278
279  do
280    {
281      c = value & 0x7f;
282      value >>= 7;
283      if (i < (bits - 1) / 7)
284	c |= 0x80;
285      FRAG_APPEND_1_CHAR (c);
286    }
287  while (++i < (bits + 6) / 7);
288}
289
290/* Produce a signed LEB128 integer, using FRAG_APPEND_1_CHAR to
291   write.  */
292
293static void
294wasm32_put_sleb128 (long value)
295{
296  unsigned char c;
297  int more;
298
299  do
300    {
301      c = (value & 0x7f);
302      value >>= 7;
303      more = !((((value == 0) && ((c & 0x40) == 0))
304		|| ((value == -1) && ((c & 0x40) != 0))));
305      if (more)
306	c |= 0x80;
307      FRAG_APPEND_1_CHAR (c);
308    }
309  while (more);
310}
311
312/* Produce an unsigned LEB128 integer, using FRAG_APPEND_1_CHAR to
313   write.  */
314
315static void
316wasm32_put_uleb128 (unsigned long value)
317{
318  unsigned char c;
319
320  do
321    {
322      c = value & 0x7f;
323      value >>= 7;
324      if (value)
325	c |= 0x80;
326      FRAG_APPEND_1_CHAR (c);
327    }
328  while (value);
329}
330
331/* Read an integer expression.  Produce an LEB128-encoded integer if
332   it's a constant, a padded LEB128 plus a relocation if it's a
333   symbol, or a special relocation for <expr>@got, <expr>@gotcode, and
334   <expr>@plt{__sigchar_<signature>}.  */
335
336static bool
337wasm32_leb128 (char **line, int bits, int sign)
338{
339  char *t = input_line_pointer;
340  char *str = *line;
341  char *str0 = str;
342  struct reloc_list *reloc;
343  expressionS ex;
344  int gotrel = 0;
345  int pltrel = 0;
346  int code = 0;
347  const char *relname;
348
349  input_line_pointer = str;
350  expression (&ex);
351
352  if (ex.X_op == O_constant && *input_line_pointer != '@')
353    {
354      long value = ex.X_add_number;
355
356      str = input_line_pointer;
357      str = skip_space (str);
358      *line = str;
359      if (sign)
360	wasm32_put_sleb128 (value);
361      else
362	{
363	  if (value < 0)
364	    as_bad (_("unexpected negative constant"));
365	  wasm32_put_uleb128 (value);
366	}
367      input_line_pointer = t;
368      return str != str0;
369    }
370
371  reloc = XNEW (struct reloc_list);
372  reloc->u.a.offset_sym = expr_build_dot ();
373  if (ex.X_op == O_symbol)
374    {
375      reloc->u.a.sym = ex.X_add_symbol;
376      reloc->u.a.addend = ex.X_add_number;
377    }
378  else
379    {
380      reloc->u.a.sym = make_expr_symbol (&ex);
381      reloc->u.a.addend = 0;
382    }
383  /* i32.const fpointer@gotcode */
384  if (startswith (input_line_pointer, "@gotcode"))
385    {
386      gotrel = 1;
387      code = 1;
388      input_line_pointer += 8;
389    }
390  /* i32.const data@got */
391  else if (startswith (input_line_pointer, "@got"))
392    {
393      gotrel = 1;
394      input_line_pointer += 4;
395    }
396  /* call f@plt{__sigchar_FiiiiE} */
397  else if (startswith (input_line_pointer, "@plt"))
398    {
399      char *end_of_sig;
400
401      pltrel = 1;
402      code = 1;
403      input_line_pointer += 4;
404
405      if (startswith (input_line_pointer, "{")
406          && (end_of_sig = strchr (input_line_pointer, '}')))
407	{
408	  char *signature;
409	  struct reloc_list *reloc2;
410	  size_t siglength = end_of_sig - (input_line_pointer + 1);
411
412	  signature = strndup (input_line_pointer + 1, siglength);
413
414	  reloc2 = XNEW (struct reloc_list);
415	  reloc2->u.a.offset_sym = expr_build_dot ();
416	  reloc2->u.a.sym = symbol_find_or_make (signature);
417	  reloc2->u.a.addend = 0;
418	  reloc2->u.a.howto = bfd_reloc_name_lookup
419	    (stdoutput, "R_WASM32_PLT_SIG");
420	  reloc2->next = reloc_list;
421	  reloc_list = reloc2;
422	  input_line_pointer = end_of_sig + 1;
423	}
424      else
425	{
426	  as_bad (_("no function type on PLT reloc"));
427	}
428    }
429
430  if (gotrel && code)
431    relname = "R_WASM32_LEB128_GOT_CODE";
432  else if (gotrel)
433    relname = "R_WASM32_LEB128_GOT";
434  else if (pltrel)
435    relname = "R_WASM32_LEB128_PLT";
436  else
437    relname = "R_WASM32_LEB128";
438
439  reloc->u.a.howto = bfd_reloc_name_lookup (stdoutput, relname);
440  if (!reloc->u.a.howto)
441    as_bad (_("couldn't find relocation to use"));
442  reloc->file = as_where (&reloc->line);
443  reloc->next = reloc_list;
444  reloc_list = reloc;
445
446  str = input_line_pointer;
447  str = skip_space (str);
448  *line = str;
449  wasm32_put_long_uleb128 (bits, 0);
450  input_line_pointer = t;
451
452  return str != str0;
453}
454
455/* Read an integer expression and produce an unsigned LEB128 integer,
456   or a relocation for it.  */
457
458static bool
459wasm32_uleb128 (char **line, int bits)
460{
461  return wasm32_leb128 (line, bits, 0);
462}
463
464/* Read an integer expression and produce a signed LEB128 integer, or
465   a relocation for it.  */
466
467static bool
468wasm32_sleb128 (char **line, int bits)
469{
470  return wasm32_leb128 (line, bits, 1);
471}
472
473/* Read an f32.  (Like float_cons ('f')).  */
474
475static void
476wasm32_f32 (char **line)
477{
478  char *t = input_line_pointer;
479
480  input_line_pointer = *line;
481  float_cons ('f');
482  *line = input_line_pointer;
483  input_line_pointer = t;
484}
485
486/* Read an f64.  (Like float_cons ('d')).  */
487
488static void
489wasm32_f64 (char **line)
490{
491  char *t = input_line_pointer;
492
493  input_line_pointer = *line;
494  float_cons ('d');
495  *line = input_line_pointer;
496  input_line_pointer = t;
497}
498
499/* Assemble a signature from LINE, replacing it with the new input
500   pointer.  Signatures are simple expressions matching the regexp
501   F[ilfd]*v?E, and interpreted as though they were C++-mangled
502   function types on a 64-bit machine. */
503
504static void
505wasm32_signature (char **line)
506{
507  unsigned long count = 0;
508  char *str = *line;
509  char *ostr;
510  char *result;
511
512  if (*str++ != 'F')
513    as_bad (_("Not a function type"));
514  result = str;
515  ostr = str + 1;
516  str++;
517
518  while (*str != 'E')
519    {
520      switch (*str++)
521	{
522	case 'i':
523	case 'l':
524	case 'f':
525	case 'd':
526	  count++;
527	  break;
528	default:
529	  as_bad (_("Unknown type %c\n"), str[-1]);
530	}
531    }
532  wasm32_put_uleb128 (count);
533  str = ostr;
534  while (*str != 'E')
535    {
536      switch (*str++)
537	{
538	case 'i':
539	  FRAG_APPEND_1_CHAR (BLOCK_TYPE_I32);
540	  break;
541	case 'l':
542	  FRAG_APPEND_1_CHAR (BLOCK_TYPE_I64);
543	  break;
544	case 'f':
545	  FRAG_APPEND_1_CHAR (BLOCK_TYPE_F32);
546	  break;
547	case 'd':
548	  FRAG_APPEND_1_CHAR (BLOCK_TYPE_F64);
549	  break;
550	default:
551	  as_bad (_("Unknown type"));
552	}
553    }
554  str++;
555  switch (*result)
556    {
557    case 'v':
558      FRAG_APPEND_1_CHAR (0x00);	/* no return value */
559      break;
560    case 'i':
561      FRAG_APPEND_1_CHAR (0x01);	/* one return value */
562      FRAG_APPEND_1_CHAR (BLOCK_TYPE_I32);
563      break;
564    case 'l':
565      FRAG_APPEND_1_CHAR (0x01);	/* one return value */
566      FRAG_APPEND_1_CHAR (BLOCK_TYPE_I64);
567      break;
568    case 'f':
569      FRAG_APPEND_1_CHAR (0x01);	/* one return value */
570      FRAG_APPEND_1_CHAR (BLOCK_TYPE_F32);
571      break;
572    case 'd':
573      FRAG_APPEND_1_CHAR (0x01);	/* one return value */
574      FRAG_APPEND_1_CHAR (BLOCK_TYPE_F64);
575      break;
576    default:
577      as_bad (_("Unknown type"));
578    }
579  *line = str;
580}
581
582/* Main operands function.  Read the operands for OPCODE from LINE,
583   replacing it with the new input pointer.  */
584
585static void
586wasm32_operands (struct wasm32_opcode_s *opcode, char **line)
587{
588  char *str = *line;
589  unsigned long block_type = 0;
590
591  FRAG_APPEND_1_CHAR (opcode->opcode);
592  str = skip_space (str);
593  if (str[0] == '[')
594    {
595      if (opcode->clas == wasm_typed)
596	{
597	  str++;
598	  block_type = BLOCK_TYPE_NONE;
599	  if (str[0] != ']')
600	    {
601	      str = skip_space (str);
602	      switch (str[0])
603		{
604		case 'i':
605		  block_type = BLOCK_TYPE_I32;
606		  str++;
607		  break;
608		case 'l':
609		  block_type = BLOCK_TYPE_I64;
610		  str++;
611		  break;
612		case 'f':
613		  block_type = BLOCK_TYPE_F32;
614		  str++;
615		  break;
616		case 'd':
617		  block_type = BLOCK_TYPE_F64;
618		  str++;
619		  break;
620		}
621	      str = skip_space (str);
622	      if (str[0] == ']')
623		str++;
624	      else
625		as_bad (_("only single block types allowed"));
626	      str = skip_space (str);
627	    }
628	  else
629	    {
630	      str++;
631	      str = skip_space (str);
632	    }
633	}
634      else
635	as_bad (_("instruction does not take a block type"));
636    }
637
638  switch (opcode->clas)
639    {
640    case wasm_drop:
641    case wasm_special:
642    case wasm_binary:
643    case wasm_unary:
644    case wasm_relational:
645    case wasm_select:
646    case wasm_eqz:
647    case wasm_conv:
648    case wasm_return:
649      break;
650    case wasm_typed:
651      if (block_type == 0)
652	as_bad (_("missing block type"));
653      FRAG_APPEND_1_CHAR (block_type);
654      break;
655    case wasm_store:
656    case wasm_load:
657      if (str[0] == 'a' && str[1] == '=')
658	{
659	  str += 2;
660	  if (!wasm32_uleb128 (&str, 32))
661	    as_bad (_("missing alignment hint"));
662	}
663      else
664	{
665	  as_bad (_("missing alignment hint"));
666	}
667      str = skip_space (str);
668      if (!wasm32_uleb128 (&str, 32))
669	as_bad (_("missing offset"));
670      break;
671    case wasm_set_local:
672    case wasm_get_local:
673    case wasm_tee_local:
674      if (!wasm32_uleb128 (&str, 32))
675	as_bad (_("missing local index"));
676      break;
677    case wasm_break:
678    case wasm_break_if:
679      if (!wasm32_uleb128 (&str, 32))
680	as_bad (_("missing break count"));
681      break;
682    case wasm_current_memory:
683    case wasm_grow_memory:
684      if (!wasm32_uleb128 (&str, 32))
685	as_bad (_("missing reserved current_memory/grow_memory argument"));
686      break;
687    case wasm_call:
688      if (!wasm32_uleb128 (&str, 32))
689	as_bad (_("missing call argument"));
690      break;
691    case wasm_call_indirect:
692      if (!wasm32_uleb128 (&str, 32))
693	as_bad (_("missing call signature"));
694      if (!wasm32_uleb128 (&str, 32))
695	as_bad (_("missing table index"));
696      break;
697    case wasm_constant_i32:
698      wasm32_sleb128 (&str, 32);
699      break;
700    case wasm_constant_i64:
701      wasm32_sleb128 (&str, 64);
702      break;
703    case wasm_constant_f32:
704      wasm32_f32 (&str);
705      return;
706    case wasm_constant_f64:
707      wasm32_f64 (&str);
708      return;
709    case wasm_break_table:
710      {
711	do
712	  {
713	    wasm32_uleb128 (&str, 32);
714	    str = skip_space (str);
715	  }
716	while (str[0]);
717
718	break;
719      }
720    case wasm_signature:
721      wasm32_signature (&str);
722    }
723  str = skip_space (str);
724
725  if (*str)
726    as_bad (_("junk at end of line, first unrecognized character is `%c'"),
727	    *str);
728
729  *line = str;
730
731  return;
732}
733
734/* Main assembly function.  Find the opcode and call
735   wasm32_operands().  */
736
737void
738md_assemble (char *str)
739{
740  char op[32];
741  char *t;
742  struct wasm32_opcode_s *opcode;
743
744  str = skip_space (extract_opcode (str, op, sizeof (op)));
745
746  if (!op[0])
747    as_bad (_("can't find opcode "));
748
749  opcode = (struct wasm32_opcode_s *) str_hash_find (wasm32_hash, op);
750
751  if (opcode == NULL)
752    {
753      as_bad (_("unknown opcode `%s'"), op);
754      return;
755    }
756
757  dwarf2_emit_insn (0);
758
759  t = input_line_pointer;
760  wasm32_operands (opcode, &str);
761  input_line_pointer = t;
762}
763
764/* Don't replace PLT/GOT relocations with section symbols, so they
765   don't get an addend.  */
766
767int
768wasm32_force_relocation (fixS * f)
769{
770  if (f->fx_r_type == BFD_RELOC_WASM32_LEB128_PLT
771      || f->fx_r_type == BFD_RELOC_WASM32_LEB128_GOT)
772    return 1;
773
774  return 0;
775}
776
777/* Don't replace PLT/GOT relocations with section symbols, so they
778   don't get an addend.  */
779
780bool
781wasm32_fix_adjustable (fixS * fixP)
782{
783  if (fixP->fx_addsy == NULL)
784    return true;
785
786  if (fixP->fx_r_type == BFD_RELOC_WASM32_LEB128_PLT
787      || fixP->fx_r_type == BFD_RELOC_WASM32_LEB128_GOT)
788    return false;
789
790  return true;
791}
792
793/* Generate a reloc for FIXP.  */
794
795arelent *
796tc_gen_reloc (asection * sec ATTRIBUTE_UNUSED, fixS * fixp)
797{
798  arelent *reloc;
799
800  reloc = (arelent *) xmalloc (sizeof (*reloc));
801  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
802  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
803  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
804
805  /* Make sure none of our internal relocations make it this far.
806     They'd better have been fully resolved by this point.  */
807  gas_assert ((int) fixp->fx_r_type > 0);
808
809  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
810  if (reloc->howto == NULL)
811    {
812      as_bad_where (fixp->fx_file, fixp->fx_line,
813		    _("cannot represent `%s' relocation in object file"),
814		    bfd_get_reloc_code_name (fixp->fx_r_type));
815      return NULL;
816    }
817
818  reloc->addend = fixp->fx_offset;
819
820  return reloc;
821}
822