1 /* tc-wasm32.c -- Assembler code for the wasm32 target. 2 3 Copyright (C) 2017-2022 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 30 enum 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 65 struct 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 77 const char comment_chars[] = ";#"; 78 const char line_comment_chars[] = ";#"; 79 const char line_separator_chars[] = ""; 80 81 const char *md_shortopts = "m:"; 82 83 const char EXP_CHARS[] = "eE"; 84 const char FLT_CHARS[] = "dD"; 85 86 /* The target specific pseudo-ops which we support. */ 87 88 const pseudo_typeS md_pseudo_table[] = 89 { 90 {NULL, NULL, 0} 91 }; 92 93 /* Opcode hash table. */ 94 95 static htab_t wasm32_hash; 96 97 struct option md_longopts[] = 98 { 99 {NULL, no_argument, NULL, 0} 100 }; 101 102 size_t md_longopts_size = sizeof (md_longopts); 103 104 /* No relaxation/no machine-dependent frags. */ 105 106 int 107 md_estimate_size_before_relax (fragS * fragp ATTRIBUTE_UNUSED, 108 asection * seg ATTRIBUTE_UNUSED) 109 { 110 abort (); 111 return 0; 112 } 113 114 void 115 md_show_usage (FILE * stream) 116 { 117 fprintf (stream, _("wasm32 assembler options:\n")); 118 } 119 120 /* No machine-dependent options. */ 121 122 int 123 md_parse_option (int c ATTRIBUTE_UNUSED, const char *arg ATTRIBUTE_UNUSED) 124 { 125 return 0; 126 } 127 128 /* No machine-dependent symbols. */ 129 130 symbolS * 131 md_undefined_symbol (char *name ATTRIBUTE_UNUSED) 132 { 133 return NULL; 134 } 135 136 /* IEEE little-endian floats. */ 137 138 const char * 139 md_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 146 void 147 md_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 156 void 157 md_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 177 valueT 178 md_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 187 static bool 188 apply_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 203 void 204 md_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 229 static inline char * 230 skip_space (char *s) 231 { 232 while (*s == ' ' || *s == '\t') 233 ++s; 234 return s; 235 } 236 237 /* Allow '/' in opcodes. */ 238 239 static inline bool 240 is_part_of_opcode (char c) 241 { 242 return is_part_of_name (c) || (c == '/'); 243 } 244 245 /* Extract an opcode. */ 246 247 static char * 248 extract_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 273 static void 274 wasm32_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 293 static void 294 wasm32_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 315 static void 316 wasm32_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 336 static bool 337 wasm32_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 458 static bool 459 wasm32_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 467 static bool 468 wasm32_sleb128 (char **line, int bits) 469 { 470 return wasm32_leb128 (line, bits, 1); 471 } 472 473 /* Read an f32. (Like float_cons ('f')). */ 474 475 static void 476 wasm32_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 488 static void 489 wasm32_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 504 static void 505 wasm32_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 585 static void 586 wasm32_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 737 void 738 md_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 767 int 768 wasm32_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 780 bool 781 wasm32_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 795 arelent * 796 tc_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