1 /* tc-wasm32.c -- Assembler code for the wasm32 target.
3 Copyright (C) 2017-2020 Free Software Foundation, Inc.
5 This file is part of GAS, the GNU Assembler.
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)
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.
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
23 #include "safe-ctype.h"
25 #include "dwarf2dbg.h"
26 #include "dw2gencfi.h"
27 #include "elf/wasm32.h"
32 wasm_typed
, /* a typed opcode: block, loop, or if */
33 wasm_special
, /* a special opcode: unreachable, nop, else,
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" */
57 wasm_current_memory
, /* "current_memory" */
58 wasm_grow_memory
, /* "grow_memory" */
59 wasm_signature
/* "signature", which isn't an opcode */
62 #define WASM_OPCODE(opcode, name, intype, outtype, class, signedness) \
63 { name, wasm_ ## class, opcode },
65 struct wasm32_opcode_s
72 #include "opcode/wasm.h"
77 const char comment_chars
[] = ";#";
78 const char line_comment_chars
[] = ";#";
79 const char line_separator_chars
[] = "";
81 const char *md_shortopts
= "m:";
83 const char EXP_CHARS
[] = "eE";
84 const char FLT_CHARS
[] = "dD";
86 /* The target specific pseudo-ops which we support. */
88 const pseudo_typeS md_pseudo_table
[] =
93 /* Opcode hash table. */
95 static htab_t wasm32_hash
;
97 struct option md_longopts
[] =
99 {NULL
, no_argument
, NULL
, 0}
102 size_t md_longopts_size
= sizeof (md_longopts
);
104 /* No relaxation/no machine-dependent frags. */
107 md_estimate_size_before_relax (fragS
* fragp ATTRIBUTE_UNUSED
,
108 asection
* seg ATTRIBUTE_UNUSED
)
115 md_show_usage (FILE * stream
)
117 fprintf (stream
, _("wasm32 assembler options:\n"));
120 /* No machine-dependent options. */
123 md_parse_option (int c ATTRIBUTE_UNUSED
, const char *arg ATTRIBUTE_UNUSED
)
128 /* No machine-dependent symbols. */
131 md_undefined_symbol (char *name ATTRIBUTE_UNUSED
)
136 /* IEEE little-endian floats. */
139 md_atof (int type
, char *litP
, int *sizeP
)
141 return ieee_md_atof (type
, litP
, sizeP
, FALSE
);
144 /* No machine-dependent frags. */
147 md_convert_frag (bfd
* abfd ATTRIBUTE_UNUSED
,
148 asection
* sec ATTRIBUTE_UNUSED
,
149 fragS
* fragP ATTRIBUTE_UNUSED
)
154 /* Build opcode hash table, set some flags. */
159 struct wasm32_opcode_s
*opcode
;
161 wasm32_hash
= str_htab_create ();
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);
170 flag_sectname_subst
= 1;
171 flag_no_comments
= 0;
172 flag_keep_locals
= 1;
175 /* Do the normal thing for md_section_align. */
178 md_section_align (asection
* seg
, valueT addr
)
180 int align
= bfd_section_alignment (seg
);
181 return ((addr
+ (1 << align
) - 1) & -(1 << align
));
184 /* Apply a fixup, return TRUE if done (and no relocation is
188 apply_full_field_fix (fixS
* fixP
, char *buf
, bfd_vma val
, int size
)
190 if (fixP
->fx_addsy
!= NULL
|| fixP
->fx_pcrel
)
192 fixP
->fx_addnumber
= val
;
196 number_to_chars_littleendian (buf
, val
, size
);
200 /* Apply a fixup (potentially PC-relative), set the fx_done flag if
204 md_apply_fix (fixS
* fixP
, valueT
* valP
, segT seg ATTRIBUTE_UNUSED
)
206 char *buf
= fixP
->fx_where
+ fixP
->fx_frag
->fr_literal
;
207 long val
= (long) *valP
;
211 switch (fixP
->fx_r_type
)
214 bfd_set_error (bfd_error_bad_value
);
218 fixP
->fx_r_type
= BFD_RELOC_32_PCREL
;
223 if (apply_full_field_fix (fixP
, buf
, val
, fixP
->fx_size
))
227 /* Skip whitespace. */
232 while (*s
== ' ' || *s
== '\t')
237 /* Allow '/' in opcodes. */
239 static inline bfd_boolean
240 is_part_of_opcode (char c
)
242 return is_part_of_name (c
) || (c
== '/');
245 /* Extract an opcode. */
248 extract_opcode (char *from
, char *to
, int limit
)
253 /* Drop leading whitespace. */
254 from
= skip_space (from
);
257 /* Find the op code end. */
258 for (op_end
= from
; *op_end
!= 0 && is_part_of_opcode (*op_end
);)
260 to
[size
++] = *op_end
++;
261 if (size
+ 1 >= limit
)
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
274 wasm32_put_long_uleb128 (int bits
, unsigned long value
)
283 if (i
< (bits
- 1) / 7)
285 FRAG_APPEND_1_CHAR (c
);
287 while (++i
< (bits
+ 6) / 7);
290 /* Produce a signed LEB128 integer, using FRAG_APPEND_1_CHAR to
294 wasm32_put_sleb128 (long value
)
303 more
= !((((value
== 0) && ((c
& 0x40) == 0))
304 || ((value
== -1) && ((c
& 0x40) != 0))));
307 FRAG_APPEND_1_CHAR (c
);
312 /* Produce an unsigned LEB128 integer, using FRAG_APPEND_1_CHAR to
316 wasm32_put_uleb128 (unsigned long value
)
326 FRAG_APPEND_1_CHAR (c
);
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>}. */
337 wasm32_leb128 (char **line
, int bits
, int sign
)
339 char *t
= input_line_pointer
;
342 struct reloc_list
*reloc
;
349 input_line_pointer
= str
;
352 if (ex
.X_op
== O_constant
&& *input_line_pointer
!= '@')
354 long value
= ex
.X_add_number
;
356 str
= input_line_pointer
;
357 str
= skip_space (str
);
360 wasm32_put_sleb128 (value
);
364 as_bad (_("unexpected negative constant"));
365 wasm32_put_uleb128 (value
);
367 input_line_pointer
= t
;
371 reloc
= XNEW (struct reloc_list
);
372 reloc
->u
.a
.offset_sym
= expr_build_dot ();
373 if (ex
.X_op
== O_symbol
)
375 reloc
->u
.a
.sym
= ex
.X_add_symbol
;
376 reloc
->u
.a
.addend
= ex
.X_add_number
;
380 reloc
->u
.a
.sym
= make_expr_symbol (&ex
);
381 reloc
->u
.a
.addend
= 0;
383 /* i32.const fpointer@gotcode */
384 if (strncmp (input_line_pointer
, "@gotcode", 8) == 0)
388 input_line_pointer
+= 8;
390 /* i32.const data@got */
391 else if (strncmp (input_line_pointer
, "@got", 4) == 0)
394 input_line_pointer
+= 4;
396 /* call f@plt{__sigchar_FiiiiE} */
397 else if (strncmp (input_line_pointer
, "@plt", 4) == 0)
403 input_line_pointer
+= 4;
405 if (strncmp (input_line_pointer
, "{", 1) == 0
406 && (end_of_sig
= strchr (input_line_pointer
, '}')))
409 struct reloc_list
*reloc2
;
410 size_t siglength
= end_of_sig
- (input_line_pointer
+ 1);
412 signature
= strndup (input_line_pointer
+ 1, siglength
);
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
;
422 input_line_pointer
= end_of_sig
+ 1;
426 as_bad (_("no function type on PLT reloc"));
431 relname
= "R_WASM32_LEB128_GOT_CODE";
433 relname
= "R_WASM32_LEB128_GOT";
435 relname
= "R_WASM32_LEB128_PLT";
437 relname
= "R_WASM32_LEB128";
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
;
446 str
= input_line_pointer
;
447 str
= skip_space (str
);
449 wasm32_put_long_uleb128 (bits
, 0);
450 input_line_pointer
= t
;
455 /* Read an integer expression and produce an unsigned LEB128 integer,
456 or a relocation for it. */
459 wasm32_uleb128 (char **line
, int bits
)
461 return wasm32_leb128 (line
, bits
, 0);
464 /* Read an integer expression and produce a signed LEB128 integer, or
465 a relocation for it. */
468 wasm32_sleb128 (char **line
, int bits
)
470 return wasm32_leb128 (line
, bits
, 1);
473 /* Read an f32. (Like float_cons ('f')). */
476 wasm32_f32 (char **line
)
478 char *t
= input_line_pointer
;
480 input_line_pointer
= *line
;
482 *line
= input_line_pointer
;
483 input_line_pointer
= t
;
486 /* Read an f64. (Like float_cons ('d')). */
489 wasm32_f64 (char **line
)
491 char *t
= input_line_pointer
;
493 input_line_pointer
= *line
;
495 *line
= input_line_pointer
;
496 input_line_pointer
= t
;
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. */
505 wasm32_signature (char **line
)
507 unsigned long count
= 0;
513 as_bad (_("Not a function type"));
529 as_bad (_("Unknown type %c\n"), str
[-1]);
532 wasm32_put_uleb128 (count
);
539 FRAG_APPEND_1_CHAR (BLOCK_TYPE_I32
);
542 FRAG_APPEND_1_CHAR (BLOCK_TYPE_I64
);
545 FRAG_APPEND_1_CHAR (BLOCK_TYPE_F32
);
548 FRAG_APPEND_1_CHAR (BLOCK_TYPE_F64
);
551 as_bad (_("Unknown type"));
558 FRAG_APPEND_1_CHAR (0x00); /* no return value */
561 FRAG_APPEND_1_CHAR (0x01); /* one return value */
562 FRAG_APPEND_1_CHAR (BLOCK_TYPE_I32
);
565 FRAG_APPEND_1_CHAR (0x01); /* one return value */
566 FRAG_APPEND_1_CHAR (BLOCK_TYPE_I64
);
569 FRAG_APPEND_1_CHAR (0x01); /* one return value */
570 FRAG_APPEND_1_CHAR (BLOCK_TYPE_F32
);
573 FRAG_APPEND_1_CHAR (0x01); /* one return value */
574 FRAG_APPEND_1_CHAR (BLOCK_TYPE_F64
);
577 as_bad (_("Unknown type"));
582 /* Main operands function. Read the operands for OPCODE from LINE,
583 replacing it with the new input pointer. */
586 wasm32_operands (struct wasm32_opcode_s
*opcode
, char **line
)
589 unsigned long block_type
= 0;
591 FRAG_APPEND_1_CHAR (opcode
->opcode
);
592 str
= skip_space (str
);
595 if (opcode
->clas
== wasm_typed
)
598 block_type
= BLOCK_TYPE_NONE
;
601 str
= skip_space (str
);
605 block_type
= BLOCK_TYPE_I32
;
609 block_type
= BLOCK_TYPE_I64
;
613 block_type
= BLOCK_TYPE_F32
;
617 block_type
= BLOCK_TYPE_F64
;
621 str
= skip_space (str
);
625 as_bad (_("only single block types allowed"));
626 str
= skip_space (str
);
631 str
= skip_space (str
);
635 as_bad (_("instruction does not take a block type"));
638 switch (opcode
->clas
)
644 case wasm_relational
:
652 as_bad (_("missing block type"));
653 FRAG_APPEND_1_CHAR (block_type
);
657 if (str
[0] == 'a' && str
[1] == '=')
660 if (!wasm32_uleb128 (&str
, 32))
661 as_bad (_("missing alignment hint"));
665 as_bad (_("missing alignment hint"));
667 str
= skip_space (str
);
668 if (!wasm32_uleb128 (&str
, 32))
669 as_bad (_("missing offset"));
674 if (!wasm32_uleb128 (&str
, 32))
675 as_bad (_("missing local index"));
679 if (!wasm32_uleb128 (&str
, 32))
680 as_bad (_("missing break count"));
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"));
688 if (!wasm32_uleb128 (&str
, 32))
689 as_bad (_("missing call argument"));
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"));
697 case wasm_constant_i32
:
698 wasm32_sleb128 (&str
, 32);
700 case wasm_constant_i64
:
701 wasm32_sleb128 (&str
, 64);
703 case wasm_constant_f32
:
706 case wasm_constant_f64
:
709 case wasm_break_table
:
713 wasm32_uleb128 (&str
, 32);
714 str
= skip_space (str
);
721 wasm32_signature (&str
);
723 str
= skip_space (str
);
726 as_bad (_("junk at end of line, first unrecognized character is `%c'"),
734 /* Main assembly function. Find the opcode and call
735 wasm32_operands(). */
738 md_assemble (char *str
)
742 struct wasm32_opcode_s
*opcode
;
744 str
= skip_space (extract_opcode (str
, op
, sizeof (op
)));
747 as_bad (_("can't find opcode "));
749 opcode
= (struct wasm32_opcode_s
*) str_hash_find (wasm32_hash
, op
);
753 as_bad (_("unknown opcode `%s'"), op
);
757 dwarf2_emit_insn (0);
759 t
= input_line_pointer
;
760 wasm32_operands (opcode
, &str
);
761 input_line_pointer
= t
;
764 /* Don't replace PLT/GOT relocations with section symbols, so they
765 don't get an addend. */
768 wasm32_force_relocation (fixS
* f
)
770 if (f
->fx_r_type
== BFD_RELOC_WASM32_LEB128_PLT
771 || f
->fx_r_type
== BFD_RELOC_WASM32_LEB128_GOT
)
777 /* Don't replace PLT/GOT relocations with section symbols, so they
778 don't get an addend. */
781 wasm32_fix_adjustable (fixS
* fixP
)
783 if (fixP
->fx_addsy
== NULL
)
786 if (fixP
->fx_r_type
== BFD_RELOC_WASM32_LEB128_PLT
787 || fixP
->fx_r_type
== BFD_RELOC_WASM32_LEB128_GOT
)
793 /* Generate a reloc for FIXP. */
796 tc_gen_reloc (asection
* sec ATTRIBUTE_UNUSED
, fixS
* fixp
)
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
;
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);
809 reloc
->howto
= bfd_reloc_type_lookup (stdoutput
, fixp
->fx_r_type
);
810 if (reloc
->howto
== NULL
)
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
));
818 reloc
->addend
= fixp
->fx_offset
;