diff options
Diffstat (limited to 'gas')
-rw-r--r-- | gas/ChangeLog | 11 | ||||
-rw-r--r-- | gas/config/tc-ppc.c | 153 | ||||
-rw-r--r-- | gas/messages.c | 19 |
3 files changed, 132 insertions, 51 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog index b6440a7..3bcaf48 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,14 @@ +2007-04-20 Alan Modra <amodra@bigpond.net.au> + + * messages.c (as_internal_value_out_of_range): Extend to report + errors for values with invalid low bits set. + * config/tc-ppc.c (ppc_setup_opcodes): Check powerpc_operands bitm + fields. Check that operands and opcode fields are disjoint. + (ppc_insert_operand): Check operands using mask rather than bit + count. Check low bits too. Handle PPC_OPERAND_PLUS1. Adjust + insertion code. + (md_apply_fix): Adjust for struct powerpc_operand change. + 2007-04-19 Paul Brook <paul@codesourcery.com> * config/tc-arm.c (md_assemble): Only allow 16-bit instructions on diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c index bcd32e0..be745ab 100644 --- a/gas/config/tc-ppc.c +++ b/gas/config/tc-ppc.c @@ -1,6 +1,6 @@ /* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000) Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, - 2004, 2005, 2006 Free Software Foundation, Inc. + 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Written by Ian Lance Taylor, Cygnus Support. This file is part of GAS, the GNU Assembler. @@ -1248,7 +1248,8 @@ ppc_setup_opcodes (void) const struct powerpc_opcode *op_end; const struct powerpc_macro *macro; const struct powerpc_macro *macro_end; - bfd_boolean dup_insn = FALSE; + unsigned int i; + bfd_boolean bad_insn = FALSE; if (ppc_hash != NULL) hash_die (ppc_hash); @@ -1258,10 +1259,60 @@ ppc_setup_opcodes (void) /* Insert the opcodes into a hash table. */ ppc_hash = hash_new (); + /* Check operand masks. Code here and in the disassembler assumes + all the 1's in the mask are contiguous. */ + for (i = 0; i < num_powerpc_operands; ++i) + { + unsigned long mask = powerpc_operands[i].bitm; + unsigned long right_bit; + + right_bit = mask & -mask; + mask += right_bit; + right_bit = mask & -mask; + if (mask != right_bit) + { + as_bad (_("powerpc_operands[%d].bitm invalid"), i); + bad_insn = TRUE; + } + } + op_end = powerpc_opcodes + powerpc_num_opcodes; for (op = powerpc_opcodes; op < op_end; op++) { - know ((op->opcode & op->mask) == op->opcode); + const unsigned char *o; + unsigned long omask = op->mask; + + /* The mask had better not trim off opcode bits. */ + if ((op->opcode & omask) != op->opcode) + { + as_bad (_("mask trims opcode bits for %s"), + op->name); + bad_insn = TRUE; + } + + /* The operands must not overlap the opcode or each other. */ + for (o = op->operands; *o; ++o) + if (*o >= num_powerpc_operands) + { + as_bad (_("operand index error for %s"), + op->name); + bad_insn = TRUE; + } + else + { + const struct powerpc_operand *operand = &powerpc_operands[*o]; + if (operand->shift >= 0) + { + unsigned long mask = operand->bitm << operand->shift; + if (omask & mask) + { + as_bad (_("operand %d overlap in %s"), + (int) (o - op->operands), op->name); + bad_insn = TRUE; + } + omask |= mask; + } + } if ((op->flags & ppc_cpu & ~(PPC_OPCODE_32 | PPC_OPCODE_64)) != 0 && ((op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64)) == 0 @@ -1296,9 +1347,9 @@ ppc_setup_opcodes (void) && (op->flags & PPC_OPCODE_POWER) != 0) continue; - as_bad (_("Internal assembler error for instruction %s"), + as_bad (_("duplicate instruction %s"), op->name); - dup_insn = TRUE; + bad_insn = TRUE; } } } @@ -1320,13 +1371,13 @@ ppc_setup_opcodes (void) retval = hash_insert (ppc_macro_hash, macro->name, (PTR) macro); if (retval != (const char *) NULL) { - as_bad (_("Internal assembler error for macro %s"), macro->name); - dup_insn = TRUE; + as_bad (_("duplicate macro %s"), macro->name); + bad_insn = TRUE; } } } - if (dup_insn) + if (bad_insn) abort (); } @@ -1447,49 +1498,52 @@ ppc_insert_operand (insn, operand, val, file, line) char *file; unsigned int line; { - if (operand->bits != 32) + long min, max, right; + offsetT test; + + max = operand->bitm; + right = max & -max; + min = 0; + + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) { - long min, max; - offsetT test; + if ((operand->flags & PPC_OPERAND_SIGNOPT) == 0) + max >>= 1; + min = ~(max | ((max & -max) - 1)) ; - if ((operand->flags & PPC_OPERAND_SIGNED) != 0) + if (!ppc_obj64) { - if ((operand->flags & PPC_OPERAND_SIGNOPT) != 0) - max = (1 << operand->bits) - 1; - else - max = (1 << (operand->bits - 1)) - 1; - min = - (1 << (operand->bits - 1)); - - if (!ppc_obj64) + /* Some people write 32 bit hex constants with the sign + extension done by hand. This shouldn't really be + valid, but, to permit this code to assemble on a 64 + bit host, we sign extend the 32 bit value. */ + if (val > 0 + && (val & (offsetT) 0x80000000) != 0 + && (val & (offsetT) 0xffffffff) == val) { - /* Some people write 32 bit hex constants with the sign - extension done by hand. This shouldn't really be - valid, but, to permit this code to assemble on a 64 - bit host, we sign extend the 32 bit value. */ - if (val > 0 - && (val & (offsetT) 0x80000000) != 0 - && (val & (offsetT) 0xffffffff) == val) - { - val -= 0x80000000; - val -= 0x80000000; - } + val -= 0x80000000; + val -= 0x80000000; } } - else - { - max = (1 << operand->bits) - 1; - min = 0; - } - - if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0) - test = - val; - else - test = val; + } - if (test < (offsetT) min || test > (offsetT) max) - as_bad_value_out_of_range (_("operand"), test, (offsetT) min, (offsetT) max, file, line); + if ((operand->flags & PPC_OPERAND_PLUS1) != 0) + { + max++; + min++; } + if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0) + test = - val; + else + test = val; + + if (test < (offsetT) min + || test > (offsetT) max + || (test & (right - 1)) != 0) + as_bad_value_out_of_range (_("operand"), + test, (offsetT) min, (offsetT) max, file, line); + if (operand->insert) { const char *errmsg; @@ -1500,8 +1554,7 @@ ppc_insert_operand (insn, operand, val, file, line) as_bad_where (file, line, errmsg); } else - insn |= (((long) val & ((1 << operand->bits) - 1)) - << operand->shift); + insn |= ((long) val & operand->bitm) << operand->shift; return insn; } @@ -5637,7 +5690,7 @@ md_apply_fix (fixP, valP, seg) csect. Other usages, such as `.long sym', generate relocs. This is the documented behaviour of non-TOC symbols. */ if ((operand->flags & PPC_OPERAND_PARENS) != 0 - && operand->bits == 16 + && (operand->bitm & 0xfff0) == 0xfff0 && operand->shift == 0 && (operand->insert == NULL || ppc_obj64) && fixP->fx_addsy != NULL @@ -5675,11 +5728,11 @@ md_apply_fix (fixP, valP, seg) We are only prepared to turn a few of the operands into relocs. */ if ((operand->flags & PPC_OPERAND_RELATIVE) != 0 - && operand->bits == 26 + && operand->bitm == 0x3fffffc && operand->shift == 0) fixP->fx_r_type = BFD_RELOC_PPC_B26; else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0 - && operand->bits == 16 + && operand->bitm == 0xfffc && operand->shift == 0) { fixP->fx_r_type = BFD_RELOC_PPC_B16; @@ -5690,11 +5743,11 @@ md_apply_fix (fixP, valP, seg) #endif } else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0 - && operand->bits == 26 + && operand->bitm == 0x3fffffc && operand->shift == 0) fixP->fx_r_type = BFD_RELOC_PPC_BA26; else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0 - && operand->bits == 16 + && operand->bitm == 0xfffc && operand->shift == 0) { fixP->fx_r_type = BFD_RELOC_PPC_BA16; @@ -5706,7 +5759,7 @@ md_apply_fix (fixP, valP, seg) } #if defined (OBJ_XCOFF) || defined (OBJ_ELF) else if ((operand->flags & PPC_OPERAND_PARENS) != 0 - && operand->bits == 16 + && (operand->bitm & 0xfff0) == 0xfff0 && operand->shift == 0) { if (ppc_is_toc_sym (fixP->fx_addsy)) diff --git a/gas/messages.c b/gas/messages.c index 21381b4..5736bac 100644 --- a/gas/messages.c +++ b/gas/messages.c @@ -1,6 +1,6 @@ /* messages.c - error reporter - Copyright 1987, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 2000, 2001, - 2003, 2004, 2005, 2006 + 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. @@ -456,6 +456,23 @@ as_internal_value_out_of_range (char * prefix, if (prefix == NULL) prefix = ""; + if (val >= min && val <= max) + { + addressT right = max & -max; + + if (max <= 1) + abort (); + + /* xgettext:c-format */ + err = _("%s out of domain (%d is not a multiple of %d"); + if (bad) + as_bad_where (file, line, err, + prefix, (int) val, (int) right); + else + as_warn_where (file, line, err, + prefix, (int) val, (int) right); + } + if ( val < HEX_MAX_THRESHOLD && min < HEX_MAX_THRESHOLD && max < HEX_MAX_THRESHOLD |