diff options
author | obrien <obrien@FreeBSD.org> | 2000-05-13 17:16:40 +0000 |
---|---|---|
committer | obrien <obrien@FreeBSD.org> | 2000-05-13 17:16:40 +0000 |
commit | dfcb32c8453b3b41772d1935c05b7029999bc88d (patch) | |
tree | ba718ba76c1c93616d046435ae39c919741dca22 | |
parent | b64c124edf73cbd145cabcc9d9b228570916496b (diff) | |
download | FreeBSD-src-dfcb32c8453b3b41772d1935c05b7029999bc88d.zip FreeBSD-src-dfcb32c8453b3b41772d1935c05b7029999bc88d.tar.gz |
Merge in rev 1.2 (don't use "/" to start a comment).
-rw-r--r-- | contrib/binutils/gas/config/tc-i386.c | 4136 |
1 files changed, 2711 insertions, 1425 deletions
diff --git a/contrib/binutils/gas/config/tc-i386.c b/contrib/binutils/gas/config/tc-i386.c index d93456d..1c39080 100644 --- a/contrib/binutils/gas/config/tc-i386.c +++ b/contrib/binutils/gas/config/tc-i386.c @@ -1,5 +1,5 @@ /* i386.c -- Assemble code for the Intel 80386 - Copyright (C) 1989, 91, 92, 93, 94, 95, 96, 97, 1998 + Copyright (C) 1989, 91, 92, 93, 94, 95, 96, 97, 98, 99, 2000 Free Software Foundation. This file is part of GAS, the GNU Assembler. @@ -32,21 +32,38 @@ #include "as.h" #include "subsegs.h" - -#include "obstack.h" #include "opcode/i386.h" -#ifndef TC_RELOC -#define TC_RELOC(X,Y) (Y) +#ifndef REGISTER_WARNINGS +#define REGISTER_WARNINGS 1 +#endif + +#ifndef INFER_ADDR_PREFIX +#define INFER_ADDR_PREFIX 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 -static unsigned long mode_from_disp_size PARAMS ((unsigned long)); +#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_16bit_gcc_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)); @@ -55,13 +72,21 @@ static bfd_reloc_code_real_type reloc /* 'md_assemble ()' gathers together information and puts it into a i386_insn. */ +union i386_op + { + expressionS *disps; + expressionS *imms; + const reg_entry *regs; + }; + struct _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. */ unsigned int operands; @@ -72,12 +97,12 @@ struct _i386_insn unsigned int reg_operands, disp_operands, mem_operands, imm_operands; /* TYPES [i] is the type (see above #defines) which tells us how to - search through DISPS [i] & IMMS [i] & REGS [i] for the required - operand. */ + use OP[i] for the corresponding operand. */ unsigned int types[MAX_OPERANDS]; - /* Displacements (if given) for each operand. */ - expressionS *disps[MAX_OPERANDS]; + /* Displacement expression, immediate expression, or register for each + operand. */ + union i386_op op[MAX_OPERANDS]; /* Relocation type for operand */ #ifdef BFD_ASSEMBLER @@ -86,43 +111,48 @@ struct _i386_insn 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]; - /* 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 BI 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) || defined (OBJ_MAYBE_ELF)) && !defined(TE_FreeBSD) +#if defined (TE_I386AIX) || ((defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) && ! defined (TE_LINUX) && !defined(TE_FreeBSD)) +/* 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 @@ -133,11 +163,12 @@ const char comment_chars[] = "#"; #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) || defined (OBJ_MAYBE_ELF)) && !defined(TE_FreeBSD) +#if defined (TE_I386AIX) || ((defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) && ! defined (TE_LINUX) && !defined(TE_FreeBSD)) 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 */ @@ -149,64 +180,73 @@ const char EXP_CHARS[] = "eE"; 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 */ - /* md_assemble() always leaves the strings it's passed unaltered. To effect this we maintain a stack of saved characters that we've smashed with '\0's (indicating end of strings for various sub-fields of the 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 */ + +static char stackop_size = '\0'; /* Used in 16 bit gcc mode to add an l + suffix to call, ret, enter, leave, push, + and pop instructions so that gcc has the + same stack frame as in 32 bit mode. */ + /* 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 - of jumps add different sizes to frags when we're figuring out what + one for unconditional jumps. This is because these two types of + jumps add different sizes to frags when we're figuring out what sort of jump to choose to reach a given label. */ /* 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__ @@ -219,40 +259,46 @@ static int flag_16bit_code; /* 1 if we're writing 16-bit code, 0 if 32-bit */ #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[] = { -/* The fields are: - 1) most positive reach of this state, - 2) most negative reach of this state, - 3) how many bytes this mode will add to the size of the current frag - 4) which index into the table to try if we can't fit into this one. - */ + /* The fields are: + 1) most positive reach of this state, + 2) most negative reach of this state, + 3) how many bytes this mode will add to the size of the current frag + 4) which index into the table to try if we can't fit into this one. + */ {1, 1, 0, 0}, {1, 1, 0, 0}, {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} }; @@ -304,6 +350,8 @@ i386_align_code (fragP, count) 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_3[] = + {0x8d,0x74,0x00}; /* lea 0(%esi),%esi */ static const char f16_4[] = {0x8d,0xb4,0x00,0x00}; /* lea 0w(%si),%si */ static const char f16_5[] = @@ -323,7 +371,7 @@ i386_align_code (fragP, count) 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_1, f32_2, f16_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 }; @@ -345,34 +393,23 @@ i386_align_code (fragP, 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 -symbolS *GOT_symbol; /* Pre-defined "__GLOBAL_OFFSET_TABLE" */ +symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ -static INLINE unsigned long +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; @@ -424,11 +461,114 @@ smallest_imm_type (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; + int new_16bit_code_flag; { flag_16bit_code = new_16bit_code_flag; + stackop_size = '\0'; +} + +static void +set_16bit_gcc_code_flag (new_16bit_code_flag) + int new_16bit_code_flag; +{ + flag_16bit_code = new_16bit_code_flag; + stackop_size = new_16bit_code_flag ? 'l' : '\0'; +} + +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[] = @@ -447,23 +587,21 @@ const pseudo_typeS md_pseudo_table[] = {"value", cons, 2}, {"noopt", s_ignore, 0}, {"optim", s_ignore, 0}, + {"code16gcc", set_16bit_gcc_code_flag, 1}, {"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; void @@ -471,46 +609,40 @@ md_begin () { 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; } } } @@ -520,7 +652,9 @@ md_begin () { 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) @@ -528,66 +662,48 @@ md_begin () } } - 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)) + else if (islower (c)) { - opcode_chars[c] = tolower (c); - register_chars[c] = opcode_chars[c]; - } - else if (c == PREFIX_SEPERATOR) - { - 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; + else if (c >= 128) + { + identifier_chars[c] = c; + operand_chars[c] = c; + } + } #ifdef LEX_AT - identifier_chars['@'] = '@'; + identifier_chars['@'] = '@'; #endif - - if (c == ' ' || c == '\t') - space_chars[c] = c; - } + digit_chars['-'] = '-'; + identifier_chars['_'] = '_'; + identifier_chars['.'] = '.'; for (p = operand_special_chars; *p != '\0'; p++) operand_chars[(unsigned char) *p] = *p; @@ -609,7 +725,6 @@ i386_print_statistics (file) { hash_print_statistics (file, "i386 opcode", op_hash); hash_print_statistics (file, "i386 register", reg_hash); - hash_print_statistics (file, "i386 prefix", prefix_hash); } @@ -642,12 +757,12 @@ pi (line, x) pt (x->types[i]); fprintf (stdout, "\n"); if (x->types[i] - & (Reg | SReg2 | SReg3 | Control | Debug | Test | RegMMX)) - fprintf (stdout, "%s\n", x->regs[i]->reg_name); + & (Reg | SReg2 | SReg3 | Control | Debug | Test | RegMMX | RegXMM)) + fprintf (stdout, "%s\n", x->op[i].regs->reg_name); if (x->types[i] & Imm) - pe (x->imms[i]); - if (x->types[i] & (Disp | Abs)) - pe (x->disps[i]); + pe (x->op[i].imms); + if (x->types[i] & Disp) + pe (x->op[i].disps); } } @@ -678,9 +793,9 @@ static void pe (e) expressionS *e; { - fprintf (stdout, " operation %d\n", e->X_op); - fprintf (stdout, " add_number %d (%x)\n", - e->X_add_number, e->X_add_number); + fprintf (stdout, " operation %d\n", e->X_op); + fprintf (stdout, " add_number %ld (%lx)\n", + (long) e->X_add_number, (long) e->X_add_number); if (e->X_add_symbol) { fprintf (stdout, " add_symbol "); @@ -720,28 +835,25 @@ type_names[] = { 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" }, + { RegXMM, "rXMM" }, + { EsSeg, "es" }, { 0, "" } }; @@ -753,7 +865,7 @@ pt (t) if (t == Unknown) { - fprintf (stdout, "Unknown"); + fprintf (stdout, _("Unknown")); } else { @@ -766,7 +878,25 @@ pt (t) #endif /* DEBUG386 */ +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, other) int size; @@ -776,22 +906,26 @@ reloc (size, pcrel, other) if (other != NO_RELOC) return other; if (pcrel) - switch (size) - { - case 1: return BFD_RELOC_8_PCREL; - case 2: return BFD_RELOC_16_PCREL; - 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) - { - case 1: return BFD_RELOC_8; - case 2: return BFD_RELOC_16; - 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; } @@ -802,32 +936,54 @@ reloc (size, pcrel, other) * some cases we force the original symbol to be used. */ int -tc_i386_fix_adjustable(fixP) - fixS * fixP; +tc_i386_fix_adjustable (fixP) + fixS *fixP; { -#ifdef OBJ_ELF - /* Prevent all adjustments to global symbols. */ +#if defined (OBJ_ELF) || defined (TE_PE) + /* Prevent all adjustments to global symbols, or else dynamic + linking will not work correctly. */ if (S_IS_EXTERN (fixP->fx_addsy)) return 0; if (S_IS_WEAK (fixP->fx_addsy)) return 0; -#endif /* ! defined (OBJ_AOUT) */ +#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_386_GOT32 + || fixP->fx_r_type == BFD_RELOC_RVA + || 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,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 +static int +intel_float_operand PARAMS ((char *mnemonic)); + +static int +intel_float_operand (mnemonic) + char *mnemonic; +{ + if (mnemonic[0] == 'f' && mnemonic[1] =='i') + return 2; + + 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. */ @@ -842,15 +998,9 @@ md_assemble (line) /* Count the size of the instruction generated. */ int insn_size = 0; - /* Possible templates for current insn */ - templates *current_templates = (templates *) 0; - int j; - /* Wait prefix needs to come before any other prefixes, so handle it - specially. wait_prefix will hold the opcode modifier flag FWait - if a wait prefix is given. */ - int wait_prefix = 0; + char mnemonic[MAX_MNEM_SIZE]; /* Initialize globals. */ memset (&i, '\0', sizeof (i)); @@ -860,147 +1010,144 @@ md_assemble (line) memset (im_expressions, '\0', sizeof (im_expressions)); save_stack_p = save_stack; /* reset stack pointer */ - /* First 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 balanced. */ - unsigned int paren_not_balanced; char *token_start = l; + char *mnem_p; - while (!is_space_char (*l) && *l != END_OF_INSN) + /* Non-zero if we found a prefix only acceptable with string insns. */ + const char *expecting_string_instruction = NULL; + + 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; + } + + /* Look up instruction (or prefix) via hash table. */ + current_templates = hash_find (op_hash, mnemonic); - if (l == token_start) + 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 ("expecting prefix; got nothing"); + as_bad (_("redundant %s prefix"), + current_templates->start->name); return; } - END_STRING_AND_SAVE (l); - prefix = (prefix_entry *) hash_find (prefix_hash, token_start); - if (!prefix) + /* Add prefix, checking for repeated prefixes. */ + switch (add_prefix (current_templates->start->base_opcode)) { - as_bad ("no such opcode prefix ('%s')", token_start); + case 0: return; + case 2: + expecting_string_instruction = + current_templates->start->name; + break; } - 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 (prefix->prefix_code == FWAIT_OPCODE) - { - if (wait_prefix != 0) - { - as_bad ("same prefix used twice; you don't really want this!"); - return; - } - wait_prefix = FWait; - } - else - { - if (i.prefixes == MAX_PREFIXES) - { - as_bad ("too many opcode prefixes"); - return; - } - 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 WORD_MNEM_SUFFIX: + case BYTE_MNEM_SUFFIX: + case SHORT_MNEM_SUFFIX: + case LONG_MNEM_SUFFIX: + i.suffix = mnem_p[-1]; + mnem_p[-1] = '\0'; + current_templates = hash_find (op_hash, mnemonic); + break; + + /* Intel Syntax */ + case 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_balanced = 0; @@ -1010,8 +1157,12 @@ md_assemble (line) { if (paren_not_balanced) { - as_bad ("unbalanced 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 @@ -1019,15 +1170,25 @@ md_assemble (line) } 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_balanced; - if (*l == ')') - --paren_not_balanced; + 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) @@ -1036,13 +1197,18 @@ md_assemble (line) 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; @@ -1052,12 +1218,12 @@ md_assemble (line) 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; } } @@ -1076,52 +1242,161 @@ md_assemble (line) } } - /* 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 & ~JumpAbsolute) \ + && ((given) & (BaseIndex|JumpAbsolute)) == ((overlap) & (BaseIndex|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". We also don't reverse intersegment "jmp" and "call" + instructions with 2 immediate operands so that the immediate segment + precedes the offset, as it does when in AT&T mode. "enter" and the + intersegment "jmp" and "call" instructions are the only ones that + have two immediate operands. */ + if (intel_syntax && i.operands > 1 + && (strcmp (mnemonic, "bound") != 0) + && !((i.types[0] & Imm) && (i.types[1] & Imm))) + { + union i386_op temp_op; + unsigned int temp_type; + int xchg1 = 0; + int xchg2 = 0; + + if (i.operands == 2) + { + xchg1 = 0; + xchg2 = 1; + } + else if (i.operands == 3) + { + xchg1 = 0; + xchg2 = 2; + } + temp_type = i.types[xchg2]; + i.types[xchg2] = i.types[xchg1]; + i.types[xchg1] = temp_type; + temp_op = i.op[xchg2]; + i.op[xchg2] = i.op[xchg1]; + i.op[xchg1] = temp_op; + + if (i.mem_operands == 2) + { + const seg_entry *temp_seg; + temp_seg = i.seg[0]; + i.seg[0] = i.seg[1]; + i.seg[1] = temp_seg; + } + } + + if (i.imm_operands) + { + /* Try to ensure constant immediates are represented in the smallest + opcode possible. */ + char guess_suffix = 0; + int op; + + if (i.suffix) + guess_suffix = i.suffix; + else if (i.reg_operands) + { + /* Figure out a suffix from the last register operand specified. + We can't do this properly yet, ie. excluding InOutPortReg, + but the following works for instructions with immediates. + In any case, we can't set i.suffix yet. */ + for (op = i.operands; --op >= 0; ) + if (i.types[op] & Reg) + { + if (i.types[op] & Reg8) + guess_suffix = BYTE_MNEM_SUFFIX; + else if (i.types[op] & Reg16) + guess_suffix = WORD_MNEM_SUFFIX; + break; + } + } + else if (flag_16bit_code ^ (i.prefix[DATA_PREFIX] != 0)) + guess_suffix = WORD_MNEM_SUFFIX; + + for (op = i.operands; --op >= 0; ) + if ((i.types[op] & Imm) + && i.op[op].imms->X_op == O_constant) + { + /* If a suffix is given, this operand may be shortened. */ + switch (guess_suffix) + { + case WORD_MNEM_SUFFIX: + i.types[op] |= Imm16; + break; + case BYTE_MNEM_SUFFIX: + i.types[op] |= Imm16 | Imm8 | Imm8S; + break; + } + + /* If this operand is at most 16 bits, convert it to a + signed 16 bit number before trying to see whether it will + fit in an even smaller size. This allows a 16-bit operand + such as $0xffe0 to be recognised as within Imm8S range. */ + if ((i.types[op] & Imm16) + && (i.op[op].imms->X_add_number & ~(offsetT)0xffff) == 0) + { + i.op[op].imms->X_add_number = + (((i.op[op].imms->X_add_number & 0xffff) ^ 0x8000) - 0x8000); + } + i.types[op] |= smallest_imm_type ((long) i.op[op].imms->X_add_number); + } + } + + 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 == 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; + + /* Check the suffix, except for some instructions in intel mode. */ + if ((t->opcode_modifier & suffix_check) + && !(intel_syntax + && t->base_opcode == 0xd9 + && (t->extension_opcode == 5 /* 0xd9,5 "fldcw" */ + || t->extension_opcode == 7))) /* 0xd9,7 "f{n}stcw" */ + continue; + else if (!t->operands) break; /* 0 operands always matches */ @@ -1129,50 +1404,56 @@ md_assemble (line) 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[1], - t->operand_types[0])) + 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])) + 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: @@ -1182,247 +1463,397 @@ md_assemble (line) } /* 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; } + if (!intel_syntax + && (i.types[0] & JumpAbsolute) != (t->operand_types[0] & JumpAbsolute)) + { + as_warn (_("indirect %s without `*'"), t->name); + } + + 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); + } + /* Copy the template we found. */ i.tm = *t; - i.tm.opcode_modifier |= wait_prefix; - if (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; + i.tm.operand_types[0] = t->operand_types[1]; i.tm.operand_types[1] = t->operand_types[0]; } - /* If the matched instruction specifies an explicit opcode suffix, - use it - and make sure none has already been specified. */ - if (i.tm.opcode_modifier & (Data16|Data32)) + /* Undo SYSV386_COMPAT brokenness when in Intel mode. See i386.h */ + if (SYSV386_COMPAT + && intel_syntax + && (i.tm.base_opcode & 0xfffffde0) == 0xdce0) + i.tm.base_opcode ^= FloatR; + + 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 ("extraneous opcode suffix given"); - 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]; + } + 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 (i.tm.opcode_modifier & Data16) - i.suffix = WORD_OPCODE_SUFFIX; - else - i.suffix = DWORD_OPCODE_SUFFIX; } - /* If there's no opcode suffix we try to invent one based on register - operands. */ - if (!i.suffix && i.reg_operands) + /* If matched instruction specifies an explicit instruction mnemonic + suffix, use it. */ + if (i.tm.opcode_modifier & (Size16 | Size32)) { - /* 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); - } + if (i.tm.opcode_modifier & Size16) + i.suffix = WORD_MNEM_SUFFIX; + else + i.suffix = LONG_MNEM_SUFFIX; } - else if (i.suffix != 0 - && i.reg_operands != 0 - && (i.types[i.operands - 1] & Reg) != 0) + else if (i.reg_operands) { - int bad; + /* 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.tm.operand_types[op] & InOutPortReg)) + { + i.suffix = ((i.types[op] & Reg8) ? BYTE_MNEM_SUFFIX : + (i.types[op] & Reg16) ? WORD_MNEM_SUFFIX : + LONG_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; - /* If the last operand is a register, make sure it is - compatible with the suffix. */ + /* 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; - bad = 0; - switch (i.suffix) + if ((i.types[op] & WordReg) && i.op[op].regs->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.op[op].regs - (i.types[op] & Reg16 ? 8 : 16))->reg_name, + i.op[op].regs->reg_name, + i.suffix); +#endif + continue; + } + /* Any other register is bad */ + if (i.types[op] & (Reg | RegMMX | RegXMM + | SReg2 | SReg3 + | Control | Debug | Test + | FloatReg | FloatAcc)) + { + as_bad (_("`%%%s' not allowed with `%s%c'"), + i.op[op].regs->reg_name, + i.tm.name, + i.suffix); + return; + } + } + } + else if (i.suffix == LONG_MNEM_SUFFIX) { - default: - abort (); - case BYTE_OPCODE_SUFFIX: - /* 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[i.operands - 1] & Reg8) == 0 - && i.regs[i.operands - 1]->reg_num >= 4) - bad = 1; - break; - case WORD_OPCODE_SUFFIX: - case DWORD_OPCODE_SUFFIX: - /* We don't insist on the presence or absence of the e - prefix on the register, but we reject eight bit - registers. */ - if ((i.types[i.operands - 1] & Reg8) != 0) - bad = 1; + 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.op[op].regs->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.op[op].regs + 8)->reg_name, + i.op[op].regs->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.op[op].regs->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.op[op].regs - 8)->reg_name, + i.op[op].regs->reg_name, + i.suffix); + } +#endif } - if (bad) - as_bad ("register does not match opcode suffix"); + else + abort(); + } + else if ((i.tm.opcode_modifier & DefaultSize) && !i.suffix) + { + i.suffix = stackop_size; } /* Make still unresolved immediate matches conform to size of immediate - given in i.suffix. Note: overlap2 cannot be an immediate! - We assume this. */ + 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) + if (i.suffix) + { + overlap0 &= (i.suffix == BYTE_MNEM_SUFFIX ? (Imm8 | Imm8S) : + (i.suffix == WORD_MNEM_SUFFIX ? Imm16 : Imm32)); + } + else if (overlap0 == (Imm16 | Imm32)) { - as_bad ("no opcode suffix given; can't determine immediate size"); + 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; } - overlap0 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8 | Imm8S) : - (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32)); } 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)) { - as_bad ("no opcode suffix given; can't determine immediate size"); + overlap1 = + (flag_16bit_code ^ (i.prefix[DATA_PREFIX] != 0)) ? Imm16 : Imm32; + } + else + { + 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 */ /* 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 && (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.op[1].regs->reg_type & Reg16) != 0) + if (!add_prefix (prefix)) + return; + } + + if (i.suffix && i.suffix != BYTE_MNEM_SUFFIX) { - /* Select between byte and word/dword operations. */ + /* It's not a byte, select word/dword operation. */ if (i.tm.opcode_modifier & W) - i.tm.base_opcode |= W; - /* Now select between word & dword operations via the - operand size prefix. */ - if ((i.suffix == WORD_OPCODE_SUFFIX) ^ flag_16bit_code) { - if (i.prefixes == MAX_PREFIXES) - { - as_bad ("%d prefixes given and data size prefix 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 == 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 == DWORD_MNEM_SUFFIX)) + { + if (i.tm.opcode_modifier & FloatMF) + i.tm.base_opcode ^= 4; + } + } + + if (i.tm.opcode_modifier & ImmExt) + { + /* These AMD 3DNow! and Intel Katmai New 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 && 2 < MAX_OPERANDS); + + exp = &im_expressions[i.imm_operands++]; + i.op[i.operands].imms = 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) { - /* Default segment register this instruction will use + /* 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; - /* True if this instruction uses a memory addressing mode, - and therefore may need an address-size prefix. */ - int uses_mem_addrmode = 0; - - - /* 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). */ - - if (found_reverse_match) - { - i.tm.base_opcode |= found_reverse_match; - } - /* The imul $imm, %reg instruction is converted into - imul $imm, %reg, %reg. */ - if (i.tm.opcode_modifier & imulKludge) - { - /* Pretend we saw the 3 operand case. */ - i.regs[2] = i.regs[1]; - i.reg_operands = 2; - } - - /* The clr %reg instruction is converted into xor %reg, %reg. */ - if (i.tm.opcode_modifier & iclrKludge) + imul $imm, %reg, %reg, and the clr %reg instruction + is converted into xor %reg, %reg. */ + if (i.tm.opcode_modifier & regKludge) { - i.regs[1] = i.regs[0]; + unsigned int first_reg_op = (i.types[0] & Reg) ? 0 : 1; + /* Pretend we saw the extra register operand. */ + assert (i.op[first_reg_op+1].regs == 0); + i.op[first_reg_op+1].regs = i.op[first_reg_op].regs; + i.types[first_reg_op+1] = i.types[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 ((i.tm.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 (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. */ - i.tm.base_opcode |= i.regs[op]->reg_num; - } - else if (i.tm.opcode_modifier & ShortFormW) - { - /* Short form with 0x8 width bit. Register is always dest. operand */ - i.tm.base_opcode |= i.regs[1]->reg_num; - if (i.suffix == WORD_OPCODE_SUFFIX || - i.suffix == DWORD_OPCODE_SUFFIX) - i.tm.base_opcode |= 0x8; - } - else if (i.tm.opcode_modifier & Seg2ShortForm) - { - if (i.tm.base_opcode == POP_SEG_SHORT && i.regs[0]->reg_num == 1) + i.tm.base_opcode |= i.op[op].regs->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.op[1].regs->reg_name, + i.op[0].regs->reg_name); + } + else + { + /* extraneous `l' suffix on fp insn */ + as_warn (_("translating to `%s %%%s'"), i.tm.name, + i.op[0].regs->reg_name); + } } - i.tm.base_opcode |= (i.regs[0]->reg_num << 3); - } - else if (i.tm.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) - i.tm.base_opcode |= 0x08; - } - else if ((i.tm.base_opcode & ~DW) == MOV_AX_DISP32) - { - /* This is a special non-modrm instruction - that addresses memory with a 32-bit displacement mode anyway, - and thus requires an address-size prefix if in 16-bit mode. */ - uses_mem_addrmode = 1; - default_seg = &ds; } else if (i.tm.opcode_modifier & Modrm) { @@ -1437,30 +1868,29 @@ md_assemble (line) { unsigned int source, dest; source = ((i.types[0] - & (Reg - | SReg2 - | SReg3 - | Control - | Debug - | Test - | RegMMX)) + & (Reg | RegMMX | RegXMM + | SReg2 | SReg3 + | Control | Debug | Test)) ? 0 : 1); dest = source + 1; + i.rm.mode = 3; - /* We must be careful to make sure that all - segment/control/test/debug/MMX 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 | RegMMX)) + /* One of the register operands will be encoded in the + i.tm.reg field, the other in the combined i.tm.mode + and i.tm.regmem fields. If no form of this + instruction supports a memory destination operand, + then we assume the source operand may sometimes be + a memory operand and so we need to store the + destination in the i.rm.reg field. */ + if ((i.tm.operand_types[dest] & AnyMem) == 0) { - i.rm.reg = i.regs[dest]->reg_num; - i.rm.regmem = i.regs[source]->reg_num; + i.rm.reg = i.op[dest].regs->reg_num; + i.rm.regmem = i.op[source].regs->reg_num; } else { - i.rm.reg = i.regs[source]->reg_num; - i.rm.regmem = i.regs[dest]->reg_num; + i.rm.reg = i.op[source].regs->reg_num; + i.rm.regmem = i.op[dest].regs->reg_num; } } else @@ -1468,116 +1898,129 @@ md_assemble (line) 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) - { - /* <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.base_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] holds the correct displacement size. */ + expressionS *exp; + + assert (i.op[op].disps == 0); exp = &disp_expressions[i.disp_operands++]; - i.disps[op] = exp; + i.op[op].disps = exp; exp->X_op = O_constant; exp->X_add_number = 0; exp->X_add_symbol = (symbolS *) 0; exp->X_op_symbol = (symbolS *) 0; } - - /* Find the default segment for the memory operand. - Used to optimize out explicit segment specifications. */ - if (i.seg) - { - unsigned int seg_index; - - 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]; - } - } } /* Fill in i.rm.reg or i.rm.regmem field with register @@ -1589,24 +2032,26 @@ md_assemble (line) { unsigned int op = ((i.types[0] - & (Reg | SReg2 | SReg3 | Control | Debug - | Test | RegMMX)) + & (Reg | RegMMX | RegXMM + | SReg2 | SReg3 + | Control | Debug | Test)) ? 0 : ((i.types[1] - & (Reg | SReg2 | SReg3 | Control | Debug - | Test | RegMMX)) + & (Reg | RegMMX | RegXMM + | SReg2 | SReg3 + | Control | Debug | Test)) ? 1 : 2)); /* If there is an extension opcode to put here, the register number must be put into the regmem field. */ if (i.tm.extension_opcode != None) - i.rm.regmem = i.regs[op]->reg_num; + i.rm.regmem = i.op[op].regs->reg_num; else - i.rm.reg = i.regs[op]->reg_num; + i.rm.reg = i.op[op].regs->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; } @@ -1615,23 +2060,25 @@ md_assemble (line) if (i.tm.extension_opcode != None) i.rm.reg = i.tm.extension_opcode; } - - if (i.rm.mode != 3) - uses_mem_addrmode = 1; } - - /* GAS currently doesn't support 16-bit memory addressing modes at all, - so if we're writing 16-bit code and using a memory addressing mode, - always spew out an address size prefix. */ - if (uses_mem_addrmode && flag_16bit_code) + else if (i.tm.opcode_modifier & (Seg2ShortForm | Seg3ShortForm)) { - if (i.prefixes == MAX_PREFIXES) + if (i.tm.base_opcode == POP_SEG_SHORT && i.op[0].regs->reg_num == 1) { - as_bad ("%d prefixes given and address size override gives too many prefixes", - MAX_PREFIXES); - return; + as_bad (_("you can't `pop %%cs'")); + return; } - i.prefix[i.prefixes++] = ADDR_PREFIX_OPCODE; + i.tm.base_opcode |= (i.op[0].regs->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, @@ -1640,26 +2087,36 @@ md_assemble (line) 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) && (i.seg != default_seg)) + if ((i.seg[0]) && (i.seg[0] != 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; + 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 (i.tm.base_opcode == INT_OPCODE && i.imms[0]->X_add_number == 3) + if (i.tm.base_opcode == INT_OPCODE && i.op[0].imms->X_add_number == 3) { i.tm.base_opcode = INT3_OPCODE; i.imm_operands = 0; } + if ((i.tm.opcode_modifier & (Jump | JumpByte | JumpDword)) + && i.op[0].disps->X_op == O_constant) + { + /* Convert "jmp constant" (and "call constant") to a jump (call) to + the absolute address given by the constant. Since ix86 jumps and + calls are pc relative, we need to generate a reloc. */ + i.op[0].disps->X_add_symbol = &abs_symbol; + i.op[0].disps->X_op = O_symbol; + } + /* We are ready to output the insn. */ { register char *p; @@ -1667,204 +2124,187 @@ md_assemble (line) /* Output jumps. */ if (i.tm.opcode_modifier & Jump) { - unsigned long n = i.disps[0]->X_add_number; + int size; + int code16; + int prefix; - if (i.disps[0]->X_op == O_constant) - { - if (fits_in_signed_byte (n)) - { - p = frag_more (2); - insn_size += 2; - p[0] = i.tm.base_opcode; - p[1] = n; - } - else - { /* It's an absolute word/dword displacement. */ - - /* Use only 16-bit jumps for 16-bit code, - because text segments are limited to 64K anyway; - use only 32-bit jumps for 32-bit code, - because they're faster. */ - int jmp_size = flag_16bit_code ? 2 : 4; - if (flag_16bit_code && !fits_in_signed_word (n)) - { - as_bad ("16-bit jump out of range"); - return; - } + code16 = 0; + if (flag_16bit_code) + code16 = CODE16; - if (i.tm.base_opcode == JUMP_PC_RELATIVE) - { /* pace */ - /* unconditional jump */ - p = frag_more (1 + jmp_size); - insn_size += 1 + jmp_size; - p[0] = (char) 0xe9; - md_number_to_chars (&p[1], (valueT) n, jmp_size); - } - else - { - /* conditional jump */ - p = frag_more (2 + jmp_size); - insn_size += 2 + jmp_size; - p[0] = TWO_BYTE_OPCODE_ESCAPE; - p[1] = i.tm.base_opcode + 0x10; - md_number_to_chars (&p[2], (valueT) n, jmp_size); - } - } - } - else + prefix = 0; + if (i.prefix[DATA_PREFIX]) { - if (flag_16bit_code) - { - FRAG_APPEND_1_CHAR (WORD_PREFIX_OPCODE); - insn_size += 1; - } - - /* 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. */ - frag_grow (7); - p = frag_more (1); - insn_size += 1; - p[0] = i.tm.base_opcode; - frag_var (rs_machine_dependent, - 6, /* 2 opcode/prefix + 4 displacement */ - 1, - ((unsigned char) *p == JUMP_PC_RELATIVE - ? ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE) - : ENCODE_RELAX_STATE (COND_JUMP, BYTE)), - i.disps[0]->X_add_symbol, - (offsetT) n, p); + prefix = 1; + i.prefixes -= 1; + code16 ^= CODE16; } + + size = 4; + if (code16) + size = 2; + + if (i.prefixes != 0 && !intel_syntax) + as_warn (_("skipping prefixes on this instruction")); + + /* It's always a symbol; End frag & setup for relax. + Make sure there is enough room in this frag for the largest + instruction we may generate in md_convert_frag. This is 2 + bytes for the opcode and room for the prefix and largest + displacement. */ + frag_grow (prefix + 2 + size); + insn_size += prefix + 1; + /* Prefix and 1 opcode byte go in fr_fix. */ + p = frag_more (prefix + 1); + if (prefix) + *p++ = DATA_PREFIX_OPCODE; + *p = i.tm.base_opcode; + /* 1 possible extra opcode + displacement go in fr_var. */ + frag_var (rs_machine_dependent, + 1 + size, + 1, + ((unsigned char) *p == JUMP_PC_RELATIVE + ? ENCODE_RELAX_STATE (UNCOND_JUMP, SMALL) | code16 + : ENCODE_RELAX_STATE (COND_JUMP, SMALL) | code16), + i.op[0].disps->X_add_symbol, + i.op[0].disps->X_add_number, + p); } else if (i.tm.opcode_modifier & (JumpByte | JumpDword)) { - int size = (i.tm.opcode_modifier & JumpByte) ? 1 : 4; - unsigned long n = i.disps[0]->X_add_number; - unsigned char *q; + int size; - /* The jcx/jecx instruction might need a data size prefix. */ - for (q = i.prefix; q < i.prefix + i.prefixes; q++) + if (i.tm.opcode_modifier & JumpByte) { - if (*q == WORD_PREFIX_OPCODE) + /* This is a loop or jecxz type instruction. */ + size = 1; + if (i.prefix[ADDR_PREFIX]) { - /* The jcxz/jecxz instructions are marked with Data16 - and Data32, which means that they may get - WORD_PREFIX_OPCODE added to the list of prefixes. - However, the are correctly distinguished using - ADDR_PREFIX_OPCODE. Here we look for - WORD_PREFIX_OPCODE, and actually emit - ADDR_PREFIX_OPCODE. This is a hack, but, then, so - is the instruction itself. - - If an explicit suffix is used for the loop - instruction, that actually controls whether we use - cx vs. ecx. This is also controlled by - ADDR_PREFIX_OPCODE. - - I don't know if there is any valid case in which we - want to emit WORD_PREFIX_OPCODE, but I am keeping - the old behaviour for safety. */ - - if (IS_JUMP_ON_CX_ZERO (i.tm.base_opcode) - || IS_LOOP_ECX_TIMES (i.tm.base_opcode)) - FRAG_APPEND_1_CHAR (ADDR_PREFIX_OPCODE); - else - FRAG_APPEND_1_CHAR (WORD_PREFIX_OPCODE); - insn_size += 1; - break; + insn_size += 1; + FRAG_APPEND_1_CHAR (ADDR_PREFIX_OPCODE); + i.prefixes -= 1; } } - - if ((size == 4) && (flag_16bit_code)) + else { - FRAG_APPEND_1_CHAR (WORD_PREFIX_OPCODE); - insn_size += 1; + int code16; + + code16 = 0; + if (flag_16bit_code) + code16 = CODE16; + + if (i.prefix[DATA_PREFIX]) + { + insn_size += 1; + FRAG_APPEND_1_CHAR (DATA_PREFIX_OPCODE); + i.prefixes -= 1; + code16 ^= CODE16; + } + + size = 4; + if (code16) + size = 2; } + if (i.prefixes != 0 && !intel_syntax) + as_warn (_("skipping prefixes on this instruction")); + if (fits_in_unsigned_byte (i.tm.base_opcode)) { - FRAG_APPEND_1_CHAR (i.tm.base_opcode); - insn_size += 1; + insn_size += 1 + size; + p = frag_more (1 + size); } 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! */ + /* opcode can be at most two bytes */ + insn_size += 2 + size; + p = frag_more (2 + size); *p++ = (i.tm.base_opcode >> 8) & 0xff; - *p = i.tm.base_opcode & 0xff; } + *p++ = i.tm.base_opcode & 0xff; - p = frag_more (size); - insn_size += size; - 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); - } - } - else - { - fix_new_exp (frag_now, p - frag_now->fr_literal, size, - i.disps[0], 1, reloc (size, 1, i.disp_reloc[0])); - - } + fix_new_exp (frag_now, p - frag_now->fr_literal, size, + i.op[0].disps, 1, reloc (size, 1, i.disp_reloc[0])); } else if (i.tm.opcode_modifier & JumpInterSegment) { + int size; + int prefix; + int code16; + + code16 = 0; if (flag_16bit_code) + code16 = CODE16; + + prefix = 0; + if (i.prefix[DATA_PREFIX]) { - FRAG_APPEND_1_CHAR (WORD_PREFIX_OPCODE); - insn_size += 1; + prefix = 1; + i.prefixes -= 1; + code16 ^= CODE16; } - p = frag_more (1 + 2 + 4); /* 1 opcode; 2 segment; 4 offset */ - insn_size += 1 + 2 + 4; - p[0] = 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); + size = 4; + if (code16) + size = 2; + + if (i.prefixes != 0 && !intel_syntax) + 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.op[1].imms->X_op == O_constant) + { + long n = (long) i.op[1].imms->X_add_number; + + if (size == 2 + && !fits_in_unsigned_word (n) + && !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 + 1 - frag_now->fr_literal, 4, - i.imms[1], 0, BFD_RELOC_32); - 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); + fix_new_exp (frag_now, p - frag_now->fr_literal, size, + i.op[1].imms, 0, reloc (size, 0, i.disp_reloc[0])); + if (i.op[0].imms->X_op != O_constant) + as_bad (_("can't handle non absolute segment in `%s'"), + i.tm.name); + md_number_to_chars (p + size, (valueT) i.op[0].imms->X_add_number, 2); } else { /* Output normal instructions here. */ unsigned char *q; - /* Hack for fwait. It must come before any prefixes, as it - really is an instruction rather than a prefix. */ - if ((i.tm.opcode_modifier & FWait) != 0) - { - p = frag_more (1); - insn_size += 1; - md_number_to_chars (p, (valueT) FWAIT_OPCODE, 1); - } - /* The prefix bytes. */ - for (q = i.prefix; q < i.prefix + i.prefixes; q++) + 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 (i.tm.base_opcode)) { - FRAG_APPEND_1_CHAR (i.tm.base_opcode); insn_size += 1; + FRAG_APPEND_1_CHAR (i.tm.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++ = (i.tm.base_opcode >> 8) & 0xff; *p = i.tm.base_opcode & 0xff; @@ -1873,41 +2313,44 @@ md_assemble (line) { /* opcode is either 3 or 4 bytes */ if (i.tm.base_opcode & 0xff000000) { - p = frag_more (4); insn_size += 4; + p = frag_more (4); *p++ = (i.tm.base_opcode >> 24) & 0xff; } else { - p = frag_more (3); insn_size += 3; + p = frag_more (3); } *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). */ + /* 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); } } @@ -1918,43 +2361,45 @@ md_assemble (line) for (n = 0; n < i.operands; n++) { - if (i.disps[n]) + if (i.types[n] & Disp) { - if (i.disps[n]->X_op == O_constant) + if (i.op[n].disps->X_op == O_constant) { - if (i.types[n] & (Disp8 | Abs8)) - { - p = frag_more (1); - insn_size += 1; - md_number_to_chars (p, - (valueT) i.disps[n]->X_add_number, - 1); - } - else if (i.types[n] & (Disp16 | Abs16)) + int size = 4; + long val = (long) i.op[n].disps->X_add_number; + + if (i.types[n] & (Disp8 | Disp16)) { - p = frag_more (2); - insn_size += 2; - md_number_to_chars (p, - (valueT) i.disps[n]->X_add_number, - 2); - } - else - { /* Disp32|Abs32 */ - p = frag_more (4); - insn_size += 4; - md_number_to_chars (p, - (valueT) i.disps[n]->X_add_number, - 4); + long mask; + + size = 2; + mask = ~ (long) 0xffff; + if (i.types[n] & Disp8) + { + size = 1; + mask = ~ (long) 0xff; + } + + if ((val & mask) != 0 && (val & mask) != mask) + as_warn (_("%ld shortened to %ld"), + val, val & ~mask); } + insn_size += size; + p = frag_more (size); + md_number_to_chars (p, (valueT) val, size); } else - { /* not absolute_section */ - /* need a 32-bit fixup (don't support 8bit non-absolute disps) */ - p = frag_more (4); - insn_size += 4; - fix_new_exp (frag_now, p - frag_now->fr_literal, 4, - i.disps[n], 0, - TC_RELOC(i.disp_reloc[n], BFD_RELOC_32)); + { + int size = 4; + + if (i.types[n] & Disp16) + size = 2; + + insn_size += size; + p = frag_more (size); + fix_new_exp (frag_now, p - frag_now->fr_literal, size, + i.op[n].disps, 0, + reloc (size, 0, i.disp_reloc[n])); } } } @@ -1967,68 +2412,68 @@ md_assemble (line) for (n = 0; n < i.operands; n++) { - if (i.imms[n]) + if (i.types[n] & Imm) { - if (i.imms[n]->X_op == O_constant) + if (i.op[n].imms->X_op == O_constant) { - if (i.types[n] & (Imm8 | Imm8S)) - { - p = frag_more (1); - insn_size += 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; - md_number_to_chars (p, - (valueT) i.imms[n]->X_add_number, - 2); - } - else + int size = 4; + long val = (long) i.op[n].imms->X_add_number; + + if (i.types[n] & (Imm8 | Imm8S | Imm16)) { - p = frag_more (4); - insn_size += 4; - md_number_to_chars (p, - (valueT) i.imms[n]->X_add_number, - 4); + long mask; + + size = 2; + mask = ~ (long) 0xffff; + if (i.types[n] & (Imm8 | Imm8S)) + { + size = 1; + mask = ~ (long) 0xff; + } + if ((val & mask) != 0 && (val & mask) != mask) + as_warn (_("%ld shortened to %ld"), + val, val & ~mask); } + insn_size += size; + p = frag_more (size); + md_number_to_chars (p, (valueT) val, size); } else { /* not absolute_section */ /* Need a 32-bit fixup (don't support 8bit - non-absolute ims). Try to support other + non-absolute imms). Try to support other sizes ... */ - int r_type; - int size; - int pcrel = 0; +#ifdef BFD_ASSEMBLER + enum bfd_reloc_code_real reloc_type; +#else + int reloc_type; +#endif + int size = 4; - if (i.types[n] & (Imm8 | Imm8S)) - size = 1; - else if (i.types[n] & Imm16) + if (i.types[n] & Imm16) size = 2; - else - size = 4; - r_type = reloc (size, 0, i.disp_reloc[0]); - p = frag_more (size); + else if (i.types[n] & (Imm8 | Imm8S)) + size = 1; + insn_size += size; + p = frag_more (size); + reloc_type = reloc (size, 0, i.disp_reloc[0]); #ifdef BFD_ASSEMBLER - if (r_type == BFD_RELOC_32 + if (reloc_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 + && GOT_symbol == i.op[n].imms->X_add_symbol + && (i.op[n].imms->X_op == O_symbol + || (i.op[n].imms->X_op == O_add + && ((symbol_get_value_expression + (i.op[n].imms->X_op_symbol)->X_op) == O_subtract)))) { - r_type = BFD_RELOC_386_GOTPC; - i.imms[n]->X_add_number += 3; + reloc_type = BFD_RELOC_386_GOTPC; + i.op[n].imms->X_add_number += 3; } #endif fix_new_exp (frag_now, p - frag_now->fr_literal, size, - i.imms[n], pcrel, r_type); + i.op[n].imms, 0, reloc_type); } } } @@ -2044,6 +2489,882 @@ md_assemble (line) } } +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.op[this_operand].imms = 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 = 0; + int 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); + + SKIP_WHITESPACE (); + 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 || exp->X_op == O_big) + { + /* 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; + } + + if (exp->X_op == O_constant) + { + i.types[this_operand] |= Imm32; /* Size it properly later. */ + } +#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT)) + else if ( +#ifdef BFD_ASSEMBLER + OUTPUT_FLAVOR == bfd_target_aout_flavour && +#endif + 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 + ) + { +#ifdef BFD_ASSEMBLER + as_bad (_("unimplemented segment %s in operand"), exp_seg->name); +#else + as_bad (_("unimplemented segment type %d in operand"), exp_seg); +#endif + 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; + + if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0)) + bigdisp = Disp16; + i.types[this_operand] |= bigdisp; + + exp = &disp_expressions[i.disp_operands]; + i.op[this_operand].disps = exp; + 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 = 0; + int 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 (S_GET_SEGMENT (exp->X_add_symbol)); + 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 + + SKIP_WHITESPACE (); + 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_absent || exp->X_op == O_big) + { + /* missing or bad expr becomes absolute 0 */ + as_bad (_("missing or invalid displacement expression `%s' taken as 0"), + disp_start); + exp->X_op = O_constant; + exp->X_add_number = 0; + exp->X_add_symbol = (symbolS *) 0; + exp->X_op_symbol = (symbolS *) 0; + } + + if (exp->X_op == O_constant) + { + if (i.types[this_operand] & Disp16) + { + /* We know this operand is at most 16 bits, so convert to a + signed 16 bit number before trying to see whether it will + fit in an even smaller size. */ + exp->X_add_number = + (((exp->X_add_number & 0xffff) ^ 0x8000) - 0x8000); + } + if (fits_in_signed_byte (exp->X_add_number)) + i.types[this_operand] |= Disp8; + } +#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT)) + else if ( +#ifdef BFD_ASSEMBLER + OUTPUT_FLAVOR == bfd_target_aout_flavour && +#endif + exp_seg != text_section + && exp_seg != data_section + && exp_seg != bss_section + && exp_seg != undefined_section) + { +#ifdef BFD_ASSEMBLER + as_bad (_("unimplemented segment %s in operand"), exp_seg->name); +#else + as_bad (_("unimplemented segment type %d in operand"), exp_seg); +#endif + 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)) + { + if (got_a_float == 2) /* "fi..." */ + i.suffix = SHORT_MNEM_SUFFIX; + else + i.suffix = WORD_MNEM_SUFFIX; + *op_string += 8; + return WORD_PTR; + } + + else if (!strncasecmp (*op_string, "DWORD PTR", 9)) + { + if (got_a_float == 1) /* "f..." */ + i.suffix = SHORT_MNEM_SUFFIX; + else + i.suffix = LONG_MNEM_SUFFIX; + *op_string += 9; + return DWORD_PTR; + } + + else if (!strncasecmp (*op_string, "QWORD PTR", 9)) + { + i.suffix = 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') + { + char *end_op; + 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) + && parse_register (temp_disp, &end_op)) ) + { + 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_index_check PARAMS((const char *)); + +/* Make sure the memory operand we've been dealt is valid. + Returns 1 on success, 0 on a failure. +*/ +static int +i386_index_check (operand_string) + const char *operand_string; +{ +#if INFER_ADDR_PREFIX + int fudged = 0; + + tryprefix: +#endif + if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0) + /* 16 bit mode checks */ + ? ((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)))) + /* 32 bit mode checks */ + : ((i.base_reg + && (i.base_reg->reg_type & Reg32) == 0) + || (i.index_reg + && ((i.index_reg->reg_type & (Reg32|BaseIndex)) + != (Reg32|BaseIndex))))) + { +#if INFER_ADDR_PREFIX + if (i.prefix[ADDR_PREFIX] == 0 && stackop_size != '\0') + { + i.prefix[ADDR_PREFIX] = ADDR_PREFIX_OPCODE; + i.prefixes += 1; + /* Change the size of any displacement too. At most one of + Disp16 or Disp32 is set. + FIXME. There doesn't seem to be any real need for separate + Disp16 and Disp32 flags. The same goes for Imm16 and Imm32. + Removing them would probably clean up the code quite a lot. + */ + if (i.types[this_operand] & (Disp16|Disp32)) + i.types[this_operand] ^= (Disp16|Disp32); + fudged = 1; + goto tryprefix; + } + if (fudged) + as_bad (_("`%s' is not a valid base/index expression"), + operand_string); + else +#endif + as_bad (_("`%s' is not a valid %s bit base/index expression"), + operand_string, + flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0) ? "16" : "32"); + return 0; + } + return 1; +} + +static int i386_intel_memory_operand PARAMS ((char *)); + +static int +i386_intel_memory_operand (operand_string) + char *operand_string; +{ + char *op_string = operand_string; + char *end_of_operand_string; + + 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; + } + + /* First check for a segment override. */ + if (*op_string != '[') + { + char *end_seg; + + end_seg = strchr (op_string, ':'); + if (end_seg) + { + if (!i386_parse_seg (op_string)) + return 0; + op_string = end_seg + 1; + } + } + + /* Look for displacement preceding open bracket */ + if (*op_string != '[') + { + char *temp_string; + + if (i.disp_operands) + return 0; + + temp_string = build_displacement_string (true, op_string); + + if (!i386_displacement (temp_string, temp_string + strlen (temp_string))) + { + free (temp_string); + return 0; + } + free (temp_string); + + 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 != ']') + { + const reg_entry *temp_reg; + char *end_op; + char *temp_string; + + while (*end_of_operand_string != '+' + && *end_of_operand_string != '-' + && *end_of_operand_string != '*' + && *end_of_operand_string != ']') + end_of_operand_string++; + + temp_string = op_string; + if (*temp_string == '+') + { + ++temp_string; + if (is_space_char (*temp_string)) + ++temp_string; + } + + if ((*temp_string == REGISTER_PREFIX || allow_naked_reg) + && (temp_reg = parse_register (temp_string, &end_op)) != NULL) + { + if (i.base_reg == NULL) + i.base_reg = temp_reg; + else + i.index_reg = temp_reg; + + i.types[this_operand] |= BaseIndex; + } + else if (*temp_string == REGISTER_PREFIX) + { + as_bad (_("bad register name `%s'"), temp_string); + return 0; + } + else if (is_digit_char (*op_string) + || *op_string == '+' || *op_string == '-') + { + char *temp_str; + + if (i.disp_operands != 0) + return 0; + + temp_string = build_displacement_string (false, op_string); + + temp_str = temp_string; + if (*temp_str == '+') + ++temp_str; + + if (!i386_displacement (temp_str, temp_str + strlen (temp_str))) + { + free (temp_string); + return 0; + } + free (temp_string); + + ++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; + } + } + + if (i386_index_check (operand_string) == 0) + return 0; + + i.mem_operands++; + return 1; +} + +static int +i386_intel_operand (operand_string, got_a_float) + char *operand_string; + int got_a_float; +{ + const reg_entry * r; + char *end_op; + 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 (!i386_intel_memory_operand (op_string)) + return 0; + 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) + && (r = parse_register (op_string, &end_op)) != NULL) + { + /* 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.op[this_operand].regs = r; + i.reg_operands++; + } + else if (*op_string == REGISTER_PREFIX) + { + as_bad (_("bad register name `%s'"), op_string); + return 0; + } + else if (!i386_intel_memory_operand (op_string)) + return 0; + + break; + } /* end switch */ + + return 1; +} + /* Parse OPERAND_STRING into the i386_insn structure I. Returns non-zero on error. */ @@ -2051,189 +3372,142 @@ static int i386_operand (operand_string) char *operand_string; { - register char *op_string = operand_string; - - /* Address of '\0' at end of operand_string. */ - char *end_of_operand_string = operand_string + strlen (operand_string); + const reg_entry *r; + char *end_op; + char *op_string = 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) + && (r = parse_register (op_string, &end_op)) != NULL) { - 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] == ':') + /* 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 = (seg_entry *) & es; + 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; + + 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; - i.regs[this_operand] = r; + if (*op_string) + { + as_bad (_("junk `%s' after register"), op_string); + return 0; + } + i.types[this_operand] |= r->reg_type & ~BaseIndex; + i.op[this_operand].regs = r; i.reg_operands++; } + else if (*op_string == REGISTER_PREFIX) + { + as_bad (_("bad register name `%s'"), op_string); + return 0; + } 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); - if (*input_line_pointer != '\0') - as_bad ("warning: unrecognized characters `%s' in 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", - 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 - ) + ++op_string; + if (i.types[this_operand] & JumpAbsolute) { - seg_unimplemented: - as_bad ("Unimplemented segment type %d in parse_operand", exp_seg); + as_bad (_("immediate operand illegal with absolute jump")); 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; - } + 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. */ - register char *base_string; - unsigned int found_base_index_form; + char *base_string; + + /* 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) + if ((i.mem_operands == 1 + && (current_templates->start->opcode_modifier & IsString) == 0) + || i.mem_operands == 2) { - as_bad ("more than 1 memory reference in instruction"); + as_bad (_("too many memory references for `%s'"), + current_templates->start->name); return 0; } - i.mem_operands++; - - /* Determine type of memory operand from opcode_suffix; - no opcode suffix implies general memory references. */ - switch (i.suffix) - { - 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; - } /* 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; - found_base_index_form = 0; + after the '('. */ + base_string = op_string + strlen (op_string); + + --base_string; + if (is_space_char (*base_string)) + --base_string; + + /* If we only have a displacement, set-up for it to be parsed later. */ + displacement_string_start = op_string; + displacement_string_end = base_string + 1; + if (*base_string == ')') { + char *temp_string; 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. */ @@ -2246,283 +3520,129 @@ i386_operand (operand_string) parens_balanced--; } while (parens_balanced); - base_string++; /* Skip past '('. */ - if (*base_string == REGISTER_PREFIX || *base_string == ',') - found_base_index_form = 1; - } - /* If we can't parse a base index register expression, we've found - a pure displacement expression. We set up displacement_string_start - and displacement_string_end for the code below. */ - if (!found_base_index_form) - { - displacement_string_start = op_string; - displacement_string_end = end_of_operand_string; - } - else - { - char *base_reg_name, *index_reg_name, *num_string; - int num; + temp_string = base_string; - i.types[this_operand] |= BaseIndex; + /* Skip past '(' and whitespace. */ + ++base_string; + if (is_space_char (*base_string)) + ++base_string; - /* If there is a displacement set-up for it to be parsed later. */ - if (base_string != op_string + 1) + if (*base_string == ',' + || ((*base_string == REGISTER_PREFIX || allow_naked_reg) + && (i.base_reg = parse_register (base_string, &end_op)) != NULL)) { - displacement_string_start = op_string; - displacement_string_end = base_string - 1; - } + displacement_string_end = temp_string; - /* 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); - } + i.types[this_operand] |= BaseIndex; - /* 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; - } - - /* 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))) + if (i.base_reg) { - as_bad ("bad index register name ('%s')", index_reg_name); - return 0; + base_string = end_op; + if (is_space_char (*base_string)) + ++base_string; } - RESTORE_END_STRING (base_string); - } - /* Check for scale factor. */ - if (*base_string == ',' && isdigit (*(base_string + 1))) - { - num_string = ++base_string; - while (is_digit_char (*base_string)) - base_string++; - if (base_string == num_string) + /* There may be an index reg or scale factor here. */ + if (*base_string == ',') { - as_bad ("can't find a scale factor after ','"); - return 0; + ++base_string; + if (is_space_char (*base_string)) + ++base_string; + + if ((*base_string == REGISTER_PREFIX || allow_naked_reg) + && (i.index_reg = parse_register (base_string, &end_op)) != NULL) + { + 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; + } + } + else if (*base_string == REGISTER_PREFIX) + { + as_bad (_("bad register name `%s'"), base_string); + return 0; + } + + /* Check for scale factor. */ + if (isdigit ((unsigned char) *base_string)) + { + 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) + { + as_bad (_("expecting index register or scale factor after `,'; got '%c'"), + *base_string); + return 0; + } } - END_STRING_AND_SAVE (base_string); - /* We've got a scale factor. */ - if (!sscanf (num_string, "%d", &num)) + else if (*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); + as_bad (_("expecting `,' or `)' after base register in `%s'"), + operand_string); return 0; } } - else + else if (*base_string == REGISTER_PREFIX) { - if (!i.index_reg && *base_string == ',') - { - as_bad ("expecting index register or scale factor after ','; got '%c'", - *(base_string + 1)); - return 0; - } + as_bad (_("bad register name `%s'"), 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) + /* 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) { - 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_reloc[this_operand] = NO_RELOC; - i.disp_operands++; - save_input_line_pointer = input_line_pointer; - input_line_pointer = displacement_string_start; - END_STRING_AND_SAVE (displacement_string_end); - -#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; - - if (GOT_symbol == NULL) - GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME); - - tmpbuf = (char *) alloca ((cp - input_line_pointer) + 20); - - if (strncmp (cp + 1, "PLT", 3) == 0) - { - i.disp_reloc[this_operand] = BFD_RELOC_386_PLT32; - *cp = '\0'; - strcpy (tmpbuf, input_line_pointer); - strcat (tmpbuf, cp + 1 + 3); - *cp = '@'; - } - else if (strncmp (cp + 1, "GOTOFF", 6) == 0) - { - i.disp_reloc[this_operand] = BFD_RELOC_386_GOTOFF; - *cp = '\0'; - strcpy (tmpbuf, input_line_pointer); - strcat (tmpbuf, cp + 1 + 6); - *cp = '@'; - } - else if (strncmp (cp + 1, "GOT", 3) == 0) - { - i.disp_reloc[this_operand] = BFD_RELOC_386_GOT32; - *cp = '\0'; - strcpy (tmpbuf, input_line_pointer); - strcat (tmpbuf, cp + 1 + 3); - *cp = '@'; - } - else - as_bad ("Bad reloc specifier '%s' in expression", cp + 1); - - 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); - RESTORE_END_STRING (displacement_string_end); - input_line_pointer = save_input_line_pointer; - if (exp->X_op == O_absent) - { - /* missing expr becomes absolute 0 */ - as_bad ("missing or invalid displacement '%s' taken as 0", - 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 - } + if (!i386_displacement (displacement_string_start, + displacement_string_end)) + 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)) + /* 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) { - as_bad ("register size mismatch in (base,index,scale) expression"); - 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))) - { - 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)) - { - as_bad ("base/index register must be 32 bit register"); - return 0; - } - if (i.index_reg && i.index_reg == esp) - { - as_bad ("%s may not be used as an index register", esp->reg_name); - return 0; - } + + if (i386_index_check (operand_string) == 0) + 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; } @@ -2530,7 +3650,7 @@ i386_operand (operand_string) } /* - * md_estimate_size_before_relax() + * md_estimate_size_before_relax() * * Called just before relax(). * Any symbol that is now undefined will not become defined. @@ -2551,44 +3671,53 @@ md_estimate_size_before_relax (fragP, segment) 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; +#ifdef BFD_ASSEMBLER + enum bfd_reloc_code_real reloc_type; +#else + int reloc_type; +#endif + + if (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) + reloc_type = BFD_RELOC_386_PLT32; + else if (code16) + reloc_type = BFD_RELOC_16_PCREL; + else + reloc_type = 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_symbol, + fragP->fr_fix += size; + fix_new (fragP, old_fr_fix, size, + fragP->fr_symbol, 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 : BFD_RELOC_32_PCREL); + reloc_type); break; default: - /* This changes the byte-displacement jump 0x7N --> - the dword-displacement jump 0x0f8N */ + /* This changes the byte-displacement jump 0x7N + to the dword-displacement jump 0x0f,0x8N. */ 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, + opcode[0] = TWO_BYTE_OPCODE_ESCAPE; + 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, - (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 : BFD_RELOC_32_PCREL); + fragP->fr_offset, 1, + reloc_type); break; } frag_wane (fragP); @@ -2610,14 +3739,14 @@ md_estimate_size_before_relax (fragP, segment) #ifndef BFD_ASSEMBLER void md_convert_frag (headers, sec, fragP) - object_headers *headers; - segT sec; + object_headers *headers ATTRIBUTE_UNUSED; + segT sec ATTRIBUTE_UNUSED; register fragS *fragP; #else void md_convert_frag (abfd, sec, fragP) - bfd *abfd; - segT sec; + bfd *abfd ATTRIBUTE_UNUSED; + segT sec ATTRIBUTE_UNUSED; register fragS *fragP; #endif { @@ -2633,7 +3762,7 @@ md_convert_frag (abfd, sec, fragP) /* Address we want to reach in file space. */ target_address = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset; #ifdef BFD_ASSEMBLER /* not needed otherwise? */ - target_address += fragP->fr_symbol->sy_frag->fr_address; + target_address += symbol_get_frag (fragP->fr_symbol)->fr_address; #endif /* Address opcode resides at in file space. */ @@ -2644,38 +3773,38 @@ md_convert_frag (abfd, sec, fragP) 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; @@ -2699,8 +3828,8 @@ void md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol) char *ptr; addressT from_addr, to_addr; - fragS *frag; - symbolS *to_symbol; + fragS *frag ATTRIBUTE_UNUSED; + symbolS *to_symbol ATTRIBUTE_UNUSED; { long offset; @@ -2745,52 +3874,109 @@ 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. */ + segT seg ATTRIBUTE_UNUSED; /* 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) - /* - * This is a hack. There should be a better way to - * handle this. - */ - if (fixP->fx_r_type == BFD_RELOC_32_PCREL && fixP->fx_addsy) + if (fixP->fx_pcrel) + { + switch (fixP->fx_r_type) + { + default: + break; + + case BFD_RELOC_32: + fixP->fx_r_type = BFD_RELOC_32_PCREL; + break; + case BFD_RELOC_16: + fixP->fx_r_type = BFD_RELOC_16_PCREL; + break; + case BFD_RELOC_8: + fixP->fx_r_type = BFD_RELOC_8_PCREL; + break; + } + } + + /* This is a hack. There should be a better way to handle this. + This covers for the fact that bfd_install_relocation will + subtract the current location (for partial_inplace, PC relative + relocations); see more below. */ + if ((fixP->fx_r_type == BFD_RELOC_32_PCREL + || fixP->fx_r_type == BFD_RELOC_16_PCREL + || fixP->fx_r_type == BFD_RELOC_8_PCREL) + && fixP->fx_addsy) { #ifndef OBJ_AOUT if (OUTPUT_FLAVOR == bfd_target_elf_flavour - || OUTPUT_FLAVOR == bfd_target_coff_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)) + if (OUTPUT_FLAVOR == bfd_target_elf_flavour) { - /* Yes, we add the values in twice. This is because - bfd_perform_relocation subtracts them out again. I think - bfd_perform_relocation is broken, but I don't dare change - it. FIXME. */ - value += fixP->fx_where + fixP->fx_frag->fr_address; + segT fseg = S_GET_SEGMENT (fixP->fx_addsy); + + if ((fseg == seg + || (symbol_section_p (fixP->fx_addsy) + && fseg != absolute_section)) + && ! 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 + bfd_perform_relocation is broken, but I don't dare change + 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. */ + address offset for a PC relative symbol. */ if (S_GET_SEGMENT (fixP->fx_addsy) != seg) value += md_pcrel_from (fixP); + else if (S_IS_EXTERNAL (fixP->fx_addsy) + || S_IS_WEAK (fixP->fx_addsy)) + { + /* We are generating an external relocation for this defined + symbol. We add the address, because + bfd_install_relocation will subtract it. VALUE already + holds the symbol value, because fixup_segment added it + in. We subtract it out, and then we subtract it out + again because bfd_install_relocation will add it in + again. */ + value += md_pcrel_from (fixP); + value -= 2 * S_GET_VALUE (fixP->fx_addsy); + } #endif } +#ifdef TE_PE + else if (fixP->fx_addsy != NULL + && S_IS_DEFINED (fixP->fx_addsy) + && (S_IS_EXTERNAL (fixP->fx_addsy) + || S_IS_WEAK (fixP->fx_addsy))) + { + /* We are generating an external relocation for this defined + symbol. VALUE already holds the symbol value, and + bfd_install_relocation will add it in again. We don't want + either addition. */ + value -= 2 * S_GET_VALUE (fixP->fx_addsy); + } +#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) { + 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. */ @@ -2798,19 +3984,19 @@ md_apply_fix3 (fixP, valp, seg) break; case BFD_RELOC_386_GOTPC: /* - * This is tough to explain. We end up with this one if we have + * 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 + * relocation for this. The actual sequence of instructions often look * something like: - * - * call .L66 + * + * call .L66 * .L66: - * popl %ebx - * addl $_GLOBAL_OFFSET_TABLE_+[.-.L66],%ebx - * - * The call and pop essentially return the absolute address of + * 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 @@ -2819,17 +4005,17 @@ md_apply_fix3 (fixP, valp, seg) * 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 + * + * 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] - * + * + * .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 + * 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 @@ -2838,17 +4024,22 @@ md_apply_fix3 (fixP, valp, seg) value -= 1; break; case BFD_RELOC_386_GOT32: - value = 0; /* Fully resolved at runtime. No addend. */ + 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 +#endif /* defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) */ + *valp = value; +#endif /* defined (BFD_ASSEMBLER) && !defined (TE_Mach) */ md_number_to_chars (p, value, fixP->fx_size); return 1; @@ -2879,7 +4070,7 @@ md_chars_to_number (con, nbytes) is stored in *sizeP . An error message is returned, or NULL on OK. */ char * md_atof (type, litP, sizeP) - char type; + int type; char *litP; int *sizeP; { @@ -2907,7 +4098,7 @@ md_atof (type, litP, sizeP) 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) @@ -2926,9 +4117,11 @@ md_atof (type, litP, sizeP) 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); @@ -2937,40 +4130,82 @@ output_invalid (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 (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 *s = reg_string; + char *p; + 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; + return (const reg_entry *) NULL; + s++; + } + + *end_op = s; + + r = (const reg_entry *) hash_find (reg_hash, reg_name_given); + + /* Handle floating point regs, allowing spaces in the (i) part. */ + if (r == i386_regtab /* %st is first entry of table */) + { + if (is_space_char (*s)) + ++s; + if (*s == '(') + { + ++s; + if (is_space_char (*s)) + ++s; + if (*s >= '0' && *s <= '7') + { + r = &i386_float_regtab[*s - '0']; + ++s; + if (is_space_char (*s)) + ++s; + if (*s == ')') + { + *end_op = s + 1; + return r; + } + } + /* We have "%st(" then garbage */ + return (const reg_entry *) NULL; + } } - *p = '\0'; - return (reg_entry *) hash_find (reg_hash, reg_name_given); + + return r; } -#ifdef OBJ_ELF -CONST char *md_shortopts = "kmVQ:"; +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) +CONST char *md_shortopts = "kmVQ:sq"; #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) int c; - char *arg; + char *arg ATTRIBUTE_UNUSED; { switch (c) { @@ -2992,6 +4227,16 @@ md_parse_option (c, arg) should be emitted or not. FIXME: Not implemented. */ case 'Q': break; + + case 's': + /* -s: On i386 Solaris, this tells the native assembler to use + .stab instead of .stab.excl. We always use .stab anyhow. */ + break; + + case 'q': + /* -q: On i386 Solaris, this tells the native assembler does + fewer checks. */ + break; #endif default: @@ -3004,13 +4249,22 @@ void md_show_usage (stream) FILE *stream; { - fprintf (stream, "\ --m do long jump\n"); + fprintf (stream, _("\ + -m do long jump\n")); +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + fprintf (stream, _("\ + -V print assembler version number\n\ + -k ignored\n\ + -Qy, -Qn ignored\n\ + -q ignored\n\ + -s ignored\n")); +#endif } #ifdef BFD_ASSEMBLER -#ifdef OBJ_MAYBE_ELF -#ifdef OBJ_MAYBE_COFF +#if ((defined (OBJ_MAYBE_ELF) && defined (OBJ_MAYBE_COFF)) \ + || (defined (OBJ_MAYBE_ELF) && defined (OBJ_MAYBE_AOUT)) \ + || (defined (OBJ_MAYBE_COFF) && defined (OBJ_MAYBE_AOUT))) /* Pick the target format to use. */ @@ -3019,66 +4273,78 @@ i386_target_format () { switch (OUTPUT_FLAVOR) { +#ifdef OBJ_MAYBE_AOUT + case bfd_target_aout_flavour: + return AOUT_TARGET_FORMAT; +#endif +#ifdef OBJ_MAYBE_COFF case bfd_target_coff_flavour: return "coff-i386"; +#endif +#ifdef OBJ_MAYBE_ELF case bfd_target_elf_flavour: return "elf32-i386"; +#endif default: abort (); return NULL; } } -#endif /* OBJ_MAYBE_COFF */ -#endif /* OBJ_MAYBE_ELF */ +#endif /* OBJ_MAYBE_ more than one */ #endif /* BFD_ASSEMBLER */ -/* 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; - } + if (name[0] == GLOBAL_OFFSET_TABLE_NAME[0] + && name[1] == GLOBAL_OFFSET_TABLE_NAME[1] + && name[2] == GLOBAL_OFFSET_TABLE_NAME[2] + && 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; } /* Round up a section size to the appropriate boundary. */ valueT md_section_align (segment, size) - segT segment; + segT segment ATTRIBUTE_UNUSED; valueT size; { -#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)); +#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT)) + if (OUTPUT_FLAVOR == bfd_target_aout_flavour) + { + /* 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; @@ -3090,7 +4356,7 @@ md_pcrel_from (fixP) static void s_bss (ignore) - int ignore; + int ignore ATTRIBUTE_UNUSED; { register int temp; @@ -3115,42 +4381,55 @@ i386_validate_fix (fixp) } } -#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; + asection *section ATTRIBUTE_UNUSED; fixS *fixp; { arelent *rel; bfd_reloc_code_real_type code; - switch(fixp->fx_r_type) + switch (fixp->fx_r_type) { 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: - switch (F (fixp->fx_size, fixp->fx_pcrel)) + if (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: - as_bad ("Can not do %d byte %srelocation", fixp->fx_size, - fixp->fx_pcrel ? "pc-relative " : ""); + switch (fixp->fx_size) + { + default: + as_bad (_("can not do %d byte pc-relative relocation"), + fixp->fx_size); + code = BFD_RELOC_32_PCREL; + break; + case 1: code = BFD_RELOC_8_PCREL; break; + case 2: code = BFD_RELOC_16_PCREL; break; + case 4: code = BFD_RELOC_32_PCREL; break; + } } + else + { + switch (fixp->fx_size) + { + default: + as_bad (_("can not do %d byte relocation"), fixp->fx_size); + code = BFD_RELOC_32; + break; + case 1: code = BFD_RELOC_8; break; + case 2: code = BFD_RELOC_16; break; + case 4: code = BFD_RELOC_32; break; + } + } + break; } -#undef MAP -#undef F if (code == BFD_RELOC_32 && GOT_symbol @@ -3158,8 +4437,15 @@ tc_gen_reloc (section, fixp) code = BFD_RELOC_386_GOTPC; rel = (arelent *) xmalloc (sizeof (arelent)); - rel->sym_ptr_ptr = &fixp->fx_addsy->bsym; + rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + 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 @@ -3169,7 +4455,7 @@ tc_gen_reloc (section, fixp) if (rel->howto == NULL) { as_bad_where (fixp->fx_file, fixp->fx_line, - "Cannot represent relocation type %s", + _("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); @@ -3247,6 +4533,6 @@ tc_coff_sizemachdep (frag) #endif /* I386COFF */ -#endif /* BFD_ASSEMBLER? */ +#endif /* ! BFD_ASSEMBLER */ /* end of tc-i386.c */ |