2017-03-30 11:57:21 +02:00
|
|
|
/* tc-wasm32.c -- Assembler code for the wasm32 target.
|
|
|
|
|
2019-01-01 11:31:27 +01:00
|
|
|
Copyright (C) 2017-2019 Free Software Foundation, Inc.
|
2017-03-30 11:57:21 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
bfd_section_* macros
This large patch removes the unnecessary bfd parameter from various
bfd section macros and functions. The bfd is hardly ever used and if
needed for the bfd_set_section_* or bfd_rename_section functions can
be found via section->owner except for the com, und, abs, and ind
std_section special sections. Those sections shouldn't be modified
anyway.
The patch also removes various bfd_get_section_<field> macros,
replacing their use with bfd_section_<field>, and adds
bfd_set_section_lma. I've also fixed a minor bug in gas where
compressed section renaming was done directly rather than calling
bfd_rename_section. This would have broken bfd_get_section_by_name
and similar functions, but that hardly mattered at such a late stage
in gas processing.
bfd/
* bfd-in.h (bfd_get_section_name, bfd_get_section_vma),
(bfd_get_section_lma, bfd_get_section_alignment),
(bfd_get_section_size, bfd_get_section_flags),
(bfd_get_section_userdata): Delete.
(bfd_section_name, bfd_section_size, bfd_section_vma),
(bfd_section_lma, bfd_section_alignment): Lose bfd parameter.
(bfd_section_flags, bfd_section_userdata): New.
(bfd_is_com_section): Rename parameter.
* section.c (bfd_set_section_userdata, bfd_set_section_vma),
(bfd_set_section_alignment, bfd_set_section_flags, bfd_rename_section),
(bfd_set_section_size): Delete bfd parameter, rename section parameter.
(bfd_set_section_lma): New.
* bfd-in2.h: Regenerate.
* mach-o.c (bfd_mach_o_init_section_from_mach_o): Delete bfd param,
update callers.
* aoutx.h, * bfd.c, * coff-alpha.c, * coff-arm.c, * coff-mips.c,
* coff64-rs6000.c, * coffcode.h, * coffgen.c, * cofflink.c,
* compress.c, * ecoff.c, * elf-eh-frame.c, * elf-hppa.h,
* elf-ifunc.c, * elf-m10200.c, * elf-m10300.c, * elf-properties.c,
* elf-s390-common.c, * elf-vxworks.c, * elf.c, * elf32-arc.c,
* elf32-arm.c, * elf32-avr.c, * elf32-bfin.c, * elf32-cr16.c,
* elf32-cr16c.c, * elf32-cris.c, * elf32-crx.c, * elf32-csky.c,
* elf32-d10v.c, * elf32-epiphany.c, * elf32-fr30.c, * elf32-frv.c,
* elf32-ft32.c, * elf32-h8300.c, * elf32-hppa.c, * elf32-i386.c,
* elf32-ip2k.c, * elf32-iq2000.c, * elf32-lm32.c, * elf32-m32c.c,
* elf32-m32r.c, * elf32-m68hc1x.c, * elf32-m68k.c, * elf32-mcore.c,
* elf32-mep.c, * elf32-metag.c, * elf32-microblaze.c,
* elf32-moxie.c, * elf32-msp430.c, * elf32-mt.c, * elf32-nds32.c,
* elf32-nios2.c, * elf32-or1k.c, * elf32-ppc.c, * elf32-pru.c,
* elf32-rl78.c, * elf32-rx.c, * elf32-s390.c, * elf32-score.c,
* elf32-score7.c, * elf32-sh.c, * elf32-spu.c, * elf32-tic6x.c,
* elf32-tilepro.c, * elf32-v850.c, * elf32-vax.c, * elf32-visium.c,
* elf32-xstormy16.c, * elf32-xtensa.c, * elf64-alpha.c,
* elf64-bpf.c, * elf64-hppa.c, * elf64-ia64-vms.c, * elf64-mmix.c,
* elf64-ppc.c, * elf64-s390.c, * elf64-sparc.c, * elf64-x86-64.c,
* elflink.c, * elfnn-aarch64.c, * elfnn-ia64.c, * elfnn-riscv.c,
* elfxx-aarch64.c, * elfxx-mips.c, * elfxx-sparc.c,
* elfxx-tilegx.c, * elfxx-x86.c, * i386msdos.c, * linker.c,
* mach-o.c, * mmo.c, * opncls.c, * pdp11.c, * pei-x86_64.c,
* peicode.h, * reloc.c, * section.c, * syms.c, * vms-alpha.c,
* xcofflink.c: Update throughout for bfd section macro and function
changes.
binutils/
* addr2line.c, * bucomm.c, * coffgrok.c, * dlltool.c, * nm.c,
* objcopy.c, * objdump.c, * od-elf32_avr.c, * od-macho.c,
* od-xcoff.c, * prdbg.c, * rdcoff.c, * rddbg.c, * rescoff.c,
* resres.c, * size.c, * srconv.c, * strings.c, * windmc.c: Update
throughout for bfd section macro and function changes.
gas/
* as.c, * as.h, * dw2gencfi.c, * dwarf2dbg.c, * ecoff.c,
* read.c, * stabs.c, * subsegs.c, * subsegs.h, * write.c,
* config/obj-coff-seh.c, * config/obj-coff.c, * config/obj-ecoff.c,
* config/obj-elf.c, * config/obj-macho.c, * config/obj-som.c,
* config/tc-aarch64.c, * config/tc-alpha.c, * config/tc-arc.c,
* config/tc-arm.c, * config/tc-avr.c, * config/tc-bfin.c,
* config/tc-bpf.c, * config/tc-d10v.c, * config/tc-d30v.c,
* config/tc-epiphany.c, * config/tc-fr30.c, * config/tc-frv.c,
* config/tc-h8300.c, * config/tc-hppa.c, * config/tc-i386.c,
* config/tc-ia64.c, * config/tc-ip2k.c, * config/tc-iq2000.c,
* config/tc-lm32.c, * config/tc-m32c.c, * config/tc-m32r.c,
* config/tc-m68hc11.c, * config/tc-mep.c, * config/tc-microblaze.c,
* config/tc-mips.c, * config/tc-mmix.c, * config/tc-mn10200.c,
* config/tc-mn10300.c, * config/tc-msp430.c, * config/tc-mt.c,
* config/tc-nds32.c, * config/tc-or1k.c, * config/tc-ppc.c,
* config/tc-pru.c, * config/tc-rl78.c, * config/tc-rx.c,
* config/tc-s12z.c, * config/tc-s390.c, * config/tc-score.c,
* config/tc-score7.c, * config/tc-sh.c, * config/tc-sparc.c,
* config/tc-spu.c, * config/tc-tic4x.c, * config/tc-tic54x.c,
* config/tc-tic6x.c, * config/tc-tilegx.c, * config/tc-tilepro.c,
* config/tc-v850.c, * config/tc-visium.c, * config/tc-wasm32.c,
* config/tc-xc16x.c, * config/tc-xgate.c, * config/tc-xstormy16.c,
* config/tc-xtensa.c, * config/tc-z8k.c: Update throughout for
bfd section macro and function changes.
* write.c (compress_debug): Use bfd_rename_section.
gdb/
* aarch64-linux-tdep.c, * arm-tdep.c, * auto-load.c,
* coff-pe-read.c, * coffread.c, * corelow.c, * dbxread.c,
* dicos-tdep.c, * dwarf2-frame.c, * dwarf2read.c, * elfread.c,
* exec.c, * fbsd-tdep.c, * gcore.c, * gdb_bfd.c, * gdb_bfd.h,
* hppa-tdep.c, * i386-cygwin-tdep.c, * i386-fbsd-tdep.c,
* i386-linux-tdep.c, * jit.c, * linux-tdep.c, * machoread.c,
* maint.c, * mdebugread.c, * minidebug.c, * mips-linux-tdep.c,
* mips-sde-tdep.c, * mips-tdep.c, * mipsread.c, * nto-tdep.c,
* objfiles.c, * objfiles.h, * osabi.c, * ppc-linux-tdep.c,
* ppc64-tdep.c, * record-btrace.c, * record-full.c, * remote.c,
* rs6000-aix-tdep.c, * rs6000-tdep.c, * s390-linux-tdep.c,
* s390-tdep.c, * solib-aix.c, * solib-dsbt.c, * solib-frv.c,
* solib-spu.c, * solib-svr4.c, * solib-target.c,
* spu-linux-nat.c, * spu-tdep.c, * symfile-mem.c, * symfile.c,
* symmisc.c, * symtab.c, * target.c, * windows-nat.c,
* xcoffread.c, * cli/cli-dump.c, * compile/compile-object-load.c,
* mi/mi-interp.c: Update throughout for bfd section macro and
function changes.
* gcore (gcore_create_callback): Use bfd_set_section_lma.
* spu-tdep.c (spu_overlay_new_objfile): Likewise.
gprof/
* corefile.c, * symtab.c: Update throughout for bfd section
macro and function changes.
ld/
* ldcref.c, * ldctor.c, * ldelf.c, * ldlang.c, * pe-dll.c,
* emultempl/aarch64elf.em, * emultempl/aix.em,
* emultempl/armcoff.em, * emultempl/armelf.em,
* emultempl/cr16elf.em, * emultempl/cskyelf.em,
* emultempl/m68hc1xelf.em, * emultempl/m68kelf.em,
* emultempl/mipself.em, * emultempl/mmix-elfnmmo.em,
* emultempl/mmo.em, * emultempl/msp430.em,
* emultempl/nios2elf.em, * emultempl/pe.em, * emultempl/pep.em,
* emultempl/ppc64elf.em, * emultempl/xtensaelf.em: Update
throughout for bfd section macro and function changes.
libctf/
* ctf-open-bfd.c: Update throughout for bfd section macro changes.
opcodes/
* arc-ext.c: Update throughout for bfd section macro changes.
sim/
* common/sim-load.c, * common/sim-utils.c, * cris/sim-if.c,
* erc32/func.c, * lm32/sim-if.c, * m32c/load.c, * m32c/trace.c,
* m68hc11/interp.c, * ppc/hw_htab.c, * ppc/hw_init.c,
* rl78/load.c, * rl78/trace.c, * rx/gdb-if.c, * rx/load.c,
* rx/trace.c: Update throughout for bfd section macro changes.
2019-09-16 12:55:17 +02:00
|
|
|
int align = bfd_section_alignment (seg);
|
2017-03-30 11:57:21 +02:00
|
|
|
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;
|
|
|
|
}
|