/* i386.c -- Assemble code for the Intel 80386
- Copyright (C) 1989, 1991, 1992, 1993 Free Software Foundation.
+ Copyright (C) 1989, 91, 92, 93, 94, 95, 96, 97, 98, 1999
+ Free Software Foundation.
This file is part of GAS, the GNU Assembler.
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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
/*
Intel 80386 machine specific gas.
#include <ctype.h>
#include "as.h"
-
-#include "obstack.h"
+#include "subsegs.h"
#include "opcode/i386.h"
+#ifndef TC_RELOC
+#define TC_RELOC(X,Y) (Y)
+#endif
+
+#ifndef REGISTER_WARNINGS
+#define REGISTER_WARNINGS 1
+#endif
+
+#ifndef SCALE1_WHEN_NO_INDEX
+/* Specifying a scale factor besides 1 when there is no index is
+ futile. eg. `mov (%ebx,2),%al' does exactly the same as
+ `mov (%ebx),%al'. To slavishly follow what the programmer
+ specified, set SCALE1_WHEN_NO_INDEX to 0. */
+#define SCALE1_WHEN_NO_INDEX 1
+#endif
+
+#define true 1
+#define false 0
+
+static unsigned int mode_from_disp_size PARAMS ((unsigned int));
+static int fits_in_signed_byte PARAMS ((long));
+static int fits_in_unsigned_byte PARAMS ((long));
+static int fits_in_unsigned_word PARAMS ((long));
+static int fits_in_signed_word PARAMS ((long));
+static int smallest_imm_type PARAMS ((long));
+static int add_prefix PARAMS ((unsigned int));
+static void set_16bit_code_flag PARAMS ((int));
+static void set_intel_syntax PARAMS ((int));
+
+#ifdef BFD_ASSEMBLER
+static bfd_reloc_code_real_type reloc
+ PARAMS ((int, int, bfd_reloc_code_real_type));
+#endif
+
/* 'md_assemble ()' gathers together information and puts it into a
i386_insn. */
{
/* TM holds the template for the insn were currently assembling. */
template tm;
- /* SUFFIX holds the opcode suffix (e.g. 'l' for 'movl') if given. */
+
+ /* SUFFIX holds the instruction mnemonic suffix if given.
+ (e.g. 'l' for 'movl') */
char suffix;
+
/* Operands are coded with OPERANDS, TYPES, DISPS, IMMS, and REGS. */
/* OPERANDS gives the number of given operands. */
/* Displacements (if given) for each operand. */
expressionS *disps[MAX_OPERANDS];
+ /* Relocation type for operand */
+#ifdef BFD_ASSEMBLER
+ enum bfd_reloc_code_real disp_reloc[MAX_OPERANDS];
+#else
+ int disp_reloc[MAX_OPERANDS];
+#endif
+
/* Immediate operands (if given) for each operand. */
expressionS *imms[MAX_OPERANDS];
/* Register operands (if given) for each operand. */
- reg_entry *regs[MAX_OPERANDS];
+ const reg_entry *regs[MAX_OPERANDS];
/* BASE_REG, INDEX_REG, and LOG2_SCALE_FACTOR are used to encode
the base index byte below. */
- reg_entry *base_reg;
- reg_entry *index_reg;
+ const reg_entry *base_reg;
+ const reg_entry *index_reg;
unsigned int log2_scale_factor;
- /* SEG gives the seg_entry of this insn. It is equal to zero unless
- an explicit segment override is given. */
- const seg_entry *seg; /* segment for memory operands (if given) */
+ /* SEG gives the seg_entries of this insn. They are zero unless
+ explicit segment overrides are given. */
+ const seg_entry *seg[2]; /* segments for memory operands (if given) */
/* PREFIX holds all the given prefix opcodes (usually null).
- PREFIXES is the size of PREFIX. */
- /* richfix: really unsigned? */
- unsigned char prefix[MAX_PREFIXES];
+ PREFIXES is the number of prefix opcodes. */
unsigned int prefixes;
+ unsigned char prefix[MAX_PREFIXES];
- /* RM and IB are the modrm byte and the base index byte where the
- addressing modes of this insn are encoded. */
+ /* RM and SIB are the modrm byte and the sib byte where the
+ addressing modes of this insn are encoded. */
modrm_byte rm;
- base_index_byte bi;
+ sib_byte sib;
};
typedef struct _i386_insn i386_insn;
+/* List of chars besides those in app.c:symbol_chars that can start an
+ operand. Used to prevent the scrubber eating vital white-space. */
+#ifdef LEX_AT
+const char extra_symbol_chars[] = "*%-(@";
+#else
+const char extra_symbol_chars[] = "*%-(";
+#endif
+
/* This array holds the chars that always start a comment. If the
pre-processor is disabled, these aren't very useful */
-#if defined (TE_I386AIX) || defined (OBJ_ELF)
+#if defined (TE_I386AIX) || ((defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) && ! defined (TE_LINUX))
+/* Putting '/' here makes it impossible to use the divide operator.
+ However, we need it for compatibility with SVR4 systems. */
const char comment_chars[] = "#/";
+#define PREFIX_SEPARATOR '\\'
#else
const char comment_chars[] = "#";
+#define PREFIX_SEPARATOR '/'
#endif
/* This array holds the chars that only start a comment at the beginning of
#NO_APP at the beginning of its output. */
/* Also note that comments started like this one will always work if
'/' isn't otherwise defined. */
-#if defined (TE_I386AIX) || defined (OBJ_ELF)
+#if defined (TE_I386AIX) || ((defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) && ! defined (TE_LINUX))
const char line_comment_chars[] = "";
#else
const char line_comment_chars[] = "/";
#endif
+
const char line_separator_chars[] = "";
/* Chars that can be used to separate mant from exp in floating point nums */
const char FLT_CHARS[] = "fFdDxX";
/* tables for lexical analysis */
-static char opcode_chars[256];
+static char mnemonic_chars[256];
static char register_chars[256];
static char operand_chars[256];
-static char space_chars[256];
static char identifier_chars[256];
static char digit_chars[256];
/* lexical macros */
-#define is_opcode_char(x) (opcode_chars[(unsigned char) x])
+#define is_mnemonic_char(x) (mnemonic_chars[(unsigned char) x])
#define is_operand_char(x) (operand_chars[(unsigned char) x])
#define is_register_char(x) (register_chars[(unsigned char) x])
-#define is_space_char(x) (space_chars[(unsigned char) x])
+#define is_space_char(x) ((x) == ' ')
#define is_identifier_char(x) (identifier_chars[(unsigned char) x])
#define is_digit_char(x) (digit_chars[(unsigned char) x])
/* put here all non-digit non-letter charcters that may occur in an operand */
-static char operand_special_chars[] = "%$-+(,)*._~/<>|&^!:";
-
-static char *ordinal_names[] = {"first", "second", "third"}; /* for printfs */
+static char operand_special_chars[] = "%$-+(,)*._~/<>|&^!:[@]";
/* md_assemble() always leaves the strings it's passed unaltered. To
effect this we maintain a stack of saved characters that we've smashed
assembler instruction). */
static char save_stack[32];
static char *save_stack_p; /* stack pointer */
-#define END_STRING_AND_SAVE(s) *save_stack_p++ = *s; *s = '\0'
-#define RESTORE_END_STRING(s) *s = *--save_stack_p
+#define END_STRING_AND_SAVE(s) \
+ do { *save_stack_p++ = *(s); *(s) = '\0'; } while (0)
+#define RESTORE_END_STRING(s) \
+ do { *(s) = *--save_stack_p; } while (0)
/* The instruction we're assembling. */
static i386_insn i;
+/* Possible templates for current insn. */
+static const templates *current_templates;
+
/* Per instruction expressionS buffers: 2 displacements & 2 immediate max. */
static expressionS disp_expressions[2], im_expressions[2];
-/* pointers to ebp & esp entries in reg_hash hash table */
-static reg_entry *ebp, *esp;
-
static int this_operand; /* current operand we are working on */
static int flag_do_long_jump; /* FIXME what does this do? */
+static int flag_16bit_code; /* 1 if we're writing 16-bit code, 0 if 32-bit */
+
+static int intel_syntax = 0; /* 1 for intel syntax, 0 if att syntax */
+
+static int allow_naked_reg = 0; /* 1 if register prefix % not required */
+
/* Interface to relax_segment.
There are 2 relax states for 386 jump insns: one for conditional &
one for unconditional jumps. This is because the these two types
#define COND_JUMP 1 /* conditional jump */
#define UNCOND_JUMP 2 /* unconditional jump */
/* sizes */
-#define BYTE 0
-#define WORD 1
-#define DWORD 2
-#define UNKNOWN_SIZE 3
+#define CODE16 1
+#define SMALL 0
+#define SMALL16 (SMALL|CODE16)
+#define BIG 2
+#define BIG16 (BIG|CODE16)
#ifndef INLINE
#ifdef __GNUC__
#define ENCODE_RELAX_STATE(type,size) \
((relax_substateT)((type<<2) | (size)))
#define SIZE_FROM_RELAX_STATE(s) \
- ( (((s) & 0x3) == BYTE ? 1 : (((s) & 0x3) == WORD ? 2 : 4)) )
+ ( (((s) & 0x3) == BIG ? 4 : (((s) & 0x3) == BIG16 ? 2 : 1)) )
+
+/* This table is used by relax_frag to promote short jumps to long
+ ones where necessary. SMALL (short) jumps may be promoted to BIG
+ (32 bit long) ones, and SMALL16 jumps to BIG16 (16 bit long). We
+ don't allow a short jump in a 32 bit code segment to be promoted to
+ a 16 bit offset jump because it's slower (requires data size
+ prefix), and doesn't work, unless the destination is in the bottom
+ 64k of the code segment (The top 16 bits of eip are zeroed). */
const relax_typeS md_relax_table[] =
{
{1, 1, 0, 0},
{1, 1, 0, 0},
- /* For now we don't use word displacement jumps; they may be
- untrustworthy. */
- {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (COND_JUMP, DWORD)},
- /* word conditionals add 3 bytes to frag:
- 2 opcode prefix; 1 displacement bytes */
- {32767 + 2, -32768 + 2, 3, ENCODE_RELAX_STATE (COND_JUMP, DWORD)},
+ {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (COND_JUMP, BIG)},
+ {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (COND_JUMP, BIG16)},
/* dword conditionals adds 4 bytes to frag:
- 1 opcode prefix; 3 displacement bytes */
+ 1 extra opcode byte, 3 extra displacement bytes. */
{0, 0, 4, 0},
- {1, 1, 0, 0},
+ /* word conditionals add 2 bytes to frag:
+ 1 extra opcode byte, 1 extra displacement byte. */
+ {0, 0, 2, 0},
- {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (UNCOND_JUMP, DWORD)},
- /* word jmp adds 2 bytes to frag:
- 1 opcode prefix; 1 displacement bytes */
- {32767 + 2, -32768 + 2, 2, ENCODE_RELAX_STATE (UNCOND_JUMP, DWORD)},
+ {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (UNCOND_JUMP, BIG)},
+ {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (UNCOND_JUMP, BIG16)},
/* dword jmp adds 3 bytes to frag:
- 0 opcode prefix; 3 displacement bytes */
+ 0 extra opcode bytes, 3 extra displacement bytes. */
{0, 0, 3, 0},
- {1, 1, 0, 0},
+ /* word jmp adds 1 byte to frag:
+ 0 extra opcode bytes, 1 extra displacement byte. */
+ {0, 0, 1, 0}
};
+
+void
+i386_align_code (fragP, count)
+ fragS *fragP;
+ int count;
+{
+ /* Various efficient no-op patterns for aligning code labels. */
+ /* Note: Don't try to assemble the instructions in the comments. */
+ /* 0L and 0w are not legal */
+ static const char f32_1[] =
+ {0x90}; /* nop */
+ static const char f32_2[] =
+ {0x89,0xf6}; /* movl %esi,%esi */
+ static const char f32_3[] =
+ {0x8d,0x76,0x00}; /* leal 0(%esi),%esi */
+ static const char f32_4[] =
+ {0x8d,0x74,0x26,0x00}; /* leal 0(%esi,1),%esi */
+ static const char f32_5[] =
+ {0x90, /* nop */
+ 0x8d,0x74,0x26,0x00}; /* leal 0(%esi,1),%esi */
+ static const char f32_6[] =
+ {0x8d,0xb6,0x00,0x00,0x00,0x00}; /* leal 0L(%esi),%esi */
+ static const char f32_7[] =
+ {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00}; /* leal 0L(%esi,1),%esi */
+ static const char f32_8[] =
+ {0x90, /* nop */
+ 0x8d,0xb4,0x26,0x00,0x00,0x00,0x00}; /* leal 0L(%esi,1),%esi */
+ static const char f32_9[] =
+ {0x89,0xf6, /* movl %esi,%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_10[] =
+ {0x8d,0x76,0x00, /* leal 0(%esi),%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_11[] =
+ {0x8d,0x74,0x26,0x00, /* leal 0(%esi,1),%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_12[] =
+ {0x8d,0xb6,0x00,0x00,0x00,0x00, /* leal 0L(%esi),%esi */
+ 0x8d,0xbf,0x00,0x00,0x00,0x00}; /* leal 0L(%edi),%edi */
+ static const char f32_13[] =
+ {0x8d,0xb6,0x00,0x00,0x00,0x00, /* leal 0L(%esi),%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_14[] =
+ {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00, /* leal 0L(%esi,1),%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_15[] =
+ {0xeb,0x0d,0x90,0x90,0x90,0x90,0x90, /* jmp .+15; lotsa nops */
+ 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90};
+ static const char f16_4[] =
+ {0x8d,0xb4,0x00,0x00}; /* lea 0w(%si),%si */
+ static const char f16_5[] =
+ {0x90, /* nop */
+ 0x8d,0xb4,0x00,0x00}; /* lea 0w(%si),%si */
+ static const char f16_6[] =
+ {0x89,0xf6, /* mov %si,%si */
+ 0x8d,0xbd,0x00,0x00}; /* lea 0w(%di),%di */
+ static const char f16_7[] =
+ {0x8d,0x74,0x00, /* lea 0(%si),%si */
+ 0x8d,0xbd,0x00,0x00}; /* lea 0w(%di),%di */
+ static const char f16_8[] =
+ {0x8d,0xb4,0x00,0x00, /* lea 0w(%si),%si */
+ 0x8d,0xbd,0x00,0x00}; /* lea 0w(%di),%di */
+ static const char *const f32_patt[] = {
+ f32_1, f32_2, f32_3, f32_4, f32_5, f32_6, f32_7, f32_8,
+ f32_9, f32_10, f32_11, f32_12, f32_13, f32_14, f32_15
+ };
+ static const char *const f16_patt[] = {
+ f32_1, f32_2, f32_3, f16_4, f16_5, f16_6, f16_7, f16_8,
+ f32_15, f32_15, f32_15, f32_15, f32_15, f32_15, f32_15
+ };
+
+ if (count > 0 && count <= 15)
+ {
+ if (flag_16bit_code)
+ {
+ memcpy(fragP->fr_literal + fragP->fr_fix,
+ f16_patt[count - 1], count);
+ if (count > 8) /* adjust jump offset */
+ fragP->fr_literal[fragP->fr_fix + 1] = count - 2;
+ }
+ else
+ memcpy(fragP->fr_literal + fragP->fr_fix,
+ f32_patt[count - 1], count);
+ fragP->fr_var = count;
+ }
+}
+
static char *output_invalid PARAMS ((int c));
static int i386_operand PARAMS ((char *operand_string));
-static reg_entry *parse_register PARAMS ((char *reg_string));
+static int i386_intel_operand PARAMS ((char *operand_string, int got_a_float));
+static const reg_entry *parse_register PARAMS ((char *reg_string,
+ char **end_op));
+
#ifndef I386COFF
static void s_bss PARAMS ((int));
#endif
-static INLINE unsigned long
+symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+
+static INLINE unsigned int
mode_from_disp_size (t)
- unsigned long t;
+ unsigned int t;
{
- return (t & Disp8) ? 1 : (t & Disp32) ? 2 : 0;
+ return (t & Disp8) ? 1 : (t & (Disp16|Disp32)) ? 2 : 0;
}
-#if 0
-/* Not used. */
-/* convert opcode suffix ('b' 'w' 'l' typically) into type specifier */
-
-static INLINE unsigned long
-opcode_suffix_to_type (s)
- unsigned long s;
-{
- return (s == BYTE_OPCODE_SUFFIX
- ? Byte : (s == WORD_OPCODE_SUFFIX
- ? Word : DWord));
-} /* opcode_suffix_to_type() */
-#endif
-
static INLINE int
fits_in_signed_byte (num)
long num;
: (Imm32));
} /* smallest_imm_type() */
+/* Returns 0 if attempting to add a prefix where one from the same
+ class already exists, 1 if non rep/repne added, 2 if rep/repne
+ added. */
+static int
+add_prefix (prefix)
+ unsigned int prefix;
+{
+ int ret = 1;
+ int q;
+
+ switch (prefix)
+ {
+ default:
+ abort ();
+
+ case CS_PREFIX_OPCODE:
+ case DS_PREFIX_OPCODE:
+ case ES_PREFIX_OPCODE:
+ case FS_PREFIX_OPCODE:
+ case GS_PREFIX_OPCODE:
+ case SS_PREFIX_OPCODE:
+ q = SEG_PREFIX;
+ break;
+
+ case REPNE_PREFIX_OPCODE:
+ case REPE_PREFIX_OPCODE:
+ ret = 2;
+ /* fall thru */
+ case LOCK_PREFIX_OPCODE:
+ q = LOCKREP_PREFIX;
+ break;
+
+ case FWAIT_OPCODE:
+ q = WAIT_PREFIX;
+ break;
+
+ case ADDR_PREFIX_OPCODE:
+ q = ADDR_PREFIX;
+ break;
+
+ case DATA_PREFIX_OPCODE:
+ q = DATA_PREFIX;
+ break;
+ }
+
+ if (i.prefix[q])
+ {
+ as_bad (_("same type of prefix used twice"));
+ return 0;
+ }
+
+ i.prefixes += 1;
+ i.prefix[q] = prefix;
+ return ret;
+}
+
+static void
+set_16bit_code_flag (new_16bit_code_flag)
+ int new_16bit_code_flag;
+{
+ flag_16bit_code = new_16bit_code_flag;
+}
+
+static void
+set_intel_syntax (syntax_flag)
+ int syntax_flag;
+{
+ /* Find out if register prefixing is specified. */
+ int ask_naked_reg = 0;
+
+ SKIP_WHITESPACE ();
+ if (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ char *string = input_line_pointer;
+ int e = get_symbol_end ();
+
+ if (strcmp(string, "prefix") == 0)
+ ask_naked_reg = 1;
+ else if (strcmp(string, "noprefix") == 0)
+ ask_naked_reg = -1;
+ else
+ as_bad (_("Bad argument to syntax directive."));
+ *input_line_pointer = e;
+ }
+ demand_empty_rest_of_line ();
+
+ intel_syntax = syntax_flag;
+
+ if (ask_naked_reg == 0)
+ {
+#ifdef BFD_ASSEMBLER
+ allow_naked_reg = (intel_syntax
+ && (bfd_get_symbol_leading_char (stdoutput) != '\0'));
+#else
+ allow_naked_reg = 0; /* conservative default */
+#endif
+ }
+ else
+ allow_naked_reg = (ask_naked_reg < 0);
+}
+
const pseudo_typeS md_pseudo_table[] =
{
#ifndef I386COFF
{"bss", s_bss, 0},
#endif
-#ifndef OBJ_AOUT
+#if !defined(OBJ_AOUT) && !defined(USE_ALIGN_PTWO)
{"align", s_align_bytes, 0},
#else
{"align", s_align_ptwo, 0},
{"value", cons, 2},
{"noopt", s_ignore, 0},
{"optim", s_ignore, 0},
+ {"code16", set_16bit_code_flag, 1},
+ {"code32", set_16bit_code_flag, 0},
+ {"intel_syntax", set_intel_syntax, 1},
+ {"att_syntax", set_intel_syntax, 0},
{0, 0, 0}
};
/* for interface with expression () */
extern char *input_line_pointer;
-/* obstack for constructing various things in md_begin */
-struct obstack o;
-
-/* hash table for opcode lookup */
+/* hash table for instruction mnemonic lookup */
static struct hash_control *op_hash;
/* hash table for register lookup */
static struct hash_control *reg_hash;
-/* hash table for prefix lookup */
-static struct hash_control *prefix_hash;
\f
void
{
const char *hash_err;
- obstack_begin (&o, 4096);
-
/* initialize op_hash hash table */
op_hash = hash_new ();
{
register const template *optab;
register templates *core_optab;
- char *prev_name;
optab = i386_optab; /* setup for loop */
- prev_name = optab->name;
- obstack_grow (&o, optab, sizeof (template));
core_optab = (templates *) xmalloc (sizeof (templates));
+ core_optab->start = optab;
- for (optab++; optab < i386_optab_end; optab++)
+ while (1)
{
- if (!strcmp (optab->name, prev_name))
- {
- /* same name as before --> append to current template list */
- obstack_grow (&o, optab, sizeof (template));
- }
- else
+ ++optab;
+ if (optab->name == NULL
+ || strcmp (optab->name, (optab - 1)->name) != 0)
{
/* different name --> ship out current template list;
add to hash table; & begin anew */
- /* Note: end must be set before start! since obstack_next_free
- changes upon opstack_finish */
- core_optab->end = (template *) obstack_next_free (&o);
- core_optab->start = (template *) obstack_finish (&o);
- hash_err = hash_insert (op_hash, prev_name, (char *) core_optab);
+ core_optab->end = optab;
+ hash_err = hash_insert (op_hash,
+ (optab - 1)->name,
+ (PTR) core_optab);
if (hash_err)
{
hash_error:
- as_fatal ("Internal Error: Can't hash %s: %s", prev_name,
+ as_fatal (_("Internal Error: Can't hash %s: %s"),
+ (optab - 1)->name,
hash_err);
}
- prev_name = optab->name;
+ if (optab->name == NULL)
+ break;
core_optab = (templates *) xmalloc (sizeof (templates));
- obstack_grow (&o, optab, sizeof (template));
+ core_optab->start = optab;
}
}
}
{
register const reg_entry *regtab;
- for (regtab = i386_regtab; regtab < i386_regtab_end; regtab++)
+ for (regtab = i386_regtab;
+ regtab < i386_regtab + sizeof (i386_regtab) / sizeof (i386_regtab[0]);
+ regtab++)
{
hash_err = hash_insert (reg_hash, regtab->reg_name, (PTR) regtab);
if (hash_err)
}
}
- esp = (reg_entry *) hash_find (reg_hash, "esp");
- ebp = (reg_entry *) hash_find (reg_hash, "ebp");
-
- /* initialize reg_hash hash table */
- prefix_hash = hash_new ();
- {
- register const prefix_entry *prefixtab;
-
- for (prefixtab = i386_prefixtab;
- prefixtab < i386_prefixtab_end; prefixtab++)
- {
- hash_err = hash_insert (prefix_hash, prefixtab->prefix_name,
- (PTR) prefixtab);
- if (hash_err)
- goto hash_error;
- }
- }
-
- /* fill in lexical tables: opcode_chars, operand_chars, space_chars */
+ /* fill in lexical tables: mnemonic_chars, operand_chars. */
{
register int c;
register char *p;
for (c = 0; c < 256; c++)
{
- if (islower (c) || isdigit (c))
+ if (isdigit (c))
{
- opcode_chars[c] = c;
+ digit_chars[c] = c;
+ mnemonic_chars[c] = c;
register_chars[c] = c;
+ operand_chars[c] = c;
}
- else if (isupper (c))
- {
- opcode_chars[c] = tolower (c);
- register_chars[c] = opcode_chars[c];
- }
- else if (c == PREFIX_SEPERATOR)
+ else if (islower (c))
{
- opcode_chars[c] = c;
+ mnemonic_chars[c] = c;
+ register_chars[c] = c;
+ operand_chars[c] = c;
}
- else if (c == ')' || c == '(')
+ else if (isupper (c))
{
- register_chars[c] = c;
+ mnemonic_chars[c] = tolower (c);
+ register_chars[c] = mnemonic_chars[c];
+ operand_chars[c] = c;
}
- if (isupper (c) || islower (c) || isdigit (c))
- operand_chars[c] = c;
-
- if (isdigit (c) || c == '-')
- digit_chars[c] = c;
-
- if (isalpha (c) || c == '_' || c == '.' || isdigit (c))
+ if (isalpha (c) || isdigit (c))
identifier_chars[c] = c;
-
- if (c == ' ' || c == '\t')
- space_chars[c] = c;
+ else if (c >= 128)
+ {
+ identifier_chars[c] = c;
+ operand_chars[c] = c;
+ }
}
+#ifdef LEX_AT
+ identifier_chars['@'] = '@';
+#endif
+ register_chars[')'] = ')';
+ register_chars['('] = '(';
+ digit_chars['-'] = '-';
+ identifier_chars['_'] = '_';
+ identifier_chars['.'] = '.';
+
for (p = operand_special_chars; *p != '\0'; p++)
operand_chars[(unsigned char) *p] = *p;
}
-#ifdef OBJ_ELF
- record_alignment (text_section, 2);
- record_alignment (data_section, 2);
- record_alignment (bss_section, 2);
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ record_alignment (text_section, 2);
+ record_alignment (data_section, 2);
+ record_alignment (bss_section, 2);
+ }
#endif
}
+
+void
+i386_print_statistics (file)
+ FILE *file;
+{
+ hash_print_statistics (file, "i386 opcode", op_hash);
+ hash_print_statistics (file, "i386 register", reg_hash);
+}
\f
#ifdef DEBUG386
fprintf (stdout, " #%d: ", i + 1);
pt (x->types[i]);
fprintf (stdout, "\n");
- if (x->types[i] & Reg)
+ if (x->types[i]
+ & (Reg | SReg2 | SReg3 | Control | Debug | Test | RegMMX))
fprintf (stdout, "%s\n", x->regs[i]->reg_name);
if (x->types[i] & Imm)
pe (x->imms[i]);
- if (x->types[i] & (Disp | Abs))
+ if (x->types[i] & Disp)
pe (x->disps[i]);
}
}
{ Imm8S, "i8s" },
{ Imm16, "i16" },
{ Imm32, "i32" },
- { Mem8, "Mem8" },
- { Mem16, "Mem16" },
- { Mem32, "Mem32" },
+ { Imm1, "i1" },
{ BaseIndex, "BaseIndex" },
- { Abs8, "Abs8" },
- { Abs16, "Abs16" },
- { Abs32, "Abs32" },
{ Disp8, "d8" },
{ Disp16, "d16" },
{ Disp32, "d32" },
- { SReg2, "SReg2" },
- { SReg3, "SReg3" },
- { Acc, "Acc" },
{ InOutPortReg, "InOutPortReg" },
{ ShiftCount, "ShiftCount" },
- { Imm1, "i1" },
{ Control, "control reg" },
{ Test, "test reg" },
+ { Debug, "debug reg" },
{ FloatReg, "FReg" },
{ FloatAcc, "FAcc" },
+ { SReg2, "SReg2" },
+ { SReg3, "SReg3" },
+ { Acc, "Acc" },
{ JumpAbsolute, "Jump Absolute" },
+ { RegMMX, "rMMX" },
+ { EsSeg, "es" },
{ 0, "" }
};
if (t == Unknown)
{
- fprintf (stdout, "Unknown");
+ fprintf (stdout, _("Unknown"));
}
else
{
#endif /* DEBUG386 */
\f
+int
+tc_i386_force_relocation (fixp)
+ struct fix *fixp;
+{
#ifdef BFD_ASSEMBLER
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+ return 0;
+#else
+ /* For COFF */
+ return fixp->fx_r_type==7;
+#endif
+}
+
+#ifdef BFD_ASSEMBLER
+static bfd_reloc_code_real_type reloc
+ PARAMS ((int, int, bfd_reloc_code_real_type));
+
static bfd_reloc_code_real_type
-reloc (size, pcrel)
+reloc (size, pcrel, other)
int size;
int pcrel;
+ bfd_reloc_code_real_type other;
{
+ if (other != NO_RELOC) return other;
+
if (pcrel)
- switch (size)
- {
-#ifndef OBJ_ELF
- case 1: return BFD_RELOC_8_PCREL;
- case 2: return BFD_RELOC_16_PCREL;
-#endif
- case 4: return BFD_RELOC_32_PCREL;
- }
+ {
+ switch (size)
+ {
+ case 1: return BFD_RELOC_8_PCREL;
+ case 2: return BFD_RELOC_16_PCREL;
+ case 4: return BFD_RELOC_32_PCREL;
+ }
+ as_bad (_("Can not do %d byte pc-relative relocation"), size);
+ }
else
- switch (size)
- {
-#ifndef OBJ_ELF
- case 1: return BFD_RELOC_8;
- case 2: return BFD_RELOC_16;
-#endif
- case 4: return BFD_RELOC_32;
- }
+ {
+ switch (size)
+ {
+ case 1: return BFD_RELOC_8;
+ case 2: return BFD_RELOC_16;
+ case 4: return BFD_RELOC_32;
+ }
+ as_bad (_("Can not do %d byte relocation"), size);
+ }
- as_bad ("Can not do %d byte %srelocation", size,
- pcrel ? "pc-relative" : "");
return BFD_RELOC_NONE;
}
+
+/*
+ * Here we decide which fixups can be adjusted to make them relative to
+ * the beginning of the section instead of the symbol. Basically we need
+ * to make sure that the dynamic relocations are done correctly, so in
+ * some cases we force the original symbol to be used.
+ */
+int
+tc_i386_fix_adjustable(fixP)
+ fixS * fixP;
+{
+#ifdef OBJ_ELF
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERN (fixP->fx_addsy))
+ return 0;
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+#endif
+ /* adjust_reloc_syms doesn't know about the GOT */
+ if (fixP->fx_r_type == BFD_RELOC_386_GOTOFF
+ || fixP->fx_r_type == BFD_RELOC_386_PLT32
+ || fixP->fx_r_type == BFD_RELOC_386_GOT32
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+ return 1;
+}
#else
-#define reloc(SIZE,PCREL) 0
+#define reloc(SIZE,PCREL,OTHER) 0
+#define BFD_RELOC_16 0
#define BFD_RELOC_32 0
+#define BFD_RELOC_16_PCREL 0
#define BFD_RELOC_32_PCREL 0
+#define BFD_RELOC_386_PLT32 0
+#define BFD_RELOC_386_GOT32 0
+#define BFD_RELOC_386_GOTOFF 0
#endif
+int
+intel_float_operand (mnemonic)
+ char *mnemonic;
+{
+ if (mnemonic[0] == 'f' && mnemonic[1] =='i')
+ return 0;
+
+ if (mnemonic[0] == 'f')
+ return 1;
+
+ return 0;
+}
+
/* This is the guts of the machine-dependent assembler. LINE points to a
machine dependent instruction. This function is supposed to emit
the frags/bytes it assembles to. */
+
void
md_assemble (line)
char *line;
{
- /* Holds template once we've found it. */
- register template *t;
+ /* Points to template once we've found it. */
+ const template *t;
/* Count the size of the instruction generated. */
int insn_size = 0;
- /* Possible templates for current insn */
- templates *current_templates = (templates *) 0;
+ int j;
+
+ char mnemonic[MAX_MNEM_SIZE];
/* Initialize globals. */
memset (&i, '\0', sizeof (i));
+ for (j = 0; j < MAX_OPERANDS; j++)
+ i.disp_reloc[j] = NO_RELOC;
memset (disp_expressions, '\0', sizeof (disp_expressions));
memset (im_expressions, '\0', sizeof (im_expressions));
save_stack_p = save_stack; /* reset stack pointer */
- /* Fist parse an opcode & call i386_operand for the operands.
+ /* First parse an instruction mnemonic & call i386_operand for the operands.
We assume that the scrubber has arranged it so that line[0] is the valid
- start of a (possibly prefixed) opcode. */
+ start of a (possibly prefixed) mnemonic. */
{
char *l = line;
-
- /* 1 if operand is pending after ','. */
- unsigned int expecting_operand = 0;
- /* 1 if we found a prefix only acceptable with string insns. */
- unsigned int expecting_string_instruction = 0;
- /* Non-zero if operand parens not balenced. */
- unsigned int paren_not_balenced;
char *token_start = l;
+ char *mnem_p;
+
+ /* Non-zero if we found a prefix only acceptable with string insns. */
+ const char *expecting_string_instruction = NULL;
- while (!is_space_char (*l) && *l != END_OF_INSN)
+ while (1)
{
- if (!is_opcode_char (*l))
+ mnem_p = mnemonic;
+ while ((*mnem_p = mnemonic_chars[(unsigned char) *l]) != 0)
{
- as_bad ("invalid character %s in opcode", output_invalid (*l));
- return;
+ mnem_p++;
+ if (mnem_p >= mnemonic + sizeof (mnemonic))
+ {
+ as_bad (_("no such 386 instruction: `%s'"), token_start);
+ return;
+ }
+ l++;
}
- else if (*l != PREFIX_SEPERATOR)
+ if (!is_space_char (*l)
+ && *l != END_OF_INSN
+ && *l != PREFIX_SEPARATOR)
{
- *l = opcode_chars[(unsigned char) *l]; /* fold case of opcodes */
- l++;
+ as_bad (_("invalid character %s in mnemonic"),
+ output_invalid (*l));
+ return;
}
- else
+ if (token_start == l)
{
- /* This opcode's got a prefix. */
- unsigned int q;
- prefix_entry *prefix;
+ if (*l == PREFIX_SEPARATOR)
+ as_bad (_("expecting prefix; got nothing"));
+ else
+ as_bad (_("expecting mnemonic; got nothing"));
+ return;
+ }
- if (l == token_start)
- {
- as_bad ("expecting prefix; got nothing");
- return;
- }
- END_STRING_AND_SAVE (l);
- prefix = (prefix_entry *) hash_find (prefix_hash, token_start);
- if (!prefix)
+ /* Look up instruction (or prefix) via hash table. */
+ current_templates = hash_find (op_hash, mnemonic);
+
+ if (*l != END_OF_INSN
+ && (! is_space_char (*l) || l[1] != END_OF_INSN)
+ && current_templates
+ && (current_templates->start->opcode_modifier & IsPrefix))
+ {
+ /* If we are in 16-bit mode, do not allow addr16 or data16.
+ Similarly, in 32-bit mode, do not allow addr32 or data32. */
+ if ((current_templates->start->opcode_modifier & (Size16 | Size32))
+ && (((current_templates->start->opcode_modifier & Size32) != 0)
+ ^ flag_16bit_code))
{
- as_bad ("no such opcode prefix ('%s')", token_start);
+ as_bad (_("redundant %s prefix"),
+ current_templates->start->name);
return;
}
- RESTORE_END_STRING (l);
- /* check for repeated prefix */
- for (q = 0; q < i.prefixes; q++)
- if (i.prefix[q] == prefix->prefix_code)
- {
- as_bad ("same prefix used twice; you don't really want this!");
- return;
- }
- if (i.prefixes == MAX_PREFIXES)
+ /* Add prefix, checking for repeated prefixes. */
+ switch (add_prefix (current_templates->start->base_opcode))
{
- as_bad ("too many opcode prefixes");
+ case 0:
return;
+ case 2:
+ expecting_string_instruction =
+ current_templates->start->name;
+ break;
}
- i.prefix[i.prefixes++] = prefix->prefix_code;
- if (prefix->prefix_code == REPE || prefix->prefix_code == REPNE)
- expecting_string_instruction = 1;
- /* skip past PREFIX_SEPERATOR and reset token_start */
+ /* Skip past PREFIX_SEPARATOR and reset token_start. */
token_start = ++l;
}
- }
- END_STRING_AND_SAVE (l);
- if (token_start == l)
- {
- as_bad ("expecting opcode; got nothing");
- return;
+ else
+ break;
}
- /* Lookup insn in hash; try intel & att naming conventions if appropriate;
- that is: we only use the opcode suffix 'b' 'w' or 'l' if we need to. */
- current_templates = (templates *) hash_find (op_hash, token_start);
if (!current_templates)
{
- int last_index = strlen (token_start) - 1;
- char last_char = token_start[last_index];
- switch (last_char)
+ /* See if we can get a match by trimming off a suffix. */
+ switch (mnem_p[-1])
{
- case DWORD_OPCODE_SUFFIX:
- case WORD_OPCODE_SUFFIX:
- case BYTE_OPCODE_SUFFIX:
- token_start[last_index] = '\0';
- current_templates = (templates *) hash_find (op_hash, token_start);
- token_start[last_index] = last_char;
- i.suffix = last_char;
+ case DWORD_MNEM_SUFFIX:
+ case WORD_MNEM_SUFFIX:
+ case BYTE_MNEM_SUFFIX:
+ case SHORT_MNEM_SUFFIX:
+#if LONG_MNEM_SUFFIX != DWORD_MNEM_SUFFIX
+ case LONG_MNEM_SUFFIX:
+#endif
+ i.suffix = mnem_p[-1];
+ mnem_p[-1] = '\0';
+ current_templates = hash_find (op_hash, mnemonic);
+ break;
+
+ /* Intel Syntax */
+ case INTEL_DWORD_MNEM_SUFFIX:
+ if (intel_syntax)
+ {
+ i.suffix = mnem_p[-1];
+ mnem_p[-1] = '\0';
+ current_templates = hash_find (op_hash, mnemonic);
+ break;
+ }
}
if (!current_templates)
{
- as_bad ("no such 386 instruction: `%s'", token_start);
+ as_bad (_("no such 386 instruction: `%s'"), token_start);
return;
}
}
- RESTORE_END_STRING (l);
/* check for rep/repne without a string instruction */
- if (expecting_string_instruction &&
- !IS_STRING_INSTRUCTION (current_templates->
- start->base_opcode))
+ if (expecting_string_instruction
+ && !(current_templates->start->opcode_modifier & IsString))
{
- as_bad ("expecting string instruction after rep/repne");
+ as_bad (_("expecting string instruction after `%s'"),
+ expecting_string_instruction);
return;
}
/* There may be operands to parse. */
- if (*l != END_OF_INSN &&
- /* For string instructions, we ignore any operands if given. This
- kludges, for example, 'rep/movsb %ds:(%esi), %es:(%edi)' where
- the operands are always going to be the same, and are not really
- encoded in machine code. */
- !IS_STRING_INSTRUCTION (current_templates->
- start->base_opcode))
+ if (*l != END_OF_INSN)
{
/* parse operands */
+
+ /* 1 if operand is pending after ','. */
+ unsigned int expecting_operand = 0;
+
+ /* Non-zero if operand parens not balanced. */
+ unsigned int paren_not_balanced;
+
do
{
/* skip optional white space before operand */
- while (!is_operand_char (*l) && *l != END_OF_INSN)
+ if (is_space_char (*l))
+ ++l;
+ if (!is_operand_char (*l) && *l != END_OF_INSN)
{
- if (!is_space_char (*l))
- {
- as_bad ("invalid character %s before %s operand",
- output_invalid (*l),
- ordinal_names[i.operands]);
- return;
- }
- l++;
+ as_bad (_("invalid character %s before operand %d"),
+ output_invalid (*l),
+ i.operands + 1);
+ return;
}
token_start = l; /* after white space */
- paren_not_balenced = 0;
- while (paren_not_balenced || *l != ',')
+ paren_not_balanced = 0;
+ while (paren_not_balanced || *l != ',')
{
if (*l == END_OF_INSN)
{
- if (paren_not_balenced)
+ if (paren_not_balanced)
{
- as_bad ("unbalenced parenthesis in %s operand.",
- ordinal_names[i.operands]);
+ if (!intel_syntax)
+ as_bad (_("unbalanced parenthesis in operand %d."),
+ i.operands + 1);
+ else
+ as_bad (_("unbalanced brackets in operand %d."),
+ i.operands + 1);
return;
}
else
}
else if (!is_operand_char (*l) && !is_space_char (*l))
{
- as_bad ("invalid character %s in %s operand",
+ as_bad (_("invalid character %s in operand %d"),
output_invalid (*l),
- ordinal_names[i.operands]);
+ i.operands + 1);
return;
}
- if (*l == '(')
- ++paren_not_balenced;
- if (*l == ')')
- --paren_not_balenced;
+ if (!intel_syntax)
+ {
+ if (*l == '(')
+ ++paren_not_balanced;
+ if (*l == ')')
+ --paren_not_balanced;
+ }
+ else
+ {
+ if (*l == '[')
+ ++paren_not_balanced;
+ if (*l == ']')
+ --paren_not_balanced;
+ }
l++;
}
if (l != token_start)
this_operand = i.operands++;
if (i.operands > MAX_OPERANDS)
{
- as_bad ("spurious operands; (%d operands/instruction max)",
+ as_bad (_("spurious operands; (%d operands/instruction max)"),
MAX_OPERANDS);
return;
}
/* now parse operand adding info to 'i' as we go along */
END_STRING_AND_SAVE (l);
- operand_ok = i386_operand (token_start);
+
+ if (intel_syntax)
+ operand_ok = i386_intel_operand (token_start, intel_float_operand (mnemonic));
+ else
+ operand_ok = i386_operand (token_start);
+
RESTORE_END_STRING (l); /* restore old contents */
if (!operand_ok)
return;
if (expecting_operand)
{
expecting_operand_after_comma:
- as_bad ("expecting operand after ','; got nothing");
+ as_bad (_("expecting operand after ','; got nothing"));
return;
}
if (*l == ',')
{
- as_bad ("expecting operand before ','; got nothing");
+ as_bad (_("expecting operand before ','; got nothing"));
return;
}
}
}
}
- /* Now we've parsed the opcode into a set of templates, and have the
+ /* Now we've parsed the mnemonic into a set of templates, and have the
operands at hand.
Next, we find a template that matches the given insn,
making sure the overlap of the given operands types is consistent
with the template operand types. */
-#define MATCH(overlap,given_type) \
- (overlap && \
- (((overlap & (JumpAbsolute|BaseIndex|Mem8)) \
- == (given_type & (JumpAbsolute|BaseIndex|Mem8))) \
- || (overlap == InOutPortReg)))
-
-
- /* If m0 and m1 are register matches they must be consistent
- with the expected operand types t0 and t1.
- That is, if both m0 & m1 are register matches
- i.e. ( ((m0 & (Reg)) && (m1 & (Reg)) ) ?
- then, either 1. or 2. must be true:
- 1. the expected operand type register overlap is null:
- (t0 & t1 & Reg) == 0
- AND
- the given register overlap is null:
- (m0 & m1 & Reg) == 0
- 2. the expected operand type register overlap == the given
- operand type overlap: (t0 & t1 & m0 & m1 & Reg).
- */
-#define CONSISTENT_REGISTER_MATCH(m0, m1, t0, t1) \
- ( ((m0 & (Reg)) && (m1 & (Reg))) ? \
- ( ((t0 & t1 & (Reg)) == 0 && (m0 & m1 & (Reg)) == 0) || \
- ((t0 & t1) & (m0 & m1) & (Reg)) \
- ) : 1)
+#define MATCH(overlap, given, template) \
+ ((overlap) \
+ && ((given) & BaseIndex) == ((overlap) & BaseIndex) \
+ && ((given) & JumpAbsolute) == ((template) & JumpAbsolute))
+
+ /* If given types r0 and r1 are registers they must be of the same type
+ unless the expected operand type register overlap is null.
+ Note that Acc in a template matches every size of reg. */
+#define CONSISTENT_REGISTER_MATCH(m0, g0, t0, m1, g1, t1) \
+ ( ((g0) & Reg) == 0 || ((g1) & Reg) == 0 || \
+ ((g0) & Reg) == ((g1) & Reg) || \
+ ((((m0) & Acc) ? Reg : (t0)) & (((m1) & Acc) ? Reg : (t1)) & Reg) == 0 )
+
{
register unsigned int overlap0, overlap1;
expressionS *exp;
unsigned int overlap2;
unsigned int found_reverse_match;
+ int suffix_check;
+
+ /* All intel opcodes have reversed operands except for BOUND and ENTER */
+ if (intel_syntax
+ && (strcmp (mnemonic, "enter") != 0)
+ && (strcmp (mnemonic, "bound") != 0)
+ && (strncmp (mnemonic, "fsub", 4) !=0)
+ && (strncmp (mnemonic, "fdiv", 4) !=0))
+ {
+ const reg_entry *temp_reg;
+ expressionS *temp_disp;
+ expressionS *temp_imm;
+ unsigned int temp_type;
+ int xchg1, xchg2;
+
+ if (i.operands == 2)
+ {
+ xchg1 = 0;
+ xchg2 = 1;
+ }
+ else if (i.operands == 3)
+ {
+ xchg1 = 0;
+ xchg2 = 2;
+ }
+
+ if (i.operands > 1)
+ {
+ temp_type = i.types[xchg2];
+ if (temp_type & (Reg | FloatReg))
+ temp_reg = i.regs[xchg2];
+ else if (temp_type & Imm)
+ temp_imm = i.imms[xchg2];
+ else if (temp_type & Disp)
+ temp_disp = i.disps[xchg2];
+
+ i.types[xchg2] = i.types[xchg1];
+
+ if (i.types[xchg1] & (Reg | FloatReg))
+ {
+ i.regs[xchg2] = i.regs[xchg1];
+ i.regs[xchg1] = NULL;
+ }
+ else if (i.types[xchg2] & Imm)
+ {
+ i.imms[xchg2] = i.imms[xchg1];
+ i.imms[xchg1] = NULL;
+ }
+ else if (i.types[xchg2] & Disp)
+ {
+ i.disps[xchg2] = i.disps[xchg1];
+ i.disps[xchg1] = NULL;
+ }
+
+ if (temp_type & (Reg | FloatReg))
+ {
+ i.regs[xchg1] = temp_reg;
+ if (! (i.types[xchg1] & (Reg | FloatReg)))
+ i.regs[xchg2] = NULL;
+ }
+ else if (temp_type & Imm)
+ {
+ i.imms[xchg1] = temp_imm;
+ if (! (i.types[xchg1] & Imm))
+ i.imms[xchg2] = NULL;
+ }
+ else if (temp_type & Disp)
+ {
+ i.disps[xchg1] = temp_disp;
+ if (! (i.types[xchg1] & Disp))
+ i.disps[xchg2] = NULL;
+ }
+
+ i.types[xchg1] = temp_type;
+ }
+ if (!strcmp(mnemonic,"jmp")
+ || !strcmp (mnemonic, "call"))
+ if ((i.types[0] & Reg) || i.types[0] & BaseIndex)
+ i.types[0] |= JumpAbsolute;
+
+ }
+ overlap0 = 0;
+ overlap1 = 0;
+ overlap2 = 0;
+ found_reverse_match = 0;
+ suffix_check = (i.suffix == BYTE_MNEM_SUFFIX
+ ? No_bSuf
+ : (i.suffix == WORD_MNEM_SUFFIX
+ ? No_wSuf
+ : (i.suffix == SHORT_MNEM_SUFFIX
+ ? No_sSuf
+ : (i.suffix == LONG_MNEM_SUFFIX
+ ? No_lSuf
+ : (i.suffix == INTEL_DWORD_MNEM_SUFFIX
+ ? No_dSuf
+ : (i.suffix == LONG_DOUBLE_MNEM_SUFFIX ? No_xSuf : 0))))));
- overlap0 = overlap1 = overlap2 = found_reverse_match = 0;
for (t = current_templates->start;
t < current_templates->end;
t++)
{
- /* must have right number of operands */
+ /* Must have right number of operands. */
if (i.operands != t->operands)
continue;
+
+ /* For some opcodes, don't check the suffix */
+ if (intel_syntax)
+ {
+ if (strcmp (t->name, "fnstcw")
+ && strcmp (t->name, "fldcw")
+ && (t->opcode_modifier & suffix_check))
+ continue;
+ }
+ /* Must not have disallowed suffix. */
+ else if ((t->opcode_modifier & suffix_check))
+ continue;
+
else if (!t->operands)
break; /* 0 operands always matches */
switch (t->operands)
{
case 1:
- if (!MATCH (overlap0, i.types[0]))
+ if (!MATCH (overlap0, i.types[0], t->operand_types[0]))
continue;
break;
case 2:
case 3:
overlap1 = i.types[1] & t->operand_types[1];
- if (!MATCH (overlap0, i.types[0]) ||
- !MATCH (overlap1, i.types[1]) ||
- !CONSISTENT_REGISTER_MATCH (overlap0, overlap1,
- t->operand_types[0],
- t->operand_types[1]))
+ if (!MATCH (overlap0, i.types[0], t->operand_types[0])
+ || !MATCH (overlap1, i.types[1], t->operand_types[1])
+ || !CONSISTENT_REGISTER_MATCH (overlap0, i.types[0],
+ t->operand_types[0],
+ overlap1, i.types[1],
+ t->operand_types[1]))
{
/* check if other direction is valid ... */
- if (!(t->opcode_modifier & COMES_IN_BOTH_DIRECTIONS))
+ if ((t->opcode_modifier & (D|FloatD)) == 0)
continue;
/* try reversing direction of operands */
overlap0 = i.types[0] & t->operand_types[1];
overlap1 = i.types[1] & t->operand_types[0];
- if (!MATCH (overlap0, i.types[0]) ||
- !MATCH (overlap1, i.types[1]) ||
- !CONSISTENT_REGISTER_MATCH (overlap0, overlap1,
- t->operand_types[0],
- t->operand_types[1]))
+ if (!MATCH (overlap0, i.types[0], t->operand_types[1])
+ || !MATCH (overlap1, i.types[1], t->operand_types[0])
+ || !CONSISTENT_REGISTER_MATCH (overlap0, i.types[0],
+ t->operand_types[1],
+ overlap1, i.types[1],
+ t->operand_types[0]))
{
/* does not match either direction */
continue;
}
- /* found a reverse match here -- slip through */
- /* found_reverse_match holds which of D or FloatD we've found */
- found_reverse_match = t->opcode_modifier & COMES_IN_BOTH_DIRECTIONS;
- } /* endif: not forward match */
- /* found either forward/reverse 2 operand match here */
+ /* found_reverse_match holds which of D or FloatDR
+ we've found. */
+ found_reverse_match = t->opcode_modifier & (D|FloatDR);
+ break;
+ }
+ /* found a forward 2 operand match here */
if (t->operands == 3)
{
+ /* Here we make use of the fact that there are no
+ reverse match 3 operand instructions, and all 3
+ operand instructions only need to be checked for
+ register consistency between operands 2 and 3. */
overlap2 = i.types[2] & t->operand_types[2];
- if (!MATCH (overlap2, i.types[2]) ||
- !CONSISTENT_REGISTER_MATCH (overlap0, overlap2,
- t->operand_types[0],
- t->operand_types[2]) ||
- !CONSISTENT_REGISTER_MATCH (overlap1, overlap2,
- t->operand_types[1],
- t->operand_types[2]))
- continue;
+ if (!MATCH (overlap2, i.types[2], t->operand_types[2])
+ || !CONSISTENT_REGISTER_MATCH (overlap1, i.types[1],
+ t->operand_types[1],
+ overlap2, i.types[2],
+ t->operand_types[2]))
+
+ continue;
}
/* found either forward/reverse 2 or 3 operand match here:
slip through to break */
} /* for (t = ... */
if (t == current_templates->end)
{ /* we found no match */
- as_bad ("operands given don't match any known 386 instruction");
+ as_bad (_("suffix or operands invalid for `%s'"),
+ current_templates->start->name);
return;
}
- /* Copy the template we found (we may change it!). */
- i.tm = *t;
- t = &i.tm; /* alter new copy of template */
+ if ((t->opcode_modifier & (IsPrefix|IgnoreSize)) == (IsPrefix|IgnoreSize))
+ {
+ /* Warn them that a data or address size prefix doesn't affect
+ assembly of the next line of code. */
+ as_warn (_("stand-alone `%s' prefix"), t->name);
+ }
- /* If there's no opcode suffix we try to invent one based on register
- operands. */
- if (!i.suffix && i.reg_operands)
+ /* Copy the template we found. */
+ i.tm = *t;
+ if (found_reverse_match)
{
- /* We take i.suffix from the LAST register operand specified. This
- assumes that the last register operands is the destination register
- operand. */
- int op;
- for (op = 0; op < MAX_OPERANDS; op++)
- if (i.types[op] & Reg)
- {
- i.suffix = ((i.types[op] == Reg8) ? BYTE_OPCODE_SUFFIX :
- (i.types[op] == Reg16) ? WORD_OPCODE_SUFFIX :
- DWORD_OPCODE_SUFFIX);
- }
+ i.tm.operand_types[0] = t->operand_types[1];
+ i.tm.operand_types[1] = t->operand_types[0];
}
- /* Make still unresolved immediate matches conform to size of immediate
- given in i.suffix. Note: overlap2 cannot be an immediate!
- We assume this. */
- if ((overlap0 & (Imm8 | Imm8S | Imm16 | Imm32))
- && overlap0 != Imm8 && overlap0 != Imm8S
- && overlap0 != Imm16 && overlap0 != Imm32)
+
+ if (i.tm.opcode_modifier & FWait)
+ if (! add_prefix (FWAIT_OPCODE))
+ return;
+
+ /* Check string instruction segment overrides */
+ if ((i.tm.opcode_modifier & IsString) != 0 && i.mem_operands != 0)
{
- if (!i.suffix)
+ int mem_op = (i.types[0] & AnyMem) ? 0 : 1;
+ if ((i.tm.operand_types[mem_op] & EsSeg) != 0)
{
- as_bad ("no opcode suffix given; can't determine immediate size");
- return;
+ if (i.seg[0] != NULL && i.seg[0] != &es)
+ {
+ as_bad (_("`%s' operand %d must use `%%es' segment"),
+ i.tm.name,
+ mem_op + 1);
+ return;
+ }
+ /* There's only ever one segment override allowed per instruction.
+ This instruction possibly has a legal segment override on the
+ second operand, so copy the segment to where non-string
+ instructions store it, allowing common code. */
+ i.seg[0] = i.seg[1];
}
- overlap0 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8 | Imm8S) :
- (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32));
- }
- if ((overlap1 & (Imm8 | Imm8S | Imm16 | Imm32))
+ else if ((i.tm.operand_types[mem_op + 1] & EsSeg) != 0)
+ {
+ if (i.seg[1] != NULL && i.seg[1] != &es)
+ {
+ as_bad (_("`%s' operand %d must use `%%es' segment"),
+ i.tm.name,
+ mem_op + 2);
+ return;
+ }
+ }
+ }
+
+ /* If matched instruction specifies an explicit instruction mnemonic
+ suffix, use it. */
+ if (i.tm.opcode_modifier & (Size16 | Size32))
+ {
+ if (i.tm.opcode_modifier & Size16)
+ i.suffix = WORD_MNEM_SUFFIX;
+ else
+ i.suffix = DWORD_MNEM_SUFFIX;
+ }
+ else if (i.reg_operands)
+ {
+ /* If there's no instruction mnemonic suffix we try to invent one
+ based on register operands. */
+ if (!i.suffix)
+ {
+ /* We take i.suffix from the last register operand specified,
+ Destination register type is more significant than source
+ register type. */
+ int op;
+ for (op = i.operands; --op >= 0; )
+ if (i.types[op] & Reg)
+ {
+ i.suffix = ((i.types[op] & Reg8) ? BYTE_MNEM_SUFFIX :
+ (i.types[op] & Reg16) ? WORD_MNEM_SUFFIX :
+ DWORD_MNEM_SUFFIX);
+ break;
+ }
+ }
+ else if (i.suffix == BYTE_MNEM_SUFFIX)
+ {
+ int op;
+ for (op = i.operands; --op >= 0; )
+ {
+ /* If this is an eight bit register, it's OK. If it's
+ the 16 or 32 bit version of an eight bit register,
+ we will just use the low portion, and that's OK too. */
+ if (i.types[op] & Reg8)
+ continue;
+
+ /* movzx and movsx should not generate this warning. */
+ if (intel_syntax
+ && (i.tm.base_opcode == 0xfb7
+ || i.tm.base_opcode == 0xfb6
+ || i.tm.base_opcode == 0xfbe
+ || i.tm.base_opcode == 0xfbf))
+ continue;
+
+ if ((i.types[op] & WordReg) && i.regs[op]->reg_num < 4
+#if 0
+ /* Check that the template allows eight bit regs
+ This kills insns such as `orb $1,%edx', which
+ maybe should be allowed. */
+ && (i.tm.operand_types[op] & (Reg8|InOutPortReg))
+#endif
+ )
+ {
+#if REGISTER_WARNINGS
+ if ((i.tm.operand_types[op] & InOutPortReg) == 0)
+ as_warn (_("using `%%%s' instead of `%%%s' due to `%c' suffix"),
+ (i.regs[op] - (i.types[op] & Reg16 ? 8 : 16))->reg_name,
+ i.regs[op]->reg_name,
+ i.suffix);
+#endif
+ continue;
+ }
+ /* Any other register is bad */
+ if (i.types[op] & (Reg | RegMMX | Control | Debug | Test
+ | FloatReg | FloatAcc | SReg2 | SReg3))
+ {
+ as_bad (_("`%%%s' not allowed with `%s%c'"),
+ i.regs[op]->reg_name,
+ i.tm.name,
+ i.suffix);
+ return;
+ }
+ }
+ }
+ else if (i.suffix == DWORD_MNEM_SUFFIX)
+ {
+ int op;
+ for (op = i.operands; --op >= 0; )
+ /* Reject eight bit registers, except where the template
+ requires them. (eg. movzb) */
+ if ((i.types[op] & Reg8) != 0
+ && (i.tm.operand_types[op] & (Reg16|Reg32|Acc)) != 0)
+ {
+ as_bad (_("`%%%s' not allowed with `%s%c'"),
+ i.regs[op]->reg_name,
+ i.tm.name,
+ i.suffix);
+ return;
+ }
+#if REGISTER_WARNINGS
+ /* Warn if the e prefix on a general reg is missing. */
+ else if ((i.types[op] & Reg16) != 0
+ && (i.tm.operand_types[op] & (Reg32|Acc)) != 0)
+ {
+ as_warn (_("using `%%%s' instead of `%%%s' due to `%c' suffix"),
+ (i.regs[op] + 8)->reg_name,
+ i.regs[op]->reg_name,
+ i.suffix);
+ }
+#endif
+ }
+ else if (i.suffix == WORD_MNEM_SUFFIX)
+ {
+ int op;
+ for (op = i.operands; --op >= 0; )
+ /* Reject eight bit registers, except where the template
+ requires them. (eg. movzb) */
+ if ((i.types[op] & Reg8) != 0
+ && (i.tm.operand_types[op] & (Reg16|Reg32|Acc)) != 0)
+ {
+ as_bad (_("`%%%s' not allowed with `%s%c'"),
+ i.regs[op]->reg_name,
+ i.tm.name,
+ i.suffix);
+ return;
+ }
+#if REGISTER_WARNINGS
+ /* Warn if the e prefix on a general reg is present. */
+ else if ((i.types[op] & Reg32) != 0
+ && (i.tm.operand_types[op] & (Reg16|Acc)) != 0)
+ {
+ as_warn (_("using `%%%s' instead of `%%%s' due to `%c' suffix"),
+ (i.regs[op] - 8)->reg_name,
+ i.regs[op]->reg_name,
+ i.suffix);
+ }
+#endif
+ }
+ else
+ abort();
+ }
+
+ /* Make still unresolved immediate matches conform to size of immediate
+ given in i.suffix. Note: overlap2 cannot be an immediate! */
+ if ((overlap0 & (Imm8 | Imm8S | Imm16 | Imm32))
+ && overlap0 != Imm8 && overlap0 != Imm8S
+ && overlap0 != Imm16 && overlap0 != Imm32)
+ {
+ if (i.suffix)
+ {
+ overlap0 &= (i.suffix == BYTE_MNEM_SUFFIX ? (Imm8 | Imm8S) :
+ (i.suffix == WORD_MNEM_SUFFIX ? Imm16 : Imm32));
+ }
+ else if (overlap0 == (Imm16 | Imm32))
+ {
+ overlap0 =
+ (flag_16bit_code ^ (i.prefix[DATA_PREFIX] != 0)) ? Imm16 : Imm32;
+ }
+ else
+ {
+ as_bad (_("no instruction mnemonic suffix given; can't determine immediate size"));
+ return;
+ }
+ }
+ if ((overlap1 & (Imm8 | Imm8S | Imm16 | Imm32))
&& overlap1 != Imm8 && overlap1 != Imm8S
&& overlap1 != Imm16 && overlap1 != Imm32)
{
- if (!i.suffix)
+ if (i.suffix)
+ {
+ overlap1 &= (i.suffix == BYTE_MNEM_SUFFIX ? (Imm8 | Imm8S) :
+ (i.suffix == WORD_MNEM_SUFFIX ? Imm16 : Imm32));
+ }
+ else if (overlap1 == (Imm16 | Imm32))
+ {
+ overlap1 =
+ (flag_16bit_code ^ (i.prefix[DATA_PREFIX] != 0)) ? Imm16 : Imm32;
+ }
+ else
{
- as_bad ("no opcode suffix given; can't determine immediate size");
+ as_bad (_("no instruction mnemonic suffix given; can't determine immediate size"));
return;
}
- overlap1 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8 | Imm8S) :
- (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32));
}
+ assert ((overlap2 & Imm) == 0);
i.types[0] = overlap0;
- i.types[1] = overlap1;
- i.types[2] = overlap2;
-
if (overlap0 & ImplicitRegister)
i.reg_operands--;
+ if (overlap0 & Imm1)
+ i.imm_operands = 0; /* kludge for shift insns */
+
+ i.types[1] = overlap1;
if (overlap1 & ImplicitRegister)
i.reg_operands--;
+
+ i.types[2] = overlap2;
if (overlap2 & ImplicitRegister)
i.reg_operands--;
- if (overlap0 & Imm1)
- i.imm_operands = 0; /* kludge for shift insns */
-
- if (found_reverse_match)
- {
- unsigned int save;
- save = t->operand_types[0];
- t->operand_types[0] = t->operand_types[1];
- t->operand_types[1] = save;
- }
/* Finalize opcode. First, we change the opcode based on the operand
- size given by i.suffix: we never have to change things for byte insns,
- or when no opcode suffix is need to size the operands. */
+ size given by i.suffix: We need not change things for byte insns. */
- if (!i.suffix && (t->opcode_modifier & W))
+ if (!i.suffix && (i.tm.opcode_modifier & W))
{
- as_bad ("no opcode suffix given and no register operands; can't size instruction");
+ as_bad (_("no instruction mnemonic suffix given and no register operands; can't size instruction"));
return;
}
- if (i.suffix && i.suffix != BYTE_OPCODE_SUFFIX)
+ /* For movzx and movsx, need to check the register type */
+ if (intel_syntax
+ && (i.tm.base_opcode == 0xfb6 || i.tm.base_opcode == 0xfbe))
+ if (i.suffix && i.suffix == BYTE_MNEM_SUFFIX)
+ {
+ unsigned int prefix = DATA_PREFIX_OPCODE;
+
+ if ((i.regs[1]->reg_type & Reg16) != 0)
+ if (!add_prefix (prefix))
+ return;
+ }
+
+ if (i.suffix && i.suffix != BYTE_MNEM_SUFFIX)
{
- /* Select between byte and word/dword operations. */
- if (t->opcode_modifier & W)
- t->base_opcode |= W;
- /* Now select between word & dword operations via the
- operand size prefix. */
- if (i.suffix == WORD_OPCODE_SUFFIX)
+ /* It's not a byte, select word/dword operation. */
+ if (i.tm.opcode_modifier & W)
{
- if (i.prefixes == MAX_PREFIXES)
- {
- as_bad ("%d prefixes given and 'w' opcode suffix gives too many prefixes",
- MAX_PREFIXES);
- return;
- }
- i.prefix[i.prefixes++] = WORD_PREFIX_OPCODE;
+ if (i.tm.opcode_modifier & ShortForm)
+ i.tm.base_opcode |= 8;
+ else
+ i.tm.base_opcode |= 1;
+ }
+ /* Now select between word & dword operations via the operand
+ size prefix, except for instructions that will ignore this
+ prefix anyway. */
+ if (((intel_syntax && (i.suffix == INTEL_DWORD_MNEM_SUFFIX))
+ || i.suffix == DWORD_MNEM_SUFFIX
+ || i.suffix == LONG_MNEM_SUFFIX) == flag_16bit_code
+ && !(i.tm.opcode_modifier & IgnoreSize))
+ {
+ unsigned int prefix = DATA_PREFIX_OPCODE;
+ if (i.tm.opcode_modifier & JumpByte) /* jcxz, loop */
+ prefix = ADDR_PREFIX_OPCODE;
+
+ if (! add_prefix (prefix))
+ return;
+ }
+ /* Size floating point instruction. */
+ if (i.suffix == LONG_MNEM_SUFFIX
+ || (intel_syntax && i.suffix == INTEL_DWORD_MNEM_SUFFIX))
+ {
+ if (i.tm.opcode_modifier & FloatMF)
+ i.tm.base_opcode ^= 4;
}
+
+ if (intel_syntax && i.suffix == LONG_DOUBLE_MNEM_SUFFIX)
+ {
+ if (i.tm.opcode_modifier & FloatMF)
+ i.tm.base_opcode ^= 2;
+ }
+ }
+
+ if (i.tm.base_opcode == AMD_3DNOW_OPCODE)
+ {
+ /* These AMD specific instructions have an opcode suffix which
+ is coded in the same place as an 8-bit immediate field
+ would be. Here we fake an 8-bit immediate operand from the
+ opcode suffix stored in tm.extension_opcode. */
+
+ expressionS *exp;
+
+ assert(i.imm_operands == 0 && i.operands <= 2);
+
+ exp = &im_expressions[i.imm_operands++];
+ i.imms[i.operands] = exp;
+ i.types[i.operands++] = Imm8;
+ exp->X_op = O_constant;
+ exp->X_add_number = i.tm.extension_opcode;
+ i.tm.extension_opcode = None;
}
/* For insns with operands there are more diddles to do to the opcode. */
if (i.operands)
{
- /* If we found a reverse match we must alter the opcode direction bit
- found_reverse_match holds bit to set (different for int &
- float insns). */
+ /* Default segment register this instruction will use
+ for memory accesses. 0 means unknown.
+ This is only for optimizing out unnecessary segment overrides. */
+ const seg_entry *default_seg = 0;
- if (found_reverse_match)
- {
- t->base_opcode |= found_reverse_match;
- }
+ /* If we found a reverse match we must alter the opcode
+ direction bit. found_reverse_match holds bits to change
+ (different for int & float insns). */
+
+ i.tm.base_opcode ^= found_reverse_match;
/* The imul $imm, %reg instruction is converted into
- imul $imm, %reg, %reg. */
- if (t->opcode_modifier & imulKludge)
+ imul $imm, %reg, %reg, and the clr %reg instruction
+ is converted into xor %reg, %reg. */
+ if (i.tm.opcode_modifier & regKludge)
{
- /* Pretend we saw the 3 operand case. */
- i.regs[2] = i.regs[1];
+ unsigned int first_reg_op = (i.types[0] & Reg) ? 0 : 1;
+ /* Pretend we saw the extra register operand. */
+ i.regs[first_reg_op+1] = i.regs[first_reg_op];
i.reg_operands = 2;
}
- /* Certain instructions expect the destination to be in the i.rm.reg
- field. This is by far the exceptional case. For these
- instructions, if the source operand is a register, we must reverse
- the i.rm.reg and i.rm.regmem fields. We accomplish this by faking
- that the two register operands were given in the reverse order. */
- if ((t->opcode_modifier & ReverseRegRegmem) && i.reg_operands == 2)
- {
- unsigned int first_reg_operand = (i.types[0] & Reg) ? 0 : 1;
- unsigned int second_reg_operand = first_reg_operand + 1;
- reg_entry *tmp = i.regs[first_reg_operand];
- i.regs[first_reg_operand] = i.regs[second_reg_operand];
- i.regs[second_reg_operand] = tmp;
- }
-
- if (t->opcode_modifier & ShortForm)
+ if (i.tm.opcode_modifier & ShortForm)
{
/* The register or float register operand is in operand 0 or 1. */
unsigned int op = (i.types[0] & (Reg | FloatReg)) ? 0 : 1;
/* Register goes in low 3 bits of opcode. */
- t->base_opcode |= i.regs[op]->reg_num;
- }
- else if (t->opcode_modifier & ShortFormW)
- {
- /* Short form with 0x8 width bit. Register is always dest. operand */
- t->base_opcode |= i.regs[1]->reg_num;
- if (i.suffix == WORD_OPCODE_SUFFIX ||
- i.suffix == DWORD_OPCODE_SUFFIX)
- t->base_opcode |= 0x8;
- }
- else if (t->opcode_modifier & Seg2ShortForm)
- {
- if (t->base_opcode == POP_SEG_SHORT && i.regs[0]->reg_num == 1)
+ i.tm.base_opcode |= i.regs[op]->reg_num;
+ if ((i.tm.opcode_modifier & Ugh) != 0)
{
- as_bad ("you can't 'pop cs' on the 386.");
- return;
+ /* Warn about some common errors, but press on regardless.
+ The first case can be generated by gcc (<= 2.8.1). */
+ if (i.operands == 2)
+ {
+ /* reversed arguments on faddp, fsubp, etc. */
+ as_warn (_("translating to `%s %%%s,%%%s'"), i.tm.name,
+ i.regs[1]->reg_name,
+ i.regs[0]->reg_name);
+ }
+ else
+ {
+ /* extraneous `l' suffix on fp insn */
+ as_warn (_("translating to `%s %%%s'"), i.tm.name,
+ i.regs[0]->reg_name);
+ }
}
- t->base_opcode |= (i.regs[0]->reg_num << 3);
- }
- else if (t->opcode_modifier & Seg3ShortForm)
- {
- /* 'push %fs' is 0x0fa0; 'pop %fs' is 0x0fa1.
- 'push %gs' is 0x0fa8; 'pop %fs' is 0x0fa9.
- So, only if i.regs[0]->reg_num == 5 (%gs) do we need
- to change the opcode. */
- if (i.regs[0]->reg_num == 5)
- t->base_opcode |= 0x08;
}
- else if (t->opcode_modifier & Modrm)
+ else if (i.tm.opcode_modifier & Modrm)
{
- /* The opcode is completed (modulo t->extension_opcode which must
- be put into the modrm byte.
- Now, we make the modrm & index base bytes based on all the info
- we've collected. */
+ /* The opcode is completed (modulo i.tm.extension_opcode which
+ must be put into the modrm byte).
+ Now, we make the modrm & index base bytes based on all the
+ info we've collected. */
/* i.reg_operands MUST be the number of real register operands;
implicit registers do not count. */
if (i.reg_operands == 2)
{
unsigned int source, dest;
- source = (i.types[0] & (Reg | SReg2 | SReg3 | Control | Debug | Test)) ? 0 : 1;
+ source = ((i.types[0]
+ & (Reg
+ | SReg2
+ | SReg3
+ | Control
+ | Debug
+ | Test
+ | RegMMX))
+ ? 0 : 1);
dest = source + 1;
+
+ /* Certain instructions expect the destination to be
+ in the i.rm.reg field. This is by far the
+ exceptional case. For these instructions, if the
+ source operand is a register, we must reverse the
+ i.rm.reg and i.rm.regmem fields. We accomplish
+ this by pretending that the two register operands
+ were given in the reverse order. */
+ if (i.tm.opcode_modifier & ReverseRegRegmem)
+ {
+ const reg_entry *tmp = i.regs[source];
+ i.regs[source] = i.regs[dest];
+ i.regs[dest] = tmp;
+ }
+
i.rm.mode = 3;
/* We must be careful to make sure that all
- segment/control/test/debug registers go into the i.rm.reg
- field (despite the whether they are source or destination
- operands). */
- if (i.regs[dest]->reg_type & (SReg2 | SReg3 | Control | Debug | Test))
+ segment/control/test/debug/MMX registers go into
+ the i.rm.reg field (despite whether they are
+ source or destination operands). */
+ if (i.regs[dest]->reg_type
+ & (SReg2 | SReg3 | Control | Debug | Test | RegMMX))
{
i.rm.reg = i.regs[dest]->reg_num;
i.rm.regmem = i.regs[source]->reg_num;
if (i.mem_operands)
{
unsigned int fake_zero_displacement = 0;
- unsigned int op = (i.types[0] & Mem) ? 0 : ((i.types[1] & Mem) ? 1 : 2);
+ unsigned int op = ((i.types[0] & AnyMem)
+ ? 0
+ : (i.types[1] & AnyMem) ? 1 : 2);
- /* Encode memory operand into modrm byte and base index
- byte. */
+ default_seg = &ds;
- if (i.base_reg == esp && !i.index_reg)
+ if (! i.base_reg)
{
- /* <disp>(%esp) becomes two byte modrm with no index
- register. */
- i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
- i.rm.mode = mode_from_disp_size (i.types[op]);
- i.bi.base = ESP_REG_NUM;
- i.bi.index = NO_INDEX_REGISTER;
- i.bi.scale = 0; /* Must be zero! */
- }
- else if (i.base_reg == ebp && !i.index_reg)
- {
- if (!(i.types[op] & Disp))
+ i.rm.mode = 0;
+ if (! i.disp_operands)
+ fake_zero_displacement = 1;
+ if (! i.index_reg)
{
- /* Must fake a zero byte displacement. There is
- no direct way to code '(%ebp)' directly. */
- fake_zero_displacement = 1;
- /* fake_zero_displacement code does not set this. */
- i.types[op] |= Disp8;
- }
- i.rm.mode = mode_from_disp_size (i.types[op]);
- i.rm.regmem = EBP_REG_NUM;
- }
- else if (!i.base_reg && (i.types[op] & BaseIndex))
- {
- /* There are three cases here.
- Case 1: '<32bit disp>(,1)' -- indirect absolute.
- (Same as cases 2 & 3 with NO index register)
- Case 2: <32bit disp> (,<index>) -- no base register with disp
- Case 3: (, <index>) --- no base register;
- no disp (must add 32bit 0 disp). */
- i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
- i.rm.mode = 0; /* 32bit mode */
- i.bi.base = NO_BASE_REGISTER;
- i.types[op] &= ~Disp;
- i.types[op] |= Disp32; /* Must be 32bit! */
- if (i.index_reg)
- { /* case 2 or case 3 */
- i.bi.index = i.index_reg->reg_num;
- i.bi.scale = i.log2_scale_factor;
- if (i.disp_operands == 0)
- fake_zero_displacement = 1; /* case 3 */
+ /* Operand is just <disp> */
+ if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0))
+ {
+ i.rm.regmem = NO_BASE_REGISTER_16;
+ i.types[op] &= ~Disp;
+ i.types[op] |= Disp16;
+ }
+ else
+ {
+ i.rm.regmem = NO_BASE_REGISTER;
+ i.types[op] &= ~Disp;
+ i.types[op] |= Disp32;
+ }
}
- else
+ else /* ! i.base_reg && i.index_reg */
{
- i.bi.index = NO_INDEX_REGISTER;
- i.bi.scale = 0;
+ i.sib.index = i.index_reg->reg_num;
+ i.sib.base = NO_BASE_REGISTER;
+ i.sib.scale = i.log2_scale_factor;
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ i.types[op] &= ~Disp;
+ i.types[op] |= Disp32; /* Must be 32 bit */
}
}
- else if (i.disp_operands && !i.base_reg && !i.index_reg)
+ else if (i.base_reg->reg_type & Reg16)
{
- /* Operand is just <32bit disp> */
- i.rm.regmem = EBP_REG_NUM;
- i.rm.mode = 0;
- i.types[op] &= ~Disp;
- i.types[op] |= Disp32;
+ switch (i.base_reg->reg_num)
+ {
+ case 3: /* (%bx) */
+ if (! i.index_reg)
+ i.rm.regmem = 7;
+ else /* (%bx,%si) -> 0, or (%bx,%di) -> 1 */
+ i.rm.regmem = i.index_reg->reg_num - 6;
+ break;
+ case 5: /* (%bp) */
+ default_seg = &ss;
+ if (! i.index_reg)
+ {
+ i.rm.regmem = 6;
+ if ((i.types[op] & Disp) == 0)
+ {
+ /* fake (%bp) into 0(%bp) */
+ i.types[op] |= Disp8;
+ fake_zero_displacement = 1;
+ }
+ }
+ else /* (%bp,%si) -> 2, or (%bp,%di) -> 3 */
+ i.rm.regmem = i.index_reg->reg_num - 6 + 2;
+ break;
+ default: /* (%si) -> 4 or (%di) -> 5 */
+ i.rm.regmem = i.base_reg->reg_num - 6 + 4;
+ }
+ i.rm.mode = mode_from_disp_size (i.types[op]);
}
- else
+ else /* i.base_reg and 32 bit mode */
{
- /* It's not a special case; rev'em up. */
i.rm.regmem = i.base_reg->reg_num;
- i.rm.mode = mode_from_disp_size (i.types[op]);
- if (i.index_reg)
+ i.sib.base = i.base_reg->reg_num;
+ if (i.base_reg->reg_num == EBP_REG_NUM)
{
- i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
- i.bi.base = i.base_reg->reg_num;
- i.bi.index = i.index_reg->reg_num;
- i.bi.scale = i.log2_scale_factor;
- if (i.base_reg == ebp && i.disp_operands == 0)
- { /* pace */
+ default_seg = &ss;
+ if (i.disp_operands == 0)
+ {
fake_zero_displacement = 1;
i.types[op] |= Disp8;
- i.rm.mode = mode_from_disp_size (i.types[op]);
}
}
+ else if (i.base_reg->reg_num == ESP_REG_NUM)
+ {
+ default_seg = &ss;
+ }
+ i.sib.scale = i.log2_scale_factor;
+ if (! i.index_reg)
+ {
+ /* <disp>(%esp) becomes two byte modrm
+ with no index register. We've already
+ stored the code for esp in i.rm.regmem
+ ie. ESCAPE_TO_TWO_BYTE_ADDRESSING. Any
+ base register besides %esp will not use
+ the extra modrm byte. */
+ i.sib.index = NO_INDEX_REGISTER;
+#if ! SCALE1_WHEN_NO_INDEX
+ /* Another case where we force the second
+ modrm byte. */
+ if (i.log2_scale_factor)
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+#endif
+ }
+ else
+ {
+ i.sib.index = i.index_reg->reg_num;
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ }
+ i.rm.mode = mode_from_disp_size (i.types[op]);
}
+
if (fake_zero_displacement)
{
/* Fakes a zero displacement assuming that i.types[op]
exp->X_add_symbol = (symbolS *) 0;
exp->X_op_symbol = (symbolS *) 0;
}
-
- /* Select the correct segment for the memory operand. */
- if (i.seg)
- {
- unsigned int seg_index;
- const seg_entry *default_seg;
-
- if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING)
- {
- seg_index = (i.rm.mode << 3) | i.bi.base;
- default_seg = two_byte_segment_defaults[seg_index];
- }
- else
- {
- seg_index = (i.rm.mode << 3) | i.rm.regmem;
- default_seg = one_byte_segment_defaults[seg_index];
- }
- /* If the specified segment is not the default, use an
- opcode prefix to select it */
- if (i.seg != default_seg)
- {
- if (i.prefixes == MAX_PREFIXES)
- {
- as_bad ("%d prefixes given and %s segment override gives too many prefixes",
- MAX_PREFIXES, i.seg->seg_name);
- return;
- }
- i.prefix[i.prefixes++] = i.seg->seg_prefix;
- }
- }
}
- /* Fill in i.rm.reg or i.rm.regmem field with register operand
- (if any) based on t->extension_opcode. Again, we must be
- careful to make sure that segment/control/debug/test
- registers are coded into the i.rm.reg field. */
+ /* Fill in i.rm.reg or i.rm.regmem field with register
+ operand (if any) based on i.tm.extension_opcode.
+ Again, we must be careful to make sure that
+ segment/control/debug/test/MMX registers are coded
+ into the i.rm.reg field. */
if (i.reg_operands)
{
unsigned int op =
- (i.types[0] & (Reg | SReg2 | SReg3 | Control | Debug | Test)) ? 0 :
- (i.types[1] & (Reg | SReg2 | SReg3 | Control | Debug | Test)) ? 1 : 2;
+ ((i.types[0]
+ & (Reg | SReg2 | SReg3 | Control | Debug
+ | Test | RegMMX))
+ ? 0
+ : ((i.types[1]
+ & (Reg | SReg2 | SReg3 | Control | Debug
+ | Test | RegMMX))
+ ? 1
+ : 2));
/* If there is an extension opcode to put here, the
register number must be put into the regmem field. */
- if (t->extension_opcode != None)
+ if (i.tm.extension_opcode != None)
i.rm.regmem = i.regs[op]->reg_num;
else
i.rm.reg = i.regs[op]->reg_num;
/* Now, if no memory operand has set i.rm.mode = 0, 1, 2
we must set it to 3 to indicate this is a register
- operand int the regmem field */
+ operand in the regmem field. */
if (!i.mem_operands)
i.rm.mode = 3;
}
/* Fill in i.rm.reg field with extension opcode (if any). */
- if (t->extension_opcode != None)
- i.rm.reg = t->extension_opcode;
+ if (i.tm.extension_opcode != None)
+ i.rm.reg = i.tm.extension_opcode;
+ }
+ }
+ else if (i.tm.opcode_modifier & (Seg2ShortForm | Seg3ShortForm))
+ {
+ if (i.tm.base_opcode == POP_SEG_SHORT && i.regs[0]->reg_num == 1)
+ {
+ as_bad (_("you can't `pop %%cs'"));
+ return;
}
+ i.tm.base_opcode |= (i.regs[0]->reg_num << 3);
+ }
+ else if ((i.tm.base_opcode & ~(D|W)) == MOV_AX_DISP32)
+ {
+ default_seg = &ds;
+ }
+ else if ((i.tm.opcode_modifier & IsString) != 0)
+ {
+ /* For the string instructions that allow a segment override
+ on one of their operands, the default segment is ds. */
+ default_seg = &ds;
+ }
+
+ /* If a segment was explicitly specified,
+ and the specified segment is not the default,
+ use an opcode prefix to select it.
+ If we never figured out what the default segment is,
+ then default_seg will be zero at this point,
+ and the specified segment prefix will always be used. */
+ if ((i.seg[0]) && (i.seg[0] != default_seg))
+ {
+ if (! add_prefix (i.seg[0]->seg_prefix))
+ return;
}
}
+ else if ((i.tm.opcode_modifier & Ugh) != 0)
+ {
+ /* UnixWare fsub no args is alias for fsubp, fadd -> faddp, etc */
+ as_warn (_("translating to `%sp'"), i.tm.name);
+ }
}
/* Handle conversion of 'int $3' --> special int3 insn. */
- if (t->base_opcode == INT_OPCODE && i.imms[0]->X_add_number == 3)
+ if (i.tm.base_opcode == INT_OPCODE && i.imms[0]->X_add_number == 3)
{
- t->base_opcode = INT3_OPCODE;
+ i.tm.base_opcode = INT3_OPCODE;
i.imm_operands = 0;
}
register char *p;
/* Output jumps. */
- if (t->opcode_modifier & Jump)
+ if (i.tm.opcode_modifier & Jump)
{
- unsigned long n = i.disps[0]->X_add_number;
+ long n = (long) i.disps[0]->X_add_number;
+ int prefix = (i.prefix[DATA_PREFIX] != 0);
+ int code16 = 0;
+
+ if (prefix)
+ {
+ i.prefixes -= 1;
+ code16 = CODE16;
+ }
+ if (flag_16bit_code)
+ code16 ^= CODE16;
+
+ if (!intel_syntax && (i.prefixes != 0))
+ as_warn (_("skipping prefixes on this instruction"));
if (i.disps[0]->X_op == O_constant)
{
if (fits_in_signed_byte (n))
{
- p = frag_more (2);
insn_size += 2;
- p[0] = t->base_opcode;
+ p = frag_more (2);
+ p[0] = i.tm.base_opcode;
p[1] = n;
}
-#if 0 /* leave out 16 bit jumps - pace */
- else if (fits_in_signed_word (n))
- {
- p = frag_more (4);
- insn_size += 4;
- p[0] = WORD_PREFIX_OPCODE;
- p[1] = t->base_opcode;
- md_number_to_chars (&p[2], (valueT) n, 2);
- }
-#endif
else
- { /* It's an absolute dword displacement. */
- if (t->base_opcode == JUMP_PC_RELATIVE)
+ {
+ /* Use 16-bit jumps only for 16-bit code,
+ because text segments are limited to 64K anyway;
+ Use 32-bit jumps for 32-bit code, because they're faster,
+ and a 16-bit jump will clear the top 16 bits of %eip. */
+ int jmp_size = code16 ? 2 : 4;
+ if (code16 && !fits_in_signed_word (n))
+ {
+ as_bad (_("16-bit jump out of range"));
+ return;
+ }
+
+ if (i.tm.base_opcode == JUMP_PC_RELATIVE)
{ /* pace */
/* unconditional jump */
- p = frag_more (5);
- insn_size += 5;
- p[0] = (char) 0xe9;
- md_number_to_chars (&p[1], (valueT) n, 4);
+ insn_size += prefix + 1 + jmp_size;
+ p = frag_more (prefix + 1 + jmp_size);
+ if (prefix)
+ *p++ = DATA_PREFIX_OPCODE;
+ *p++ = (char) 0xe9;
+ md_number_to_chars (p, (valueT) n, jmp_size);
}
else
{
/* conditional jump */
- p = frag_more (6);
- insn_size += 6;
- p[0] = TWO_BYTE_OPCODE_ESCAPE;
- p[1] = t->base_opcode + 0x10;
- md_number_to_chars (&p[2], (valueT) n, 4);
+ insn_size += prefix + 2 + jmp_size;
+ p = frag_more (prefix + 2 + jmp_size);
+ if (prefix)
+ *p++ = DATA_PREFIX_OPCODE;
+ *p++ = TWO_BYTE_OPCODE_ESCAPE;
+ *p++ = i.tm.base_opcode + 0x10;
+ md_number_to_chars (p, (valueT) n, jmp_size);
}
}
}
else
{
+ int size = code16 ? 2 : 4;
+
/* It's a symbol; end frag & setup for relax.
Make sure there are more than 6 chars left in the current frag;
if not we'll have to start a new one. */
- if (obstack_room (&frags) <= 6)
- {
- frag_wane (frag_now);
- frag_new (0);
- }
- p = frag_more (1);
- insn_size += 1;
- p[0] = t->base_opcode;
+ frag_grow (prefix + 1 + 2 + size);
+ insn_size += 1 + prefix;
+ p = frag_more (1 + prefix);
+ if (prefix)
+ *p++ = DATA_PREFIX_OPCODE;
+ *p = i.tm.base_opcode;
frag_var (rs_machine_dependent,
- 6, /* 2 opcode/prefix + 4 displacement */
+ prefix + 2 + size, /* 2 opcode/prefix + displacement */
1,
((unsigned char) *p == JUMP_PC_RELATIVE
- ? ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE)
- : ENCODE_RELAX_STATE (COND_JUMP, BYTE)),
+ ? ENCODE_RELAX_STATE (UNCOND_JUMP, SMALL) | code16
+ : ENCODE_RELAX_STATE (COND_JUMP, SMALL) | code16),
i.disps[0]->X_add_symbol,
- (long) n, p);
+ (offsetT) n, p);
}
}
- else if (t->opcode_modifier & (JumpByte | JumpDword))
+ else if (i.tm.opcode_modifier & (JumpByte | JumpDword))
{
- int size = (t->opcode_modifier & JumpByte) ? 1 : 4;
- unsigned long n = i.disps[0]->X_add_number;
+ int size = (i.tm.opcode_modifier & JumpByte) ? 1 : 4;
+ long n = (long) i.disps[0]->X_add_number;
- if (fits_in_unsigned_byte (t->base_opcode))
+ if (size == 1) /* then this is a loop or jecxz type instruction */
{
- FRAG_APPEND_1_CHAR (t->base_opcode);
- insn_size += 1;
+ if (i.prefix[ADDR_PREFIX])
+ {
+ insn_size += 1;
+ FRAG_APPEND_1_CHAR (ADDR_PREFIX_OPCODE);
+ i.prefixes -= 1;
+ }
}
else
{
- p = frag_more (2); /* opcode can be at most two bytes */
- insn_size += 2;
- /* put out high byte first: can't use md_number_to_chars! */
- *p++ = (t->base_opcode >> 8) & 0xff;
- *p = t->base_opcode & 0xff;
+ int code16 = 0;
+
+ if (i.prefix[DATA_PREFIX])
+ {
+ insn_size += 1;
+ FRAG_APPEND_1_CHAR (DATA_PREFIX_OPCODE);
+ i.prefixes -= 1;
+ code16 = CODE16;
+ }
+ if (flag_16bit_code)
+ code16 ^= CODE16;
+
+ if (code16)
+ size = 2;
}
- p = frag_more (size);
- insn_size += size;
+ if (!intel_syntax && (i.prefixes != 0))
+ as_warn (_("skipping prefixes on this instruction"));
+
+ if (fits_in_unsigned_byte (i.tm.base_opcode))
+ {
+ insn_size += 1 + size;
+ p = frag_more (1 + size);
+ }
+ else
+ {
+ insn_size += 2 + size; /* opcode can be at most two bytes */
+ p = frag_more (2 + size);
+ *p++ = (i.tm.base_opcode >> 8) & 0xff;
+ }
+ *p++ = i.tm.base_opcode & 0xff;
+
if (i.disps[0]->X_op == O_constant)
{
- md_number_to_chars (p, (valueT) n, size);
if (size == 1 && !fits_in_signed_byte (n))
{
- as_bad ("loop/jecx only takes byte displacement; %lu shortened to %d",
- n, *p);
+ as_bad (_("`%s' only takes byte displacement; %ld shortened to %d"),
+ i.tm.name, n, *p);
}
+ else if (size == 2 && !fits_in_signed_word (n))
+ {
+ as_bad (_("16-bit jump out of range"));
+ return;
+ }
+ md_number_to_chars (p, (valueT) n, size);
}
else
{
fix_new_exp (frag_now, p - frag_now->fr_literal, size,
- i.disps[0], 1, reloc (size, 1));
+ i.disps[0], 1, reloc (size, 1, i.disp_reloc[0]));
+
}
}
- else if (t->opcode_modifier & JumpInterSegment)
+ else if (i.tm.opcode_modifier & JumpInterSegment)
{
- p = frag_more (1 + 2 + 4); /* 1 opcode; 2 segment; 4 offset */
- insn_size += 1 + 2 + 4;
- p[0] = t->base_opcode;
+ int size;
+ int reloc_type;
+ int prefix = i.prefix[DATA_PREFIX] != 0;
+ int code16 = 0;
+
+ if (prefix)
+ {
+ code16 = CODE16;
+ i.prefixes -= 1;
+ }
+ if (flag_16bit_code)
+ code16 ^= CODE16;
+
+ size = 4;
+ reloc_type = BFD_RELOC_32;
+ if (code16)
+ {
+ size = 2;
+ reloc_type = BFD_RELOC_16;
+ }
+
+ if (!intel_syntax && (i.prefixes != 0))
+ as_warn (_("skipping prefixes on this instruction"));
+
+ insn_size += prefix + 1 + 2 + size; /* 1 opcode; 2 segment; offset */
+ p = frag_more (prefix + 1 + 2 + size);
+ if (prefix)
+ *p++ = DATA_PREFIX_OPCODE;
+ *p++ = i.tm.base_opcode;
if (i.imms[1]->X_op == O_constant)
- md_number_to_chars (p + 1, (valueT) i.imms[1]->X_add_number, 4);
+ {
+ long n = (long) i.imms[1]->X_add_number;
+
+ if (size == 2 && !fits_in_unsigned_word (n))
+ {
+ as_bad (_("16-bit jump out of range"));
+ return;
+ }
+ md_number_to_chars (p, (valueT) n, size);
+ }
else
- fix_new_exp (frag_now, p + 1 - frag_now->fr_literal, 4,
- i.imms[1], 0, BFD_RELOC_32);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, size,
+ i.imms[1], 0, reloc_type);
if (i.imms[0]->X_op != O_constant)
- as_bad ("can't handle non absolute segment in long call/jmp");
- md_number_to_chars (p + 5, (valueT) i.imms[0]->X_add_number, 2);
+ as_bad (_("can't handle non absolute segment in `%s'"),
+ i.tm.name);
+ md_number_to_chars (p + size, (valueT) i.imms[0]->X_add_number, 2);
}
else
{
/* Output normal instructions here. */
unsigned char *q;
- /* First the prefix bytes. */
- for (q = i.prefix; q < i.prefix + i.prefixes; q++)
+ /* The prefix bytes. */
+ for (q = i.prefix;
+ q < i.prefix + sizeof (i.prefix) / sizeof (i.prefix[0]);
+ q++)
{
- p = frag_more (1);
- insn_size += 1;
- md_number_to_chars (p, (valueT) *q, 1);
- }
-
+ if (*q)
+ {
+ insn_size += 1;
+ p = frag_more (1);
+ md_number_to_chars (p, (valueT) *q, 1);
+ }
+ }
+
/* Now the opcode; be careful about word order here! */
- if (fits_in_unsigned_byte (t->base_opcode))
+ if (fits_in_unsigned_byte (i.tm.base_opcode))
{
- FRAG_APPEND_1_CHAR (t->base_opcode);
insn_size += 1;
+ FRAG_APPEND_1_CHAR (i.tm.base_opcode);
}
- else if (fits_in_unsigned_word (t->base_opcode))
+ else if (fits_in_unsigned_word (i.tm.base_opcode))
{
- p = frag_more (2);
insn_size += 2;
+ p = frag_more (2);
/* put out high byte first: can't use md_number_to_chars! */
- *p++ = (t->base_opcode >> 8) & 0xff;
- *p = t->base_opcode & 0xff;
+ *p++ = (i.tm.base_opcode >> 8) & 0xff;
+ *p = i.tm.base_opcode & 0xff;
}
else
{ /* opcode is either 3 or 4 bytes */
- if (t->base_opcode & 0xff000000)
+ if (i.tm.base_opcode & 0xff000000)
{
- p = frag_more (4);
insn_size += 4;
- *p++ = (t->base_opcode >> 24) & 0xff;
+ p = frag_more (4);
+ *p++ = (i.tm.base_opcode >> 24) & 0xff;
}
else
{
- p = frag_more (3);
insn_size += 3;
+ p = frag_more (3);
}
- *p++ = (t->base_opcode >> 16) & 0xff;
- *p++ = (t->base_opcode >> 8) & 0xff;
- *p = (t->base_opcode) & 0xff;
+ *p++ = (i.tm.base_opcode >> 16) & 0xff;
+ *p++ = (i.tm.base_opcode >> 8) & 0xff;
+ *p = (i.tm.base_opcode) & 0xff;
}
- /* Now the modrm byte and base index byte (if present). */
- if (t->opcode_modifier & Modrm)
+ /* Now the modrm byte and sib byte (if present). */
+ if (i.tm.opcode_modifier & Modrm)
{
- p = frag_more (1);
insn_size += 1;
- /* md_number_to_chars (p, i.rm, 1); */
+ p = frag_more (1);
md_number_to_chars (p,
(valueT) (i.rm.regmem << 0
| i.rm.reg << 3
| i.rm.mode << 6),
1);
- /* If i.rm.regmem == ESP (4) && i.rm.mode != Mode 3 (Register mode)
- ==> need second modrm byte. */
- if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING && i.rm.mode != 3)
+ /* If i.rm.regmem == ESP (4)
+ && i.rm.mode != (Register mode)
+ && not 16 bit
+ ==> need second modrm byte. */
+ if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING
+ && i.rm.mode != 3
+ && !(i.base_reg && (i.base_reg->reg_type & Reg16) != 0))
{
- p = frag_more (1);
insn_size += 1;
- /* md_number_to_chars (p, i.bi, 1); */
- md_number_to_chars (p, (valueT) (i.bi.base << 0
- | i.bi.index << 3
- | i.bi.scale << 6),
+ p = frag_more (1);
+ md_number_to_chars (p,
+ (valueT) (i.sib.base << 0
+ | i.sib.index << 3
+ | i.sib.scale << 6),
1);
}
}
{
if (i.disps[n]->X_op == O_constant)
{
- if (i.types[n] & (Disp8 | Abs8))
+ if (i.types[n] & Disp8)
{
- p = frag_more (1);
insn_size += 1;
+ p = frag_more (1);
md_number_to_chars (p,
(valueT) i.disps[n]->X_add_number,
1);
}
- else if (i.types[n] & (Disp16 | Abs16))
+ else if (i.types[n] & Disp16)
{
- p = frag_more (2);
insn_size += 2;
+ p = frag_more (2);
md_number_to_chars (p,
(valueT) i.disps[n]->X_add_number,
2);
}
else
- { /* Disp32|Abs32 */
- p = frag_more (4);
+ { /* Disp32 */
insn_size += 4;
+ p = frag_more (4);
md_number_to_chars (p,
(valueT) i.disps[n]->X_add_number,
4);
}
}
- else
- { /* not absolute_section */
- /* need a 32-bit fixup (don't support 8bit non-absolute disps) */
- p = frag_more (4);
+ else if (i.types[n] & Disp32)
+ {
insn_size += 4;
+ p = frag_more (4);
fix_new_exp (frag_now, p - frag_now->fr_literal, 4,
- i.disps[n], 0, BFD_RELOC_32);
+ i.disps[n], 0,
+ TC_RELOC (i.disp_reloc[n], BFD_RELOC_32));
+ }
+ else
+ { /* must be Disp16 */
+ insn_size += 2;
+ p = frag_more (2);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 2,
+ i.disps[n], 0,
+ TC_RELOC (i.disp_reloc[n], BFD_RELOC_16));
}
}
}
{
if (i.types[n] & (Imm8 | Imm8S))
{
- p = frag_more (1);
insn_size += 1;
+ p = frag_more (1);
md_number_to_chars (p,
(valueT) i.imms[n]->X_add_number,
1);
}
else if (i.types[n] & Imm16)
{
- p = frag_more (2);
insn_size += 2;
+ p = frag_more (2);
md_number_to_chars (p,
(valueT) i.imms[n]->X_add_number,
2);
}
else
{
- p = frag_more (4);
insn_size += 4;
+ p = frag_more (4);
md_number_to_chars (p,
(valueT) i.imms[n]->X_add_number,
4);
}
else
{ /* not absolute_section */
- /* need a 32-bit fixup (don't support 8bit non-absolute ims) */
- /* try to support other sizes ... */
+ /* Need a 32-bit fixup (don't support 8bit
+ non-absolute ims). Try to support other
+ sizes ... */
+ int r_type;
int size;
+ int pcrel = 0;
+
if (i.types[n] & (Imm8 | Imm8S))
size = 1;
else if (i.types[n] & Imm16)
size = 2;
else
size = 4;
- p = frag_more (size);
insn_size += size;
+ p = frag_more (size);
+ r_type = reloc (size, 0, i.disp_reloc[0]);
+#ifdef BFD_ASSEMBLER
+ if (r_type == BFD_RELOC_32
+ && GOT_symbol
+ && GOT_symbol == i.imms[n]->X_add_symbol
+ && (i.imms[n]->X_op == O_symbol
+ || (i.imms[n]->X_op == O_add
+ && (i.imms[n]->X_op_symbol->sy_value.X_op
+ == O_subtract))))
+ {
+ r_type = BFD_RELOC_386_GOTPC;
+ i.imms[n]->X_add_number += 3;
+ }
+#endif
fix_new_exp (frag_now, p - frag_now->fr_literal, size,
- i.imms[n], 0, reloc (size, 0));
+ i.imms[n], pcrel, r_type);
}
}
}
#ifdef DEBUG386
if (flag_debug)
{
- pi (line, &i);
+ pi (line, &i);
+ }
+#endif /* DEBUG386 */
+ }
+}
+\f
+static int i386_is_reg PARAMS ((char *));
+
+static int
+i386_is_reg (reg_string)
+ char *reg_string;
+{
+ register char *s = reg_string;
+ register char *p;
+ char reg_name_given[MAX_REG_NAME_SIZE + 1];
+
+ if (is_space_char (*s))
+ ++s;
+
+ p = reg_name_given;
+ while ((*p++ = register_chars[(unsigned char) *s++]) != '\0')
+ if (p >= reg_name_given + MAX_REG_NAME_SIZE)
+ return 0;
+
+ if (!hash_find (reg_hash, reg_name_given))
+ return 0;
+ else
+ return 1;
+}
+
+static int i386_immediate PARAMS ((char *));
+
+static int
+i386_immediate (imm_start)
+ char *imm_start;
+{
+ char *save_input_line_pointer;
+ segT exp_seg = 0;
+ expressionS * exp;
+
+ if (i.imm_operands == MAX_IMMEDIATE_OPERANDS)
+ {
+ as_bad (_("Only 1 or 2 immediate operands are allowed"));
+ return 0;
+ }
+
+ exp = &im_expressions[i.imm_operands++];
+ i.imms[this_operand] = exp;
+
+ if (is_space_char (*imm_start))
+ ++imm_start;
+
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = imm_start;
+
+#ifndef LEX_AT
+ {
+ /*
+ * We can have operands of the form
+ * <symbol>@GOTOFF+<nnn>
+ * Take the easy way out here and copy everything
+ * into a temporary buffer...
+ */
+ register char *cp;
+
+ cp = strchr (input_line_pointer, '@');
+ if (cp != NULL)
+ {
+ char *tmpbuf;
+ int len, first;
+
+ /* GOT relocations are not supported in 16 bit mode */
+ if (flag_16bit_code)
+ as_bad (_("GOT relocations not supported in 16 bit mode"));
+
+ if (GOT_symbol == NULL)
+ GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+ if (strncmp (cp + 1, "PLT", 3) == 0)
+ {
+ i.disp_reloc[this_operand] = BFD_RELOC_386_PLT32;
+ len = 3;
+ }
+ else if (strncmp (cp + 1, "GOTOFF", 6) == 0)
+ {
+ i.disp_reloc[this_operand] = BFD_RELOC_386_GOTOFF;
+ len = 6;
+ }
+ else if (strncmp (cp + 1, "GOT", 3) == 0)
+ {
+ i.disp_reloc[this_operand] = BFD_RELOC_386_GOT32;
+ len = 3;
+ }
+ else
+ as_bad (_("Bad reloc specifier in expression"));
+
+ /* Replace the relocation token with ' ', so that errors like
+ foo@GOTOFF1 will be detected. */
+ first = cp - input_line_pointer;
+ tmpbuf = (char *) alloca (strlen(input_line_pointer));
+ memcpy (tmpbuf, input_line_pointer, first);
+ tmpbuf[first] = ' ';
+ strcpy (tmpbuf + first + 1, cp + 1 + len);
+ input_line_pointer = tmpbuf;
+ }
+ }
+#endif
+
+ exp_seg = expression (exp);
+
+ if (*input_line_pointer)
+ as_bad (_("Ignoring junk `%s' after expression"), input_line_pointer);
+
+ input_line_pointer = save_input_line_pointer;
+
+ if (exp->X_op == O_absent)
+ {
+ /* missing or bad expr becomes absolute 0 */
+ as_bad (_("Missing or invalid immediate expression `%s' taken as 0"),
+ imm_start);
+ exp->X_op = O_constant;
+ exp->X_add_number = 0;
+ exp->X_add_symbol = (symbolS *) 0;
+ exp->X_op_symbol = (symbolS *) 0;
+ i.types[this_operand] |= Imm;
+ }
+ else if (exp->X_op == O_constant)
+ {
+ i.types[this_operand] |=
+ smallest_imm_type ((long) exp->X_add_number);
+
+ /* If a suffix is given, this operand may be shortended. */
+ switch (i.suffix)
+ {
+ case WORD_MNEM_SUFFIX:
+ i.types[this_operand] |= Imm16;
+ break;
+ case BYTE_MNEM_SUFFIX:
+ i.types[this_operand] |= Imm16 | Imm8 | Imm8S;
+ break;
+ }
+ }
+#ifdef OBJ_AOUT
+ else if (exp_seg != text_section
+ && exp_seg != data_section
+ && exp_seg != bss_section
+ && exp_seg != undefined_section
+#ifdef BFD_ASSEMBLER
+ && !bfd_is_com_section (exp_seg)
+#endif
+ )
+ {
+ seg_unimplemented:
+ as_bad (_("Unimplemented segment type %d in operand"), exp_seg);
+ return 0;
+ }
+#endif
+ else
+ {
+ /* This is an address. The size of the address will be
+ determined later, depending on destination register,
+ suffix, or the default for the section. We exclude
+ Imm8S here so that `push $foo' and other instructions
+ with an Imm8S form will use Imm16 or Imm32. */
+ i.types[this_operand] |= (Imm8 | Imm16 | Imm32);
+ }
+
+ return 1;
+}
+
+static int i386_scale PARAMS ((char *));
+
+static int
+i386_scale (scale)
+ char *scale;
+{
+ if (!isdigit (*scale))
+ goto bad_scale;
+
+ switch (*scale)
+ {
+ case '0':
+ case '1':
+ i.log2_scale_factor = 0;
+ break;
+ case '2':
+ i.log2_scale_factor = 1;
+ break;
+ case '4':
+ i.log2_scale_factor = 2;
+ break;
+ case '8':
+ i.log2_scale_factor = 3;
+ break;
+ default:
+ bad_scale:
+ as_bad (_("expecting scale factor of 1, 2, 4, or 8: got `%s'"),
+ scale);
+ return 0;
+ }
+ if (i.log2_scale_factor != 0 && ! i.index_reg)
+ {
+ as_warn (_("scale factor of %d without an index register"),
+ 1 << i.log2_scale_factor);
+#if SCALE1_WHEN_NO_INDEX
+ i.log2_scale_factor = 0;
+#endif
+ }
+ return 1;
+}
+
+static int i386_displacement PARAMS ((char *, char *));
+
+static int
+i386_displacement (disp_start, disp_end)
+ char *disp_start;
+ char *disp_end;
+{
+ register expressionS *exp;
+ segT exp_seg = 0;
+ char *save_input_line_pointer;
+ int bigdisp = Disp32;
+
+ /* All of the pieces of the displacement expression are handled together. */
+ if (intel_syntax && i.disp_operands != 0)
+ return 1;
+
+ if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0))
+ bigdisp = Disp16;
+ i.types[this_operand] |= bigdisp;
+
+ exp = &disp_expressions[i.disp_operands];
+ i.disps[this_operand] = exp;
+ i.disp_reloc[this_operand] = NO_RELOC;
+ i.disp_operands++;
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = disp_start;
+ END_STRING_AND_SAVE (disp_end);
+
+#ifndef GCC_ASM_O_HACK
+#define GCC_ASM_O_HACK 0
+#endif
+#if GCC_ASM_O_HACK
+ END_STRING_AND_SAVE (disp_end + 1);
+ if ((i.types[this_operand] & BaseIndex) != 0
+ && displacement_string_end[-1] == '+')
+ {
+ /* This hack is to avoid a warning when using the "o"
+ constraint within gcc asm statements.
+ For instance:
+
+ #define _set_tssldt_desc(n,addr,limit,type) \
+ __asm__ __volatile__ ( \
+ "movw %w2,%0\n\t" \
+ "movw %w1,2+%0\n\t" \
+ "rorl $16,%1\n\t" \
+ "movb %b1,4+%0\n\t" \
+ "movb %4,5+%0\n\t" \
+ "movb $0,6+%0\n\t" \
+ "movb %h1,7+%0\n\t" \
+ "rorl $16,%1" \
+ : "=o"(*(n)) : "q" (addr), "ri"(limit), "i"(type))
+
+ This works great except that the output assembler ends
+ up looking a bit weird if it turns out that there is
+ no offset. You end up producing code that looks like:
+
+ #APP
+ movw $235,(%eax)
+ movw %dx,2+(%eax)
+ rorl $16,%edx
+ movb %dl,4+(%eax)
+ movb $137,5+(%eax)
+ movb $0,6+(%eax)
+ movb %dh,7+(%eax)
+ rorl $16,%edx
+ #NO_APP
+
+ So here we provide the missing zero.
+ */
+
+ *displacement_string_end = '0';
+ }
+#endif
+#ifndef LEX_AT
+ {
+ /*
+ * We can have operands of the form
+ * <symbol>@GOTOFF+<nnn>
+ * Take the easy way out here and copy everything
+ * into a temporary buffer...
+ */
+ register char *cp;
+
+ cp = strchr (input_line_pointer, '@');
+ if (cp != NULL)
+ {
+ char *tmpbuf;
+ int len, first;
+
+ /* GOT relocations are not supported in 16 bit mode */
+ if (flag_16bit_code)
+ as_bad (_("GOT relocations not supported in 16 bit mode"));
+
+ if (GOT_symbol == NULL)
+ GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+ if (strncmp (cp + 1, "PLT", 3) == 0)
+ {
+ i.disp_reloc[this_operand] = BFD_RELOC_386_PLT32;
+ len = 3;
+ }
+ else if (strncmp (cp + 1, "GOTOFF", 6) == 0)
+ {
+ i.disp_reloc[this_operand] = BFD_RELOC_386_GOTOFF;
+ len = 6;
+ }
+ else if (strncmp (cp + 1, "GOT", 3) == 0)
+ {
+ i.disp_reloc[this_operand] = BFD_RELOC_386_GOT32;
+ len = 3;
+ }
+ else
+ as_bad (_("Bad reloc specifier in expression"));
+
+ /* Replace the relocation token with ' ', so that errors like
+ foo@GOTOFF1 will be detected. */
+ first = cp - input_line_pointer;
+ tmpbuf = (char *) alloca (strlen(input_line_pointer));
+ memcpy (tmpbuf, input_line_pointer, first);
+ tmpbuf[first] = ' ';
+ strcpy (tmpbuf + first + 1, cp + 1 + len);
+ input_line_pointer = tmpbuf;
+ }
+ }
+#endif
+
+ exp_seg = expression (exp);
+
+#ifdef BFD_ASSEMBLER
+ /* We do this to make sure that the section symbol is in
+ the symbol table. We will ultimately change the relocation
+ to be relative to the beginning of the section */
+ if (i.disp_reloc[this_operand] == BFD_RELOC_386_GOTOFF)
+ {
+ if (S_IS_LOCAL(exp->X_add_symbol)
+ && S_GET_SEGMENT (exp->X_add_symbol) != undefined_section)
+ section_symbol(exp->X_add_symbol->bsym->section);
+ assert (exp->X_op == O_symbol);
+ exp->X_op = O_subtract;
+ exp->X_op_symbol = GOT_symbol;
+ i.disp_reloc[this_operand] = BFD_RELOC_32;
+ }
+#endif
+
+ if (*input_line_pointer)
+ as_bad (_("Ignoring junk `%s' after expression"),
+ input_line_pointer);
+#if GCC_ASM_O_HACK
+ RESTORE_END_STRING (disp_end + 1);
+#endif
+ RESTORE_END_STRING (disp_end);
+ input_line_pointer = save_input_line_pointer;
+
+ if (exp->X_op == O_constant)
+ {
+ if (fits_in_signed_byte (exp->X_add_number))
+ i.types[this_operand] |= Disp8;
+ }
+#ifdef OBJ_AOUT
+ else if (exp_seg != text_section
+ && exp_seg != data_section
+ && exp_seg != bss_section
+ && exp_seg != undefined_section)
+ {
+ as_bad (_ ("Unimplemented segment type %d in operand"), exp_seg);
+ return 0;
+ }
+#endif
+ return 1;
+}
+
+static int i386_operand_modifier PARAMS ((char **, int));
+
+static int
+i386_operand_modifier (op_string, got_a_float)
+ char **op_string;
+ int got_a_float;
+{
+ if (!strncasecmp (*op_string, "BYTE PTR", 8))
+ {
+ i.suffix = BYTE_MNEM_SUFFIX;
+ *op_string += 8;
+ return BYTE_PTR;
+
+ }
+ else if (!strncasecmp (*op_string, "WORD PTR", 8))
+ {
+ i.suffix = WORD_MNEM_SUFFIX;
+ *op_string += 8;
+ return WORD_PTR;
+ }
+
+ else if (!strncasecmp (*op_string, "DWORD PTR", 9))
+ {
+ if (got_a_float)
+ i.suffix = SHORT_MNEM_SUFFIX;
+ else
+ i.suffix = DWORD_MNEM_SUFFIX;
+ *op_string += 9;
+ return DWORD_PTR;
+ }
+
+ else if (!strncasecmp (*op_string, "QWORD PTR", 9))
+ {
+ i.suffix = INTEL_DWORD_MNEM_SUFFIX;
+ *op_string += 9;
+ return QWORD_PTR;
+ }
+
+ else if (!strncasecmp (*op_string, "XWORD PTR", 9))
+ {
+ i.suffix = LONG_DOUBLE_MNEM_SUFFIX;
+ *op_string += 9;
+ return XWORD_PTR;
+ }
+
+ else if (!strncasecmp (*op_string, "SHORT", 5))
+ {
+ *op_string += 5;
+ return SHORT;
+ }
+
+ else if (!strncasecmp (*op_string, "OFFSET FLAT:", 12))
+ {
+ *op_string += 12;
+ return OFFSET_FLAT;
+ }
+
+ else if (!strncasecmp (*op_string, "FLAT", 4))
+ {
+ *op_string += 4;
+ return FLAT;
+ }
+
+ else return NONE_FOUND;
+}
+
+static char * build_displacement_string PARAMS ((int, char *));
+
+static char *
+build_displacement_string (initial_disp, op_string)
+ int initial_disp;
+ char *op_string;
+{
+ char *temp_string = (char *) malloc (strlen (op_string) + 1);
+ char *end_of_operand_string;
+ char *tc;
+ char *temp_disp;
+
+ temp_string[0] = '\0';
+ tc = end_of_operand_string = strchr (op_string, '[');
+ if ( initial_disp && !end_of_operand_string)
+ {
+ strcpy (temp_string, op_string);
+ return (temp_string);
+ }
+
+ /* Build the whole displacement string */
+ if (initial_disp)
+ {
+ strncpy (temp_string, op_string, end_of_operand_string - op_string);
+ temp_string[end_of_operand_string - op_string] = '\0';
+ temp_disp = tc;
+ }
+ else
+ temp_disp = op_string;
+
+ while (*temp_disp != '\0')
+ {
+ int add_minus = (*temp_disp == '-');
+
+ if (*temp_disp == '+' || *temp_disp == '-' || *temp_disp == '[')
+ temp_disp++;
+
+ if (is_space_char (*temp_disp))
+ temp_disp++;
+
+ /* Don't consider registers */
+ if (*temp_disp != REGISTER_PREFIX
+ && !(allow_naked_reg && i386_is_reg (temp_disp)))
+ {
+ char *string_start = temp_disp;
+
+ while (*temp_disp != ']'
+ && *temp_disp != '+'
+ && *temp_disp != '-'
+ && *temp_disp != '*')
+ ++temp_disp;
+
+ if (add_minus)
+ strcat (temp_string, "-");
+ else
+ strcat (temp_string, "+");
+
+ strncat (temp_string, string_start, temp_disp - string_start);
+ if (*temp_disp == '+' || *temp_disp == '-')
+ --temp_disp;
+ }
+
+ while (*temp_disp != '\0'
+ && *temp_disp != '+'
+ && *temp_disp != '-')
+ ++temp_disp;
+ }
+
+ return temp_string;
+}
+
+static int i386_parse_seg PARAMS ((char *));
+
+static int
+i386_parse_seg (op_string)
+ char *op_string;
+{
+ if (is_space_char (*op_string))
+ ++op_string;
+
+ /* Should be one of es, cs, ss, ds fs or gs */
+ switch (*op_string++)
+ {
+ case 'e':
+ i.seg[i.mem_operands] = &es;
+ break;
+ case 'c':
+ i.seg[i.mem_operands] = &cs;
+ break;
+ case 's':
+ i.seg[i.mem_operands] = &ss;
+ break;
+ case 'd':
+ i.seg[i.mem_operands] = &ds;
+ break;
+ case 'f':
+ i.seg[i.mem_operands] = &fs;
+ break;
+ case 'g':
+ i.seg[i.mem_operands] = &gs;
+ break;
+ default:
+ as_bad (_("bad segment name `%s'"), op_string);
+ return 0;
+ }
+
+ if (*op_string++ != 's')
+ {
+ as_bad (_("bad segment name `%s'"), op_string);
+ return 0;
+ }
+
+ if (is_space_char (*op_string))
+ ++op_string;
+
+ if (*op_string != ':')
+ {
+ as_bad (_("bad segment name `%s'"), op_string);
+ return 0;
+ }
+
+ return 1;
+
+}
+
+static int i386_intel_memory_operand PARAMS ((char *));
+
+static int
+i386_intel_memory_operand (op_string)
+ char *op_string;
+{
+
+ char *end_of_operand_string;
+
+ if (is_digit_char (*op_string)
+ && strchr (op_string, '[') == 0)
+ {
+ if (!i386_immediate (op_string))
+ return 0;
+ else
+ return 1;
+ }
+
+ /* Look for displacement preceding open bracket */
+ if (*op_string != '[')
+ {
+ char *end_seg;
+ char *temp_string;
+
+ end_seg = strchr (op_string, ':');
+ if (end_seg)
+ {
+ if (!i386_parse_seg (op_string))
+ return 0;
+ op_string = end_seg + 1;
+ }
+
+ temp_string = build_displacement_string (true, op_string);
+ if (!i386_displacement (temp_string, temp_string + strlen (temp_string)))
+ return 0;
+
+ end_of_operand_string = strchr (op_string, '[');
+ if (!end_of_operand_string)
+ end_of_operand_string = op_string + strlen (op_string);
+
+ if (is_space_char (*end_of_operand_string))
+ --end_of_operand_string;
+
+ op_string = end_of_operand_string;
+ }
+
+ if (*op_string == '[')
+ {
+ ++op_string;
+
+ /* Pick off each component and figure out where it belongs */
+
+ end_of_operand_string = op_string;
+
+ while (*op_string != ']')
+ {
+
+ while (*end_of_operand_string != '+'
+ && *end_of_operand_string != '-'
+ && *end_of_operand_string != '*'
+ && *end_of_operand_string != ']')
+ end_of_operand_string++;
+
+ if (*op_string == '+')
+ {
+ char *temp_string = op_string + 1;
+ if (is_space_char (*temp_string))
+ ++temp_string;
+ if (*temp_string == REGISTER_PREFIX
+ || (allow_naked_reg && i386_is_reg (temp_string)))
+ ++op_string;
+ }
+
+ if (*op_string == REGISTER_PREFIX
+ || (allow_naked_reg && i386_is_reg (op_string)))
+ {
+ const reg_entry *temp_reg;
+ char *end_op;
+
+ END_STRING_AND_SAVE (end_of_operand_string);
+ temp_reg = parse_register (op_string, &end_op);
+ RESTORE_END_STRING (end_of_operand_string);
+
+ if (temp_reg == NULL)
+ return 0;
+
+ if (i.base_reg == NULL)
+ i.base_reg = temp_reg;
+ else
+ i.index_reg = temp_reg;
+
+ i.types[this_operand] |= BaseIndex;
+
+ }
+ else if (is_digit_char (*op_string) || *op_string == '+' || *op_string == '-')
+ {
+
+ char *temp_string = build_displacement_string (false, op_string);
+
+ if (*temp_string == '+')
+ ++temp_string;
+
+ if (!i386_displacement (temp_string, temp_string + strlen (temp_string)))
+ return 0;
+
+ ++op_string;
+ end_of_operand_string = op_string;
+ while (*end_of_operand_string != ']'
+ && *end_of_operand_string != '+'
+ && *end_of_operand_string != '-'
+ && *end_of_operand_string != '*')
+ ++end_of_operand_string;
+ }
+ else if (*op_string == '*')
+ {
+ ++op_string;
+
+ if (i.base_reg && !i.index_reg)
+ {
+ i.index_reg = i.base_reg;
+ i.base_reg = 0;
+ }
+
+ if (!i386_scale (op_string))
+ return 0;
+ }
+ op_string = end_of_operand_string;
+ ++end_of_operand_string;
+ }
+ }
+
+ return 1;
+}
+
+static int i386_intel_operand PARAMS ((char *, int));
+
+static int
+i386_intel_operand (operand_string, got_a_float)
+ char *operand_string;
+ int got_a_float;
+{
+ char *op_string = operand_string;
+
+ int operand_modifier = i386_operand_modifier (&op_string, got_a_float);
+ if (is_space_char (*op_string))
+ ++op_string;
+
+ switch (operand_modifier)
+ {
+ case BYTE_PTR:
+ case WORD_PTR:
+ case DWORD_PTR:
+ case QWORD_PTR:
+ case XWORD_PTR:
+ if ((i.mem_operands == 1
+ && (current_templates->start->opcode_modifier & IsString) == 0)
+ || i.mem_operands == 2)
+ {
+ as_bad (_("too many memory references for `%s'"),
+ current_templates->start->name);
+ return 0;
+ }
+
+ if (!i386_intel_memory_operand (op_string))
+ return 0;
+
+ i.mem_operands++;
+ break;
+
+ case FLAT:
+
+ case OFFSET_FLAT:
+ if (!i386_immediate (op_string))
+ return 0;
+ break;
+
+ case SHORT:
+
+ case NONE_FOUND:
+ /* Should be register or immediate */
+ if (is_digit_char (*op_string)
+ && strchr (op_string, '[') == 0)
+ {
+ if (!i386_immediate (op_string))
+ return 0;
+ }
+ else if (*op_string == REGISTER_PREFIX
+ || (allow_naked_reg
+ && i386_is_reg (op_string)))
+ {
+
+ register const reg_entry * r;
+ char *end_op;
+
+ r = parse_register (op_string, &end_op);
+ if (r == NULL)
+ return 0;
+
+ /* Check for a segment override by searching for ':' after a
+ segment register. */
+ op_string = end_op;
+ if (is_space_char (*op_string))
+ ++op_string;
+ if (*op_string == ':' && (r->reg_type & (SReg2 | SReg3)))
+ {
+ switch (r->reg_num)
+ {
+ case 0:
+ i.seg[i.mem_operands] = &es;
+ break;
+ case 1:
+ i.seg[i.mem_operands] = &cs;
+ break;
+ case 2:
+ i.seg[i.mem_operands] = &ss;
+ break;
+ case 3:
+ i.seg[i.mem_operands] = &ds;
+ break;
+ case 4:
+ i.seg[i.mem_operands] = &fs;
+ break;
+ case 5:
+ i.seg[i.mem_operands] = &gs;
+ break;
+ }
+
+ }
+ i.types[this_operand] |= r->reg_type & ~BaseIndex;
+ i.regs[this_operand] = r;
+ i.reg_operands++;
+ }
+
+ else
+ {
+
+ if (!i386_intel_memory_operand (op_string))
+ return 0;
+
+ i.mem_operands++;
}
-#endif /* DEBUG386 */
- }
+ break;
+
+ } /* end switch */
+ /* Special case for (%dx) while doing input/output op. */
+ if (i.base_reg
+ && i.base_reg->reg_type == (Reg16 | InOutPortReg)
+ && i.index_reg == 0
+ && i.log2_scale_factor == 0
+ && i.seg[i.mem_operands] == 0
+ && (i.types[this_operand] & Disp) == 0)
+ {
+ i.types[this_operand] = InOutPortReg;
+ return 1;
+ }
+ /* Make sure the memory operand we've been dealt is valid. */
+ if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0))
+ {
+ if ((i.base_reg
+ && ((i.base_reg->reg_type & (Reg16|BaseIndex))
+ != (Reg16|BaseIndex)))
+ || (i.index_reg
+ && (((i.index_reg->reg_type & (Reg16|BaseIndex))
+ != (Reg16|BaseIndex))
+ || ! (i.base_reg
+ && i.base_reg->reg_num < 6
+ && i.index_reg->reg_num >= 6
+ && i.log2_scale_factor == 0))))
+ {
+ as_bad (_("`%s' is not a valid %s bit base/index expression"),
+ operand_string, "16");
+ return 0;
+ }
+ }
+ else
+ {
+ if ((i.base_reg
+ && (i.base_reg->reg_type & Reg32) == 0)
+ || (i.index_reg
+ && ((i.index_reg->reg_type & (Reg32|BaseIndex))
+ != (Reg32|BaseIndex))))
+ {
+ as_bad (_("`%s' is not a valid %s bit base/index expression"),
+ operand_string, "32");
+ return 0;
+ }
+ }
+ return 1;
}
-\f
+
/* Parse OPERAND_STRING into the i386_insn structure I. Returns non-zero
on error. */
+static int i386_operand PARAMS ((char *));
+
static int
i386_operand (operand_string)
char *operand_string;
{
- register char *op_string = operand_string;
+ char *op_string = operand_string;
- /* Address of '\0' at end of operand_string. */
- char *end_of_operand_string = operand_string + strlen (operand_string);
-
- /* Start and end of displacement string expression (if found). */
- char *displacement_string_start = NULL;
- char *displacement_string_end = NULL;
+ if (is_space_char (*op_string))
+ ++op_string;
/* We check for an absolute prefix (differentiating,
for example, 'jmp pc_relative_label' from 'jmp *absolute_label'. */
if (*op_string == ABSOLUTE_PREFIX)
{
- op_string++;
+ ++op_string;
+ if (is_space_char (*op_string))
+ ++op_string;
i.types[this_operand] |= JumpAbsolute;
}
/* Check if operand is a register. */
- if (*op_string == REGISTER_PREFIX)
+ if (*op_string == REGISTER_PREFIX
+ || (allow_naked_reg && i386_is_reg (op_string)))
{
- register reg_entry *r;
- if (!(r = parse_register (op_string)))
- {
- as_bad ("bad register name ('%s')", op_string);
- return 0;
- }
- /* Check for segment override, rather than segment register by
- searching for ':' after %<x>s where <x> = s, c, d, e, f, g. */
- if ((r->reg_type & (SReg2 | SReg3)) && op_string[3] == ':')
- {
- switch (r->reg_num)
- {
- case 0:
- i.seg = (seg_entry *) & es;
+ register const reg_entry *r;
+ char *end_op;
+
+ r = parse_register (op_string, &end_op);
+ if (r == NULL)
+ return 0;
+
+ /* Check for a segment override by searching for ':' after a
+ segment register. */
+ op_string = end_op;
+ if (is_space_char (*op_string))
+ ++op_string;
+ if (*op_string == ':' && (r->reg_type & (SReg2 | SReg3)))
+ {
+ switch (r->reg_num)
+ {
+ case 0:
+ i.seg[i.mem_operands] = &es;
break;
case 1:
- i.seg = (seg_entry *) & cs;
+ i.seg[i.mem_operands] = &cs;
break;
case 2:
- i.seg = (seg_entry *) & ss;
+ i.seg[i.mem_operands] = &ss;
break;
case 3:
- i.seg = (seg_entry *) & ds;
+ i.seg[i.mem_operands] = &ds;
break;
case 4:
- i.seg = (seg_entry *) & fs;
+ i.seg[i.mem_operands] = &fs;
break;
case 5:
- i.seg = (seg_entry *) & gs;
+ i.seg[i.mem_operands] = &gs;
break;
}
- op_string += 4; /* skip % <x> s : */
- operand_string = op_string; /* Pretend given string starts here. */
- if (!is_digit_char (*op_string) && !is_identifier_char (*op_string)
- && *op_string != '(' && *op_string != ABSOLUTE_PREFIX)
+
+ /* Skip the ':' and whitespace. */
+ ++op_string;
+ if (is_space_char (*op_string))
+ ++op_string;
+
+ /* Pretend given string starts here. */
+ operand_string = op_string;
+ if (!is_digit_char (*op_string)
+ && !is_identifier_char (*op_string)
+ && *op_string != '('
+ && *op_string != ABSOLUTE_PREFIX)
{
- as_bad ("bad memory operand after segment override");
+ as_bad (_("bad memory operand `%s'"), op_string);
return 0;
}
/* Handle case of %es:*foo. */
if (*op_string == ABSOLUTE_PREFIX)
{
- op_string++;
+ ++op_string;
+ if (is_space_char (*op_string))
+ ++op_string;
i.types[this_operand] |= JumpAbsolute;
}
goto do_memory_reference;
}
- i.types[this_operand] |= r->reg_type;
+ if (*op_string)
+ {
+ as_bad (_("Junk `%s' after register"), op_string);
+ return 0;
+ }
+ i.types[this_operand] |= r->reg_type & ~BaseIndex;
i.regs[this_operand] = r;
i.reg_operands++;
}
else if (*op_string == IMMEDIATE_PREFIX)
{ /* ... or an immediate */
- char *save_input_line_pointer;
- segT exp_seg = 0;
- expressionS *exp;
-
- if (i.imm_operands == MAX_IMMEDIATE_OPERANDS)
- {
- as_bad ("only 1 or 2 immediate operands are allowed");
- return 0;
- }
-
- exp = &im_expressions[i.imm_operands++];
- i.imms[this_operand] = exp;
- save_input_line_pointer = input_line_pointer;
- input_line_pointer = ++op_string; /* must advance op_string! */
- SKIP_WHITESPACE ();
- exp_seg = expression (exp);
- input_line_pointer = save_input_line_pointer;
-
- if (exp->X_op == O_absent)
- {
- /* missing or bad expr becomes absolute 0 */
- as_bad ("missing or invalid immediate expression '%s' taken as 0",
- operand_string);
- exp->X_op = O_constant;
- exp->X_add_number = 0;
- exp->X_add_symbol = (symbolS *) 0;
- exp->X_op_symbol = (symbolS *) 0;
- i.types[this_operand] |= Imm;
- }
- else if (exp->X_op == O_constant)
- {
- i.types[this_operand] |=
- smallest_imm_type ((unsigned long) exp->X_add_number);
- }
-#ifdef OBJ_AOUT
- else if (exp_seg != text_section
- && exp_seg != data_section
- && exp_seg != bss_section
- && exp_seg != undefined_section
-#ifdef BFD_ASSEMBLER
- && ! bfd_is_com_section (exp_seg)
-#endif
- )
- {
- seg_unimplemented:
- as_bad ("Unimplemented segment type %d in parse_operand", exp_seg);
- return 0;
- }
-#endif
- else
- {
- /* this is an address ==> 32bit */
- i.types[this_operand] |= Imm32;
- }
- /* shorten this type of this operand if the instruction wants
- * fewer bits than are present in the immediate. The bit field
- * code can put out 'andb $0xffffff, %al', for example. pace
- * also 'movw $foo,(%eax)'
- */
- switch (i.suffix)
- {
- case WORD_OPCODE_SUFFIX:
- i.types[this_operand] |= Imm16;
- break;
- case BYTE_OPCODE_SUFFIX:
- i.types[this_operand] |= Imm16 | Imm8 | Imm8S;
- break;
- }
+ ++op_string;
+ if (!i386_immediate (op_string))
+ return 0;
}
- else if (is_digit_char (*op_string) || is_identifier_char (*op_string)
- || *op_string == '(')
+ else if (is_digit_char (*op_string)
+ || is_identifier_char (*op_string)
+ || *op_string == '(' )
{
/* This is a memory reference of some sort. */
+ char *end_of_operand_string;
register char *base_string;
- unsigned int found_base_index_form;
+ int found_base_index_form;
+
+ /* Start and end of displacement string expression (if found). */
+ char *displacement_string_start;
+ char *displacement_string_end;
do_memory_reference:
- if (i.mem_operands == MAX_MEMORY_OPERANDS)
- {
- as_bad ("more than 1 memory reference in instruction");
- return 0;
- }
- i.mem_operands++;
- /* Determine type of memory operand from opcode_suffix;
- no opcode suffix implies general memory references. */
- switch (i.suffix)
+ if ((i.mem_operands == 1
+ && (current_templates->start->opcode_modifier & IsString) == 0)
+ || i.mem_operands == 2)
{
- case BYTE_OPCODE_SUFFIX:
- i.types[this_operand] |= Mem8;
- break;
- case WORD_OPCODE_SUFFIX:
- i.types[this_operand] |= Mem16;
- break;
- case DWORD_OPCODE_SUFFIX:
- default:
- i.types[this_operand] |= Mem32;
+ as_bad (_("too many memory references for `%s'"),
+ current_templates->start->name);
+ return 0;
}
/* Check for base index form. We detect the base index form by
looking for an ')' at the end of the operand, searching
for the '(' matching it, and finding a REGISTER_PREFIX or ','
- after it. */
- base_string = end_of_operand_string - 1;
+ after the '('. */
found_base_index_form = 0;
+ end_of_operand_string = op_string + strlen (op_string);
+
+ --end_of_operand_string;
+ if (is_space_char (*end_of_operand_string))
+ --end_of_operand_string;
+
+ base_string = end_of_operand_string;
+
if (*base_string == ')')
{
- unsigned int parens_balenced = 1;
+ unsigned int parens_balanced = 1;
/* We've already checked that the number of left & right ()'s are
equal, so this loop will not be infinite. */
do
{
base_string--;
if (*base_string == ')')
- parens_balenced++;
+ parens_balanced++;
if (*base_string == '(')
- parens_balenced--;
+ parens_balanced--;
}
- while (parens_balenced);
- base_string++; /* Skip past '('. */
- if (*base_string == REGISTER_PREFIX || *base_string == ',')
+ while (parens_balanced);
+
+ /* If there is a displacement set-up for it to be parsed later. */
+ displacement_string_start = op_string;
+ displacement_string_end = base_string;
+
+ /* Skip past '(' and whitespace. */
+ ++base_string;
+ if (is_space_char (*base_string))
+ ++base_string;
+
+ if (*base_string == REGISTER_PREFIX
+ || (allow_naked_reg && i386_is_reg (base_string))
+ || *base_string == ',')
found_base_index_form = 1;
}
if (!found_base_index_form)
{
displacement_string_start = op_string;
- displacement_string_end = end_of_operand_string;
+ displacement_string_end = end_of_operand_string + 1;
}
else
{
- char *base_reg_name, *index_reg_name, *num_string;
- int num;
-
i.types[this_operand] |= BaseIndex;
- /* If there is a displacement set-up for it to be parsed later. */
- if (base_string != op_string + 1)
- {
- displacement_string_start = op_string;
- displacement_string_end = base_string - 1;
- }
-
/* Find base register (if any). */
if (*base_string != ',')
{
- base_reg_name = base_string++;
- /* skip past register name & parse it */
- while (isalpha (*base_string))
- base_string++;
- if (base_string == base_reg_name + 1)
- {
- as_bad ("can't find base register name after '(%c'",
- REGISTER_PREFIX);
- return 0;
- }
- END_STRING_AND_SAVE (base_string);
- if (!(i.base_reg = parse_register (base_reg_name)))
- {
- as_bad ("bad base register name ('%s')", base_reg_name);
- return 0;
- }
- RESTORE_END_STRING (base_string);
- }
+ char *end_op;
- /* Now check seperator; must be ',' ==> index reg
- OR num ==> no index reg. just scale factor
- OR ')' ==> end. (scale factor = 1) */
- if (*base_string != ',' && *base_string != ')')
- {
- as_bad ("expecting ',' or ')' after base register in `%s'",
- operand_string);
- return 0;
- }
+ /* Trim off the closing ')' so that parse_register won't
+ see it. */
+ END_STRING_AND_SAVE (end_of_operand_string);
+ i.base_reg = parse_register (base_string, &end_op);
+ RESTORE_END_STRING (end_of_operand_string);
- /* There may index reg here; and there may be a scale factor. */
- if (*base_string == ',' && *(base_string + 1) == REGISTER_PREFIX)
- {
- index_reg_name = ++base_string;
- while (isalpha (*++base_string));
- END_STRING_AND_SAVE (base_string);
- if (!(i.index_reg = parse_register (index_reg_name)))
- {
- as_bad ("bad index register name ('%s')", index_reg_name);
- return 0;
- }
- RESTORE_END_STRING (base_string);
+ if (i.base_reg == NULL)
+ return 0;
+
+ base_string = end_op;
+ if (is_space_char (*base_string))
+ ++base_string;
}
- /* Check for scale factor. */
- if (*base_string == ',' && isdigit (*(base_string + 1)))
+ /* There may be an index reg or scale factor here. */
+ if (*base_string == ',')
{
- num_string = ++base_string;
- while (is_digit_char (*base_string))
- base_string++;
- if (base_string == num_string)
+ ++base_string;
+ if (is_space_char (*base_string))
+ ++base_string;
+
+ if (*base_string == REGISTER_PREFIX
+ || (allow_naked_reg && i386_is_reg (base_string)))
{
- as_bad ("can't find a scale factor after ','");
- return 0;
+ char *end_op;
+
+ END_STRING_AND_SAVE (end_of_operand_string);
+ i.index_reg = parse_register (base_string, &end_op);
+ RESTORE_END_STRING (end_of_operand_string);
+
+ if (i.index_reg == NULL)
+ return 0;
+
+ base_string = end_op;
+ if (is_space_char (*base_string))
+ ++base_string;
+ if (*base_string == ',')
+ {
+ ++base_string;
+ if (is_space_char (*base_string))
+ ++base_string;
+ }
+ else if (*base_string != ')' )
+ {
+ as_bad (_("expecting `,' or `)' after index register in `%s'"),
+ operand_string);
+ return 0;
+ }
}
- END_STRING_AND_SAVE (base_string);
- /* We've got a scale factor. */
- if (!sscanf (num_string, "%d", &num))
+
+ /* Check for scale factor. */
+ if (isdigit ((unsigned char) *base_string))
{
- as_bad ("can't parse scale factor from '%s'", num_string);
- return 0;
- }
- RESTORE_END_STRING (base_string);
- switch (num)
- { /* must be 1 digit scale */
- case 1:
- i.log2_scale_factor = 0;
- break;
- case 2:
- i.log2_scale_factor = 1;
- break;
- case 4:
- i.log2_scale_factor = 2;
- break;
- case 8:
- i.log2_scale_factor = 3;
- break;
- default:
- as_bad ("expecting scale factor of 1, 2, 4, 8; got %d", num);
- return 0;
+ if (!i386_scale (base_string))
+ return 0;
+
+ ++base_string;
+ if (is_space_char (*base_string))
+ ++base_string;
+ if (*base_string != ')')
+ {
+ as_bad (_("expecting `)' after scale factor in `%s'"),
+ operand_string);
+ return 0;
+ }
}
- }
- else
- {
- if (!i.index_reg && *base_string == ',')
+ else if (!i.index_reg)
{
- as_bad ("expecting index register or scale factor after ','; got '%c'",
- *(base_string + 1));
+ as_bad (_("expecting index register or scale factor after `,'; got '%c'"),
+ *base_string);
return 0;
}
}
- }
-
- /* If there's an expression begining the operand, parse it,
- assuming displacement_string_start and displacement_string_end
- are meaningful. */
- if (displacement_string_start)
- {
- register expressionS *exp;
- segT exp_seg = 0;
- char *save_input_line_pointer;
- exp = &disp_expressions[i.disp_operands];
- i.disps[this_operand] = exp;
- i.disp_operands++;
- save_input_line_pointer = input_line_pointer;
- input_line_pointer = displacement_string_start;
- END_STRING_AND_SAVE (displacement_string_end);
- exp_seg = expression (exp);
- if (*input_line_pointer)
- as_bad ("Ignoring junk '%s' after expression", input_line_pointer);
- RESTORE_END_STRING (displacement_string_end);
- input_line_pointer = save_input_line_pointer;
- if (exp->X_op == O_absent)
+ else if (*base_string != ')')
{
- /* missing expr becomes absolute 0 */
- as_bad ("missing or invalid displacement '%s' taken as 0",
+ as_bad (_("expecting `,' or `)' after base register in `%s'"),
operand_string);
- i.types[this_operand] |= (Disp | Abs);
- exp->X_op = O_constant;
- exp->X_add_number = 0;
- exp->X_add_symbol = (symbolS *) 0;
- exp->X_op_symbol = (symbolS *) 0;
- }
- else if (exp->X_op == O_constant)
- {
- i.types[this_operand] |= SMALLEST_DISP_TYPE (exp->X_add_number);
- }
- else if (exp_seg == text_section
- || exp_seg == data_section
- || exp_seg == bss_section
- || exp_seg == undefined_section)
- {
- i.types[this_operand] |= Disp32;
- }
- else
- {
-#ifndef OBJ_AOUT
- i.types[this_operand] |= Disp32;
-#else
- goto seg_unimplemented;
-#endif
+ return 0;
}
}
- /* Make sure the memory operand we've been dealt is valid. */
- if (i.base_reg && i.index_reg &&
- !(i.base_reg->reg_type & i.index_reg->reg_type & Reg))
+ /* If there's an expression beginning the operand, parse it,
+ assuming displacement_string_start and
+ displacement_string_end are meaningful. */
+ if (displacement_string_start != displacement_string_end)
{
- as_bad ("register size mismatch in (base,index,scale) expression");
- return 0;
+ if (!i386_displacement (displacement_string_start,
+ displacement_string_end))
+ return 0;
}
- /*
- * special case for (%dx) while doing input/output op
- */
- if ((i.base_reg &&
- (i.base_reg->reg_type == (Reg16 | InOutPortReg)) &&
- (i.index_reg == 0)))
+
+ /* Special case for (%dx) while doing input/output op. */
+ if (i.base_reg
+ && i.base_reg->reg_type == (Reg16 | InOutPortReg)
+ && i.index_reg == 0
+ && i.log2_scale_factor == 0
+ && i.seg[i.mem_operands] == 0
+ && (i.types[this_operand] & Disp) == 0)
{
- i.types[this_operand] |= InOutPortReg;
+ i.types[this_operand] = InOutPortReg;
return 1;
}
- if ((i.base_reg && (i.base_reg->reg_type & Reg32) == 0) ||
- (i.index_reg && (i.index_reg->reg_type & Reg32) == 0))
+ /* Make sure the memory operand we've been dealt is valid. */
+ if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0))
{
- as_bad ("base/index register must be 32 bit register");
- return 0;
+ if ((i.base_reg
+ && ((i.base_reg->reg_type & (Reg16|BaseIndex))
+ != (Reg16|BaseIndex)))
+ || (i.index_reg
+ && (((i.index_reg->reg_type & (Reg16|BaseIndex))
+ != (Reg16|BaseIndex))
+ || ! (i.base_reg
+ && i.base_reg->reg_num < 6
+ && i.index_reg->reg_num >= 6
+ && i.log2_scale_factor == 0))))
+ {
+ as_bad (_("`%s' is not a valid %s bit base/index expression"),
+ operand_string, "16");
+ return 0;
+ }
}
- if (i.index_reg && i.index_reg == esp)
+ else
{
- as_bad ("%s may not be used as an index register", esp->reg_name);
- return 0;
+ if ((i.base_reg
+ && (i.base_reg->reg_type & Reg32) == 0)
+ || (i.index_reg
+ && ((i.index_reg->reg_type & (Reg32|BaseIndex))
+ != (Reg32|BaseIndex))))
+ {
+ as_bad (_("`%s' is not a valid %s bit base/index expression"),
+ operand_string, "32");
+ return 0;
+ }
}
+ i.mem_operands++;
}
else
{ /* it's not a memory operand; argh! */
- as_bad ("invalid char %s begining %s operand '%s'",
- output_invalid (*op_string), ordinal_names[this_operand],
+ as_bad (_("invalid char %s beginning operand %d `%s'"),
+ output_invalid (*op_string),
+ this_operand + 1,
op_string);
return 0;
}
old_fr_fix = fragP->fr_fix;
opcode = (unsigned char *) fragP->fr_opcode;
- /* We've already got fragP->fr_subtype right; all we have to do is check
- for un-relaxable symbols. */
+ /* We've already got fragP->fr_subtype right; all we have to do is
+ check for un-relaxable symbols. */
if (S_GET_SEGMENT (fragP->fr_symbol) != segment)
{
/* symbol is undefined in this segment */
+ int code16 = fragP->fr_subtype & CODE16;
+ int size = code16 ? 2 : 4;
+ int pcrel_reloc = code16 ? BFD_RELOC_16_PCREL : BFD_RELOC_32_PCREL;
+
switch (opcode[0])
{
case JUMP_PC_RELATIVE: /* make jmp (0xeb) a dword displacement jump */
opcode[0] = 0xe9; /* dword disp jmp */
- fragP->fr_fix += 4;
- fix_new (fragP, old_fr_fix, 4,
+ fragP->fr_fix += size;
+ fix_new (fragP, old_fr_fix, size,
fragP->fr_symbol,
- fragP->fr_offset, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_offset, 1,
+ (GOT_symbol && /* Not quite right - we should switch on
+ presence of @PLT, but I cannot see how
+ to get to that from here. We should have
+ done this in md_assemble to really
+ get it right all of the time, but I
+ think it does not matter that much, as
+ this will be right most of the time. ERY*/
+ S_GET_SEGMENT(fragP->fr_symbol) == undefined_section)
+ ? BFD_RELOC_386_PLT32 : pcrel_reloc);
break;
default:
the dword-displacement jump 0x0f8N */
opcode[1] = opcode[0] + 0x10;
opcode[0] = TWO_BYTE_OPCODE_ESCAPE; /* two-byte escape */
- fragP->fr_fix += 1 + 4; /* we've added an opcode byte */
- fix_new (fragP, old_fr_fix + 1, 4,
+ fragP->fr_fix += 1 + size; /* we've added an opcode byte */
+ fix_new (fragP, old_fr_fix + 1, size,
fragP->fr_symbol,
- fragP->fr_offset, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_offset, 1,
+ (GOT_symbol && /* Not quite right - we should switch on
+ presence of @PLT, but I cannot see how
+ to get to that from here. ERY */
+ S_GET_SEGMENT(fragP->fr_symbol) == undefined_section)
+ ? BFD_RELOC_386_PLT32 : pcrel_reloc);
break;
}
frag_wane (fragP);
*/
#ifndef BFD_ASSEMBLER
void
-md_convert_frag (headers, fragP)
+md_convert_frag (headers, sec, fragP)
object_headers *headers;
+ segT sec;
register fragS *fragP;
#else
void
switch (fragP->fr_subtype)
{
- case ENCODE_RELAX_STATE (COND_JUMP, BYTE):
- case ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE):
+ case ENCODE_RELAX_STATE (COND_JUMP, SMALL):
+ case ENCODE_RELAX_STATE (COND_JUMP, SMALL16):
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, SMALL):
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, SMALL16):
/* don't have to change opcode */
extension = 1; /* 1 opcode + 1 displacement */
where_to_put_displacement = &opcode[1];
break;
- case ENCODE_RELAX_STATE (COND_JUMP, WORD):
- opcode[1] = TWO_BYTE_OPCODE_ESCAPE;
- opcode[2] = opcode[0] + 0x10;
- opcode[0] = WORD_PREFIX_OPCODE;
- extension = 4; /* 3 opcode + 2 displacement */
- where_to_put_displacement = &opcode[3];
+ case ENCODE_RELAX_STATE (COND_JUMP, BIG):
+ extension = 5; /* 2 opcode + 4 displacement */
+ opcode[1] = opcode[0] + 0x10;
+ opcode[0] = TWO_BYTE_OPCODE_ESCAPE;
+ where_to_put_displacement = &opcode[2];
break;
- case ENCODE_RELAX_STATE (UNCOND_JUMP, WORD):
- opcode[1] = 0xe9;
- opcode[0] = WORD_PREFIX_OPCODE;
- extension = 3; /* 2 opcode + 2 displacement */
- where_to_put_displacement = &opcode[2];
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, BIG):
+ extension = 4; /* 1 opcode + 4 displacement */
+ opcode[0] = 0xe9;
+ where_to_put_displacement = &opcode[1];
break;
- case ENCODE_RELAX_STATE (COND_JUMP, DWORD):
+ case ENCODE_RELAX_STATE (COND_JUMP, BIG16):
+ extension = 3; /* 2 opcode + 2 displacement */
opcode[1] = opcode[0] + 0x10;
opcode[0] = TWO_BYTE_OPCODE_ESCAPE;
- extension = 5; /* 2 opcode + 4 displacement */
where_to_put_displacement = &opcode[2];
break;
- case ENCODE_RELAX_STATE (UNCOND_JUMP, DWORD):
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, BIG16):
+ extension = 2; /* 1 opcode + 2 displacement */
opcode[0] = 0xe9;
- extension = 4; /* 1 opcode + 4 displacement */
where_to_put_displacement = &opcode[1];
break;
}
}
\f
-void /* Knows about order of bytes in address. */
-md_number_to_chars (con, value, nbytes)
- char con[]; /* Return 'nbytes' of chars here. */
- valueT value; /* The value of the bits. */
- int nbytes; /* Number of bytes in the output. */
-{
- number_to_chars_littleendian (con, value, nbytes);
-}
-
-
/* Apply a fixup (fixS) to segment data, once it has been determined
by our caller that we have all the info we need to fix it up.
the same (little-endian) format, so we don't need to care about which
we are handling. */
-static void
-md_apply_fix_1 (fixP, value)
- fixS *fixP; /* The fix we're to put in */
- long value; /* The value of the bits. */
+int
+md_apply_fix3 (fixP, valp, seg)
+ fixS *fixP; /* The fix we're to put in. */
+ valueT *valp; /* Pointer to the value of the bits. */
+ segT seg; /* Segment fix is from. */
{
register char *p = fixP->fx_where + fixP->fx_frag->fr_literal;
+ valueT value = *valp;
+
+ if (fixP->fx_r_type == BFD_RELOC_32 && fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
#if defined (BFD_ASSEMBLER) && !defined (TE_Mach)
/*
*/
if (fixP->fx_r_type == BFD_RELOC_32_PCREL && fixP->fx_addsy)
{
- value += fixP->fx_where + fixP->fx_frag->fr_address;
-#ifdef OBJ_ELF
- if (S_GET_SEGMENT (fixP->fx_addsy) != undefined_section)
+#ifndef OBJ_AOUT
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+#ifdef TE_PE
+ || OUTPUT_FLAVOR == bfd_target_coff_flavour
+#endif
+ )
+ value += fixP->fx_where + fixP->fx_frag->fr_address;
+#endif
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg
+ || (fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0)
+ && ! S_IS_EXTERNAL (fixP->fx_addsy)
+ && ! S_IS_WEAK (fixP->fx_addsy)
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && ! S_IS_COMMON (fixP->fx_addsy))
{
/* Yes, we add the values in twice. This is because
bfd_perform_relocation subtracts them out again. I think
it. FIXME. */
value += fixP->fx_where + fixP->fx_frag->fr_address;
}
+#endif
+#if defined (OBJ_COFF) && defined (TE_PE)
+ /* For some reason, the PE format does not store a section
+ address offset for a PC relative symbol. */
+ if (S_GET_SEGMENT (fixP->fx_addsy) != seg)
+ value += md_pcrel_from (fixP);
#endif
}
+
+ /* Fix a few things - the dynamic linker expects certain values here,
+ and we must not dissappoint it. */
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && fixP->fx_addsy)
+ switch (fixP->fx_r_type) {
+ case BFD_RELOC_386_PLT32:
+ /* Make the jump instruction point to the address of the operand. At
+ runtime we merely add the offset to the actual PLT entry. */
+ value = 0xfffffffc;
+ break;
+ case BFD_RELOC_386_GOTPC:
+/*
+ * This is tough to explain. We end up with this one if we have
+ * operands that look like "_GLOBAL_OFFSET_TABLE_+[.-.L284]". The goal
+ * here is to obtain the absolute address of the GOT, and it is strongly
+ * preferable from a performance point of view to avoid using a runtime
+ * relocation for this. The actual sequence of instructions often look
+ * something like:
+ *
+ * call .L66
+ * .L66:
+ * popl %ebx
+ * addl $_GLOBAL_OFFSET_TABLE_+[.-.L66],%ebx
+ *
+ * The call and pop essentially return the absolute address of
+ * the label .L66 and store it in %ebx. The linker itself will
+ * ultimately change the first operand of the addl so that %ebx points to
+ * the GOT, but to keep things simple, the .o file must have this operand
+ * set so that it generates not the absolute address of .L66, but the
+ * absolute address of itself. This allows the linker itself simply
+ * treat a GOTPC relocation as asking for a pcrel offset to the GOT to be
+ * added in, and the addend of the relocation is stored in the operand
+ * field for the instruction itself.
+ *
+ * Our job here is to fix the operand so that it would add the correct
+ * offset so that %ebx would point to itself. The thing that is tricky is
+ * that .-.L66 will point to the beginning of the instruction, so we need
+ * to further modify the operand so that it will point to itself.
+ * There are other cases where you have something like:
+ *
+ * .long $_GLOBAL_OFFSET_TABLE_+[.-.L66]
+ *
+ * and here no correction would be required. Internally in the assembler
+ * we treat operands of this form as not being pcrel since the '.' is
+ * explicitly mentioned, and I wonder whether it would simplify matters
+ * to do it this way. Who knows. In earlier versions of the PIC patches,
+ * the pcrel_adjust field was used to store the correction, but since the
+ * expression is not pcrel, I felt it would be confusing to do it this way.
+ */
+ value -= 1;
+ break;
+ case BFD_RELOC_386_GOT32:
+ value = 0; /* Fully resolved at runtime. No addend. */
+ break;
+ case BFD_RELOC_386_GOTOFF:
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return 1;
+
+ default:
+ break;
+ }
+#endif
+
#endif
md_number_to_chars (p, value, fixP->fx_size);
-}
-#ifdef BFD_ASSEMBLER
-int
-md_apply_fix (fixP, valp)
- fixS *fixP;
- valueT *valp;
-{
- md_apply_fix_1 (fixP, *valp);
return 1;
}
-#else
-void
-md_apply_fix (fixP, val)
- fixS *fixP;
- long val;
-{
- md_apply_fix_1 (fixP, val);
-}
-#endif
#if 0
/* This is never used. */
default:
*sizeP = 0;
- return "Bad call to md_atof ()";
+ return _("Bad call to md_atof ()");
}
t = atof_ieee (input_line_pointer, type, words);
if (t)
\f
char output_invalid_buf[8];
+static char * output_invalid PARAMS ((int));
+
static char *
output_invalid (c)
- char c;
+ int c;
{
if (isprint (c))
sprintf (output_invalid_buf, "'%c'", c);
return output_invalid_buf;
}
-/* reg_string starts *before* REGISTER_PREFIX */
-static reg_entry *
-parse_register (reg_string)
+/* REG_STRING starts *before* REGISTER_PREFIX. */
+
+static const reg_entry * parse_register PARAMS ((char *, char **));
+
+static const reg_entry *
+parse_register (reg_string, end_op)
char *reg_string;
+ char **end_op;
{
register char *s = reg_string;
register char *p;
- char reg_name_given[MAX_REG_NAME_SIZE];
+ char reg_name_given[MAX_REG_NAME_SIZE + 1];
+ const reg_entry *r;
+
+ /* Skip possible REGISTER_PREFIX and possible whitespace. */
+ if (*s == REGISTER_PREFIX)
+ ++s;
- s++; /* skip REGISTER_PREFIX */
- for (p = reg_name_given; is_register_char (*s); p++, s++)
+ if (is_space_char (*s))
+ ++s;
+
+ p = reg_name_given;
+ while ((*p++ = register_chars[(unsigned char) *s++]) != '\0')
{
- *p = register_chars[(unsigned char) *s];
if (p >= reg_name_given + MAX_REG_NAME_SIZE)
- return (reg_entry *) 0;
+ {
+ if (!allow_naked_reg)
+ {
+ *p = '\0';
+ as_bad (_("bad register name `%s'"), reg_name_given);
+ }
+ return (const reg_entry *) NULL;
+ }
+ }
+
+ *end_op = s - 1;
+
+ r = (const reg_entry *) hash_find (reg_hash, reg_name_given);
+
+ if (r == NULL)
+ {
+ if (!allow_naked_reg)
+ as_bad (_("bad register name `%s'"), reg_name_given);
+ return (const reg_entry *) NULL;
}
- *p = '\0';
- return (reg_entry *) hash_find (reg_hash, reg_name_given);
+
+ return r;
}
\f
#ifdef OBJ_ELF
-CONST char *md_shortopts = "mVQ:";
+CONST char *md_shortopts = "kmVQ:";
#else
CONST char *md_shortopts = "m";
#endif
struct option md_longopts[] = {
{NULL, no_argument, NULL, 0}
};
-size_t md_longopts_size = sizeof(md_longopts);
+size_t md_longopts_size = sizeof (md_longopts);
int
md_parse_option (c, arg)
flag_do_long_jump = 1;
break;
-#ifdef OBJ_ELF
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ /* -k: Ignore for FreeBSD compatibility. */
+ case 'k':
+ break;
+
/* -V: SVR4 argument to print version ID. */
case 'V':
print_version_id ();
md_show_usage (stream)
FILE *stream;
{
- fprintf (stream, "\
--m do long jump\n");
+ fprintf (stream, _("\
+-m do long jump\n"));
+}
+
+#ifdef BFD_ASSEMBLER
+#ifdef OBJ_MAYBE_ELF
+#ifdef OBJ_MAYBE_COFF
+
+/* Pick the target format to use. */
+
+const char *
+i386_target_format ()
+{
+ switch (OUTPUT_FLAVOR)
+ {
+ case bfd_target_coff_flavour:
+ return "coff-i386";
+ case bfd_target_elf_flavour:
+ return "elf32-i386";
+ default:
+ abort ();
+ return NULL;
+ }
}
-\f
-/* We have no need to default values of symbols. */
+#endif /* OBJ_MAYBE_COFF */
+#endif /* OBJ_MAYBE_ELF */
+#endif /* BFD_ASSEMBLER */
+\f
/* ARGSUSED */
symbolS *
md_undefined_symbol (name)
char *name;
{
+ if (*name == '_' && *(name+1) == 'G'
+ && strcmp(name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+ {
+ if (!GOT_symbol)
+ {
+ if (symbol_find (name))
+ as_bad (_("GOT already in symbol table"));
+ GOT_symbol = symbol_new (name, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ };
+ return GOT_symbol;
+ }
return 0;
}
-/* Parse an operand that is machine-specific.
- We just return without modifying the expression if we have nothing
- to do. */
-
-/* ARGSUSED */
-void
-md_operand (expressionP)
- expressionS *expressionP;
-{
-}
-
/* Round up a section size to the appropriate boundary. */
valueT
md_section_align (segment, size)
segT segment;
valueT size;
{
- return size; /* Byte alignment is fine */
+#ifdef OBJ_AOUT
+#ifdef BFD_ASSEMBLER
+ /* For a.out, force the section size to be aligned. If we don't do
+ this, BFD will align it for us, but it will not write out the
+ final bytes of the section. This may be a bug in BFD, but it is
+ easier to fix it here since that is how the other a.out targets
+ work. */
+ int align;
+
+ align = bfd_get_section_alignment (stdoutput, segment);
+ size = ((size + (1 << align) - 1) & ((valueT) -1 << align));
+#endif
+#endif
+
+ return size;
}
-/* Exactly what point is a PC-relative offset relative TO? On the
- i386, they're relative to the address of the offset, plus its
- size. (??? Is this right? FIXME-SOON!) */
+/* On the i386, PC-relative offsets are relative to the start of the
+ next instruction. That is, the address of the offset, plus its
+ size, since the offset is always the last part of the insn. */
+
long
md_pcrel_from (fixP)
fixS *fixP;
#ifdef BFD_ASSEMBLER
+void
+i386_validate_fix (fixp)
+ fixS *fixp;
+{
+ if (fixp->fx_subsy && fixp->fx_subsy == GOT_symbol)
+ {
+ fixp->fx_r_type = BFD_RELOC_386_GOTOFF;
+ fixp->fx_subsy = 0;
+ }
+}
+
+#define F(SZ,PCREL) (((SZ) << 1) + (PCREL))
+#define MAP(SZ,PCREL,TYPE) case F(SZ,PCREL): code = (TYPE); break
+
arelent *
tc_gen_reloc (section, fixp)
asection *section;
arelent *rel;
bfd_reloc_code_real_type code;
-#define F(SZ,PCREL) (((SZ) << 1) + (PCREL))
- switch (F (fixp->fx_size, fixp->fx_pcrel))
+ switch (fixp->fx_r_type)
{
-#define MAP(SZ,PCREL,TYPE) case F(SZ,PCREL): code = (TYPE); break
-#ifndef OBJ_ELF
- MAP (1, 0, BFD_RELOC_8);
- MAP (2, 0, BFD_RELOC_16);
-#endif
- MAP (4, 0, BFD_RELOC_32);
-#ifndef OBJ_ELF
- MAP (1, 1, BFD_RELOC_8_PCREL);
- MAP (2, 1, BFD_RELOC_16_PCREL);
-#endif
- MAP (4, 1, BFD_RELOC_32_PCREL);
+ case BFD_RELOC_386_PLT32:
+ case BFD_RELOC_386_GOT32:
+ case BFD_RELOC_386_GOTOFF:
+ case BFD_RELOC_386_GOTPC:
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_VTABLE_INHERIT:
+ code = fixp->fx_r_type;
+ break;
default:
- as_bad ("Can not do %d byte %srelocation", fixp->fx_size,
- fixp->fx_pcrel ? "pc-relative" : "");
+ switch (F (fixp->fx_size, fixp->fx_pcrel))
+ {
+ MAP (1, 0, BFD_RELOC_8);
+ MAP (2, 0, BFD_RELOC_16);
+ MAP (4, 0, BFD_RELOC_32);
+ MAP (1, 1, BFD_RELOC_8_PCREL);
+ MAP (2, 1, BFD_RELOC_16_PCREL);
+ MAP (4, 1, BFD_RELOC_32_PCREL);
+ default:
+ if (fixp->fx_pcrel)
+ as_bad (_("Can not do %d byte pc-relative relocation"),
+ fixp->fx_size);
+ else
+ as_bad (_("Can not do %d byte relocation"), fixp->fx_size);
+ code = BFD_RELOC_32;
+ break;
+ }
+ break;
}
#undef MAP
#undef F
- rel = (arelent *) bfd_alloc_by_size_t (stdoutput, sizeof (arelent));
- assert (rel != 0);
+ if (code == BFD_RELOC_32
+ && GOT_symbol
+ && fixp->fx_addsy == GOT_symbol)
+ code = BFD_RELOC_386_GOTPC;
+
+ rel = (arelent *) xmalloc (sizeof (arelent));
rel->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+
rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ /* HACK: Since i386 ELF uses Rel instead of Rela, encode the
+ vtable entry to be used in the relocation's section offset. */
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ rel->address = fixp->fx_offset;
+
if (fixp->fx_pcrel)
rel->addend = fixp->fx_addnumber;
else
rel->addend = 0;
rel->howto = bfd_reloc_type_lookup (stdoutput, code);
- if (!rel->howto)
+ if (rel->howto == NULL)
{
- const char *name;
-
- name = S_GET_NAME (fixp->fx_addsy);
- if (name == NULL)
- name = "<unknown>";
- as_fatal ("Cannot generate relocation type for symbol %s, code %s",
- name, bfd_get_reloc_code_name (code));
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (code));
+ /* Set howto to a garbage value so that we can keep going. */
+ rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+ assert (rel->howto != NULL);
}
return rel;
tc_coff_fix2rtype (fixP)
fixS *fixP;
{
+ if (fixP->fx_r_type == R_IMAGEBASE)
+ return R_IMAGEBASE;
+
return (fixP->fx_pcrel ?
(fixP->fx_size == 1 ? R_PCRBYTE :
fixP->fx_size == 2 ? R_PCRWORD :
#endif /* I386COFF */
#endif /* BFD_ASSEMBLER? */
-
+\f
/* end of tc-i386.c */