aboutsummaryrefslogtreecommitdiff
path: root/gas
diff options
context:
space:
mode:
authorStephane Carrez <stcarrez@nerim.fr>2002-12-01 11:02:10 +0000
committerStephane Carrez <stcarrez@nerim.fr>2002-12-01 11:02:10 +0000
commitc9e03e8be9f3e216d282f5ad02c0b8c8d15405ed (patch)
tree28be5cc9281b3d43fd3ba50666b23aea59291c24 /gas
parent2f9046641f748e9a27c97759570dcb983cbab4e9 (diff)
downloadgdb-c9e03e8be9f3e216d282f5ad02c0b8c8d15405ed.zip
gdb-c9e03e8be9f3e216d282f5ad02c0b8c8d15405ed.tar.gz
gdb-c9e03e8be9f3e216d282f5ad02c0b8c8d15405ed.tar.bz2
Fix Bug savannah/1825:
* config/tc-m68hc11.h (md_relax_frag): Define to support relaxations that are not pc-relative. (m68hc11_relax_frag): Declare. * config/tc-m68hc11.c (build_indexed_byte): Use a frag_var to handle the offsetable indexed addressing modes (n,r). (build_insn): Cleanup some locals. (m68hc11_relax_frag): New function imported from tc-cris.c to handle relaxation of difference between two symbols of same section. (md_convert_frag): For INDEXED_OFFSET relaxs, use the displacement only when this is a PC-relative operand and the offset is not absolute. (md_estimate_size_before_relax): Convert the INDEXED_OFFSET,UNDEF frag to INDEXED_OFFSET,STATE_BITS5 when the symbol is absolute; this will be handled by m68hc11_relax_frag.
Diffstat (limited to 'gas')
-rw-r--r--gas/ChangeLog18
-rw-r--r--gas/config/tc-m68hc11.c162
-rw-r--r--gas/config/tc-m68hc11.h9
3 files changed, 163 insertions, 26 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog
index f233231..76b52d5 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,5 +1,23 @@
2002-12-01 Stephane Carrez <stcarrez@nerim.fr>
+ Fix Bug savannah/1825:
+ * config/tc-m68hc11.h (md_relax_frag): Define to support relaxations
+ that are not pc-relative.
+ (m68hc11_relax_frag): Declare.
+
+ * config/tc-m68hc11.c (build_indexed_byte): Use a frag_var to handle
+ the offsetable indexed addressing modes (n,r).
+ (build_insn): Cleanup some locals.
+ (m68hc11_relax_frag): New function imported from tc-cris.c to handle
+ relaxation of difference between two symbols of same section.
+ (md_convert_frag): For INDEXED_OFFSET relaxs, use the displacement
+ only when this is a PC-relative operand and the offset is not absolute.
+ (md_estimate_size_before_relax): Convert the INDEXED_OFFSET,UNDEF frag
+ to INDEXED_OFFSET,STATE_BITS5 when the symbol is absolute; this will
+ be handled by m68hc11_relax_frag.
+
+2002-12-01 Stephane Carrez <stcarrez@nerim.fr>
+
* config/tc-m68hc11.c (elf_flags): Set default ABI to gcc default
(32-bit int, 64-bit double).
(md_longopts): New options -mshort, -mlong, -mshort-double and
diff --git a/gas/config/tc-m68hc11.c b/gas/config/tc-m68hc11.c
index 34dbf6b..2586902 100644
--- a/gas/config/tc-m68hc11.c
+++ b/gas/config/tc-m68hc11.c
@@ -1933,14 +1933,21 @@ build_indexed_byte (op, format, move_insn)
}
else if (op->reg1 != REG_PC)
{
- byte = (byte << 3) | 0xe2;
+ symbolS *sym;
+ offsetT off;
+
f = frag_more (1);
number_to_chars_bigendian (f, byte, 1);
-
- f = frag_more (2);
- fix_new_exp (frag_now, f - frag_now->fr_literal, 2,
- &op->exp, FALSE, BFD_RELOC_16);
- number_to_chars_bigendian (f, 0, 2);
+ sym = op->exp.X_add_symbol;
+ off = op->exp.X_add_number;
+ if (op->exp.X_op != O_symbol)
+ {
+ sym = make_expr_symbol (&op->exp);
+ off = 0;
+ }
+ frag_var (rs_machine_dependent, 2, 2,
+ ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_UNDF),
+ sym, off, f);
}
else
{
@@ -2060,17 +2067,12 @@ build_insn (opcode, operands, nb_operands)
char *f;
long format;
int move_insn = 0;
- fragS *frag;
- int where;
/* Put the page code instruction if there is one. */
format = opcode->format;
- frag = frag_now;
- where = frag_now_fix ();
-
if (format & M6811_OP_BRANCH)
- fix_new (frag, where, 1,
+ fix_new (frag_now, frag_now_fix (), 1,
&abs_symbol, 0, 1, BFD_RELOC_M68HC11_RL_JUMP);
if (format & OP_EXTENDED)
@@ -2732,6 +2734,97 @@ tc_gen_reloc (section, fixp)
return reloc;
}
+/* We need a port-specific relaxation function to cope with sym2 - sym1
+ relative expressions with both symbols in the same segment (but not
+ necessarily in the same frag as this insn), for example:
+ ldab sym2-(sym1-2),pc
+ sym1:
+ The offset can be 5, 9 or 16 bits long. */
+
+long
+m68hc11_relax_frag (seg, fragP, stretch)
+ segT seg ATTRIBUTE_UNUSED;
+ fragS *fragP;
+ long stretch ATTRIBUTE_UNUSED;
+{
+ long growth;
+ offsetT aim = 0;
+ symbolS *symbolP;
+ const relax_typeS *this_type;
+ const relax_typeS *start_type;
+ relax_substateT next_state;
+ relax_substateT this_state;
+ const relax_typeS *table = TC_GENERIC_RELAX_TABLE;
+
+ /* We only have to cope with frags as prepared by
+ md_estimate_size_before_relax. The STATE_BITS16 case may geet here
+ because of the different reasons that it's not relaxable. */
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16):
+ /* When we get to this state, the frag won't grow any more. */
+ return 0;
+
+ case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS5):
+ case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9):
+ if (fragP->fr_symbol == NULL
+ || S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+ as_fatal (_("internal inconsistency problem in %s: fr_symbol %lx"),
+ __FUNCTION__, (long) fragP->fr_symbol);
+ symbolP = fragP->fr_symbol;
+ if (symbol_resolved_p (symbolP))
+ as_fatal (_("internal inconsistency problem in %s: resolved symbol"),
+ __FUNCTION__);
+ aim = S_GET_VALUE (symbolP);
+ break;
+
+ default:
+ as_fatal (_("internal inconsistency problem in %s: fr_subtype %d"),
+ __FUNCTION__, fragP->fr_subtype);
+ }
+
+ /* The rest is stolen from relax_frag. There's no obvious way to
+ share the code, but fortunately no requirement to keep in sync as
+ long as fragP->fr_symbol does not have its segment changed. */
+
+ this_state = fragP->fr_subtype;
+ start_type = this_type = table + this_state;
+
+ if (aim < 0)
+ {
+ /* Look backwards. */
+ for (next_state = this_type->rlx_more; next_state;)
+ if (aim >= this_type->rlx_backward)
+ next_state = 0;
+ else
+ {
+ /* Grow to next state. */
+ this_state = next_state;
+ this_type = table + this_state;
+ next_state = this_type->rlx_more;
+ }
+ }
+ else
+ {
+ /* Look forwards. */
+ for (next_state = this_type->rlx_more; next_state;)
+ if (aim <= this_type->rlx_forward)
+ next_state = 0;
+ else
+ {
+ /* Grow to next state. */
+ this_state = next_state;
+ this_type = table + this_state;
+ next_state = this_type->rlx_more;
+ }
+ }
+
+ growth = this_type->rlx_length - start_type->rlx_length;
+ if (growth != 0)
+ fragP->fr_subtype = this_state;
+ return growth;
+}
+
void
md_convert_frag (abfd, sec, fragP)
bfd *abfd ATTRIBUTE_UNUSED;
@@ -2799,25 +2892,32 @@ md_convert_frag (abfd, sec, fragP)
break;
case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS5):
+ if (fragP->fr_opcode[0] == 3
+ && fragP->fr_symbol != 0
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+ value = disp;
fragP->fr_opcode[0] = fragP->fr_opcode[0] << 6;
- if ((fragP->fr_opcode[0] & 0x0ff) == 0x0c0)
- fragP->fr_opcode[0] |= disp & 0x1f;
- else
- fragP->fr_opcode[0] |= value & 0x1f;
+ fragP->fr_opcode[0] |= value & 0x1f;
break;
case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9):
+ if (fragP->fr_opcode[0] == 3
+ && fragP->fr_symbol != 0
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+ value = disp;
fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3);
fragP->fr_opcode[0] |= 0xE0;
- fix_new (fragP, fragP->fr_fix, 1,
- fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_8);
+ fragP->fr_opcode[0] |= (value >> 8) & 1;
+ fragP->fr_opcode[1] = value;
fragP->fr_fix += 1;
break;
case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16):
fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3);
fragP->fr_opcode[0] |= 0xe2;
- if ((fragP->fr_opcode[0] & 0x0ff) == 0x0fa)
+ if ((fragP->fr_opcode[0] & 0x0ff) == 0x0fa
+ && fragP->fr_symbol != 0
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
{
fixp = fix_new (fragP, fragP->fr_fix, 2,
fragP->fr_symbol, fragP->fr_offset,
@@ -2927,13 +3027,23 @@ md_estimate_size_before_relax (fragP, segment)
case STATE_INDEXED_OFFSET:
assert (current_architecture & cpu6812);
- /* Switch the indexed operation to 16-bit mode. */
- fragP->fr_opcode[0] = fragP->fr_opcode[0] << 3;
- fragP->fr_opcode[0] |= 0xe2;
- fragP->fr_fix++;
- fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
- fragP->fr_offset, 0, BFD_RELOC_16);
- fragP->fr_fix++;
+ if (fragP->fr_symbol
+ && S_GET_SEGMENT (fragP->fr_symbol) == absolute_section)
+ {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_OFFSET,
+ STATE_BITS5);
+ /* Return the size of the variable part of the frag. */
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+ }
+ else
+ {
+ /* Switch the indexed operation to 16-bit mode. */
+ fragP->fr_opcode[0] = fragP->fr_opcode[0] << 3;
+ fragP->fr_opcode[0] |= 0xe2;
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 0, BFD_RELOC_16);
+ fragP->fr_fix += 2;
+ }
break;
case STATE_XBCC_BRANCH:
diff --git a/gas/config/tc-m68hc11.h b/gas/config/tc-m68hc11.h
index c57637f..e7ea44c 100644
--- a/gas/config/tc-m68hc11.h
+++ b/gas/config/tc-m68hc11.h
@@ -93,6 +93,15 @@ extern int m68hc11_parse_long_option PARAMS ((char *));
#define TC_GENERIC_RELAX_TABLE md_relax_table
extern struct relax_type md_relax_table[];
+/* GAS only handles relaxations for pc-relative data targeting addresses
+ in the same segment, so we have to handle the rest on our own. */
+#define md_relax_frag(SEG, FRAGP, STRETCH) \
+ ((FRAGP)->fr_symbol != NULL \
+ && S_GET_SEGMENT ((FRAGP)->fr_symbol) == (SEG) \
+ ? relax_frag (SEG, FRAGP, STRETCH) \
+ : m68hc11_relax_frag (SEG, FRAGP, STRETCH))
+extern long m68hc11_relax_frag PARAMS ((segT, fragS*, long));
+
#define TC_HANDLES_FX_DONE
#define DIFF_EXPR_OK /* .-foo gets turned into PC relative relocs */