#include "as.h"
#include "subsegs.h"
#include "symcat.h"
-#include "opcodes/bpf-desc.h"
-#include "opcodes/bpf-opc.h"
-#include "cgen.h"
+#include "opcode/bpf.h"
#include "elf/common.h"
#include "elf/bpf.h"
#include "dwarf2dbg.h"
+#include "libiberty.h"
#include <ctype.h>
-const char comment_chars[] = ";";
+/* Data structure representing a parsed BPF instruction. */
+
+struct bpf_insn
+{
+ enum bpf_insn_id id;
+ int size; /* Instruction size in bytes. */
+ bpf_insn_word opcode;
+ uint8_t dst;
+ uint8_t src;
+ expressionS offset16;
+ expressionS imm32;
+ expressionS imm64;
+ expressionS disp16;
+ expressionS disp32;
+
+ unsigned int has_dst : 1;
+ unsigned int has_src : 1;
+ unsigned int has_offset16 : 1;
+ unsigned int has_disp16 : 1;
+ unsigned int has_disp32 : 1;
+ unsigned int has_imm32 : 1;
+ unsigned int has_imm64 : 1;
+
+ unsigned int is_relaxable : 1;
+ expressionS *relaxed_exp;
+};
+
+const char comment_chars[] = ";#";
const char line_comment_chars[] = "#";
const char line_separator_chars[] = "`";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "fFdD";
-static const char *invalid_expression;
-static char pseudoc_lex[256];
-static const char symbol_chars[] =
-"_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-
-static const char arithm_op[] = "+-/<>%&|^";
-
-static void init_pseudoc_lex (void);
-
-#define LEX_IS_SYMBOL_COMPONENT 1
-#define LEX_IS_WHITESPACE 2
-#define LEX_IS_NEWLINE 3
-#define LEX_IS_ARITHM_OP 4
-#define LEX_IS_STAR 6
-#define LEX_IS_CLSE_BR 7
-#define LEX_IS_OPEN_BR 8
-#define LEX_IS_EQUAL 9
-#define LEX_IS_EXCLA 10
-
-#define ST_EOI 100
-#define MAX_TOKEN_SZ 100
-
/* Like s_lcomm_internal in gas/read.c but the alignment string
is allowed to be optional. */
\f
-/* ISA handling. */
-static CGEN_BITSET *bpf_isa;
-
-\f
-
/* Command-line options processing. */
enum options
{
OPTION_LITTLE_ENDIAN = OPTION_MD_BASE,
OPTION_BIG_ENDIAN,
- OPTION_XBPF
+ OPTION_XBPF,
+ OPTION_DIALECT,
+ OPTION_ISA_SPEC,
+ OPTION_NO_RELAX,
};
struct option md_longopts[] =
{ "EL", no_argument, NULL, OPTION_LITTLE_ENDIAN },
{ "EB", no_argument, NULL, OPTION_BIG_ENDIAN },
{ "mxbpf", no_argument, NULL, OPTION_XBPF },
+ { "mdialect", required_argument, NULL, OPTION_DIALECT},
+ { "misa-spec", required_argument, NULL, OPTION_ISA_SPEC},
+ { "mno-relax", no_argument, NULL, OPTION_NO_RELAX},
{ NULL, no_argument, NULL, 0 },
};
const char * md_shortopts = "";
-extern int target_big_endian;
+/* BPF supports little-endian and big-endian variants. The following
+ global records what endianness to use. It can be configured using
+ command-line options. It defaults to the host endianness
+ initialized in md_begin. */
-/* Whether target_big_endian has been set while parsing command-line
- arguments. */
static int set_target_endian = 0;
+extern int target_big_endian;
+
+/* Whether to relax branch instructions. Default is yes. Can be
+ changed using the -mno-relax command line option. */
+
+static int do_relax = 1;
+
+/* The ISA specification can be one of BPF_V1, BPF_V2, BPF_V3, BPF_V4
+ or BPF_XPBF. The ISA spec to use can be configured using
+ command-line options. It defaults to the latest BPF spec. */
+
+static int isa_spec = BPF_V4;
+
+/* The assembler supports two different dialects: "normal" syntax and
+ "pseudoc" syntax. The dialect to use can be configured using
+ command-line options. */
-static int target_xbpf = 0;
+enum target_asm_dialect
+{
+ DIALECT_NORMAL,
+ DIALECT_PSEUDOC
+};
-static int set_xbpf = 0;
+static int asm_dialect = DIALECT_NORMAL;
int
-md_parse_option (int c, const char * arg ATTRIBUTE_UNUSED)
+md_parse_option (int c, const char * arg)
{
switch (c)
{
target_big_endian = 1;
break;
case OPTION_LITTLE_ENDIAN:
- set_target_endian = 1;
+ set_target_endian = 0;
target_big_endian = 0;
break;
+ case OPTION_DIALECT:
+ if (strcmp (arg, "normal") == 0)
+ asm_dialect = DIALECT_NORMAL;
+ else if (strcmp (arg, "pseudoc") == 0)
+ asm_dialect = DIALECT_PSEUDOC;
+ else
+ as_fatal (_("-mdialect=%s is not valid. Expected normal or pseudoc"),
+ arg);
+ break;
+ case OPTION_ISA_SPEC:
+ if (strcmp (arg, "v1") == 0)
+ isa_spec = BPF_V1;
+ else if (strcmp (arg, "v2") == 0)
+ isa_spec = BPF_V2;
+ else if (strcmp (arg, "v3") == 0)
+ isa_spec = BPF_V3;
+ else if (strcmp (arg, "v4") == 0)
+ isa_spec = BPF_V4;
+ else if (strcmp (arg, "xbpf") == 0)
+ isa_spec = BPF_XBPF;
+ else
+ as_fatal (_("-misa-spec=%s is not valid. Expected v1, v2, v3, v4 o xbpf"),
+ arg);
+ break;
case OPTION_XBPF:
- set_xbpf = 1;
- target_xbpf = 1;
+ /* This is an alias for -misa-spec=xbpf. */
+ isa_spec = BPF_XBPF;
+ break;
+ case OPTION_NO_RELAX:
+ do_relax = 0;
break;
default:
return 0;
{
fprintf (stream, _("\nBPF options:\n"));
fprintf (stream, _("\
- --EL generate code for a little endian machine\n\
- --EB generate code for a big endian machine\n\
- -mxbpf generate xBPF instructions\n"));
+BPF options:\n\
+ -EL generate code for a little endian machine\n\
+ -EB generate code for a big endian machine\n\
+ -mdialect=DIALECT set the assembly dialect (normal, pseudoc)\n\
+ -misa-spec set the BPF ISA spec (v1, v2, v3, v4, xbpf)\n\
+ -mxbpf alias for -misa-spec=xbpf\n"));
}
\f
-
-static void
-init_pseudoc_lex (void)
-{
- const char *p;
-
- for (p = symbol_chars; *p; ++p)
- pseudoc_lex[(unsigned char) *p] = LEX_IS_SYMBOL_COMPONENT;
-
- pseudoc_lex[' '] = LEX_IS_WHITESPACE;
- pseudoc_lex['\t'] = LEX_IS_WHITESPACE;
- pseudoc_lex['\r'] = LEX_IS_WHITESPACE;
- pseudoc_lex['\n'] = LEX_IS_NEWLINE;
- pseudoc_lex['*'] = LEX_IS_STAR;
- pseudoc_lex[')'] = LEX_IS_CLSE_BR;
- pseudoc_lex['('] = LEX_IS_OPEN_BR;
- pseudoc_lex[']'] = LEX_IS_CLSE_BR;
- pseudoc_lex['['] = LEX_IS_OPEN_BR;
-
- for (p = arithm_op; *p; ++p)
- pseudoc_lex[(unsigned char) *p] = LEX_IS_ARITHM_OP;
-
- pseudoc_lex['='] = LEX_IS_EQUAL;
- pseudoc_lex['!'] = LEX_IS_EXCLA;
-}
+/* This function is called once, at assembler startup time. This
+ should set up all the tables, etc that the MD part of the assembler
+ needs. */
void
md_begin (void)
{
- /* Initialize the `cgen' interface. */
-
/* If not specified in the command line, use the host
endianness. */
if (!set_target_endian)
#endif
}
- /* If not specified in the command line, use eBPF rather
- than xBPF. */
- if (!set_xbpf)
- target_xbpf = 0;
-
- /* Set the ISA, which depends on the target endianness. */
- bpf_isa = cgen_bitset_create (ISA_MAX);
- if (target_big_endian)
- {
- if (target_xbpf)
- cgen_bitset_set (bpf_isa, ISA_XBPFBE);
- else
- cgen_bitset_set (bpf_isa, ISA_EBPFBE);
- }
- else
- {
- if (target_xbpf)
- cgen_bitset_set (bpf_isa, ISA_XBPFLE);
- else
- cgen_bitset_set (bpf_isa, ISA_EBPFLE);
- }
-
/* Ensure that lines can begin with '*' in BPF store pseudoc instruction. */
lex_type['*'] |= LEX_BEGIN_NAME;
- /* Set the machine number and endian. */
- gas_cgen_cpu_desc = bpf_cgen_cpu_open (CGEN_CPU_OPEN_ENDIAN,
- target_big_endian ?
- CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE,
- CGEN_CPU_OPEN_INSN_ENDIAN,
- CGEN_ENDIAN_LITTLE,
- CGEN_CPU_OPEN_ISAS,
- bpf_isa,
- CGEN_CPU_OPEN_END);
- bpf_cgen_init_asm (gas_cgen_cpu_desc);
-
- /* This is a callback from cgen to gas to parse operands. */
- cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand);
-
/* Set the machine type. */
bfd_default_set_arch_mach (stdoutput, bfd_arch_bpf, bfd_mach_bpf);
- init_pseudoc_lex();
}
+/* Round up a section size to the appropriate boundary. */
+
valueT
md_section_align (segT segment, valueT size)
{
return ((size + (1 << align) - 1) & -(1 << align));
}
+/* Return non-zero if the indicated VALUE has overflowed the maximum
+ range expressible by an signed number with the indicated number of
+ BITS. */
+
+static bool
+signed_overflow (offsetT value, unsigned bits)
+{
+ offsetT lim;
+ if (bits >= sizeof (offsetT) * 8)
+ return false;
+ lim = (offsetT) 1 << (bits - 1);
+ return (value < -lim || value >= lim);
+}
+
\f
/* Functions concerning relocs. */
}
arelent *
-tc_gen_reloc (asection *sec, fixS *fix)
+tc_gen_reloc (asection *sec ATTRIBUTE_UNUSED, fixS *fixP)
{
- return gas_cgen_tc_gen_reloc (sec, fix);
+ bfd_reloc_code_real_type r_type = fixP->fx_r_type;
+ arelent *reloc;
+
+ reloc = XNEW (arelent);
+
+ if (fixP->fx_pcrel)
+ {
+ r_type = (r_type == BFD_RELOC_8 ? BFD_RELOC_8_PCREL
+ : r_type == BFD_RELOC_16 ? BFD_RELOC_16_PCREL
+ : r_type == BFD_RELOC_24 ? BFD_RELOC_24_PCREL
+ : r_type == BFD_RELOC_32 ? BFD_RELOC_32_PCREL
+ : r_type == BFD_RELOC_64 ? BFD_RELOC_64_PCREL
+ : r_type);
+ }
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, r_type);
+
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation is not supported"));
+ return NULL;
+ }
+
+ //XXX gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+
+ reloc->sym_ptr_ptr = XNEW (asymbol *);
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+
+ /* Use fx_offset for these cases. */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT)
+ reloc->addend = fixP->fx_offset;
+ else
+ reloc->addend = fixP->fx_addnumber;
+
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+ return reloc;
}
-/* Return the bfd reloc type for OPERAND of INSN at fixup FIXP. This
- is called when the operand is an expression that couldn't be fully
- resolved. Returns BFD_RELOC_NONE if no reloc type can be found.
- *FIXP may be modified if desired. */
+\f
+/* Relaxations supported by this assembler. */
+
+#define RELAX_BRANCH_ENCODE(uncond, constant, length) \
+ ((relax_substateT) \
+ (0xc0000000 \
+ | ((uncond) ? 1 : 0) \
+ | ((constant) ? 2 : 0) \
+ | ((length) << 2)))
+
+#define RELAX_BRANCH_P(i) (((i) & 0xf0000000) == 0xc0000000)
+#define RELAX_BRANCH_LENGTH(i) (((i) >> 2) & 0xff)
+#define RELAX_BRANCH_CONST(i) (((i) & 2) != 0)
+#define RELAX_BRANCH_UNCOND(i) (((i) & 1) != 0)
-bfd_reloc_code_real_type
-md_cgen_lookup_reloc (const CGEN_INSN *insn ATTRIBUTE_UNUSED,
- const CGEN_OPERAND *operand,
- fixS *fixP)
+
+/* Compute the length of a branch seuqence, and adjust the stored
+ length accordingly. If FRAG is NULL, the worst-case length is
+ returned. */
+
+static unsigned
+relaxed_branch_length (fragS *fragp, asection *sec, int update)
{
- switch (operand->type)
+ int length, uncond;
+
+ if (!fragp)
+ return 8 * 3;
+
+ uncond = RELAX_BRANCH_UNCOND (fragp->fr_subtype);
+ length = RELAX_BRANCH_LENGTH (fragp->fr_subtype);
+
+ if (uncond)
+ /* Length is the same for both JA and JAL. */
+ length = 8;
+ else
{
- case BPF_OPERAND_IMM64:
- return BFD_RELOC_BPF_64;
- case BPF_OPERAND_DISP32:
- fixP->fx_pcrel = 1;
- return BFD_RELOC_BPF_DISP32;
- default:
- break;
+ if (RELAX_BRANCH_CONST (fragp->fr_subtype))
+ {
+ int64_t val = fragp->fr_offset;
+
+ if (val < -32768 || val > 32767)
+ length = 8 * 3;
+ else
+ length = 8;
+ }
+ else if (fragp->fr_symbol != NULL
+ && S_IS_DEFINED (fragp->fr_symbol)
+ && !S_IS_WEAK (fragp->fr_symbol)
+ && sec == S_GET_SEGMENT (fragp->fr_symbol))
+ {
+ offsetT val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+
+ /* Convert to 64-bit words, minus one. */
+ val = (val - 8) / 8;
+
+ /* See if it fits in the signed 16-bits field. */
+ if (val < -32768 || val > 32767)
+ length = 8 * 3;
+ else
+ length = 8;
+ }
+ else
+ /* Use short version, and let the linker relax instead, if
+ appropriate and if supported. */
+ length = 8;
}
- return BFD_RELOC_NONE;
+
+ if (update)
+ fragp->fr_subtype = RELAX_BRANCH_ENCODE (uncond,
+ RELAX_BRANCH_CONST (fragp->fr_subtype),
+ length);
+
+ return length;
}
-\f
+
+/* Estimate the size of a variant frag before relaxing. */
+
+int
+md_estimate_size_before_relax (fragS *fragp, asection *sec)
+{
+ return (fragp->fr_var = relaxed_branch_length (fragp, sec, true));
+}
+
+/* Read a BPF instruction word from BUF. */
+
+static uint64_t
+read_insn_word (bfd_byte *buf)
+{
+ return bfd_getb64 (buf);
+}
+
+/* Write the given signed 16-bit value in the given BUFFER using the
+ target endianness. */
+
+static void
+encode_int16 (int16_t value, char *buffer)
+{
+ uint16_t val = value;
+
+ if (target_big_endian)
+ {
+ buffer[0] = (val >> 8) & 0xff;
+ buffer[1] = val & 0xff;
+ }
+ else
+ {
+ buffer[1] = (val >> 8) & 0xff;
+ buffer[0] = val & 0xff;
+ }
+}
+
+/* Write the given signed 32-bit value in the given BUFFER using the
+ target endianness. */
+
+static void
+encode_int32 (int32_t value, char *buffer)
+{
+ uint32_t val = value;
+
+ if (target_big_endian)
+ {
+ buffer[0] = (val >> 24) & 0xff;
+ buffer[1] = (val >> 16) & 0xff;
+ buffer[2] = (val >> 8) & 0xff;
+ buffer[3] = val & 0xff;
+ }
+ else
+ {
+ buffer[3] = (val >> 24) & 0xff;
+ buffer[2] = (val >> 16) & 0xff;
+ buffer[1] = (val >> 8) & 0xff;
+ buffer[0] = value & 0xff;
+ }
+}
+
+/* Write a BPF instruction to BUF. */
+
+static void
+write_insn_bytes (bfd_byte *buf, char *bytes)
+{
+ int i;
+
+ for (i = 0; i < 8; ++i)
+ md_number_to_chars ((char *) buf + i, (valueT) bytes[i], 1);
+}
+
/* *FRAGP has been relaxed to its final size, and now needs to have
the bytes inside it modified to conform to the new size.
void
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
segT sec ATTRIBUTE_UNUSED,
- fragS *fragP ATTRIBUTE_UNUSED)
+ fragS *fragp ATTRIBUTE_UNUSED)
{
- as_fatal (_("convert_frag called"));
-}
+ bfd_byte *buf = (bfd_byte *) fragp->fr_literal + fragp->fr_fix;
+ expressionS exp;
+ fixS *fixp;
+ bpf_insn_word word;
+ int disp_is_known = 0;
+ int64_t disp_to_target = 0;
-int
-md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
- segT segment ATTRIBUTE_UNUSED)
-{
- as_fatal (_("estimate_size_before_relax called"));
- return 0;
+ uint64_t code;
+
+ gas_assert (RELAX_BRANCH_P (fragp->fr_subtype));
+
+ /* Expression to be used in any resulting relocation in the relaxed
+ instructions. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ exp.X_add_number = fragp->fr_offset;
+
+ gas_assert (fragp->fr_var == RELAX_BRANCH_LENGTH (fragp->fr_subtype));
+
+ /* Read an instruction word from the instruction to be relaxed, and
+ get the code. */
+ word = read_insn_word (buf);
+ code = (word >> 60) & 0xf;
+
+ /* Determine whether the 16-bit displacement to the target is known
+ at this point. */
+ if (RELAX_BRANCH_CONST (fragp->fr_subtype))
+ {
+ disp_to_target = fragp->fr_offset;
+ disp_is_known = 1;
+ }
+ else if (fragp->fr_symbol != NULL
+ && S_IS_DEFINED (fragp->fr_symbol)
+ && !S_IS_WEAK (fragp->fr_symbol)
+ && sec == S_GET_SEGMENT (fragp->fr_symbol))
+ {
+ offsetT val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+ /* Convert to 64-bit blocks minus one. */
+ disp_to_target = (val - 8) / 8;
+ disp_is_known = 1;
+ }
+
+ /* The displacement should fit in a signed 32-bit number. */
+ if (disp_is_known && signed_overflow (disp_to_target, 32))
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("signed instruction operand out of range, shall fit in 32 bits"));
+
+ /* Now relax particular jump instructions. */
+ if (code == BPF_CODE_JA)
+ {
+ /* Unconditional jump.
+ JA d16 -> JAL d32 */
+
+ gas_assert (RELAX_BRANCH_UNCOND (fragp->fr_subtype));
+
+ if (disp_is_known)
+ {
+ if (disp_to_target >= -32768 && disp_to_target <= 32767)
+ {
+ /* 16-bit disp is known and in range. Install a fixup
+ for the disp16 if the branch value is not constant.
+ This will be resolved by the assembler and units
+ converted. */
+
+ if (!RELAX_BRANCH_CONST (fragp->fr_subtype))
+ {
+ /* Install fixup for the JA. */
+ reloc_howto_type *reloc_howto
+ = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_BPF_DISP16);
+ if (!reloc_howto)
+ abort();
+
+ fixp = fix_new_exp (fragp, buf - (bfd_byte *) fragp->fr_literal,
+ bfd_get_reloc_size (reloc_howto),
+ &exp,
+ reloc_howto->pc_relative,
+ BFD_RELOC_BPF_DISP16);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+ }
+ }
+ else
+ {
+ /* 16-bit disp is known and not in range. Turn the JA
+ into a JAL with a 32-bit displacement. */
+ char bytes[8];
+
+ bytes[0] = ((BPF_CLASS_JMP32|BPF_CODE_JA|BPF_SRC_K) >> 56) & 0xff;
+ bytes[1] = (word >> 48) & 0xff;
+ bytes[2] = 0; /* disp16 high */
+ bytes[3] = 0; /* disp16 lo */
+ encode_int32 ((int32_t) disp_to_target, bytes + 4);
+
+ write_insn_bytes (buf, bytes);
+ }
+ }
+ else
+ {
+ /* The displacement to the target is not known. Do not
+ relax. The linker will maybe do it if it chooses to. */
+
+ reloc_howto_type *reloc_howto = NULL;
+
+ gas_assert (!RELAX_BRANCH_CONST (fragp->fr_subtype));
+
+ /* Install fixup for the JA. */
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_BPF_DISP16);
+ if (!reloc_howto)
+ abort ();
+
+ fixp = fix_new_exp (fragp, buf - (bfd_byte *) fragp->fr_literal,
+ bfd_get_reloc_size (reloc_howto),
+ &exp,
+ reloc_howto->pc_relative,
+ BFD_RELOC_BPF_DISP16);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+ }
+
+ buf += 8;
+ }
+ else
+ {
+ /* Conditional jump.
+ JXX d16 -> JXX +1; JA +1; JAL d32 */
+
+ gas_assert (!RELAX_BRANCH_UNCOND (fragp->fr_subtype));
+
+ if (disp_is_known)
+ {
+ if (disp_to_target >= -32768 && disp_to_target <= 32767)
+ {
+ /* 16-bit disp is known and in range. Install a fixup
+ for the disp16 if the branch value is not constant.
+ This will be resolved by the assembler and units
+ converted. */
+
+ if (!RELAX_BRANCH_CONST (fragp->fr_subtype))
+ {
+ /* Install fixup for the branch. */
+ reloc_howto_type *reloc_howto
+ = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_BPF_DISP16);
+ if (!reloc_howto)
+ abort();
+
+ fixp = fix_new_exp (fragp, buf - (bfd_byte *) fragp->fr_literal,
+ bfd_get_reloc_size (reloc_howto),
+ &exp,
+ reloc_howto->pc_relative,
+ BFD_RELOC_BPF_DISP16);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+ }
+
+ buf += 8;
+ }
+ else
+ {
+ /* 16-bit disp is known and not in range. Turn the JXX
+ into a sequence JXX +1; JA +1; JAL d32. */
+
+ char bytes[8];
+
+ /* First, set the 16-bit offset in the current
+ instruction to 1. */
+
+ if (target_big_endian)
+ bfd_putb16 (1, buf + 2);
+ else
+ bfd_putl16 (1, buf + 2);
+ buf += 8;
+
+ /* Then, write the JA + 1 */
+
+ bytes[0] = 0x05; /* JA */
+ bytes[1] = 0x0;
+ encode_int16 (1, bytes + 2);
+ bytes[4] = 0x0;
+ bytes[5] = 0x0;
+ bytes[6] = 0x0;
+ bytes[7] = 0x0;
+ write_insn_bytes (buf, bytes);
+ buf += 8;
+
+ /* Finally, write the JAL to the target. */
+
+ bytes[0] = ((BPF_CLASS_JMP32|BPF_CODE_JA|BPF_SRC_K) >> 56) & 0xff;
+ bytes[1] = 0;
+ bytes[2] = 0;
+ bytes[3] = 0;
+ encode_int32 ((int32_t) disp_to_target, bytes + 4);
+ write_insn_bytes (buf, bytes);
+ buf += 8;
+ }
+ }
+ else
+ {
+ /* The displacement to the target is not known. Do not
+ relax. The linker will maybe do it if it chooses to. */
+
+ reloc_howto_type *reloc_howto = NULL;
+
+ gas_assert (!RELAX_BRANCH_CONST (fragp->fr_subtype));
+
+ /* Install fixup for the conditional jump. */
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_BPF_DISP16);
+ if (!reloc_howto)
+ abort ();
+
+ fixp = fix_new_exp (fragp, buf - (bfd_byte *) fragp->fr_literal,
+ bfd_get_reloc_size (reloc_howto),
+ &exp,
+ reloc_howto->pc_relative,
+ BFD_RELOC_BPF_DISP16);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+ buf += 8;
+ }
+ }
+
+ gas_assert (buf == (bfd_byte *)fragp->fr_literal
+ + fragp->fr_fix + fragp->fr_var);
+
+ fragp->fr_fix += fragp->fr_var;
}
\f
+/* Apply a fixS (fixup of an instruction or data that we didn't have
+ enough info to complete immediately) to the data in a frag. */
+
void
-md_apply_fix (fixS *fixP, valueT *valP, segT seg)
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
{
- /* Some fixups for instructions require special attention. This is
- handled in the code block below. */
- if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ switch (fixP->fx_r_type)
{
- int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
- const CGEN_OPERAND *operand = cgen_operand_lookup_by_num (gas_cgen_cpu_desc,
- opindex);
- char *where;
+ case BFD_RELOC_BPF_DISP16:
+ /* Convert from bytes to number of 64-bit words to the target,
+ minus one. */
+ *valP = (((long) (*valP)) - 8) / 8;
+ break;
+ case BFD_RELOC_BPF_DISPCALL32:
+ case BFD_RELOC_BPF_DISP32:
+ /* Convert from bytes to number of 64-bit words to the target,
+ minus one. */
+ *valP = (((long) (*valP)) - 8) / 8;
- switch (operand->type)
+ if (fixP->fx_r_type == BFD_RELOC_BPF_DISPCALL32)
{
- case BPF_OPERAND_DISP32:
/* eBPF supports two kind of CALL instructions: the so
called pseudo calls ("bpf to bpf") and external calls
("bpf to kernel").
Note that the CALL instruction has only one operand, so
this code is executed only once per instruction. */
- where = fixP->fx_frag->fr_literal + fixP->fx_where + 1;
- where[0] = target_big_endian ? 0x01 : 0x10;
- /* Fallthrough. */
- case BPF_OPERAND_DISP16:
- /* The PC-relative displacement fields in jump instructions
- shouldn't be in bytes. Instead, they hold the number of
- 64-bit words to the target, _minus one_. */
- *valP = (((long) (*valP)) - 8) / 8;
- break;
- default:
- break;
+ md_number_to_chars (where + 1, target_big_endian ? 0x01 : 0x10, 1);
}
+ break;
+ case BFD_RELOC_16_PCREL:
+ /* Convert from bytes to number of 64-bit words to the target,
+ minus one. */
+ *valP = (((long) (*valP)) - 8) / 8;
+ break;
+ default:
+ break;
}
- /* And now invoke CGEN's handler, which will eventually install
- *valP into the corresponding operand. */
- gas_cgen_md_apply_fix (fixP, valP, seg);
-}
-
-/*
- The BPF pseudo grammar:
-
- instruction : bpf_alu_insn
- | bpf_alu32_insn
- | bpf_jump_insn
- | bpf_load_store_insn
- | bpf_load_store32_insn
- | bpf_non_generic_load
- | bpf_endianness_conv_insn
- | bpf_64_imm_load_insn
- | bpf_atomic_insn
- ;
-
- bpf_alu_insn : BPF_REG bpf_alu_operator register_or_imm32
- ;
-
- bpf_alu32_insn : BPF_REG32 bpf_alu_operator register32_or_imm32
- ;
-
- bpf_jump_insn : BPF_JA offset
- | IF BPF_REG bpf_jump_operator register_or_imm32 BPF_JA offset
- | IF BPF_REG32 bpf_jump_operator register_or_imm32 BPF_JA offset
- | BPF_CALL offset
- | BPF_EXIT
- ;
-
- bpf_load_store_insn : BPF_REG CHR_EQUAL bpf_size_cast BPF_CHR_OPEN_BR \
- register_and_offset BPF_CHR_CLSE_BR
- | bpf_size_cast register_and_offset CHR_EQUAL BPF_REG
- ;
-
- bpf_load_store32_insn : BPF_REG CHR_EQUAL bpf_size_cast BPF_CHR_OPEN_BR \
- register32_and_offset BPF_CHR_CLSE_BR
- | bpf_size_cast register_and_offset CHR_EQUAL BPF_REG32
- ;
-
- bpf_non_generic_load : BPF_REG_R0 CHR_EQUAL bpf_size_cast BPF_LD BPF_CHR_OPEN_BR \
- imm32 BPF_CHR_CLSE_BR
- ;
-
- bpf_endianness_conv_insn : BPF_REG_N bpf_endianness_mnem BPF_REG_N
- ;
-
- bpf_64_imm_load_insn : BPF_REG imm64 BPF_LL
- ;
-
- bpf_atomic_insn : BPF_LOCK bpf_size_cast_32_64 register_and_offset BPF_ADD BPF_REG
-
- register_and_offset : BPF_CHR_OPEN_BR BPF_REG offset BPF_CHR_CLSE_BR
- ;
-
- register32_and_offset : BPF_CHR_OPEN_BR BPF_REG32 offset BPF_CHR_CLSE_BR
- ;
-
- bpf_size_cast : CHR_START BPF_CHR_OPEN_BR bpf_size CHR_START BPF_CHR_CLSE_BR
- ;
-
- bpf_size_cast_32_64 : CHR_START BPF_CHR_OPEN_BR bpf_size_cast_32_64 CHR_STAR BPF_CHR_CLSE_BR
- ;
-
- bpf_size_32_64 : BPF_CAST_U32
- | BPF_CAST_U64
- ;
-
- bpf_size : BPF_CAST_U8
- | BPF_CAST_U16
- | BPF_CAST_U32
- | BPF_CAST_U64
- ;
-
- bpf_jump_operator : BPF_JEQ
- | BPF_JGT
- | BPF_JGE
- | BPF_JNE
- | BPF_JSGT
- | BPF_JSGE
- | BPF_JLT
- | BPF_JLE
- | BPF_JSLT
- | BPF_JSLE
- ;
-
- bpf_alu_operator : BPF_ADD
- | BPF_SUB
- | BPF_MUL
- | BPF_DIV
- | BPF_OR
- | BPF_AND
- | BPF_LSH
- | BPF_RSH
- | BPF_NEG
- | BPF_MOD
- | BPF_XOR
- | BPF_ARSH
- | CHR_EQUAL
- ;
-
- bpf_endianness_mnem : BPF_LE16
- | BPF_LE32
- | BPF_LE64
- | BPF_BE16
- | BPF_BE32
- | BPF_BE64
- ;
-
- offset : BPF_EXPR
- | BPF_SYMBOL
- ;
-
- register_or_imm32 : BPF_REG
- | expression
- ;
-
- register32_or_imm32 : BPF_REG32
- | expression
- ;
-
- imm32 : BPF_EXPR
- | BPF_SYMBOL
- ;
-
- imm64 : BPF_EXPR
- | BPF_SYMBOL
- ;
-
- register_or_expression : BPF_EXPR
- | BPF_REG
- ;
-
- BPF_EXPR : GAS_EXPR
-
-*/
-
-enum bpf_token_type
- {
- /* Keep grouped to quickly access. */
- BPF_ADD,
- BPF_SUB,
- BPF_MUL,
- BPF_DIV,
- BPF_OR,
- BPF_AND,
- BPF_LSH,
- BPF_RSH,
- BPF_MOD,
- BPF_XOR,
- BPF_MOV,
- BPF_ARSH,
- BPF_NEG,
-
- BPF_REG,
-
- BPF_IF,
- BPF_GOTO,
-
- /* Keep grouped to quickly access. */
- BPF_JEQ,
- BPF_JGT,
- BPF_JGE,
- BPF_JLT,
- BPF_JLE,
- BPF_JSET,
- BPF_JNE,
- BPF_JSGT,
- BPF_JSGE,
- BPF_JSLT,
- BPF_JSLE,
-
- BPF_SYMBOL,
- BPF_CHR_CLSE_BR,
- BPF_CHR_OPEN_BR,
-
- /* Keep grouped to quickly access. */
- BPF_CAST_U8,
- BPF_CAST_U16,
- BPF_CAST_U32,
- BPF_CAST_U64,
-
- /* Keep grouped to quickly access. */
- BPF_LE16,
- BPF_LE32,
- BPF_LE64,
- BPF_BE16,
- BPF_BE32,
- BPF_BE64,
-
- BPF_LOCK,
-
- BPF_IND_CALL,
- BPF_LD,
- BPF_LL,
- BPF_EXPR,
- BPF_UNKNOWN,
- };
-
-static int
-valid_expr (const char *e, const char **end_expr)
-{
- invalid_expression = NULL;
- char *hold = input_line_pointer;
- expressionS exp;
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
- input_line_pointer = (char *) e;
- deferred_expression (&exp);
- *end_expr = input_line_pointer;
- input_line_pointer = hold;
+ if (fixP->fx_done)
+ {
+ /* We're finished with this fixup. Install it because
+ bfd_install_relocation won't be called to do it. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ md_number_to_chars (where, *valP, 1);
+ break;
+ case BFD_RELOC_16:
+ md_number_to_chars (where, *valP, 2);
+ break;
+ case BFD_RELOC_32:
+ md_number_to_chars (where, *valP, 4);
+ break;
+ case BFD_RELOC_64:
+ md_number_to_chars (where, *valP, 8);
+ break;
+ case BFD_RELOC_BPF_DISP16:
+ md_number_to_chars (where + 2, (uint16_t) *valP, 2);
+ break;
+ case BFD_RELOC_BPF_DISP32:
+ case BFD_RELOC_BPF_DISPCALL32:
+ md_number_to_chars (where + 4, (uint32_t) *valP, 4);
+ break;
+ case BFD_RELOC_16_PCREL:
+ md_number_to_chars (where + 2, (uint32_t) *valP, 2);
+ break;
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("internal error: can't install fix for reloc type %d (`%s')"),
+ fixP->fx_r_type, bfd_get_reloc_code_name (fixP->fx_r_type));
+ break;
+ }
+ }
- return invalid_expression == NULL;
+ /* Tuck `value' away for use by tc_gen_reloc.
+ See the comment describing fx_addnumber in write.h.
+ This field is misnamed (or misused :-). */
+ fixP->fx_addnumber = *valP;
}
-static char *
-build_bpf_non_generic_load (char *src, enum bpf_token_type cast,
- const char *imm32)
-{
- char *bpf_insn;
- static const char *cast_rw[] = {"b", "h", "w", "dw"};
-
- bpf_insn = xasprintf ("%s%s%s %s%s%s%s",
- "ld",
- src ? "ind" : "abs",
- cast_rw[cast - BPF_CAST_U8],
- src ? "%" : "",
- src ? src : "",
- src ? "," : "",
- imm32);
- return bpf_insn;
-}
+\f
+/* Instruction writing routines. */
-static char *
-build_bpf_atomic_insn (char *dst, char *src,
- enum bpf_token_type atomic_insn,
- enum bpf_token_type cast,
- const char *offset)
-{
- char *bpf_insn;
- static const char *cast_rw[] = {"w", "dw"};
- static const char *mnem[] = {"xadd"};
-
- bpf_insn = xasprintf ("%s%s [%%%s%s%s],%%%s", mnem[atomic_insn - BPF_ADD],
- cast_rw[cast - BPF_CAST_U32], dst,
- *offset != '+' ? "+" : "",
- offset, src);
- return bpf_insn;
-}
+/* Encode a BPF instruction in the given buffer BYTES. Non-constant
+ immediates are encoded as zeroes. */
-static char *
-build_bpf_jmp_insn (char *dst, char *src,
- char *imm32, enum bpf_token_type op,
- const char *sym, const char *offset)
+static void
+encode_insn (struct bpf_insn *insn, char *bytes, int relaxed)
{
- char *bpf_insn;
- static const char *mnem[] =
- {
- "jeq", "jgt", "jge", "jlt",
- "jle", "jset", "jne", "jsgt",
- "jsge", "jslt", "jsle"
- };
-
- const char *in32 = (*dst == 'w' ? "32" : "");
-
- *dst = 'r';
- if (src)
- *src = 'r';
-
- bpf_insn = xasprintf ("%s%s %%%s,%s%s,%s",
- mnem[op - BPF_JEQ], in32, dst,
- src ? "%" : "",
- src ? src : imm32,
- offset ? offset : sym);
- return bpf_insn;
-}
+ uint8_t src, dst;
-static char *
-build_bpf_arithm_insn (char *dst, char *src,
- int load64, const char *imm32,
- enum bpf_token_type type)
-{
- char *bpf_insn;
- static const char *mnem[] =
+ /* Zero all the bytes. */
+ memset (bytes, 0, 16);
+
+ /* First encode the opcodes. Note that we have to handle the
+ endianness groups of the BPF instructions: 8 | 4 | 4 | 16 |
+ 32. */
+ if (target_big_endian)
{
- "add", "sub", "mul", "div",
- "or", "and", "lsh", "rsh",
- "mod", "xor", "mov", "arsh",
- "neg",
- };
- const char *in32 = (*dst == 'w' ? "32" : "");
-
- *dst = 'r';
- if (src)
- *src = 'r';
-
- if (type == BPF_NEG)
- bpf_insn = xasprintf ("%s%s %%%s", mnem[type - BPF_ADD], in32, dst);
- else if (load64)
- bpf_insn = xasprintf ("%s %%%s,%s", "lddw", dst, imm32);
+ /* code */
+ bytes[0] = (insn->opcode >> 56) & 0xff;
+ /* regs */
+ bytes[1] = (insn->opcode >> 48) & 0xff;
+ /* offset16 */
+ bytes[2] = (insn->opcode >> 40) & 0xff;
+ bytes[3] = (insn->opcode >> 32) & 0xff;
+ /* imm32 */
+ bytes[4] = (insn->opcode >> 24) & 0xff;
+ bytes[5] = (insn->opcode >> 16) & 0xff;
+ bytes[6] = (insn->opcode >> 8) & 0xff;
+ bytes[7] = insn->opcode & 0xff;
+ }
else
- bpf_insn = xasprintf ("%s%s %%%s,%s%s", mnem[type - BPF_ADD],
- in32, dst,
- src ? "%" : "",
- src ? src: imm32);
- return bpf_insn;
-}
+ {
+ /* code */
+ bytes[0] = (insn->opcode >> 56) & 0xff;
+ /* regs */
+ bytes[1] = (((((insn->opcode >> 48) & 0xff) & 0xf) << 4)
+ | (((insn->opcode >> 48) & 0xff) & 0xf));
+ /* offset16 */
+ bytes[3] = (insn->opcode >> 40) & 0xff;
+ bytes[2] = (insn->opcode >> 32) & 0xff;
+ /* imm32 */
+ bytes[7] = (insn->opcode >> 24) & 0xff;
+ bytes[6] = (insn->opcode >> 16) & 0xff;
+ bytes[5] = (insn->opcode >> 8) & 0xff;
+ bytes[4] = insn->opcode & 0xff;
+ }
-static char *
-build_bpf_endianness (char *dst, enum bpf_token_type endianness)
-{
- char *bpf_insn;
- static const char *size[] = {"16", "32", "64"};
- int be = 1;
-
- if (endianness == BPF_LE16
- || endianness == BPF_LE32
- || endianness == BPF_LE64)
- be = 0;
+ /* Now the registers. */
+ src = insn->has_src ? insn->src : 0;
+ dst = insn->has_dst ? insn->dst : 0;
+
+ if (target_big_endian)
+ bytes[1] = ((dst & 0xf) << 4) | (src & 0xf);
else
- gas_assert (endianness == BPF_BE16 || endianness == BPF_BE32 || endianness == BPF_BE64);
+ bytes[1] = ((src & 0xf) << 4) | (dst & 0xf);
- bpf_insn = xasprintf ("%s %%%s,%s", be ? "endbe" : "endle",
- dst, be ? size[endianness - BPF_BE16] : size[endianness - BPF_LE16]);
- return bpf_insn;
-}
+ /* Now the immediates that are known to be constant. */
-static char *
-build_bpf_load_store_insn (char *dst, char *src,
- enum bpf_token_type cast,
- const char *offset, int isload)
-{
- char *bpf_insn;
- static const char *cast_rw[] = {"b", "h", "w", "dw"};
-
- *dst = *src = 'r';
- if (isload)
- bpf_insn = xasprintf ("%s%s %%%s,[%%%s%s%s]", "ldx",
- cast_rw[cast - BPF_CAST_U8], dst, src,
- *offset != '+' ? "+" : "",
- offset);
- else
- bpf_insn = xasprintf ("%s%s [%%%s%s%s],%%%s", "stx",
- cast_rw[cast - BPF_CAST_U8], dst,
- *offset != '+' ? "+" : "",
- offset, src);
- return bpf_insn;
-}
+ if (insn->has_imm32 && insn->imm32.X_op == O_constant)
+ {
+ int64_t imm = insn->imm32.X_add_number;
-static int
-look_for_reserved_word (const char *token, enum bpf_token_type *type)
-{
- int i;
- static struct
- {
- const char *name;
- enum bpf_token_type type;
- } reserved_words[] =
+ if (signed_overflow (imm, 32))
+ as_bad (_("signed immediate out of range, shall fit in 32 bits"));
+ else
+ encode_int32 (insn->imm32.X_add_number, bytes + 4);
+ }
+
+ if (insn->has_disp32 && insn->disp32.X_op == O_constant)
{
- {
- .name = "if",
- .type = BPF_IF
- },
- {
- .name = "goto",
- .type = BPF_GOTO
- },
- {
- .name = "le16",
- .type = BPF_LE16
- },
- {
- .name = "le32",
- .type = BPF_LE32
- },
- {
- .name = "le64",
- .type = BPF_LE64
- },
- {
- .name = "be16",
- .type = BPF_BE16
- },
- {
- .name = "be32",
- .type = BPF_BE32
- },
- {
- .name = "be64",
- .type = BPF_BE64
- },
- {
- .name = "lock",
- .type = BPF_LOCK
- },
- {
- .name = "callx",
- .type = BPF_IND_CALL
- },
- {
- .name = "skb",
- .type = BPF_LD
- },
- {
- .name = "ll",
- .type = BPF_LL
- },
- {
- .name = NULL,
- }
- };
-
- for (i = 0; reserved_words[i].name; ++i)
- if (*reserved_words[i].name == *token
- && !strcmp (reserved_words[i].name, token))
- {
- *type = reserved_words[i].type;
- return 1;
- }
+ int64_t disp = insn->disp32.X_add_number;
- return 0;
-}
+ if (signed_overflow (disp, 32))
+ as_bad (_("signed pc-relative offset out of range, shall fit in 32 bits"));
+ else
+ encode_int32 (insn->disp32.X_add_number, bytes + 4);
+ }
-static int
-is_register (const char *token, int len)
-{
- if (token[0] == 'r' || token[0] == 'w')
- if ((len == 2 && isdigit (token[1]))
- || (len == 3 && token[1] == '1' && token[2] == '0'))
- return 1;
+ if (insn->has_offset16 && insn->offset16.X_op == O_constant)
+ {
+ int64_t offset = insn->offset16.X_add_number;
- return 0;
-}
+ if (signed_overflow (offset, 16))
+ as_bad (_("signed pc-relative offset out of range, shall fit in 16 bits"));
+ else
+ encode_int16 (insn->offset16.X_add_number, bytes + 2);
+ }
-static enum bpf_token_type
-is_cast (const char *token)
-{
- static const char *cast_rw[] = {"u8", "u16", "u32", "u64"};
- unsigned int i;
+ if (insn->has_disp16 && insn->disp16.X_op == O_constant)
+ {
+ int64_t disp = insn->disp16.X_add_number;
+
+ if (!relaxed && signed_overflow (disp, 16))
+ as_bad (_("signed pc-relative offset out of range, shall fit in 16 bits"));
+ else
+ encode_int16 (insn->disp16.X_add_number, bytes + 2);
+ }
- for (i = 0; i < ARRAY_SIZE (cast_rw); ++i)
- if (!strcmp (token, cast_rw[i]))
- return BPF_CAST_U8 + i;
+ if (insn->has_imm64 && insn->imm64.X_op == O_constant)
+ {
+ uint64_t imm64 = insn->imm64.X_add_number;
- return BPF_UNKNOWN;
+ if (target_big_endian)
+ {
+ bytes[12] = (imm64 >> 56) & 0xff;
+ bytes[13] = (imm64 >> 48) & 0xff;
+ bytes[14] = (imm64 >> 40) & 0xff;
+ bytes[15] = (imm64 >> 32) & 0xff;
+ bytes[4] = (imm64 >> 24) & 0xff;
+ bytes[5] = (imm64 >> 16) & 0xff;
+ bytes[6] = (imm64 >> 8) & 0xff;
+ bytes[7] = imm64 & 0xff;
+ }
+ else
+ {
+ bytes[15] = (imm64 >> 56) & 0xff;
+ bytes[14] = (imm64 >> 48) & 0xff;
+ bytes[13] = (imm64 >> 40) & 0xff;
+ bytes[12] = (imm64 >> 32) & 0xff;
+ bytes[7] = (imm64 >> 24) & 0xff;
+ bytes[6] = (imm64 >> 16) & 0xff;
+ bytes[5] = (imm64 >> 8) & 0xff;
+ bytes[4] = imm64 & 0xff;
+ }
+ }
}
-static enum bpf_token_type
-get_token (const char **insn, char *token, size_t *tlen)
+/* Install the fixups in INSN in their proper location in the
+ specified FRAG at the location pointed by WHERE. */
+
+static void
+install_insn_fixups (struct bpf_insn *insn, fragS *frag, long where)
{
-#define GET() \
- (*str == '\0' \
- ? EOF \
- : *(unsigned char *)(str++))
-
-#define UNGET() (--str)
-
-#define START_EXPR() \
- do \
- { \
- if (expr == NULL) \
- expr = str - 1; \
- } while (0)
-
-#define SCANNER_SKIP_WHITESPACE() \
- do \
- { \
- do \
- ch = GET (); \
- while (ch != EOF \
- && ((ch) == ' ' || (ch) == '\t')); \
- if (ch != EOF) \
- UNGET (); \
- } while (0)
-
- const char *str = *insn;
- int ch, ch2 = 0;
- enum bpf_token_type ttype = BPF_UNKNOWN;
- size_t len = 0;
- const char *expr = NULL;
- const char *end_expr = NULL;
- int state = 0;
- int return_token = 0;
-
- while (1)
+ if (insn->has_imm64)
{
- ch = GET ();
-
- if (ch == EOF || len > MAX_TOKEN_SZ)
- break;
-
- switch (pseudoc_lex[(unsigned char) ch])
- {
- case LEX_IS_WHITESPACE:
- SCANNER_SKIP_WHITESPACE ();
- return_token = 1;
-
- switch (state)
- {
- case 12: /* >' ' */
- ttype = BPF_JGT;
- break;
-
- case 17: /* ==' ' */
- ttype = BPF_JEQ;
- break;
-
- case 18: /* <' ' */
- ttype = BPF_JLT;
- break;
-
- case 20: /* &' ' */
- ttype = BPF_JSET;
- break;
-
- case 22: /* s<' '*/
- ttype = BPF_JSLT;
- break;
-
- case 14: /* s> ' ' */
- ttype = BPF_JSGT;
- break;
-
- case 16: /* =' ' */
- ttype = BPF_MOV;
- break;
-
- default:
- return_token = 0;
- }
- break;
+ switch (insn->imm64.X_op)
+ {
+ case O_symbol:
+ case O_subtract:
+ case O_add:
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_BPF_64);
+ if (!reloc_howto)
+ abort ();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ fix_new_exp (frag, where,
+ size, &insn->imm64, reloc_howto->pc_relative,
+ BFD_RELOC_BPF_64);
+ break;
+ }
+ case O_constant:
+ /* Already handled in encode_insn. */
+ break;
+ default:
+ abort ();
+ }
+ }
- case LEX_IS_EXCLA:
- token[len++] = ch;
- state = 21;
- break;
+ if (insn->has_imm32)
+ {
+ switch (insn->imm32.X_op)
+ {
+ case O_symbol:
+ case O_subtract:
+ case O_add:
+ case O_uminus:
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+ if (!reloc_howto)
+ abort ();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ fix_new_exp (frag, where + 4,
+ size, &insn->imm32, reloc_howto->pc_relative,
+ BFD_RELOC_32);
+ break;
+ }
+ case O_constant:
+ /* Already handled in encode_insn. */
+ break;
+ default:
+ abort ();
+ }
+ }
- case LEX_IS_ARITHM_OP:
- if (state == 16)
- {
- /* ='-' is handle as '=' */
- UNGET ();
- ttype = BPF_MOV;
- return_token = 1;
- break;
- }
-
- START_EXPR();
- token[len++] = ch;
- switch (ch)
- {
-#define BPF_ARITHM_OP(op, type) \
- case (op): \
- state = 6; \
- ttype = (type); \
- break;
-
- BPF_ARITHM_OP('+', BPF_ADD);
- BPF_ARITHM_OP('-', BPF_SUB);
- BPF_ARITHM_OP('*', BPF_MUL);
- BPF_ARITHM_OP('/', BPF_DIV);
- BPF_ARITHM_OP('|', BPF_OR);
- BPF_ARITHM_OP('%', BPF_MOD);
- BPF_ARITHM_OP('^', BPF_XOR);
-
- case '&':
- state = 20; /* '&' */
- break;
-
- case '<':
- switch (state)
- {
- case 0:
- state = 18; /* '<' */
- break;
-
- case 18:
- state = 19; /* <'<' */
- break;
-
- case 8:
- state = 22; /* s'<' */
- break;
- }
- break;
-
- case '>':
- switch (state)
- {
- case 0:
- state = 12; /* '>' */
- break;
-
- case 12:
- state = 13; /* >'>' */
- break;
-
- case 8:
- state = 14; /* s'>' */
- break;
-
- case 14:
- state = 15; /* s>'>' */
- break;
- }
- break;
- }
- break;
+ if (insn->has_disp32)
+ {
+ switch (insn->disp32.X_op)
+ {
+ case O_symbol:
+ case O_subtract:
+ case O_add:
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+ unsigned int bfd_reloc
+ = (insn->id == BPF_INSN_CALL
+ ? BFD_RELOC_BPF_DISPCALL32
+ : BFD_RELOC_BPF_DISP32);
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, bfd_reloc);
+ if (!reloc_howto)
+ abort ();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ fix_new_exp (frag, where,
+ size, &insn->disp32, reloc_howto->pc_relative,
+ bfd_reloc);
+ break;
+ }
+ case O_constant:
+ /* Already handled in encode_insn. */
+ break;
+ default:
+ abort ();
+ }
+ }
- case LEX_IS_STAR:
- switch (state)
- {
- case 0:
- token[len++] = ch;
- START_EXPR ();
- state = 2; /* '*', It could be the fist cast char. */
- break;
-
- case 16: /* ='*' Not valid token. */
- ttype = BPF_MOV;
- return_token = 1;
- UNGET ();
- break;
-
- case 4: /* *(uXX'*' */
- token[len++] = ch;
- state = 5;
- break;
- }
- break;
+ if (insn->has_offset16)
+ {
+ switch (insn->offset16.X_op)
+ {
+ case O_symbol:
+ case O_subtract:
+ case O_add:
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+
+ /* XXX we really need a new pc-rel offset in bytes
+ relocation for this. */
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_BPF_DISP16);
+ if (!reloc_howto)
+ abort ();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ fix_new_exp (frag, where,
+ size, &insn->offset16, reloc_howto->pc_relative,
+ BFD_RELOC_BPF_DISP16);
+ break;
+ }
+ case O_constant:
+ /* Already handled in encode_insn. */
+ break;
+ default:
+ abort ();
+ }
+ }
- case LEX_IS_OPEN_BR:
- START_EXPR ();
- token[len++] = ch;
- return_token = 1;
-
- switch (state)
- {
- case 2:
- state = 3; /* *'(' second char of a cast or expr. */
- return_token = 0;
- break;
-
- case 6:
- if (valid_expr (expr, &end_expr))
- {
- len = end_expr - expr;
- memcpy (token, expr, len);
- ttype = BPF_EXPR;
- str = end_expr;
- }
- else
- {
- len = 0;
- while (*invalid_expression)
- token[len++] = *invalid_expression++;
-
- token[len] = 0;
- ttype = BPF_UNKNOWN;
- }
- break;
-
- default:
- ttype = BPF_CHR_OPEN_BR;
- SCANNER_SKIP_WHITESPACE ();
- ch2 = GET ();
-
- if ((isdigit (ch2) || ch2 == '(')
- && valid_expr (expr, &end_expr))
- {
- len = end_expr - expr;
- memcpy (token, expr, len);
- ttype = BPF_EXPR;
- str = end_expr;
- }
- else
- UNGET ();
- }
- break;
+ if (insn->has_disp16)
+ {
+ switch (insn->disp16.X_op)
+ {
+ case O_symbol:
+ case O_subtract:
+ case O_add:
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_BPF_DISP16);
+ if (!reloc_howto)
+ abort ();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ fix_new_exp (frag, where,
+ size, &insn->disp16, reloc_howto->pc_relative,
+ BFD_RELOC_BPF_DISP16);
+ break;
+ }
+ case O_constant:
+ /* Already handled in encode_insn. */
+ break;
+ default:
+ abort ();
+ }
+ }
- case LEX_IS_CLSE_BR:
- token[len++] = ch;
+}
- if (state == 0)
- {
- ttype = BPF_CHR_CLSE_BR;
- return_token = 1;
- }
- else if (state == 5) /* *(uXX*')' */
- return_token = 1;
- break;
+/* Add a new insn to the list of instructions. */
- case LEX_IS_EQUAL:
- token[len++] = ch;
- return_token = 1;
-
- switch (state)
- {
- case 0:
- state = 16; /* '=' */
- return_token = 0;
- break;
-
- case 16:
- state = 17; /* ='=' */
- return_token = 0;
- break;
-
- case 2: /* *'=' */
- ttype = BPF_MUL;
- break;
-
- case 10: /* s>>'=' */
- ttype = BPF_ARSH;
- break;
-
- case 12: /* >'=' */
- ttype = BPF_JGE;
- break;
-
- case 13: /* >>'=' */
- ttype = BPF_RSH;
- break;
-
- case 14: /* s>'=' */
- ttype = BPF_JSGE;
- break;
-
- case 15: /* s>>'=' */
- ttype = BPF_ARSH;
- break;
-
- case 18: /* <'=' */
- ttype = BPF_JLE;
- break;
-
- case 19: /* <<'=' */
- ttype = BPF_LSH;
- break;
-
- case 20: /* &'=' */
- ttype = BPF_AND;
- break;
-
- case 21: /* !'=' */
- ttype = BPF_JNE;
- break;
-
- case 22: /* s<'=' */
- ttype = BPF_JSLE;
- break;
- }
- break;
+static void
+add_fixed_insn (struct bpf_insn *insn)
+{
+ char *this_frag = frag_more (insn->size);
+ char bytes[16];
+ int i;
- case LEX_IS_SYMBOL_COMPONENT:
- return_token = 1;
-
- switch (state)
- {
- case 17: /* =='sym' */
- ttype = BPF_JEQ;
- break;
-
- case 12: /* >'sym' */
- ttype = BPF_JGT;
- break;
-
- case 18: /* <'sym' */
- ttype = BPF_JLT;
- break;
-
- case 20: /* &'sym' */
- ttype = BPF_JSET;
- break;
-
- case 14: /*s>'sym' */
- ttype = BPF_JSGT;
- break;
-
- case 22: /* s<'sym' */
- ttype = BPF_JSLT;
- break;
-
- case 16: /* ='sym' */
- ttype = BPF_MOV;
- break;
-
- default:
- return_token = 0;
- }
-
- if (return_token)
- {
- UNGET ();
- break;
- }
-
- START_EXPR ();
- token[len++] = ch;
-
- while ((ch2 = GET ()) != EOF)
- {
- int type;
-
- type = pseudoc_lex[(unsigned char) ch2];
- if (type != LEX_IS_SYMBOL_COMPONENT)
- break;
- token[len++] = ch2;
- }
-
- if (ch2 != EOF)
- UNGET ();
-
- if (state == 0)
- {
- if (len == 1 && ch == 's')
- state = 8; /* signed instructions: 's' */
- else
- {
- ttype = BPF_SYMBOL;
- if (is_register (token, len))
- ttype = BPF_REG;
- else if (look_for_reserved_word (token, &ttype))
- ;
- else if ((pseudoc_lex[(unsigned char) *token] == LEX_IS_ARITHM_OP
- || *token == '(' || isdigit(*token))
- && valid_expr (expr, &end_expr))
- {
- len = end_expr - expr;
- token[len] = '\0';
- ttype = BPF_EXPR;
- str = end_expr;
- }
-
- return_token = 1;
- }
- }
- else if (state == 3) /* *('sym' */
- {
- if ((ttype = is_cast (&token[2])) != BPF_UNKNOWN)
- state = 4; /* *('uXX' */
- else
- {
- ttype = BPF_EXPR;
- return_token = 1;
- }
- }
- else if (state == 6)
- {
- if (ttype == BPF_SUB) /* neg */
- {
- if (is_register (&token[1], len - 1))
- ttype = BPF_NEG;
- else if (valid_expr(expr, &end_expr))
- {
- len = end_expr - expr;
- memcpy(token, expr, len);
- ttype = BPF_EXPR;
- str = end_expr;
- }
- else
- {
- len = 0;
- while (*invalid_expression)
- token[len++] = *invalid_expression++;
- token[len] = 0;
- ttype = BPF_UNKNOWN;
- }
- }
- else if (valid_expr (expr, &end_expr))
- {
- len = end_expr - expr;
- memcpy(token, expr, len);
- ttype = BPF_EXPR;
- str = end_expr;
- }
- else
- ttype = BPF_UNKNOWN;
-
- return_token = 1;
- }
- break;
- }
+ /* First encode the known parts of the instruction, including
+ opcodes and constant immediates, and write them to the frag. */
+ encode_insn (insn, bytes, 0 /* relax */);
+ for (i = 0; i < insn->size; ++i)
+ md_number_to_chars (this_frag + i, (valueT) bytes[i], 1);
- if (return_token)
- {
- *tlen = len;
- *insn = str;
- break;
- }
- }
+ /* Now install the instruction fixups. */
+ install_insn_fixups (insn, frag_now,
+ this_frag - frag_now->fr_literal);
+}
- return ttype;
+/* Add a new relaxable to the list of instructions. */
-#undef GET
-#undef UNGET
-#undef START_EXPR
-#undef SCANNER_SKIP_WHITESPACE
-#undef BPF_ARITHM_OP
+static void
+add_relaxed_insn (struct bpf_insn *insn, expressionS *exp)
+{
+ char bytes[16];
+ int i;
+ char *this_frag;
+ unsigned worst_case = relaxed_branch_length (NULL, NULL, 0);
+ unsigned best_case = insn->size;
+
+ /* We only support relaxing branches, for the moment. */
+ relax_substateT subtype
+ = RELAX_BRANCH_ENCODE (insn->id == BPF_INSN_JAR,
+ exp->X_op == O_constant,
+ worst_case);
+
+ frag_grow (worst_case);
+ this_frag = frag_more (0);
+
+ /* First encode the known parts of the instruction, including
+ opcodes and constant immediates, and write them to the frag. */
+ encode_insn (insn, bytes, 1 /* relax */);
+ for (i = 0; i < insn->size; ++i)
+ md_number_to_chars (this_frag + i, (valueT) bytes[i], 1);
+
+ /* Note that instruction fixups will be applied once the frag is
+ relaxed, in md_convert_frag. */
+ frag_var (rs_machine_dependent,
+ worst_case, best_case,
+ subtype, exp->X_add_symbol, exp->X_add_number /* offset */,
+ NULL);
}
-/*
- The parser represent a FSM for the grammar described above. So for example
- the following rule:
+\f
+/* Parse an operand expression. Returns the first character that is
+ not part of the expression, or NULL in case of parse error.
- ` bpf_alu_insn : BPF_REG bpf_alu_operator register_or_imm32'
+ See md_operand below to see how exp_parse_failed is used. */
- Is parser as follows:
+static int exp_parse_failed = 0;
- 1. It starts in state 0.
+static char *
+parse_expression (char *s, expressionS *exp)
+{
+ char *saved_input_line_pointer = input_line_pointer;
+ char *saved_s = s;
- 2. Consumes next token, e.g: `BPF_REG' and set `state' variable to a
- particular state to helps to identify, in this case, that a register
- token has been read, a comment surrounded by a single quote in the
- pseudo-c token is added along with the new `state' value to indicate
- what the scanner has read, e.g.:
+ exp_parse_failed = 0;
+ input_line_pointer = s;
+ expression (exp);
+ s = input_line_pointer;
+ input_line_pointer = saved_input_line_pointer;
- state = 6; // dst_reg = str_cast ( 'src_reg'
+ switch (exp->X_op == O_absent || exp_parse_failed)
+ return NULL;
- So, in `state 6' the scanner has consumed: a destination register
- (BPF_REG), an equal character (BPF_MOV), a cast token (BPF_CAST), an
- open parenthesis (BPF_CHR_OPEN_BR) and the source register (BPF_REG).
+ /* The expression parser may consume trailing whitespaces. We have
+ to undo that since the instruction templates may be expecting
+ these whitespaces. */
+ {
+ char *p;
+ for (p = s - 1; p >= saved_s && *p == ' '; --p)
+ --s;
+ }
- 3. If the accumulated tokens represent a complete BPF pseudo-c syntax
- instruction then, a validation of the terms is made, for example: if
- the registers have the same sizes (32/64 bits), if a specific
- destination register must be used, etc., after that, a builder:
- build_bfp_{non_generic_load,atomic_insn,jmp_insn,arithm_insn,endianness,load_store_insn}
- is invoked, internally, it translates the BPF pseudo-c instruction to
- a BPF GAS instruction using the previous terms recollected by the
- scanner.
+ return s;
+}
- 4. If a successful build of BPF GAS instruction was done, a final
- state is set to `ST_EOI' (End Of Instruction) meaning that is not
- expecting for more tokens in such instruction. Otherwise if the
- conditions to calling builder are not satisfied an error is emitted
- and `parse_err' is set.
-*/
+/* Parse a BPF register name and return the corresponding register
+ number. Return NULL in case of parse error, or a pointer to the
+ first character in S that is not part of the register name. */
static char *
-bpf_pseudoc_to_normal_syntax (const char *str, char **errmsg)
+parse_bpf_register (char *s, char rw, uint8_t *regno)
{
-#define syntax_err(format, ...) \
- do \
- { \
- if (! parse_err) \
- { \
- parse_err = 1; \
- errbuf = xasprintf (format, ##__VA_ARGS__); \
- } \
- } while (0)
-
- enum bpf_token_type ttype;
- enum bpf_token_type bpf_endianness = BPF_UNKNOWN,
- bpf_atomic_insn;
- enum bpf_token_type bpf_jmp_op = BPF_JEQ; /* Arbitrary. */
- enum bpf_token_type bpf_cast = BPF_CAST_U8; /* Arbitrary. */
- enum bpf_token_type bpf_arithm_op = BPF_ADD; /* Arbitrary. */
- char *bpf_insn = NULL;
- char *errbuf = NULL;
- char src_reg[3] = {0};
- char dst_reg[3] = {0};
- char str_imm32[40] = {0};
- char str_offset[40] = {0};
- char str_symbol[MAX_TOKEN_SZ] = {0};
- char token[MAX_TOKEN_SZ] = {0};
- int state = 0;
- int parse_err = 0;
- size_t tlen;
-
- while (*str)
+ if (asm_dialect == DIALECT_NORMAL)
{
- ttype = get_token (&str, token, &tlen);
- if (ttype == BPF_UNKNOWN || state == ST_EOI)
- {
- syntax_err ("unexpected token: '%s'", token);
- break;
- }
+ rw = 'r';
+ if (*s != '%')
+ return NULL;
+ s += 1;
- switch (ttype)
+ if (*s == 'f' && *(s + 1) == 'p')
{
- case BPF_UNKNOWN:
- case BPF_LL:
- break;
-
- case BPF_REG:
- switch (state)
- {
- case 0:
- memcpy (dst_reg, token, tlen);
- state = 1; /* 'dst_reg' */
- break;
-
- case 3:
- /* dst_reg bpf_op 'src_reg' */
- memcpy (src_reg, token, tlen);
- if (*dst_reg == *src_reg)
- bpf_insn = build_bpf_arithm_insn (dst_reg, src_reg, 0,
- NULL, bpf_arithm_op);
- else
- {
- syntax_err ("different register sizes: '%s', '%s'",
- dst_reg, src_reg);
- break;
- }
- state = ST_EOI;
- break;
-
- case 5:
- memcpy (src_reg, token, tlen);
- state = 6; /* dst_reg = str_cast ( 'src_reg' */
- break;
-
- case 9:
- memcpy (dst_reg, token, tlen);
- state = 10; /* str_cast ( 'dst_reg' */
- break;
-
- case 11:
- /* str_cast ( dst_reg offset ) = 'src_reg' */
- memcpy (src_reg, token, tlen);
- bpf_insn = build_bpf_load_store_insn (dst_reg, src_reg,
- bpf_cast, str_offset, 0);
- state = ST_EOI;
- break;
-
- case 14:
- memcpy (dst_reg, token, tlen);
- state = 15; /* if 'dst_reg' */
- break;
-
- case 16:
- memcpy (src_reg, token, tlen);
- state = 17; /* if dst_reg jmp_op 'src_reg' */
- break;
-
- case 24:
- /* dst_reg = endianness src_reg */
- memcpy (src_reg, token, tlen);
- if (*dst_reg == 'r' && !strcmp (dst_reg, src_reg))
- bpf_insn = build_bpf_endianness (dst_reg, bpf_endianness);
- else
- syntax_err ("invalid operand for instruction: '%s'", token);
-
- state = ST_EOI;
- break;
-
- case 28:
- memcpy (dst_reg, token, tlen);
- state = 29; /* lock str_cast ( 'dst_reg' */
- break;
-
- case 32:
- {
- /* lock str_cast ( dst_reg offset ) atomic_insn 'src_reg' */
- int with_offset = *str_offset != '\0';
-
- memcpy (src_reg, token, tlen);
- if ((bpf_cast != BPF_CAST_U32
- && bpf_cast != BPF_CAST_U64)
- || *dst_reg != 'r'
- || *src_reg != 'r')
- syntax_err ("invalid wide atomic instruction");
- else
- bpf_insn = build_bpf_atomic_insn (dst_reg, src_reg, bpf_atomic_insn,
- bpf_cast, with_offset ? str_offset : str_symbol);
- }
-
- state = ST_EOI;
- break;
-
- case 33:
- /* callx 'dst_reg' */
- bpf_insn = xasprintf ("%s %%%s", "call", token);
- state = ST_EOI;
- break;
-
- case 35:
- memcpy (src_reg, token, tlen);
- state = 36; /* dst_reg = str_cast skb [ 'src_reg' */
- break;
- }
- break;
-
- case BPF_MOV:
- case BPF_ADD:
- case BPF_SUB:
- case BPF_MUL:
- case BPF_DIV:
- case BPF_OR:
- case BPF_AND:
- case BPF_LSH:
- case BPF_RSH:
- case BPF_MOD:
- case BPF_XOR:
- case BPF_ARSH:
- case BPF_NEG:
- switch (state)
- {
- case 1:
- state = 3; /* dst_reg 'arith_op' */
- bpf_arithm_op = ttype;
- break;
-
- case 3:
- if (ttype == BPF_NEG)
- {
- /* reg = -reg */
- bpf_arithm_op = ttype;
- memcpy (src_reg, token + 1, tlen - 1);
- if (strcmp (dst_reg, src_reg))
- {
- syntax_err ("found: '%s', expected: -%s", token, dst_reg);
- break;
- }
-
- bpf_insn = build_bpf_arithm_insn (dst_reg, src_reg, 0,
- NULL, bpf_arithm_op);
- state = ST_EOI;
- }
- break;
-
- case 23:
- memcpy (src_reg, token, tlen);
- state = 11; /* str_cast ( dst_reg offset ) '=' */
- break;
-
- case 12:
- if (ttype == BPF_MOV)
- state = 13; /* str_cast ( dst_reg offset ) '=' */
- break;
-
- case 31:
- bpf_atomic_insn = ttype;
- state = 32; /* lock str_cast ( dst_reg offset ) 'atomic_insn' */
- break;
-
- default:
- syntax_err ("unexpected '%s'", token);
- state = ST_EOI;
- }
- break;
-
- case BPF_CAST_U8:
- case BPF_CAST_U16:
- case BPF_CAST_U32:
- case BPF_CAST_U64:
- bpf_cast = ttype;
- switch (state)
- {
- case 3:
- state = 4; /* dst_reg = 'str_cast' */
- break;
-
- case 0:
- state = 8; /* 'str_cast' */
- break;
-
- case 26:
- state = 27; /* lock 'str_cast' */
- break;
- }
- break;
-
- case BPF_CHR_OPEN_BR:
- switch (state)
- {
- case 4:
- state = 5; /* dst_reg = str_cast '(' */
- break;
-
- case 8:
- state = 9; /* str_cast '(' */
- break;
-
- case 27:
- state = 28; /* lock str_cast '(' */
- break;
-
- case 34:
- state = 35; /* dst_reg = str_cast skb '[' */
- break;
- }
- break;
-
- case BPF_CHR_CLSE_BR:
- switch (state)
- {
- case 7:
- /* dst_reg = str_cast ( imm32 ')' */
- bpf_insn = build_bpf_load_store_insn (dst_reg, src_reg,
- bpf_cast, str_imm32, 1);
- state = ST_EOI;
- break;
-
- case 11:
- state = 12; /* str_cast ( dst_reg imm32 ')' */
- break;
-
- case 21:
- /* dst_reg = str_cast ( src_reg offset ')' */
- bpf_insn = build_bpf_load_store_insn (dst_reg, src_reg,
- bpf_cast, str_offset, 1);
- state = ST_EOI;
- break;
-
- case 22:
- state = 23; /* str_cast ( dst_reg offset ')' */
- break;
-
- case 30:
- state = 31; /* lock str_cast ( dst_reg offset ')' */
- break;
-
- case 37:
- /* dst_reg = str_cast skb [ src_reg imm32 ']' */
- if (*dst_reg != 'w' && !strcmp ("r0", dst_reg))
- bpf_insn = build_bpf_non_generic_load (*src_reg != '\0' ? src_reg : NULL,
- bpf_cast, str_imm32);
- else
- syntax_err ("invalid register operand: '%s'", dst_reg);
-
- state = ST_EOI;
- break;
- }
- break;
+ *regno = 10;
+ s += 2;
+ return s;
+ }
+ }
- case BPF_EXPR:
- switch (state)
- {
- case 3:
- {
- /* dst_reg bpf_arithm_op 'imm32' */
- int load64 = 0;
-
- memcpy (str_imm32, token, tlen);
- memset (token, 0, tlen);
-
- if ((ttype = get_token (&str, token, &tlen)) == BPF_LL
- && bpf_arithm_op == BPF_MOV)
- load64 = 1;
- else if (ttype != BPF_UNKNOWN)
- syntax_err ("unexpected token: '%s'", token);
-
- if (load64 && *dst_reg == 'w')
- syntax_err ("unexpected register size: '%s'", dst_reg);
-
- if (! parse_err)
- bpf_insn = build_bpf_arithm_insn (dst_reg, NULL, load64,
- str_imm32, bpf_arithm_op);
- state = ST_EOI;
- }
- break;
-
- case 18:
- {
- /* if dst_reg jmp_op src_reg goto 'offset' */
- int with_src = *src_reg != '\0';
-
- memcpy (str_offset, token, tlen);
- if (with_src && *dst_reg != *src_reg)
- syntax_err ("different register size: '%s', '%s'",
- dst_reg, src_reg);
- else
- bpf_insn = build_bpf_jmp_insn (dst_reg, with_src ? src_reg : NULL,
- with_src ? NULL: str_imm32,
- bpf_jmp_op, NULL, str_offset);
- state = ST_EOI;
- }
- break;
-
- case 19:
- /* goto 'offset' */
- memcpy (str_offset, token, tlen);
- bpf_insn = xasprintf ("%s %s", "ja", str_offset);
- state = ST_EOI;
- break;
-
- case 6:
- memcpy (str_offset, token, tlen);
- state = 21; /* dst_reg = str_cast ( src_reg 'offset' */
- break;
-
- case 10:
- memcpy (str_offset, token, tlen);
- state = 22; /* str_cast ( dst_reg 'offset' */
- break;
-
- case 16:
- memcpy (str_imm32, token, tlen);
- state = 25; /* if dst_reg jmp_op 'imm32' */
- break;
-
- case 29:
- memcpy (str_offset, token, tlen);
- state = 30; /* lock str_cast ( dst_reg 'offset' */
- break;
-
- case 34:
- /* dst_reg = str_cast skb 'imm32' */
- if (*dst_reg != 'w' && !strcmp ("r0", dst_reg))
- {
- memcpy (str_imm32, token, tlen);
- bpf_insn = build_bpf_non_generic_load (*src_reg != '\0' ? src_reg : NULL,
- bpf_cast, str_imm32);
- }
- else
- syntax_err ("invalid register operand: '%s'", dst_reg);
-
- state = ST_EOI;
- break;
-
- case 36:
- memcpy (str_imm32, token, tlen);
- state = 37; /* dst_reg = str_cast skb [ src_reg 'imm32' */
- break;
- }
- break;
+ if (*s != rw)
+ return NULL;
+ s += 1;
- case BPF_IF:
- if (state == 0)
- state = 14;
- break;
+ if (*s == '1')
+ {
+ if (*(s + 1) == '0')
+ {
+ *regno = 10;
+ s += 2;
+ }
+ else
+ {
+ *regno = 1;
+ s += 1;
+ }
+ }
+ else if (*s >= '0' && *s <= '9')
+ {
+ *regno = *s - '0';
+ s += 1;
+ }
- case BPF_JSGT:
- case BPF_JSLT:
- case BPF_JSLE:
- case BPF_JSGE:
- case BPF_JGT:
- case BPF_JGE:
- case BPF_JLE:
- case BPF_JSET:
- case BPF_JNE:
- case BPF_JLT:
- case BPF_JEQ:
- if (state == 15)
- {
- bpf_jmp_op = ttype;
- state = 16; /* if dst_reg 'jmp_op' */
- }
- break;
+ return s;
+}
- case BPF_GOTO:
- switch (state)
- {
- case 17:
- case 25:
- state = 18; /* if dst_reg jmp_op src_reg|imm32 'goto' */
- break;
-
- case 0:
- state = 19;
- break;
- }
- break;
+/* Collect a parse error message. */
- case BPF_SYMBOL:
- switch (state)
- {
- case 18:
- {
- /* if dst_reg jmp_op src_reg goto 'sym' */
- int with_src = *src_reg != '\0';
-
- memcpy (str_symbol, token, tlen);
- if (with_src && *dst_reg != *src_reg)
- syntax_err ("different register size: '%s', '%s'",
- dst_reg, src_reg);
- else
- bpf_insn = build_bpf_jmp_insn (dst_reg, with_src ? src_reg : NULL,
- with_src ? NULL: str_imm32,
- bpf_jmp_op, str_symbol, NULL);
- state = ST_EOI;
- }
- break;
-
- case 19:
- /* goto 'sym' */
- memcpy (str_symbol, token, tlen);
- bpf_insn = xasprintf ("%s %s", "ja", str_symbol);
- state = ST_EOI;
- break;
-
- case 0:
- state = ST_EOI;
- break;
-
- case 3:
- {
- /* dst_reg arithm_op 'sym' */
- int load64 = 0;
-
- memcpy (str_symbol, token, tlen);
- memset (token, 0, tlen);
-
- if ((ttype = get_token (&str, token, &tlen)) == BPF_LL
- && bpf_arithm_op == BPF_MOV)
- load64 = 1;
- else if (ttype != BPF_UNKNOWN)
- syntax_err ("unexpected token: '%s'", token);
-
- if (load64 && *dst_reg == 'w')
- syntax_err ("unexpected register size: '%s'", dst_reg);
-
- if (! parse_err)
- bpf_insn = build_bpf_arithm_insn (dst_reg, NULL, load64,
- str_symbol, bpf_arithm_op);
- state = ST_EOI;
- }
- break;
- }
- break;
+static int partial_match_length = 0;
+static char *errmsg = NULL;
- case BPF_LE16:
- case BPF_LE32:
- case BPF_LE64:
- case BPF_BE16:
- case BPF_BE32:
- case BPF_BE64:
- bpf_endianness = ttype;
- state = 24; /* dst_reg = 'endianness' */
- break;
+static void
+parse_error (int length, const char *fmt, ...)
+{
+ if (length > partial_match_length)
+ {
+ va_list args;
- case BPF_LOCK:
- state = 26;
- break;
+ free (errmsg);
+ va_start (args, fmt);
+ errmsg = xvasprintf (fmt, args);
+ va_end (args);
+ partial_match_length = length;
+ }
+}
- case BPF_IND_CALL:
- state = 33;
- break;
+/* Assemble a machine instruction in STR and emit the frags/bytes it
+ assembles to. */
- case BPF_LD:
- state = 34; /* dst_reg = str_cast 'skb' */
- break;
- }
+void
+md_assemble (char *str ATTRIBUTE_UNUSED)
+{
+ /* There are two different syntaxes that can be used to write BPF
+ instructions. One is very conventional and like any other
+ assembly language where each instruction is conformed by an
+ instruction mnemonic followed by its operands. This is what we
+ call the "normal" syntax. The other syntax tries to look like C
+ statements. We have to support both syntaxes in this assembler.
+
+ One of the many nuisances introduced by this eccentricity is that
+ in the pseudo-c syntax it is not possible to hash the opcodes
+ table by instruction mnemonic, because there is none. So we have
+ no other choice than to try to parse all instruction opcodes
+ until one matches. This is slow.
+
+ Another problem is that emitting detailed diagnostics becomes
+ tricky, since the lack of mnemonic means it is not clear what
+ instruction was intended by the user, and we cannot emit
+ diagnostics for every attempted template. So if an instruction
+ is not parsed, we report the diagnostic corresponding to the
+ partially parsed instruction that was matched further. */
+
+ unsigned int idx = 0;
+ struct bpf_insn insn;
+ const struct bpf_opcode *opcode;
+
+ /* Initialize the global diagnostic variables. See the parse_error
+ function above. */
+ partial_match_length = 0;
+ errmsg = NULL;
+
+#define PARSE_ERROR(...) parse_error (s - str, __VA_ARGS__)
+
+ while ((opcode = bpf_get_opcode (idx++)) != NULL)
+ {
+ const char *p;
+ char *s;
+ const char *template
+ = (asm_dialect == DIALECT_PSEUDOC ? opcode->pseudoc : opcode->normal);
+
+ /* Do not try to match opcodes with a higher version than the
+ selected ISA spec. */
+ if (opcode->version > isa_spec)
+ continue;
+
+ memset (&insn, 0, sizeof (struct bpf_insn));
+ insn.size = 8;
+ for (s = str, p = template; *p != '\0';)
+ {
+ if (*p == ' ')
+ {
+ /* Expect zero or more spaces. */
+ while (*s != '\0' && (*s == ' ' || *s == '\t'))
+ s += 1;
+ p += 1;
+ }
+ else if (*p == '%')
+ {
+ if (*(p + 1) == '%')
+ {
+ if (*s != '%')
+ {
+ PARSE_ERROR ("expected '%%'");
+ break;
+ }
+ p += 2;
+ s += 1;
+ }
+ else if (*(p + 1) == 'w')
+ {
+ /* Expect zero or more spaces. */
+ while (*s != '\0' && (*s == ' ' || *s == '\t'))
+ s += 1;
+ p += 2;
+ }
+ else if (*(p + 1) == 'W')
+ {
+ /* Expect one or more spaces. */
+ if (*s != ' ' && *s != '\t')
+ {
+ PARSE_ERROR ("expected white space, got '%s'",
+ s);
+ break;
+ }
+ while (*s != '\0' && (*s == ' ' || *s == '\t'))
+ s += 1;
+ p += 2;
+ }
+ else if (strncmp (p, "%dr", 3) == 0)
+ {
+ uint8_t regno;
+ char *news = parse_bpf_register (s, 'r', ®no);
+
+ if (news == NULL || (insn.has_dst && regno != insn.dst))
+ {
+ if (news != NULL)
+ PARSE_ERROR ("expected register r%d, got r%d",
+ insn.dst, regno);
+ else
+ PARSE_ERROR ("expected register name, got '%s'", s);
+ break;
+ }
+ s = news;
+ insn.dst = regno;
+ insn.has_dst = 1;
+ p += 3;
+ }
+ else if (strncmp (p, "%sr", 3) == 0)
+ {
+ uint8_t regno;
+ char *news = parse_bpf_register (s, 'r', ®no);
+
+ if (news == NULL || (insn.has_src && regno != insn.src))
+ {
+ if (news != NULL)
+ PARSE_ERROR ("expected register r%d, got r%d",
+ insn.dst, regno);
+ else
+ PARSE_ERROR ("expected register name, got '%s'", s);
+ break;
+ }
+ s = news;
+ insn.src = regno;
+ insn.has_src = 1;
+ p += 3;
+ }
+ else if (strncmp (p, "%dw", 3) == 0)
+ {
+ uint8_t regno;
+ char *news = parse_bpf_register (s, 'w', ®no);
+
+ if (news == NULL || (insn.has_dst && regno != insn.dst))
+ {
+ if (news != NULL)
+ PARSE_ERROR ("expected register r%d, got r%d",
+ insn.dst, regno);
+ else
+ PARSE_ERROR ("expected register name, got '%s'", s);
+ break;
+ }
+ s = news;
+ insn.dst = regno;
+ insn.has_dst = 1;
+ p += 3;
+ }
+ else if (strncmp (p, "%sw", 3) == 0)
+ {
+ uint8_t regno;
+ char *news = parse_bpf_register (s, 'w', ®no);
+
+ if (news == NULL || (insn.has_src && regno != insn.src))
+ {
+ if (news != NULL)
+ PARSE_ERROR ("expected register r%d, got r%d",
+ insn.dst, regno);
+ else
+ PARSE_ERROR ("expected register name, got '%s'", s);
+ break;
+ }
+ s = news;
+ insn.src = regno;
+ insn.has_src = 1;
+ p += 3;
+ }
+ else if (strncmp (p, "%i32", 4) == 0
+ || strncmp (p, "%I32", 4) == 0)
+ {
+ if (p[1] == 'I')
+ {
+ while (*s == ' ' || *s == '\t')
+ s += 1;
+ if (*s != '+' && *s != '-')
+ {
+ PARSE_ERROR ("expected `+' or `-', got `%c'", *s);
+ break;
+ }
+ }
+
+ s = parse_expression (s, &insn.imm32);
+ if (s == NULL)
+ {
+ PARSE_ERROR ("expected signed 32-bit immediate");
+ break;
+ }
+ insn.has_imm32 = 1;
+ p += 4;
+ }
+ else if (strncmp (p, "%o16", 4) == 0)
+ {
+ while (*s == ' ' || *s == '\t')
+ s += 1;
+ if (*s != '+' && *s != '-')
+ {
+ PARSE_ERROR ("expected `+' or `-', got `%c'", *s);
+ break;
+ }
+
+ s = parse_expression (s, &insn.offset16);
+ if (s == NULL)
+ {
+ PARSE_ERROR ("expected signed 16-bit offset");
+ break;
+ }
+ insn.has_offset16 = 1;
+ p += 4;
+ }
+ else if (strncmp (p, "%d16", 4) == 0)
+ {
+ s = parse_expression (s, &insn.disp16);
+ if (s == NULL)
+ {
+ PARSE_ERROR ("expected signed 16-bit displacement");
+ break;
+ }
+ insn.has_disp16 = 1;
+ insn.is_relaxable = 1;
+ p += 4;
+ }
+ else if (strncmp (p, "%d32", 4) == 0)
+ {
+ s = parse_expression (s, &insn.disp32);
+ if (s == NULL)
+ {
+ PARSE_ERROR ("expected signed 32-bit displacement");
+ break;
+ }
+ insn.has_disp32 = 1;
+ p += 4;
+ }
+ else if (strncmp (p, "%i64", 4) == 0)
+ {
+ s = parse_expression (s, &insn.imm64);
+ if (s == NULL)
+ {
+ PARSE_ERROR ("expected signed 64-bit immediate");
+ break;
+ }
+ insn.has_imm64 = 1;
+ insn.size = 16;
+ p += 4;
+ }
+ else
+ as_fatal (_("invalid %%-tag in BPF opcode '%s'\n"), template);
+ }
+ else
+ {
+ /* Match a literal character. */
+ if (*s != *p)
+ {
+ if (*s == '\0')
+ PARSE_ERROR ("expected '%c'", *p);
+ else if (*s == '%')
+ {
+ /* This is to workaround a bug in as_bad. */
+ char tmp[3];
+
+ tmp[0] = '%';
+ tmp[1] = '%';
+ tmp[2] = '\0';
+
+ PARSE_ERROR ("expected '%c', got '%s'", *p, tmp);
+ }
+ else
+ PARSE_ERROR ("expected '%c', got '%c'", *p, *s);
+ break;
+ }
+ p += 1;
+ s += 1;
+ }
+ }
- memset (token, 0, tlen);
+ if (*p == '\0')
+ {
+ /* Allow white spaces at the end of the line. */
+ while (*s != '\0' && (*s == ' ' || *s == '\t'))
+ s += 1;
+ if (*s == '\0')
+ /* We parsed an instruction successfully. */
+ break;
+ PARSE_ERROR ("extra junk at end of line");
+ }
}
- if (state != ST_EOI)
- syntax_err ("incomplete instruction");
-
- *errmsg = errbuf;
- return bpf_insn;
+ if (opcode == NULL)
+ {
+ as_bad (_("unrecognized instruction `%s'"), str);
+ if (errmsg != NULL)
+ {
+ as_bad ("%s", errmsg);
+ free (errmsg);
+ }
-#undef syntax_err
-}
+ return;
+ }
+ insn.id = opcode->id;
+ insn.opcode = opcode->opcode;
-void
-md_assemble (char *str)
-{
- const CGEN_INSN *insn;
- char *errmsg;
- char *a_errmsg;
- CGEN_FIELDS fields;
- char *normal;
-
-#if CGEN_INT_INSN_P
- CGEN_INSN_INT buffer[CGEN_MAX_INSN_SIZE / sizeof (CGEN_INT_INSN_P)];
-#else
- unsigned char buffer[CGEN_MAX_INSN_SIZE];
-#endif
+#undef PARSE_ERROR
- gas_cgen_init_parse ();
- insn = bpf_cgen_assemble_insn (gas_cgen_cpu_desc, str, &fields,
- buffer, &errmsg);
- if (insn == NULL)
+ /* Generate the frags and fixups for the parsed instruction. */
+ if (do_relax && isa_spec >= BPF_V4 && insn.is_relaxable)
{
- normal = bpf_pseudoc_to_normal_syntax (str, &a_errmsg);
- if (normal)
- {
- insn = bpf_cgen_assemble_insn (gas_cgen_cpu_desc, normal, &fields,
- buffer, &a_errmsg);
- xfree (normal);
- }
+ expressionS *relaxable_exp = NULL;
- if (insn == NULL)
- {
- as_bad ("%s", errmsg);
- if (a_errmsg)
- {
- as_bad ("%s", a_errmsg);
- xfree (a_errmsg);
- }
- return;
- }
+ if (insn.has_disp16)
+ relaxable_exp = &insn.disp16;
+ else
+ abort ();
+
+ add_relaxed_insn (&insn, relaxable_exp);
}
+ else
+ add_fixed_insn (&insn);
- gas_cgen_finish_insn (insn, buffer, CGEN_FIELDS_BITSIZE (&fields),
- 0, /* zero to ban relaxable insns. */
- NULL); /* NULL so results not returned here. */
+ /* Emit DWARF2 debugging information. */
+ dwarf2_emit_insn (insn.size);
}
+/* Parse an operand that is machine-specific. */
+
void
md_operand (expressionS *expressionP)
{
- invalid_expression = input_line_pointer - 1;
- gas_cgen_md_operand (expressionP);
+ /* If this hook is invoked it means GAS failed to parse a generic
+ expression. We should inhibit the as_bad in expr.c, so we can fail
+ while parsing instruction alternatives. To do that, we change the
+ expression to not have an O_absent. But then we also need to set
+ exp_parse_failed to parse_expression above does the right thing. */
+ ++input_line_pointer;
+ expressionP->X_op = O_constant;
+ expressionP->X_add_number = 0;
+ exp_parse_failed = 1;
}
-
symbolS *
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
{
{
return ieee_md_atof (type, litP, sizeP, false);
}
+
+\f
+/* Determine whether the equal sign in the given string corresponds to
+ a BPF instruction, i.e. when it is not to be considered a symbol
+ assignment. */
+
+bool
+bpf_tc_equal_in_insn (int c ATTRIBUTE_UNUSED, char *str ATTRIBUTE_UNUSED)
+{
+ uint8_t regno;
+
+ /* Only pseudo-c instructions can have equal signs, and of these,
+ all that could be confused with a symbol assignment all start
+ with a register name. */
+ if (asm_dialect == DIALECT_PSEUDOC)
+ {
+ char *w = parse_bpf_register (str, 'w', ®no);
+ char *r = parse_bpf_register (str, 'r', ®no);
+
+ if ((w != NULL && *w == '\0')
+ || (r != NULL && *r == '\0'))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Some special processing for a BPF ELF file. */
+
+void
+bpf_elf_final_processing (void)
+{
+ /* Annotate the BPF ISA version in the ELF flag bits. */
+ elf_elfheader (stdoutput)->e_flags |= (isa_spec & EF_BPF_CPUVER);
+}