#define A29K 1
#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "coff/a29k.h"
#include "coff/internal.h"
#include "libcoff.h"
static long get_symbol_value PARAMS ((asymbol *));
static bfd_reloc_status_type a29k_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static boolean coff_a29k_relocate_section
PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
struct internal_reloc *, struct internal_syment *, asection **));
static boolean coff_a29k_adjust_symndx
PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *,
struct internal_reloc *, boolean *));
static void reloc_processing
PARAMS ((arelent *, struct internal_reloc *, asymbol **, bfd *, asection *));
#define COFF_DEFAULT_SECTION_ALIGNMENT_POWER (2)
#define INSERT_HWORD(WORD,HWORD) \
(((WORD) & 0xff00ff00) | (((HWORD) & 0xff00) << 8) | ((HWORD)& 0xff))
#define EXTRACT_HWORD(WORD) \
((((WORD) & 0x00ff0000) >> 8) | ((WORD) & 0xff))
#define SIGN_EXTEND_HWORD(HWORD) \
(((HWORD) ^ 0x8000) - 0x8000)
static long
get_symbol_value (symbol)
asymbol *symbol;
{
long relocation = 0;
if (bfd_is_com_section (symbol->section))
relocation = 0;
else
relocation = symbol->value +
symbol->section->output_section->vma +
symbol->section->output_offset;
return relocation;
}
static bfd_reloc_status_type
a29k_reloc (abfd, reloc_entry, symbol_in, data, input_section, output_bfd,
error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol_in;
PTR data;
asection *input_section;
bfd *output_bfd;
char **error_message;
{
static boolean part1_consth_active = false;
static unsigned long part1_consth_value;
unsigned long insn;
unsigned long sym_value;
unsigned long unsigned_value;
unsigned short r_type;
long signed_value;
unsigned long addr = reloc_entry->address ;
bfd_byte *hit_data =addr + (bfd_byte *) (data);
r_type = reloc_entry->howto->type;
if (output_bfd)
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
if (symbol_in != NULL
&& bfd_is_und_section (symbol_in->section))
{
if (r_type == R_IHIHALF)
{
part1_consth_active = true;
part1_consth_value = 0;
}
return bfd_reloc_undefined;
}
if ((part1_consth_active) && (r_type != R_IHCONST))
{
part1_consth_active = false;
*error_message = (char *) _("Missing IHCONST");
return bfd_reloc_dangerous;
}
sym_value = get_symbol_value(symbol_in);
switch (r_type)
{
case R_IREL:
insn = bfd_get_32 (abfd, hit_data);
signed_value = EXTRACT_HWORD(insn);
signed_value = SIGN_EXTEND_HWORD(signed_value);
signed_value <<= 2;
if (signed_value == - (long) reloc_entry->address)
signed_value = 0;
signed_value += sym_value + reloc_entry->addend;
if ((signed_value & ~0x3ffff) == 0)
{
insn |= (1 << 24);
}
else
{
signed_value -= (reloc_entry->address
+ input_section->output_section->vma
+ input_section->output_offset);
if (signed_value > 0x1ffff || signed_value < -0x20000)
return bfd_reloc_overflow;
}
signed_value >>= 2;
insn = INSERT_HWORD (insn, signed_value);
bfd_put_32 (abfd, (bfd_vma) insn ,hit_data);
break;
case R_ILOHALF:
insn = bfd_get_32 (abfd, hit_data);
unsigned_value = EXTRACT_HWORD(insn);
unsigned_value += sym_value + reloc_entry->addend;
insn = INSERT_HWORD(insn, unsigned_value);
bfd_put_32 (abfd, (bfd_vma) insn, hit_data);
break;
case R_IHIHALF:
insn = bfd_get_32 (abfd, hit_data);
part1_consth_active = true;
part1_consth_value = sym_value + reloc_entry->addend;
break;
case R_IHCONST:
insn = bfd_get_32 (abfd, hit_data);
if (part1_consth_active == false)
{
*error_message = (char *) _("Missing IHIHALF");
return bfd_reloc_dangerous;
}
unsigned_value = 0;
unsigned_value += reloc_entry->addend;
unsigned_value += part1_consth_value;
unsigned_value = unsigned_value >> 16;
insn = INSERT_HWORD(insn, unsigned_value);
part1_consth_active = false;
bfd_put_32 (abfd, (bfd_vma) insn, hit_data);
break;
case R_BYTE:
insn = bfd_get_8 (abfd, hit_data);
unsigned_value = insn + sym_value + reloc_entry->addend;
if (unsigned_value & 0xffffff00)
return bfd_reloc_overflow;
bfd_put_8 (abfd, unsigned_value, hit_data);
break;
case R_HWORD:
insn = bfd_get_16 (abfd, hit_data);
unsigned_value = insn + sym_value + reloc_entry->addend;
if (unsigned_value & 0xffff0000)
return bfd_reloc_overflow;
bfd_put_16 (abfd, (bfd_vma) insn, hit_data);
break;
case R_WORD:
insn = bfd_get_32 (abfd, hit_data);
insn += sym_value + reloc_entry->addend;
bfd_put_32 (abfd, (bfd_vma) insn, hit_data);
break;
default:
*error_message = _("Unrecognized reloc");
return bfd_reloc_dangerous;
}
return(bfd_reloc_ok);
}
static reloc_howto_type howto_table[] =
{
{R_ABS, 0, 3, 32, false, 0, complain_overflow_bitfield,a29k_reloc,"ABS", true, 0xffffffff,0xffffffff, false},
EMPTY_HOWTO (1),
EMPTY_HOWTO (2),
EMPTY_HOWTO (3),
EMPTY_HOWTO (4),
EMPTY_HOWTO (5),
EMPTY_HOWTO (6),
EMPTY_HOWTO (7),
EMPTY_HOWTO (8),
EMPTY_HOWTO (9),
EMPTY_HOWTO (10),
EMPTY_HOWTO (11),
EMPTY_HOWTO (12),
EMPTY_HOWTO (13),
EMPTY_HOWTO (14),
EMPTY_HOWTO (15),
EMPTY_HOWTO (16),
EMPTY_HOWTO (17),
EMPTY_HOWTO (18),
EMPTY_HOWTO (19),
EMPTY_HOWTO (20),
EMPTY_HOWTO (21),
EMPTY_HOWTO (22),
EMPTY_HOWTO (23),
{R_IREL, 0, 3, 32, true, 0, complain_overflow_signed,a29k_reloc,"IREL", true, 0xffffffff,0xffffffff, false},
{R_IABS, 0, 3, 32, false, 0, complain_overflow_bitfield, a29k_reloc,"IABS", true, 0xffffffff,0xffffffff, false},
{R_ILOHALF, 0, 3, 16, true, 0, complain_overflow_signed, a29k_reloc,"ILOHALF", true, 0x0000ffff,0x0000ffff, false},
{R_IHIHALF, 0, 3, 16, true, 16, complain_overflow_signed, a29k_reloc,"IHIHALF", true, 0xffff0000,0xffff0000, false},
{R_IHCONST, 0, 3, 16, true, 0, complain_overflow_signed, a29k_reloc,"IHCONST", true, 0xffff0000,0xffff0000, false},
{R_BYTE, 0, 0, 8, false, 0, complain_overflow_bitfield, a29k_reloc,"BYTE", true, 0x000000ff,0x000000ff, false},
{R_HWORD, 0, 1, 16, false, 0, complain_overflow_bitfield, a29k_reloc,"HWORD", true, 0x0000ffff,0x0000ffff, false},
{R_WORD, 0, 2, 32, false, 0, complain_overflow_bitfield, a29k_reloc,"WORD", true, 0xffffffff,0xffffffff, false},
};
#define BADMAG(x) A29KBADMAG(x)
#define RELOC_PROCESSING(relent, reloc, symbols, abfd, section) \
reloc_processing(relent, reloc, symbols, abfd, section)
static void
reloc_processing (relent,reloc, symbols, abfd, section)
arelent *relent;
struct internal_reloc *reloc;
asymbol **symbols;
bfd *abfd;
asection *section;
{
static bfd_vma ihihalf_vaddr = (bfd_vma) -1;
relent->address = reloc->r_vaddr;
relent->howto = howto_table + reloc->r_type;
if (reloc->r_type == R_IHCONST)
{
if (ihihalf_vaddr == (bfd_vma) -1)
abort ();
relent->address = ihihalf_vaddr;
ihihalf_vaddr = (bfd_vma) -1;
relent->addend = reloc->r_symndx;
relent->sym_ptr_ptr= bfd_abs_section_ptr->symbol_ptr_ptr;
}
else
{
asymbol *ptr;
relent->sym_ptr_ptr = symbols + obj_convert (abfd)[reloc->r_symndx];
ptr = *(relent->sym_ptr_ptr);
if (ptr
&& bfd_asymbol_bfd(ptr) == abfd
&& ((ptr->flags & BSF_OLD_COMMON) == 0))
relent->addend = 0;
else
relent->addend = 0;
relent->address-= section->vma;
if (reloc->r_type == R_IHIHALF)
ihihalf_vaddr = relent->address;
else if (ihihalf_vaddr != (bfd_vma) -1)
abort ();
}
}
static boolean
coff_a29k_relocate_section (output_bfd, info, input_bfd, input_section,
contents, relocs, syms, sections)
bfd *output_bfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info;
bfd *input_bfd;
asection *input_section;
bfd_byte *contents;
struct internal_reloc *relocs;
struct internal_syment *syms;
asection **sections;
{
struct internal_reloc *rel;
struct internal_reloc *relend;
boolean hihalf;
bfd_vma hihalf_val;
if (info->relocateable)
return true;
hihalf = false;
hihalf_val = 0;
rel = relocs;
relend = rel + input_section->reloc_count;
for (; rel < relend; rel++)
{
long symndx;
bfd_byte *loc;
struct coff_link_hash_entry *h;
struct internal_syment *sym;
asection *sec;
bfd_vma val;
boolean overflow;
unsigned long insn;
long signed_value;
unsigned long unsigned_value;
bfd_reloc_status_type rstat;
symndx = rel->r_symndx;
loc = contents + rel->r_vaddr - input_section->vma;
if (symndx == -1 || rel->r_type == R_IHCONST)
h = NULL;
else
h = obj_coff_sym_hashes (input_bfd)[symndx];
sym = NULL;
sec = NULL;
val = 0;
if (rel->r_type != R_IHCONST)
{
if (h == NULL)
{
if (symndx == -1)
sec = bfd_abs_section_ptr;
else
{
sym = syms + symndx;
sec = sections[symndx];
val = (sec->output_section->vma
+ sec->output_offset
+ sym->n_value
- sec->vma);
}
}
else
{
if ( h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
{
sec = h->root.u.def.section;
val = (h->root.u.def.value
+ sec->output_section->vma
+ sec->output_offset);
}
else
{
if (! ((*info->callbacks->undefined_symbol)
(info, h->root.root.string, input_bfd, input_section,
rel->r_vaddr - input_section->vma, true)))
return false;
}
}
if (hihalf)
{
if (! ((*info->callbacks->reloc_dangerous)
(info, _("missing IHCONST reloc"), input_bfd,
input_section, rel->r_vaddr - input_section->vma)))
return false;
hihalf = false;
}
}
overflow = false;
switch (rel->r_type)
{
default:
bfd_set_error (bfd_error_bad_value);
return false;
case R_IREL:
insn = bfd_get_32 (input_bfd, loc);
signed_value = EXTRACT_HWORD (insn);
signed_value = SIGN_EXTEND_HWORD (signed_value);
signed_value <<= 2;
if (signed_value == - (long) (rel->r_vaddr - input_section->vma))
signed_value = 0;
signed_value += val;
if ((signed_value & ~0x3ffff) == 0)
{
insn |= (1 << 24);
}
else
{
signed_value -= (input_section->output_section->vma
+ input_section->output_offset
+ (rel->r_vaddr - input_section->vma));
if (signed_value > 0x1ffff || signed_value < - 0x20000)
{
overflow = true;
signed_value = 0;
}
}
signed_value >>= 2;
insn = INSERT_HWORD (insn, signed_value);
bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
break;
case R_ILOHALF:
insn = bfd_get_32 (input_bfd, loc);
unsigned_value = EXTRACT_HWORD (insn);
unsigned_value += val;
insn = INSERT_HWORD (insn, unsigned_value);
bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
break;
case R_IHIHALF:
hihalf = true;
hihalf_val = val;
break;
case R_IHCONST:
if (! hihalf)
{
if (! ((*info->callbacks->reloc_dangerous)
(info, _("missing IHIHALF reloc"), input_bfd,
input_section, rel->r_vaddr - input_section->vma)))
return false;
hihalf_val = 0;
}
insn = bfd_get_32 (input_bfd, loc);
unsigned_value = rel->r_symndx + hihalf_val;
unsigned_value >>= 16;
insn = INSERT_HWORD (insn, unsigned_value);
bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
hihalf = false;
break;
case R_BYTE:
case R_HWORD:
case R_WORD:
rstat = _bfd_relocate_contents (howto_table + rel->r_type,
input_bfd, val, loc);
if (rstat == bfd_reloc_overflow)
overflow = true;
else if (rstat != bfd_reloc_ok)
abort ();
break;
}
if (overflow)
{
const char *name;
char buf[SYMNMLEN + 1];
if (symndx == -1)
name = "*ABS*";
else if (h != NULL)
name = h->root.root.string;
else if (sym == NULL)
name = "*unknown*";
else if (sym->_n._n_n._n_zeroes == 0
&& sym->_n._n_n._n_offset != 0)
name = obj_coff_strings (input_bfd) + sym->_n._n_n._n_offset;
else
{
strncpy (buf, sym->_n._n_name, SYMNMLEN);
buf[SYMNMLEN] = '\0';
name = buf;
}
if (! ((*info->callbacks->reloc_overflow)
(info, name, howto_table[rel->r_type].name, (bfd_vma) 0,
input_bfd, input_section,
rel->r_vaddr - input_section->vma)))
return false;
}
}
return true;
}
#define coff_relocate_section coff_a29k_relocate_section
static boolean
coff_a29k_adjust_symndx (obfd, info, ibfd, sec, irel, adjustedp)
bfd *obfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
bfd *ibfd ATTRIBUTE_UNUSED;
asection *sec ATTRIBUTE_UNUSED;
struct internal_reloc *irel;
boolean *adjustedp;
{
if (irel->r_type == R_IHCONST)
*adjustedp = true;
else
*adjustedp = false;
return true;
}
#define coff_adjust_symndx coff_a29k_adjust_symndx
#include "coffcode.h"
CREATE_BIG_COFF_TARGET_VEC (a29kcoff_big_vec, "coff-a29k-big", 0, SEC_READONLY, '_', NULL)