mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
f96bd6c2d7
binutils * readelf.c: Add support for wasm32 ELF format WebAssembly files. (guess_is_rela): Likewise. (dump_relocations): Likewise. (is_32bit_abs_reloc): Likewise. (is_none_reloc_): Likewise. * NEWS: Mention the new support. * testsuite/lib/binutils-common.exp (is_elf_format): Mark wasm32 as ELF target. (supports_gnu_unique): Mark wasm32 as supporting STB_GNU_UNIQUE. * testsuite/binutils-all/nm.exp: Mark wasm32 as requiring .size annotations. * testsuite/binutils-all/wasm32: New directory. * testsuite/binutils-all/wasm32/create-wasm.d: New file. * testsuite/binutils-all/wasm32/create-wasm.s: Likewise. * testsuite/binutils-all/wasm32/custom-section.d: Likewise. * testsuite/binutils-all/wasm32/custom-section.s: Likewise. * testsuite/binutils-all/wasm32/invalid-wasm-1.d: Likewise. * testsuite/binutils-all/wasm32/invalid-wasm-1.s: Likewise. * testsuite/binutils-all/wasm32/long-sections.d: Likewise. * testsuite/binutils-all/wasm32/long-sections.s: Likewise. * testsuite/binutils-all/wasm32/parse-wasm.d: Likewise. * testsuite/binutils-all/wasm32/parse-wasm.s: Likewise. * testsuite/binutils-all/wasm32/parse-wasm-2.d: Likewise. * testsuite/binutils-all/wasm32/parse-wasm-2.s: Likewise. * testsuite/binutils-all/wasm32/prepared-section.d: Likewise. * testsuite/binutils-all/wasm32/prepared-section.s: Likewise. * testsuite/binutils-all/wasm32/wasm32.exp: New file, run tests. gas * config/tc-wasm32.h: New file: Add WebAssembly assembler target. * config/tc-wasm32.c: New file: Add WebAssembly assembler target. * Makefile.am: Add WebAssembly assembler target. * configure.tgt: Add WebAssembly assembler target. * doc/c-wasm32.texi: New file: Start documenting WebAssembly assembler. * doc/all.texi: Define WASM32. * doc/as.texinfo: Add WebAssembly entries. * NEWS: Mention the new support. * Makefile.in: Regenerate. * po/gas.pot: Regenerate. * po/POTFILES.in: Regenerate. * testsuite/gas/wasm32: New directory. * testsuite/gas/wasm32/allinsn.d: New file. * testsuite/gas/wasm32/allinsn.s: New file. * testsuite/gas/wasm32/illegal.l: New file. * testsuite/gas/wasm32/illegal.s: New file. * testsuite/gas/wasm32/illegal-2.l: New file. * testsuite/gas/wasm32/illegal-2.s: New file. * testsuite/gas/wasm32/illegal-3.l: New file. * testsuite/gas/wasm32/illegal-3.s: New file. * testsuite/gas/wasm32/illegal-4.l: New file. * testsuite/gas/wasm32/illegal-4.s: New file. * testsuite/gas/wasm32/illegal-5.l: New file. * testsuite/gas/wasm32/illegal-5.s: New file. * testsuite/gas/wasm32/illegal-6.l: New file. * testsuite/gas/wasm32/illegal-6.s: New file. * testsuite/gas/wasm32/illegal-7.l: New file. * testsuite/gas/wasm32/illegal-7.s: New file. * testsuite/gas/wasm32/illegal-8.l: New file. * testsuite/gas/wasm32/illegal-8.s: New file. * testsuite/gas/wasm32/illegal-9.l: New file. * testsuite/gas/wasm32/illegal-9.s: New file. * testsuite/gas/wasm32/illegal-10.l: New file. * testsuite/gas/wasm32/illegal-10.s: New file. * testsuite/gas/wasm32/illegal-11.l: New file. * testsuite/gas/wasm32/illegal-11.s: New file. * testsuite/gas/wasm32/illegal-12.l: New file. * testsuite/gas/wasm32/illegal-12.s: New file. * testsuite/gas/wasm32/illegal-13.l: New file. * testsuite/gas/wasm32/illegal-13.s: New file. * testsuite/gas/wasm32/illegal-14.l: New file. * testsuite/gas/wasm32/illegal-14.s: New file. * testsuite/gas/wasm32/illegal-15.l: New file. * testsuite/gas/wasm32/illegal-15.s: New file. * testsuite/gas/wasm32/illegal-16.l: New file. * testsuite/gas/wasm32/illegal-16.s: New file. * testsuite/gas/wasm32/illegal-17.l: New file. * testsuite/gas/wasm32/illegal-17.s: New file. * testsuite/gas/wasm32/illegal-18.l: New file. * testsuite/gas/wasm32/illegal-18.s: New file. * testsuite/gas/wasm32/illegal-19.l: New file. * testsuite/gas/wasm32/illegal-19.s: New file. * testsuite/gas/wasm32/illegal-20.l: New file. * testsuite/gas/wasm32/illegal-20.s: New file. * testsuite/gas/wasm32/illegal-21.l: New file. * testsuite/gas/wasm32/illegal-21.s: New file. * testsuite/gas/wasm32/illegal-22.l: New file. * testsuite/gas/wasm32/illegal-22.s: New file. * testsuite/gas/wasm32/illegal-24.l: New file. * testsuite/gas/wasm32/illegal-24.s: New file. * testsuite/gas/wasm32/illegal-25.l: New file. * testsuite/gas/wasm32/illegal-25.s: New file. * testsuite/gas/wasm32/reloc.d: New file. * testsuite/gas/wasm32/reloc.s: New file. * testsuite/gas/wasm32/wasm32.exp: New tests for WebAssembly architecture. opcodes * configure.ac: Add (empty) bfd_wasm32_arch target. * configure: Regenerate * po/opcodes.pot: Regenerate. include * opcode/wasm.h: New file to support wasm32 architecture. * elf/wasm32.h: Add R_WASM32_32 relocation. bfd * elf32-wasm32.c: Add relocation code, two relocs. * reloc.c: Add wasm32 relocations. * libbfd.h: Regenerate. * bfd-in2.h: Regenerate. * bfd/po/bfd.pot: Regenerate.
822 lines
18 KiB
C
822 lines
18 KiB
C
/* tc-wasm32.c -- Assembler code for the wasm32 target.
|
|
|
|
Copyright (C) 2017 Free Software Foundation, Inc.
|
|
|
|
This file is part of GAS, the GNU Assembler.
|
|
|
|
GAS is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GAS is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
|
License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GAS; see the file COPYING. If not, write to the Free
|
|
Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
|
|
02110-1301, USA. */
|
|
|
|
#include "as.h"
|
|
#include "safe-ctype.h"
|
|
#include "subsegs.h"
|
|
#include "dwarf2dbg.h"
|
|
#include "dw2gencfi.h"
|
|
#include "elf/wasm32.h"
|
|
#include <float.h>
|
|
|
|
enum wasm_class
|
|
{
|
|
wasm_typed, /* a typed opcode: block, loop, or if */
|
|
wasm_special, /* a special opcode: unreachable, nop, else,
|
|
or end */
|
|
wasm_break, /* "br" */
|
|
wasm_break_if, /* "br_if" opcode */
|
|
wasm_break_table, /* "br_table" opcode */
|
|
wasm_return, /* "return" opcode */
|
|
wasm_call, /* "call" opcode */
|
|
wasm_call_indirect, /* "call_indirect" opcode */
|
|
wasm_get_local, /* "get_local" and "get_global" */
|
|
wasm_set_local, /* "set_local" and "set_global" */
|
|
wasm_tee_local, /* "tee_local" */
|
|
wasm_drop, /* "drop" */
|
|
wasm_constant_i32, /* "i32.const" */
|
|
wasm_constant_i64, /* "i64.const" */
|
|
wasm_constant_f32, /* "f32.const" */
|
|
wasm_constant_f64, /* "f64.const" */
|
|
wasm_unary, /* unary operators */
|
|
wasm_binary, /* binary operators */
|
|
wasm_conv, /* conversion operators */
|
|
wasm_load, /* load operators */
|
|
wasm_store, /* store operators */
|
|
wasm_select, /* "select" */
|
|
wasm_relational, /* comparison operators, except for "eqz" */
|
|
wasm_eqz, /* "eqz" */
|
|
wasm_current_memory, /* "current_memory" */
|
|
wasm_grow_memory, /* "grow_memory" */
|
|
wasm_signature /* "signature", which isn't an opcode */
|
|
};
|
|
|
|
#define WASM_OPCODE(opcode, name, intype, outtype, class, signedness) \
|
|
{ name, wasm_ ## class, opcode },
|
|
|
|
struct wasm32_opcode_s
|
|
{
|
|
const char *name;
|
|
enum wasm_class clas;
|
|
unsigned char opcode;
|
|
} wasm32_opcodes[] =
|
|
{
|
|
#include "opcode/wasm.h"
|
|
{
|
|
NULL, 0, 0}
|
|
};
|
|
|
|
const char comment_chars[] = ";#";
|
|
const char line_comment_chars[] = ";#";
|
|
const char line_separator_chars[] = "";
|
|
|
|
const char *md_shortopts = "m:";
|
|
|
|
const char EXP_CHARS[] = "eE";
|
|
const char FLT_CHARS[] = "dD";
|
|
|
|
/* The target specific pseudo-ops which we support. */
|
|
|
|
const pseudo_typeS md_pseudo_table[] =
|
|
{
|
|
{NULL, NULL, 0}
|
|
};
|
|
|
|
/* Opcode hash table. */
|
|
|
|
static struct hash_control *wasm32_hash;
|
|
|
|
struct option md_longopts[] =
|
|
{
|
|
{NULL, no_argument, NULL, 0}
|
|
};
|
|
|
|
size_t md_longopts_size = sizeof (md_longopts);
|
|
|
|
/* No relaxation/no machine-dependent frags. */
|
|
|
|
int
|
|
md_estimate_size_before_relax (fragS * fragp ATTRIBUTE_UNUSED,
|
|
asection * seg ATTRIBUTE_UNUSED)
|
|
{
|
|
abort ();
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
md_show_usage (FILE * stream)
|
|
{
|
|
fprintf (stream, _("wasm32 assembler options:\n"));
|
|
}
|
|
|
|
/* No machine-dependent options. */
|
|
|
|
int
|
|
md_parse_option (int c ATTRIBUTE_UNUSED, const char *arg ATTRIBUTE_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* No machine-dependent symbols. */
|
|
|
|
symbolS *
|
|
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* IEEE little-endian floats. */
|
|
|
|
const char *
|
|
md_atof (int type, char *litP, int *sizeP)
|
|
{
|
|
return ieee_md_atof (type, litP, sizeP, FALSE);
|
|
}
|
|
|
|
/* No machine-dependent frags. */
|
|
|
|
void
|
|
md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
|
|
asection * sec ATTRIBUTE_UNUSED,
|
|
fragS * fragP ATTRIBUTE_UNUSED)
|
|
{
|
|
abort ();
|
|
}
|
|
|
|
/* Build opcode hash table, set some flags. */
|
|
|
|
void
|
|
md_begin (void)
|
|
{
|
|
struct wasm32_opcode_s *opcode;
|
|
|
|
wasm32_hash = hash_new ();
|
|
|
|
/* Insert unique names into hash table. This hash table then
|
|
provides a quick index to the first opcode with a particular name
|
|
in the opcode table. */
|
|
for (opcode = wasm32_opcodes; opcode->name; opcode++)
|
|
hash_insert (wasm32_hash, opcode->name, (char *) opcode);
|
|
|
|
linkrelax = 0;
|
|
flag_sectname_subst = 1;
|
|
flag_no_comments = 0;
|
|
flag_keep_locals = 1;
|
|
}
|
|
|
|
/* Do the normal thing for md_section_align. */
|
|
|
|
valueT
|
|
md_section_align (asection * seg, valueT addr)
|
|
{
|
|
int align = bfd_get_section_alignment (stdoutput, seg);
|
|
return ((addr + (1 << align) - 1) & -(1 << align));
|
|
}
|
|
|
|
/* Apply a fixup, return TRUE if done (and no relocation is
|
|
needed). */
|
|
|
|
static bfd_boolean
|
|
apply_full_field_fix (fixS * fixP, char *buf, bfd_vma val, int size)
|
|
{
|
|
if (fixP->fx_addsy != NULL || fixP->fx_pcrel)
|
|
{
|
|
fixP->fx_addnumber = val;
|
|
return FALSE;
|
|
}
|
|
|
|
number_to_chars_littleendian (buf, val, size);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Apply a fixup (potentially PC-relative), set the fx_done flag if
|
|
done. */
|
|
|
|
void
|
|
md_apply_fix (fixS * fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED)
|
|
{
|
|
char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
|
|
long val = (long) *valP;
|
|
|
|
if (fixP->fx_pcrel)
|
|
{
|
|
switch (fixP->fx_r_type)
|
|
{
|
|
default:
|
|
bfd_set_error (bfd_error_bad_value);
|
|
return;
|
|
|
|
case BFD_RELOC_32:
|
|
fixP->fx_r_type = BFD_RELOC_32_PCREL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (apply_full_field_fix (fixP, buf, val, fixP->fx_size))
|
|
fixP->fx_done = 1;
|
|
}
|
|
|
|
/* Skip whitespace. */
|
|
|
|
static inline char *
|
|
skip_space (char *s)
|
|
{
|
|
while (*s == ' ' || *s == '\t')
|
|
++s;
|
|
return s;
|
|
}
|
|
|
|
/* Allow '/' in opcodes. */
|
|
|
|
static inline bfd_boolean
|
|
is_part_of_opcode (char c)
|
|
{
|
|
return is_part_of_name (c) || (c == '/');
|
|
}
|
|
|
|
/* Extract an opcode. */
|
|
|
|
static char *
|
|
extract_opcode (char *from, char *to, int limit)
|
|
{
|
|
char *op_end;
|
|
int size = 0;
|
|
|
|
/* Drop leading whitespace. */
|
|
from = skip_space (from);
|
|
*to = 0;
|
|
|
|
/* Find the op code end. */
|
|
for (op_end = from; *op_end != 0 && is_part_of_opcode (*op_end);)
|
|
{
|
|
to[size++] = *op_end++;
|
|
if (size + 1 >= limit)
|
|
break;
|
|
}
|
|
|
|
to[size] = 0;
|
|
return op_end;
|
|
}
|
|
|
|
/* Produce an unsigned LEB128 integer padded to the right number of
|
|
bytes to store BITS bits, of value VALUE. Uses FRAG_APPEND_1_CHAR
|
|
to write. */
|
|
|
|
static void
|
|
wasm32_put_long_uleb128 (int bits, unsigned long value)
|
|
{
|
|
unsigned char c;
|
|
int i = 0;
|
|
|
|
do
|
|
{
|
|
c = value & 0x7f;
|
|
value >>= 7;
|
|
if (i < (bits - 1) / 7)
|
|
c |= 0x80;
|
|
FRAG_APPEND_1_CHAR (c);
|
|
}
|
|
while (++i < (bits + 6) / 7);
|
|
}
|
|
|
|
/* Produce a signed LEB128 integer, using FRAG_APPEND_1_CHAR to
|
|
write. */
|
|
|
|
static void
|
|
wasm32_put_sleb128 (long value)
|
|
{
|
|
unsigned char c;
|
|
int more;
|
|
|
|
do
|
|
{
|
|
c = (value & 0x7f);
|
|
value >>= 7;
|
|
more = !((((value == 0) && ((c & 0x40) == 0))
|
|
|| ((value == -1) && ((c & 0x40) != 0))));
|
|
if (more)
|
|
c |= 0x80;
|
|
FRAG_APPEND_1_CHAR (c);
|
|
}
|
|
while (more);
|
|
}
|
|
|
|
/* Produce an unsigned LEB128 integer, using FRAG_APPEND_1_CHAR to
|
|
write. */
|
|
|
|
static void
|
|
wasm32_put_uleb128 (unsigned long value)
|
|
{
|
|
unsigned char c;
|
|
|
|
do
|
|
{
|
|
c = value & 0x7f;
|
|
value >>= 7;
|
|
if (value)
|
|
c |= 0x80;
|
|
FRAG_APPEND_1_CHAR (c);
|
|
}
|
|
while (value);
|
|
}
|
|
|
|
/* Read an integer expression. Produce an LEB128-encoded integer if
|
|
it's a constant, a padded LEB128 plus a relocation if it's a
|
|
symbol, or a special relocation for <expr>@got, <expr>@gotcode, and
|
|
<expr>@plt{__sigchar_<signature>}. */
|
|
|
|
static bfd_boolean
|
|
wasm32_leb128 (char **line, int bits, int sign)
|
|
{
|
|
char *t = input_line_pointer;
|
|
char *str = *line;
|
|
char *str0 = str;
|
|
struct reloc_list *reloc;
|
|
expressionS ex;
|
|
int gotrel = 0;
|
|
int pltrel = 0;
|
|
int code = 0;
|
|
const char *relname;
|
|
|
|
input_line_pointer = str;
|
|
expression (&ex);
|
|
|
|
if (ex.X_op == O_constant && *input_line_pointer != '@')
|
|
{
|
|
long value = ex.X_add_number;
|
|
|
|
str = input_line_pointer;
|
|
str = skip_space (str);
|
|
*line = str;
|
|
if (sign)
|
|
wasm32_put_sleb128 (value);
|
|
else
|
|
{
|
|
if (value < 0)
|
|
as_bad (_("unexpected negative constant"));
|
|
wasm32_put_uleb128 (value);
|
|
}
|
|
input_line_pointer = t;
|
|
return str != str0;
|
|
}
|
|
|
|
reloc = XNEW (struct reloc_list);
|
|
reloc->u.a.offset_sym = expr_build_dot ();
|
|
if (ex.X_op == O_symbol)
|
|
{
|
|
reloc->u.a.sym = ex.X_add_symbol;
|
|
reloc->u.a.addend = ex.X_add_number;
|
|
}
|
|
else
|
|
{
|
|
reloc->u.a.sym = make_expr_symbol (&ex);
|
|
reloc->u.a.addend = 0;
|
|
}
|
|
/* i32.const fpointer@gotcode */
|
|
if (strncmp (input_line_pointer, "@gotcode", 8) == 0)
|
|
{
|
|
gotrel = 1;
|
|
code = 1;
|
|
input_line_pointer += 8;
|
|
}
|
|
/* i32.const data@got */
|
|
else if (strncmp (input_line_pointer, "@got", 4) == 0)
|
|
{
|
|
gotrel = 1;
|
|
input_line_pointer += 4;
|
|
}
|
|
/* call f@plt{__sigchar_FiiiiE} */
|
|
else if (strncmp (input_line_pointer, "@plt", 4) == 0)
|
|
{
|
|
char *end_of_sig;
|
|
|
|
pltrel = 1;
|
|
code = 1;
|
|
input_line_pointer += 4;
|
|
|
|
if (strncmp (input_line_pointer, "{", 1) == 0
|
|
&& (end_of_sig = strchr (input_line_pointer, '}')))
|
|
{
|
|
char *signature;
|
|
struct reloc_list *reloc2;
|
|
size_t siglength = end_of_sig - (input_line_pointer + 1);
|
|
|
|
signature = strndup (input_line_pointer + 1, siglength);
|
|
|
|
reloc2 = XNEW (struct reloc_list);
|
|
reloc2->u.a.offset_sym = expr_build_dot ();
|
|
reloc2->u.a.sym = symbol_find_or_make (signature);
|
|
reloc2->u.a.addend = 0;
|
|
reloc2->u.a.howto = bfd_reloc_name_lookup
|
|
(stdoutput, "R_WASM32_PLT_SIG");
|
|
reloc2->next = reloc_list;
|
|
reloc_list = reloc2;
|
|
input_line_pointer = end_of_sig + 1;
|
|
}
|
|
else
|
|
{
|
|
as_bad (_("no function type on PLT reloc"));
|
|
}
|
|
}
|
|
|
|
if (gotrel && code)
|
|
relname = "R_WASM32_LEB128_GOT_CODE";
|
|
else if (gotrel)
|
|
relname = "R_WASM32_LEB128_GOT";
|
|
else if (pltrel)
|
|
relname = "R_WASM32_LEB128_PLT";
|
|
else
|
|
relname = "R_WASM32_LEB128";
|
|
|
|
reloc->u.a.howto = bfd_reloc_name_lookup (stdoutput, relname);
|
|
if (!reloc->u.a.howto)
|
|
as_bad (_("couldn't find relocation to use"));
|
|
reloc->file = as_where (&reloc->line);
|
|
reloc->next = reloc_list;
|
|
reloc_list = reloc;
|
|
|
|
str = input_line_pointer;
|
|
str = skip_space (str);
|
|
*line = str;
|
|
wasm32_put_long_uleb128 (bits, 0);
|
|
input_line_pointer = t;
|
|
|
|
return str != str0;
|
|
}
|
|
|
|
/* Read an integer expression and produce an unsigned LEB128 integer,
|
|
or a relocation for it. */
|
|
|
|
static bfd_boolean
|
|
wasm32_uleb128 (char **line, int bits)
|
|
{
|
|
return wasm32_leb128 (line, bits, 0);
|
|
}
|
|
|
|
/* Read an integer expression and produce a signed LEB128 integer, or
|
|
a relocation for it. */
|
|
|
|
static bfd_boolean
|
|
wasm32_sleb128 (char **line, int bits)
|
|
{
|
|
return wasm32_leb128 (line, bits, 1);
|
|
}
|
|
|
|
/* Read an f32. (Like float_cons ('f')). */
|
|
|
|
static void
|
|
wasm32_f32 (char **line)
|
|
{
|
|
char *t = input_line_pointer;
|
|
|
|
input_line_pointer = *line;
|
|
float_cons ('f');
|
|
*line = input_line_pointer;
|
|
input_line_pointer = t;
|
|
}
|
|
|
|
/* Read an f64. (Like float_cons ('d')). */
|
|
|
|
static void
|
|
wasm32_f64 (char **line)
|
|
{
|
|
char *t = input_line_pointer;
|
|
|
|
input_line_pointer = *line;
|
|
float_cons ('d');
|
|
*line = input_line_pointer;
|
|
input_line_pointer = t;
|
|
}
|
|
|
|
/* Assemble a signature from LINE, replacing it with the new input
|
|
pointer. Signatures are simple expressions matching the regexp
|
|
F[ilfd]*v?E, and interpreted as though they were C++-mangled
|
|
function types on a 64-bit machine. */
|
|
|
|
static void
|
|
wasm32_signature (char **line)
|
|
{
|
|
unsigned long count = 0;
|
|
char *str = *line;
|
|
char *ostr;
|
|
char *result;
|
|
|
|
if (*str++ != 'F')
|
|
as_bad (_("Not a function type"));
|
|
result = str;
|
|
ostr = str + 1;
|
|
str++;
|
|
|
|
while (*str != 'E')
|
|
{
|
|
switch (*str++)
|
|
{
|
|
case 'i':
|
|
case 'l':
|
|
case 'f':
|
|
case 'd':
|
|
count++;
|
|
break;
|
|
default:
|
|
as_bad (_("Unknown type %c\n"), str[-1]);
|
|
}
|
|
}
|
|
wasm32_put_uleb128 (count);
|
|
str = ostr;
|
|
while (*str != 'E')
|
|
{
|
|
switch (*str++)
|
|
{
|
|
case 'i':
|
|
FRAG_APPEND_1_CHAR (BLOCK_TYPE_I32);
|
|
break;
|
|
case 'l':
|
|
FRAG_APPEND_1_CHAR (BLOCK_TYPE_I64);
|
|
break;
|
|
case 'f':
|
|
FRAG_APPEND_1_CHAR (BLOCK_TYPE_F32);
|
|
break;
|
|
case 'd':
|
|
FRAG_APPEND_1_CHAR (BLOCK_TYPE_F64);
|
|
break;
|
|
default:
|
|
as_bad (_("Unknown type"));
|
|
}
|
|
}
|
|
str++;
|
|
switch (*result)
|
|
{
|
|
case 'v':
|
|
FRAG_APPEND_1_CHAR (0x00); /* no return value */
|
|
break;
|
|
case 'i':
|
|
FRAG_APPEND_1_CHAR (0x01); /* one return value */
|
|
FRAG_APPEND_1_CHAR (BLOCK_TYPE_I32);
|
|
break;
|
|
case 'l':
|
|
FRAG_APPEND_1_CHAR (0x01); /* one return value */
|
|
FRAG_APPEND_1_CHAR (BLOCK_TYPE_I64);
|
|
break;
|
|
case 'f':
|
|
FRAG_APPEND_1_CHAR (0x01); /* one return value */
|
|
FRAG_APPEND_1_CHAR (BLOCK_TYPE_F32);
|
|
break;
|
|
case 'd':
|
|
FRAG_APPEND_1_CHAR (0x01); /* one return value */
|
|
FRAG_APPEND_1_CHAR (BLOCK_TYPE_F64);
|
|
break;
|
|
default:
|
|
as_bad (_("Unknown type"));
|
|
}
|
|
*line = str;
|
|
}
|
|
|
|
/* Main operands function. Read the operands for OPCODE from LINE,
|
|
replacing it with the new input pointer. */
|
|
|
|
static void
|
|
wasm32_operands (struct wasm32_opcode_s *opcode, char **line)
|
|
{
|
|
char *str = *line;
|
|
unsigned long block_type = 0;
|
|
|
|
FRAG_APPEND_1_CHAR (opcode->opcode);
|
|
str = skip_space (str);
|
|
if (str[0] == '[')
|
|
{
|
|
if (opcode->clas == wasm_typed)
|
|
{
|
|
str++;
|
|
block_type = BLOCK_TYPE_NONE;
|
|
if (str[0] != ']')
|
|
{
|
|
str = skip_space (str);
|
|
switch (str[0])
|
|
{
|
|
case 'i':
|
|
block_type = BLOCK_TYPE_I32;
|
|
str++;
|
|
break;
|
|
case 'l':
|
|
block_type = BLOCK_TYPE_I64;
|
|
str++;
|
|
break;
|
|
case 'f':
|
|
block_type = BLOCK_TYPE_F32;
|
|
str++;
|
|
break;
|
|
case 'd':
|
|
block_type = BLOCK_TYPE_F64;
|
|
str++;
|
|
break;
|
|
}
|
|
str = skip_space (str);
|
|
if (str[0] == ']')
|
|
str++;
|
|
else
|
|
as_bad (_("only single block types allowed"));
|
|
str = skip_space (str);
|
|
}
|
|
else
|
|
{
|
|
str++;
|
|
str = skip_space (str);
|
|
}
|
|
}
|
|
else
|
|
as_bad (_("instruction does not take a block type"));
|
|
}
|
|
|
|
switch (opcode->clas)
|
|
{
|
|
case wasm_drop:
|
|
case wasm_special:
|
|
case wasm_binary:
|
|
case wasm_unary:
|
|
case wasm_relational:
|
|
case wasm_select:
|
|
case wasm_eqz:
|
|
case wasm_conv:
|
|
case wasm_return:
|
|
break;
|
|
case wasm_typed:
|
|
if (block_type == 0)
|
|
as_bad (_("missing block type"));
|
|
FRAG_APPEND_1_CHAR (block_type);
|
|
break;
|
|
case wasm_store:
|
|
case wasm_load:
|
|
if (str[0] == 'a' && str[1] == '=')
|
|
{
|
|
str += 2;
|
|
if (!wasm32_uleb128 (&str, 32))
|
|
as_bad (_("missing alignment hint"));
|
|
}
|
|
else
|
|
{
|
|
as_bad (_("missing alignment hint"));
|
|
}
|
|
str = skip_space (str);
|
|
if (!wasm32_uleb128 (&str, 32))
|
|
as_bad (_("missing offset"));
|
|
break;
|
|
case wasm_set_local:
|
|
case wasm_get_local:
|
|
case wasm_tee_local:
|
|
if (!wasm32_uleb128 (&str, 32))
|
|
as_bad (_("missing local index"));
|
|
break;
|
|
case wasm_break:
|
|
case wasm_break_if:
|
|
if (!wasm32_uleb128 (&str, 32))
|
|
as_bad (_("missing break count"));
|
|
break;
|
|
case wasm_current_memory:
|
|
case wasm_grow_memory:
|
|
if (!wasm32_uleb128 (&str, 32))
|
|
as_bad (_("missing reserved current_memory/grow_memory argument"));
|
|
break;
|
|
case wasm_call:
|
|
if (!wasm32_uleb128 (&str, 32))
|
|
as_bad (_("missing call argument"));
|
|
break;
|
|
case wasm_call_indirect:
|
|
if (!wasm32_uleb128 (&str, 32))
|
|
as_bad (_("missing call signature"));
|
|
if (!wasm32_uleb128 (&str, 32))
|
|
as_bad (_("missing table index"));
|
|
break;
|
|
case wasm_constant_i32:
|
|
wasm32_sleb128 (&str, 32);
|
|
break;
|
|
case wasm_constant_i64:
|
|
wasm32_sleb128 (&str, 64);
|
|
break;
|
|
case wasm_constant_f32:
|
|
wasm32_f32 (&str);
|
|
return;
|
|
case wasm_constant_f64:
|
|
wasm32_f64 (&str);
|
|
return;
|
|
case wasm_break_table:
|
|
{
|
|
do
|
|
{
|
|
wasm32_uleb128 (&str, 32);
|
|
str = skip_space (str);
|
|
}
|
|
while (str[0]);
|
|
|
|
break;
|
|
}
|
|
case wasm_signature:
|
|
wasm32_signature (&str);
|
|
}
|
|
str = skip_space (str);
|
|
|
|
if (*str)
|
|
as_bad (_("junk at end of line, first unrecognized character is `%c'"),
|
|
*str);
|
|
|
|
*line = str;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Main assembly function. Find the opcode and call
|
|
wasm32_operands(). */
|
|
|
|
void
|
|
md_assemble (char *str)
|
|
{
|
|
char op[32];
|
|
char *t;
|
|
struct wasm32_opcode_s *opcode;
|
|
|
|
str = skip_space (extract_opcode (str, op, sizeof (op)));
|
|
|
|
if (!op[0])
|
|
as_bad (_("can't find opcode "));
|
|
|
|
opcode = (struct wasm32_opcode_s *) hash_find (wasm32_hash, op);
|
|
|
|
if (opcode == NULL)
|
|
{
|
|
as_bad (_("unknown opcode `%s'"), op);
|
|
return;
|
|
}
|
|
|
|
dwarf2_emit_insn (0);
|
|
|
|
t = input_line_pointer;
|
|
wasm32_operands (opcode, &str);
|
|
input_line_pointer = t;
|
|
}
|
|
|
|
/* Don't replace PLT/GOT relocations with section symbols, so they
|
|
don't get an addend. */
|
|
|
|
int
|
|
wasm32_force_relocation (fixS * f)
|
|
{
|
|
if (f->fx_r_type == BFD_RELOC_WASM32_LEB128_PLT
|
|
|| f->fx_r_type == BFD_RELOC_WASM32_LEB128_GOT)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Don't replace PLT/GOT relocations with section symbols, so they
|
|
don't get an addend. */
|
|
|
|
bfd_boolean
|
|
wasm32_fix_adjustable (fixS * fixP)
|
|
{
|
|
if (fixP->fx_addsy == NULL)
|
|
return TRUE;
|
|
|
|
if (fixP->fx_r_type == BFD_RELOC_WASM32_LEB128_PLT
|
|
|| fixP->fx_r_type == BFD_RELOC_WASM32_LEB128_GOT)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Generate a reloc for FIXP. */
|
|
|
|
arelent *
|
|
tc_gen_reloc (asection * sec ATTRIBUTE_UNUSED, fixS * fixp)
|
|
{
|
|
arelent *reloc;
|
|
|
|
reloc = (arelent *) xmalloc (sizeof (*reloc));
|
|
reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
|
|
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
|
|
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
|
|
|
|
/* Make sure none of our internal relocations make it this far.
|
|
They'd better have been fully resolved by this point. */
|
|
gas_assert ((int) fixp->fx_r_type > 0);
|
|
|
|
reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
|
|
if (reloc->howto == NULL)
|
|
{
|
|
as_bad_where (fixp->fx_file, fixp->fx_line,
|
|
_("cannot represent `%s' relocation in object file"),
|
|
bfd_get_reloc_code_name (fixp->fx_r_type));
|
|
return NULL;
|
|
}
|
|
|
|
reloc->addend = fixp->fx_offset;
|
|
|
|
return reloc;
|
|
}
|