tc-wasm32.c revision 1.1.1.1
1/* tc-wasm32.c -- Assembler code for the wasm32 target. 2 3 Copyright (C) 2017-2018 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 struct hash_control *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 = hash_new (); 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 hash_insert (wasm32_hash, opcode->name, (char *) opcode); 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_get_section_alignment (stdoutput, 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 bfd_boolean 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 bfd_boolean 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 bfd_boolean 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 (strncmp (input_line_pointer, "@gotcode", 8) == 0) 385 { 386 gotrel = 1; 387 code = 1; 388 input_line_pointer += 8; 389 } 390 /* i32.const data@got */ 391 else if (strncmp (input_line_pointer, "@got", 4) == 0) 392 { 393 gotrel = 1; 394 input_line_pointer += 4; 395 } 396 /* call f@plt{__sigchar_FiiiiE} */ 397 else if (strncmp (input_line_pointer, "@plt", 4) == 0) 398 { 399 char *end_of_sig; 400 401 pltrel = 1; 402 code = 1; 403 input_line_pointer += 4; 404 405 if (strncmp (input_line_pointer, "{", 1) == 0 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 bfd_boolean 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 bfd_boolean 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 *) 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 780bfd_boolean 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