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