/* tc-v850.c -- Assembler code for the NEC V850
- Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
- Free Software Foundation, Inc.
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ 2006 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
asection *sec,
fragS *fragP)
{
+ /* This code performs some nasty type punning between the
+ fr_opcode field of the frag structure (a char *) and the
+ fx_r_type field of the fix structure (a bfd_reloc_code_real_type)
+ On a 64bit host this causes problems because these two fields
+ are not the same size, but since we know that we are only
+ ever storing small integers in the fields, it is safe to use
+ a union to convert between them. */
+ union u
+ {
+ bfd_reloc_code_real_type fx_r_type;
+ char * fr_opcode;
+ }
+ opcode_converter;
subseg_change (sec, 0);
+ opcode_converter.fr_opcode = fragP->fr_opcode;
+
/* In range conditional or unconditional branch. */
if (fragP->fr_subtype == 0 || fragP->fr_subtype == 2)
{
fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
- fragP->fr_offset, 1, BFD_RELOC_UNUSED + (int)fragP->fr_opcode);
+ fragP->fr_offset, 1,
+ BFD_RELOC_UNUSED + opcode_converter.fx_r_type);
fragP->fr_fix += 2;
}
/* Out of range conditional branch. Emit a branch around a jump. */
target. */
md_number_to_chars ((char *) buffer + 2, 0x00000780, 4);
fix_new (fragP, fragP->fr_fix + 2, 4, fragP->fr_symbol,
- fragP->fr_offset, 1, BFD_RELOC_UNUSED +
- (int) fragP->fr_opcode + 1);
+ fragP->fr_offset, 1,
+ BFD_RELOC_UNUSED + opcode_converter.fx_r_type + 1);
fragP->fr_fix += 6;
}
/* Out of range unconditional branch. Emit a jump. */
{
md_number_to_chars (fragP->fr_fix + fragP->fr_literal, 0x00000780, 4);
fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
- fragP->fr_offset, 1, BFD_RELOC_UNUSED +
- (int) fragP->fr_opcode + 1);
+ fragP->fr_offset, 1,
+ BFD_RELOC_UNUSED + opcode_converter.fx_r_type + 1);
fragP->fr_fix += 4;
}
else
if (relaxable && fc > 0)
{
+ /* On a 64-bit host the size of an 'int' is not the same
+ as the size of a pointer, so we need a union to convert
+ the opindex field of the fr_cgen structure into a char *
+ so that it can be stored in the frag. We do not have
+ to worry about loosing accuracy as we are not going to
+ be even close to the 32bit limit of the int. */
+ union
+ {
+ int opindex;
+ char * ptr;
+ }
+ opindex_converter;
+
+ opindex_converter.opindex = fixups[0].opindex;
insn_size = 2;
fc = 0;
f = frag_var (rs_machine_dependent, 4, 2, 2,
fixups[0].exp.X_add_symbol,
fixups[0].exp.X_add_number,
- (char *) fixups[0].opindex);
+ opindex_converter.ptr);
md_number_to_chars (f, insn, insn_size);
md_number_to_chars (f + 2, 0, 2);
}
f = frag_var (rs_machine_dependent, 6, 4, 0,
fixups[0].exp.X_add_symbol,
fixups[0].exp.X_add_number,
- (char *) fixups[0].opindex);
+ opindex_converter.ptr);
md_number_to_chars (f, insn, insn_size);
md_number_to_chars (f + 2, 0, 4);
}
BFD_RELOC_UNUSED plus the operand index. This lets us easily
handle fixups for any operand type, although that is admittedly
not a very exciting feature. We pick a BFD reloc type in
- md_apply_fix3. */
+ md_apply_fix. */
for (i = 0; i < fc; i++)
{
const struct v850_operand *operand;
reloc_howto->pc_relative,
reloc);
+ fixP->tc_fix_data = (void *) operand;
+
switch (reloc)
{
case BFD_RELOC_LO16:
}
void
-md_apply_fix3 (fixS *fixP, valueT *valueP, segT seg ATTRIBUTE_UNUSED)
+md_apply_fix (fixS *fixP, valueT *valueP, segT seg ATTRIBUTE_UNUSED)
{
valueT value = * valueP;
char *where;
/* We still have to insert the value into memory! */
where = fixP->fx_frag->fr_literal + fixP->fx_where;
- if (fixP->fx_r_type == BFD_RELOC_V850_LO16_SPLIT_OFFSET)
- bfd_putl32 (((value << 16) & 0xfffe0000)
- | ((value << 5) & 0x20)
- | (bfd_getl32 (where) & ~0xfffe0020), where);
- else if (fixP->fx_size == 1)
- *where = value & 0xff;
- else if (fixP->fx_size == 2)
- bfd_putl16 (value & 0xffff, (unsigned char *) where);
- else if (fixP->fx_size == 4)
- bfd_putl32 (value, (unsigned char *) where);
+ if (fixP->tc_fix_data != NULL
+ && ((struct v850_operand *) fixP->tc_fix_data)->insert != NULL)
+ {
+ const char * message = NULL;
+ struct v850_operand * operand = (struct v850_operand *) fixP->tc_fix_data;
+ unsigned long insn;
+
+ /* The variable "where" currently points at the exact point inside
+ the insn where we need to insert the value. But we need to
+ extract the entire insn so we probably need to move "where"
+ back a few bytes. */
+ if (fixP->fx_size == 2)
+ where -= 2;
+ else if (fixP->fx_size == 1)
+ where -= 3;
+
+ insn = bfd_getl32 ((unsigned char *) where);
+
+ /* Use the operand's insertion procedure, if present, in order to
+ make sure that the value is correctly stored in the insn. */
+ insn = operand->insert (insn, (offsetT) value, & message);
+ /* Ignore message even if it is set. */
+
+ bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+ }
+ else
+ {
+ if (fixP->fx_r_type == BFD_RELOC_V850_LO16_SPLIT_OFFSET)
+ bfd_putl32 (((value << 16) & 0xfffe0000)
+ | ((value << 5) & 0x20)
+ | (bfd_getl32 (where) & ~0xfffe0020), where);
+ else if (fixP->fx_size == 1)
+ *where = value & 0xff;
+ else if (fixP->fx_size == 2)
+ bfd_putl16 (value & 0xffff, (unsigned char *) where);
+ else if (fixP->fx_size == 4)
+ bfd_putl32 (value, (unsigned char *) where);
+ }
}
}
\f