Commit | Line | Data |
---|---|---|
f96bd6c2 PC |
1 | /* tc-wasm32.c -- Assembler code for the wasm32 target. |
2 | ||
b3adc24a | 3 | Copyright (C) 2017-2020 Free Software Foundation, Inc. |
f96bd6c2 PC |
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 struct hash_control *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 = 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 | ||
177 | valueT | |
178 | md_section_align (asection * seg, valueT addr) | |
179 | { | |
fd361982 | 180 | int align = bfd_section_alignment (seg); |
f96bd6c2 PC |
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 bfd_boolean | |
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 bfd_boolean | |
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 bfd_boolean | |
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 (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 | ||
458 | static bfd_boolean | |
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 bfd_boolean | |
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 *) 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 | bfd_boolean | |
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 | } |