#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/mmix.h"
#include "opcode/mmix.h"
#define MINUS_ONE (((bfd_vma) 0) - 1)
#define FATAL_DEBUG \
_bfd_abort (__FILE__, __LINE__, \
"Internal: Non-debugged code (test-case missing)")
#define BAD_CASE(x) \
_bfd_abort (__FILE__, __LINE__, \
"bad case for " #x)
static boolean mmix_elf_link_output_symbol_hook
PARAMS ((bfd *, struct bfd_link_info *, const char *,
Elf_Internal_Sym *, asection *));
static bfd_reloc_status_type mmix_elf_reloc
PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
static reloc_howto_type *bfd_elf64_bfd_reloc_type_lookup
PARAMS ((bfd *, bfd_reloc_code_real_type));
static void mmix_info_to_howto_rela
PARAMS ((bfd *, arelent *, Elf64_Internal_Rela *));
static int mmix_elf_sort_relocs PARAMS ((const PTR, const PTR));
static boolean mmix_elf_check_relocs
PARAMS ((bfd *, struct bfd_link_info *, asection *,
const Elf_Internal_Rela *));
static boolean mmix_elf_relocate_section
PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
static asection * mmix_elf_gc_mark_hook
PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *,
struct elf_link_hash_entry *, Elf_Internal_Sym *));
static bfd_reloc_status_type mmix_final_link_relocate
PARAMS ((reloc_howto_type *, asection *, bfd_byte *,
bfd_vma, bfd_signed_vma, bfd_vma, const char *, asection *));
static bfd_reloc_status_type mmix_elf_perform_relocation
PARAMS ((asection *, reloc_howto_type *, PTR, bfd_vma, bfd_vma));
static boolean mmix_elf_section_from_bfd_section
PARAMS ((bfd *, asection *, int *));
static boolean mmix_elf_add_symbol_hook
PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Sym *,
const char **, flagword *, asection **, bfd_vma *));
static boolean mmix_elf_is_local_label_name
PARAMS ((bfd *, const char *));
extern boolean mmix_elf_final_link PARAMS ((bfd *, struct bfd_link_info *));
extern void mmix_elf_symbol_processing PARAMS ((bfd *, asymbol *));
static reloc_howto_type elf_mmix_howto_table[] =
{
HOWTO (R_MMIX_NONE,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_NONE",
false,
0,
0,
false),
HOWTO (R_MMIX_8,
0,
0,
8,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_8",
false,
0xff,
0xff,
false),
HOWTO (R_MMIX_16,
0,
1,
16,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_16",
false,
0xffff,
0xffff,
false),
HOWTO (R_MMIX_24,
0,
2,
24,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_24",
false,
0xffffff,
0xffffff,
false),
HOWTO (R_MMIX_32,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_32",
false,
0xffffffff,
0xffffffff,
false),
HOWTO (R_MMIX_64,
0,
4,
64,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_64",
false,
MINUS_ONE,
MINUS_ONE,
false),
HOWTO (R_MMIX_PC_8,
0,
0,
8,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_PC_8",
false,
0xff,
0xff,
true),
HOWTO (R_MMIX_PC_16,
0,
1,
16,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_PC_16",
false,
0xffff,
0xffff,
true),
HOWTO (R_MMIX_PC_24,
0,
2,
24,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_PC_24",
false,
0xffffff,
0xffffff,
true),
HOWTO (R_MMIX_PC_32,
0,
2,
32,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_PC_32",
false,
0xffffffff,
0xffffffff,
true),
HOWTO (R_MMIX_PC_64,
0,
4,
64,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_PC_64",
false,
MINUS_ONE,
MINUS_ONE,
true),
HOWTO (R_MMIX_GNU_VTINHERIT,
0,
0,
0,
false,
0,
complain_overflow_dont,
NULL,
"R_MMIX_GNU_VTINHERIT",
false,
0,
0,
true),
HOWTO (R_MMIX_GNU_VTENTRY,
0,
0,
0,
false,
0,
complain_overflow_dont,
_bfd_elf_rel_vtable_reloc_fn,
"R_MMIX_GNU_VTENTRY",
false,
0,
0,
false),
HOWTO (R_MMIX_GETA,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_GETA",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_GETA_1,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_GETA_1",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_GETA_2,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_GETA_2",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_GETA_3,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_GETA_3",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_CBRANCH,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_CBRANCH",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_CBRANCH_J,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_CBRANCH_J",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_CBRANCH_1,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_CBRANCH_1",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_CBRANCH_2,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_CBRANCH_2",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_CBRANCH_3,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_CBRANCH_3",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_PUSHJ,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_PUSHJ",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_PUSHJ_1,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_PUSHJ_1",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_PUSHJ_2,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_PUSHJ_2",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_PUSHJ_3,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_PUSHJ_3",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_JMP,
2,
2,
27,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_JMP",
false,
0x1ffffff,
0x1ffffff,
true),
HOWTO (R_MMIX_JMP_1,
2,
2,
27,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_JMP_1",
false,
0x1ffffff,
0x1ffffff,
true),
HOWTO (R_MMIX_JMP_2,
2,
2,
27,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_JMP_2",
false,
0x1ffffff,
0x1ffffff,
true),
HOWTO (R_MMIX_JMP_3,
2,
2,
27,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_JMP_3",
false,
0x1ffffff,
0x1ffffff,
true),
HOWTO (R_MMIX_ADDR19,
2,
2,
19,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_ADDR19",
false,
0x0100ffff,
0x0100ffff,
true),
HOWTO (R_MMIX_ADDR27,
2,
2,
27,
true,
0,
complain_overflow_signed,
mmix_elf_reloc,
"R_MMIX_ADDR27",
false,
0x1ffffff,
0x1ffffff,
true),
HOWTO (R_MMIX_REG_OR_BYTE,
0,
1,
8,
false,
0,
complain_overflow_bitfield,
mmix_elf_reloc,
"R_MMIX_REG_OR_BYTE",
false,
0xff,
0xff,
false),
HOWTO (R_MMIX_REG,
0,
1,
8,
false,
0,
complain_overflow_bitfield,
mmix_elf_reloc,
"R_MMIX_REG",
false,
0xff,
0xff,
false),
HOWTO (R_MMIX_BASE_PLUS_OFFSET,
0,
4,
64,
false,
0,
complain_overflow_bitfield,
mmix_elf_reloc,
"R_MMIX_BASE_PLUS_OFFSET",
false,
0xffff,
0xffff,
false),
HOWTO (R_MMIX_LOCAL,
0,
0,
0,
false,
0,
complain_overflow_dont,
mmix_elf_reloc,
"R_MMIX_LOCAL",
false,
0,
0,
false),
};
struct mmix_reloc_map
{
bfd_reloc_code_real_type bfd_reloc_val;
enum elf_mmix_reloc_type elf_reloc_val;
};
static const struct mmix_reloc_map mmix_reloc_map[] =
{
{BFD_RELOC_NONE, R_MMIX_NONE},
{BFD_RELOC_8, R_MMIX_8},
{BFD_RELOC_16, R_MMIX_16},
{BFD_RELOC_24, R_MMIX_24},
{BFD_RELOC_32, R_MMIX_32},
{BFD_RELOC_64, R_MMIX_64},
{BFD_RELOC_8_PCREL, R_MMIX_PC_8},
{BFD_RELOC_16_PCREL, R_MMIX_PC_16},
{BFD_RELOC_24_PCREL, R_MMIX_PC_24},
{BFD_RELOC_32_PCREL, R_MMIX_PC_32},
{BFD_RELOC_64_PCREL, R_MMIX_PC_64},
{BFD_RELOC_VTABLE_INHERIT, R_MMIX_GNU_VTINHERIT},
{BFD_RELOC_VTABLE_ENTRY, R_MMIX_GNU_VTENTRY},
{BFD_RELOC_MMIX_GETA, R_MMIX_GETA},
{BFD_RELOC_MMIX_CBRANCH, R_MMIX_CBRANCH},
{BFD_RELOC_MMIX_PUSHJ, R_MMIX_PUSHJ},
{BFD_RELOC_MMIX_JMP, R_MMIX_JMP},
{BFD_RELOC_MMIX_ADDR19, R_MMIX_ADDR19},
{BFD_RELOC_MMIX_ADDR27, R_MMIX_ADDR27},
{BFD_RELOC_MMIX_REG_OR_BYTE, R_MMIX_REG_OR_BYTE},
{BFD_RELOC_MMIX_REG, R_MMIX_REG},
{BFD_RELOC_MMIX_BASE_PLUS_OFFSET, R_MMIX_BASE_PLUS_OFFSET},
{BFD_RELOC_MMIX_LOCAL, R_MMIX_LOCAL}
};
static reloc_howto_type *
bfd_elf64_bfd_reloc_type_lookup (abfd, code)
bfd *abfd ATTRIBUTE_UNUSED;
bfd_reloc_code_real_type code;
{
unsigned int i;
for (i = 0;
i < sizeof (mmix_reloc_map) / sizeof (mmix_reloc_map[0]);
i++)
{
if (mmix_reloc_map[i].bfd_reloc_val == code)
return &elf_mmix_howto_table[mmix_reloc_map[i].elf_reloc_val];
}
return NULL;
}
static bfd_reloc_status_type
mmix_elf_perform_relocation (isec, howto, datap, addr, value)
asection *isec;
reloc_howto_type *howto;
PTR datap;
bfd_vma addr ATTRIBUTE_UNUSED;
bfd_vma value;
{
bfd *abfd = isec->owner;
bfd_reloc_status_type flag = bfd_reloc_ok;
bfd_reloc_status_type r;
int offs = 0;
int reg = 255;
switch (howto->type)
{
case R_MMIX_GETA:
offs = 0;
reg = bfd_get_8 (abfd, (bfd_byte *) datap + 1);
value += addr;
break;
case R_MMIX_CBRANCH:
{
int in1 = bfd_get_16 (abfd, (bfd_byte *) datap) << 16;
bfd_put_32 (abfd,
(((in1 ^ ((PRED_INV_BIT | COND_INV_BIT) << 24)) & ~0xffff)
| (24/4)),
(bfd_byte *) datap);
bfd_put_32 (abfd,
((GO_INSN_BYTE | IMM_OFFSET_BIT) << 24) | 0xffff00,
(bfd_byte *) datap + 20);
offs = 4;
value += addr;
}
break;
case R_MMIX_PUSHJ:
{
int inreg = bfd_get_8 (abfd, (bfd_byte *) datap + 1);
bfd_put_32 (abfd,
((PUSHGO_INSN_BYTE | IMM_OFFSET_BIT) << 24)
| (inreg << 16)
| 0xff00,
(bfd_byte *) datap + 16);
value += addr;
}
break;
case R_MMIX_JMP:
if (! ((value & 3) == 0
&& (r = bfd_check_overflow (complain_overflow_signed,
howto->bitsize,
0,
bfd_arch_bits_per_address (abfd),
value)) == bfd_reloc_ok))
{
bfd_put_32 (abfd,
((GO_INSN_BYTE | IMM_OFFSET_BIT) << 24)
| 0xffff00,
(bfd_byte *) datap + 16);
value += addr;
break;
}
case R_MMIX_ADDR19:
case R_MMIX_ADDR27:
if ((value & 3) == 0
&& (r = bfd_check_overflow (complain_overflow_signed,
howto->bitsize,
0,
bfd_arch_bits_per_address (abfd),
value)) == bfd_reloc_ok)
{
bfd_vma in1
= bfd_get_32 (abfd, (bfd_byte *) datap);
bfd_vma highbit;
if ((bfd_signed_vma) value < 0)
{
highbit = (1 << 24);
value += (1 << (howto->bitsize - 1));
}
else
highbit = 0;
value >>= 2;
bfd_put_32 (abfd,
(in1 & ~howto->src_mask)
| highbit
| (value & howto->dst_mask),
(bfd_byte *) datap);
return bfd_reloc_ok;
}
else
return bfd_reloc_overflow;
case R_MMIX_REG_OR_BYTE:
case R_MMIX_REG:
if (value > 255)
return bfd_reloc_overflow;
bfd_put_8 (abfd, value, datap);
return bfd_reloc_ok;
default:
BAD_CASE (howto->type);
}
if (value & 3)
flag = bfd_reloc_overflow;
bfd_put_32 (abfd,
(SETL_INSN_BYTE << 24) | (value & 0xffff) | (reg << 16),
(bfd_byte *) datap + offs);
bfd_put_32 (abfd,
(INCML_INSN_BYTE << 24) | ((value >> 16) & 0xffff) | (reg << 16),
(bfd_byte *) datap + offs + 4);
bfd_put_32 (abfd,
(INCMH_INSN_BYTE << 24) | ((value >> 32) & 0xffff) | (reg << 16),
(bfd_byte *) datap + offs + 8);
bfd_put_32 (abfd,
(INCH_INSN_BYTE << 24) | ((value >> 48) & 0xffff) | (reg << 16),
(bfd_byte *) datap + offs + 12);
return flag;
}
static void
mmix_info_to_howto_rela (abfd, cache_ptr, dst)
bfd *abfd ATTRIBUTE_UNUSED;
arelent *cache_ptr;
Elf64_Internal_Rela *dst;
{
unsigned int r_type;
r_type = ELF64_R_TYPE (dst->r_info);
BFD_ASSERT (r_type < (unsigned int) R_MMIX_max);
cache_ptr->howto = &elf_mmix_howto_table[r_type];
}
static bfd_reloc_status_type
mmix_elf_reloc (abfd, reloc_entry, symbol, data, input_section,
output_bfd, error_message)
bfd *abfd;
arelent *reloc_entry;
asymbol *symbol;
PTR data;
asection *input_section;
bfd *output_bfd;
char **error_message ATTRIBUTE_UNUSED;
{
bfd_vma relocation;
bfd_reloc_status_type r;
asection *reloc_target_output_section;
bfd_reloc_status_type flag = bfd_reloc_ok;
bfd_vma output_base = 0;
bfd_vma addr;
r = bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
input_section, output_bfd, error_message);
if (r != bfd_reloc_continue)
return r;
if (bfd_is_und_section (symbol->section)
&& (symbol->flags & BSF_WEAK) == 0
&& output_bfd == (bfd *) NULL)
return bfd_reloc_undefined;
if (reloc_entry->address > input_section->_cooked_size)
return bfd_reloc_outofrange;
if (bfd_is_com_section (symbol->section))
relocation = 0;
else
relocation = symbol->value;
reloc_target_output_section = bfd_get_output_section (symbol);
if (output_bfd)
output_base = 0;
else
output_base = reloc_target_output_section->vma;
relocation += output_base + symbol->section->output_offset;
addr = (reloc_entry->address + input_section->output_section->vma
+ input_section->output_offset);
if (output_bfd != (bfd *) NULL)
{
relocation += reloc_entry->addend;
reloc_entry->addend = relocation;
reloc_entry->address += input_section->output_offset;
return flag;
}
return mmix_final_link_relocate (reloc_entry->howto, input_section,
data, reloc_entry->address,
reloc_entry->addend, relocation,
bfd_asymbol_name (symbol),
reloc_target_output_section);
}
static boolean
mmix_elf_relocate_section (output_bfd, info, input_bfd, input_section,
contents, relocs, local_syms, local_sections)
bfd *output_bfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info;
bfd *input_bfd;
asection *input_section;
bfd_byte *contents;
Elf_Internal_Rela *relocs;
Elf_Internal_Sym *local_syms;
asection **local_sections;
{
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
Elf_Internal_Rela *rel;
Elf_Internal_Rela *relend;
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (input_bfd);
relend = relocs + input_section->reloc_count;
for (rel = relocs; rel < relend; rel ++)
{
reloc_howto_type *howto;
unsigned long r_symndx;
Elf_Internal_Sym *sym;
asection *sec;
struct elf_link_hash_entry *h;
bfd_vma relocation;
bfd_reloc_status_type r;
const char *name = NULL;
int r_type;
boolean undefined_signalled = false;
r_type = ELF64_R_TYPE (rel->r_info);
if (r_type == R_MMIX_GNU_VTINHERIT
|| r_type == R_MMIX_GNU_VTENTRY)
continue;
r_symndx = ELF64_R_SYM (rel->r_info);
if (info->relocateable)
{
if (r_symndx < symtab_hdr->sh_info)
{
sym = local_syms + r_symndx;
if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
{
sec = local_sections [r_symndx];
rel->r_addend += sec->output_offset + sym->st_value;
}
}
continue;
}
howto = elf_mmix_howto_table + ELF64_R_TYPE (rel->r_info);
h = NULL;
sym = NULL;
sec = NULL;
if (r_symndx < symtab_hdr->sh_info)
{
sym = local_syms + r_symndx;
sec = local_sections [r_symndx];
relocation = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel);
name = bfd_elf_string_from_elf_section
(input_bfd, symtab_hdr->sh_link, sym->st_name);
name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name;
}
else
{
h = sym_hashes [r_symndx - symtab_hdr->sh_info];
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
name = h->root.root.string;
if (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
{
sec = h->root.u.def.section;
relocation = (h->root.u.def.value
+ sec->output_section->vma
+ sec->output_offset);
}
else if (h->root.type == bfd_link_hash_undefweak)
relocation = 0;
else if (info->shared
&& ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
relocation = 0;
else
{
if (! undefined_signalled
&& ! ((*info->callbacks->undefined_symbol)
(info, h->root.root.string, input_bfd,
input_section, rel->r_offset, true)))
return false;
undefined_signalled = true;
relocation = 0;
}
}
r = mmix_final_link_relocate (howto, input_section,
contents, rel->r_offset,
rel->r_addend, relocation, name, sec);
if (r != bfd_reloc_ok)
{
boolean check_ok = true;
const char * msg = (const char *) NULL;
switch (r)
{
case bfd_reloc_overflow:
check_ok = info->callbacks->reloc_overflow
(info, name, howto->name, (bfd_vma) 0,
input_bfd, input_section, rel->r_offset);
break;
case bfd_reloc_undefined:
if (! undefined_signalled)
check_ok = info->callbacks->undefined_symbol
(info, name, input_bfd, input_section, rel->r_offset,
true);
undefined_signalled = true;
break;
case bfd_reloc_outofrange:
msg = _("internal error: out of range error");
break;
case bfd_reloc_notsupported:
msg = _("internal error: unsupported relocation error");
break;
case bfd_reloc_dangerous:
msg = _("internal error: dangerous relocation");
break;
default:
msg = _("internal error: unknown error");
break;
}
if (msg)
check_ok = info->callbacks->warning
(info, msg, name, input_bfd, input_section, rel->r_offset);
if (! check_ok)
return false;
}
}
return true;
}
static bfd_reloc_status_type
mmix_final_link_relocate (howto, input_section, contents,
r_offset, r_addend, relocation, symname, symsec)
reloc_howto_type *howto;
asection *input_section;
bfd_byte *contents;
bfd_vma r_offset;
bfd_signed_vma r_addend;
bfd_vma relocation;
const char *symname;
asection *symsec;
{
bfd_reloc_status_type r = bfd_reloc_ok;
bfd_vma addr
= (input_section->output_section->vma
+ input_section->output_offset
+ r_offset);
bfd_signed_vma srel
= (bfd_signed_vma) relocation + r_addend;
switch (howto->type)
{
case R_MMIX_PUSHJ:
case R_MMIX_CBRANCH:
case R_MMIX_ADDR19:
case R_MMIX_GETA:
case R_MMIX_ADDR27:
case R_MMIX_JMP:
contents += r_offset;
srel -= (input_section->output_section->vma
+ input_section->output_offset
+ r_offset);
r = mmix_elf_perform_relocation (input_section, howto, contents,
addr, srel);
break;
case R_MMIX_REG_OR_BYTE:
case R_MMIX_REG:
if (symsec == NULL)
return bfd_reloc_undefined;
if (strcmp (bfd_get_section_name (symsec->owner, symsec),
MMIX_REG_CONTENTS_SECTION_NAME) == 0)
{
if ((srel & 7) != 0 || srel < 32*8 || srel > 255*8)
{
return bfd_reloc_overflow;
}
srel /= 8;
}
else if (strcmp (bfd_get_section_name (symsec->owner, symsec),
MMIX_REG_SECTION_NAME) == 0)
{
if (srel < 0 || srel > 255)
return bfd_reloc_overflow;
}
else
{
(*_bfd_error_handler)
(_("%s: register relocation against non-register symbol: %s in %s"),
bfd_get_filename (input_section->owner),
symname == NULL || *symname == 0 ? _("(unknown)") : symname,
bfd_get_section_name (symsec->owner, symsec));
return bfd_reloc_overflow;
}
contents += r_offset;
r = mmix_elf_perform_relocation (input_section, howto, contents,
addr, srel);
break;
case R_MMIX_LOCAL:
{
asection *regsec
= bfd_get_section_by_name (input_section->output_section->owner,
MMIX_REG_CONTENTS_SECTION_NAME);
bfd_vma first_global;
if (!bfd_is_abs_section (symsec)
&& !bfd_is_und_section (symsec)
&& strcmp (bfd_get_section_name (symsec->owner, symsec),
MMIX_REG_CONTENTS_SECTION_NAME) != 0
&& strcmp (bfd_get_section_name (symsec->owner, symsec),
MMIX_REG_SECTION_NAME) != 0)
{
(*_bfd_error_handler)
(_("%s: directive LOCAL valid only with a register or absolute value"),
bfd_get_filename (input_section->owner));
return bfd_reloc_overflow;
}
if (regsec == NULL)
first_global = 255;
else
{
first_global = bfd_get_section_vma (abfd, regsec) / 8;
if (strcmp (bfd_get_section_name (symsec->owner, symsec),
MMIX_REG_CONTENTS_SECTION_NAME) == 0)
{
if ((srel & 7) != 0 || srel < 32*8 || srel > 255*8)
return bfd_reloc_overflow;
srel /= 8;
}
}
if ((bfd_vma) srel >= first_global)
{
(*_bfd_error_handler)
(_("%s: LOCAL directive: Register $%ld is not a local register. First global register is $%ld."),
bfd_get_filename (input_section->owner), (long) srel, (long) first_global);
return bfd_reloc_overflow;
}
}
r = bfd_reloc_ok;
break;
default:
r = _bfd_final_link_relocate (howto, input_section->owner, input_section,
contents, r_offset,
relocation, r_addend);
}
return r;
}
static asection *
mmix_elf_gc_mark_hook (abfd, info, rel, h, sym)
bfd *abfd;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
Elf_Internal_Rela *rel;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
{
if (h != NULL)
{
switch (ELF64_R_TYPE (rel->r_info))
{
case R_MMIX_GNU_VTINHERIT:
case R_MMIX_GNU_VTENTRY:
break;
default:
switch (h->root.type)
{
case bfd_link_hash_defined:
case bfd_link_hash_defweak:
return h->root.u.def.section;
case bfd_link_hash_common:
return h->root.u.c.p->section;
default:
break;
}
}
}
else
{
return bfd_section_from_elf_index (abfd, sym->st_shndx);
}
return NULL;
}
static int
mmix_elf_sort_relocs (p1, p2)
const PTR p1;
const PTR p2;
{
const Elf_Internal_Rela *r1 = (const Elf_Internal_Rela *) p1;
const Elf_Internal_Rela *r2 = (const Elf_Internal_Rela *) p2;
int r1_is_reg, r2_is_reg;
if ((r1->r_offset & ~(bfd_vma) 3) > (r2->r_offset & ~(bfd_vma) 3))
return 1;
else if ((r1->r_offset & ~(bfd_vma) 3) < (r2->r_offset & ~(bfd_vma) 3))
return -1;
r1_is_reg
= (ELF64_R_TYPE (r1->r_info) == R_MMIX_REG_OR_BYTE
|| ELF64_R_TYPE (r1->r_info) == R_MMIX_REG);
r2_is_reg
= (ELF64_R_TYPE (r2->r_info) == R_MMIX_REG_OR_BYTE
|| ELF64_R_TYPE (r2->r_info) == R_MMIX_REG);
if (r1_is_reg != r2_is_reg)
return r2_is_reg - r1_is_reg;
if (r1->r_offset > r2->r_offset)
return 1;
else if (r1->r_offset < r2->r_offset)
return -1;
return 0;
}
static boolean
mmix_elf_check_relocs (abfd, info, sec, relocs)
bfd *abfd;
struct bfd_link_info *info;
asection *sec;
const Elf_Internal_Rela *relocs;
{
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes, **sym_hashes_end;
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
if (info->relocateable)
return true;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
sym_hashes_end = sym_hashes + symtab_hdr->sh_size/sizeof(Elf64_External_Sym);
if (!elf_bad_symtab (abfd))
sym_hashes_end -= symtab_hdr->sh_info;
qsort ((PTR) relocs, sec->reloc_count, sizeof (Elf_Internal_Rela),
mmix_elf_sort_relocs);
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
{
struct elf_link_hash_entry *h;
unsigned long r_symndx;
r_symndx = ELF64_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
h = NULL;
else
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
switch (ELF64_R_TYPE (rel->r_info))
{
case R_MMIX_GNU_VTINHERIT:
if (!_bfd_elf64_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
return false;
break;
case R_MMIX_GNU_VTENTRY:
if (!_bfd_elf64_gc_record_vtentry (abfd, sec, h, rel->r_addend))
return false;
break;
}
}
return true;
}
static boolean
mmix_elf_link_output_symbol_hook (abfd, info, name, sym, input_sec)
bfd *abfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
const char *name ATTRIBUTE_UNUSED;
Elf_Internal_Sym *sym;
asection *input_sec;
{
if (input_sec != NULL
&& input_sec->name != NULL
&& ELF_ST_TYPE (sym->st_info) != STT_SECTION
&& strcmp (input_sec->name, MMIX_REG_CONTENTS_SECTION_NAME) == 0)
{
sym->st_value /= 8;
sym->st_shndx = SHN_REGISTER;
}
return true;
}
static asection mmix_elf_reg_section;
static asymbol mmix_elf_reg_section_symbol;
static asymbol *mmix_elf_reg_section_symbol_ptr;
void
mmix_elf_symbol_processing (abfd, asym)
bfd *abfd ATTRIBUTE_UNUSED;
asymbol *asym;
{
elf_symbol_type *elfsym;
elfsym = (elf_symbol_type *) asym;
switch (elfsym->internal_elf_sym.st_shndx)
{
case SHN_REGISTER:
if (mmix_elf_reg_section.name == NULL)
{
mmix_elf_reg_section.name = MMIX_REG_SECTION_NAME;
mmix_elf_reg_section.flags = SEC_NO_FLAGS;
mmix_elf_reg_section.output_section = &mmix_elf_reg_section;
mmix_elf_reg_section.symbol = &mmix_elf_reg_section_symbol;
mmix_elf_reg_section.symbol_ptr_ptr = &mmix_elf_reg_section_symbol_ptr;
mmix_elf_reg_section_symbol.name = MMIX_REG_SECTION_NAME;
mmix_elf_reg_section_symbol.flags = BSF_SECTION_SYM;
mmix_elf_reg_section_symbol.section = &mmix_elf_reg_section;
mmix_elf_reg_section_symbol_ptr = &mmix_elf_reg_section_symbol;
}
asym->section = &mmix_elf_reg_section;
break;
default:
break;
}
}
static boolean
mmix_elf_section_from_bfd_section (abfd, sec, retval)
bfd * abfd ATTRIBUTE_UNUSED;
asection * sec;
int * retval;
{
if (strcmp (bfd_get_section_name (abfd, sec), MMIX_REG_SECTION_NAME) == 0)
*retval = SHN_REGISTER;
else
return false;
return true;
}
boolean
mmix_elf_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp)
bfd *abfd;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
const Elf_Internal_Sym *sym;
const char **namep ATTRIBUTE_UNUSED;
flagword *flagsp ATTRIBUTE_UNUSED;
asection **secp;
bfd_vma *valp ATTRIBUTE_UNUSED;
{
if (sym->st_shndx == SHN_REGISTER)
*secp = bfd_make_section_old_way (abfd, MMIX_REG_SECTION_NAME);
else if ((*namep)[0] == '_' && (*namep)[1] == '_' && (*namep)[2] == '.'
&& strncmp (*namep, MMIX_LOC_SECTION_START_SYMBOL_PREFIX,
strlen (MMIX_LOC_SECTION_START_SYMBOL_PREFIX)) == 0)
{
struct elf_link_hash_entry *h
= (struct elf_link_hash_entry *) bfd_link_hash_lookup (info->hash,
*namep,
false,
false, false);
if (h != NULL && h->root.type != bfd_link_hash_undefined)
{
((*_bfd_error_handler)
(_("%s: Error: multiple definition of `%s'; start of %s is set in a earlier linked file\n"),
bfd_get_filename (abfd), *namep,
*namep + strlen (MMIX_LOC_SECTION_START_SYMBOL_PREFIX)));
bfd_set_error (bfd_error_bad_value);
return false;
}
}
return true;
}
boolean
mmix_elf_is_local_label_name (abfd, name)
bfd *abfd;
const char *name;
{
const char *colpos;
int digits;
if (_bfd_elf_is_local_label_name (abfd, name))
return true;
if (*name != 'L')
return false;
colpos = strchr (name, ':');
if (colpos == NULL || strchr (colpos + 1, ':') != NULL)
return false;
if (colpos[1] == 0)
return false;
digits = strspn (colpos + 1, "0123456789");
return digits != 0 && colpos[1 + digits] == 0;
}
boolean
mmix_elf_final_link (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
asection *reg_section;
asection **secpp;
reg_section = bfd_get_section_by_name (abfd, MMIX_REG_SECTION_NAME);
if (reg_section != NULL)
{
if (bfd_get_section_flags (abfd, reg_section) & SEC_HAS_CONTENTS)
_bfd_abort (__FILE__, __LINE__, _("Register section has contents\n"));
for (secpp = &abfd->sections;
*secpp != reg_section;
secpp = &(*secpp)->next)
;
bfd_section_list_remove (abfd, secpp);
--abfd->section_count;
}
if (! bfd_elf64_bfd_final_link (abfd, info))
return false;
return true;
}
#define ELF_ARCH bfd_arch_mmix
#define ELF_MACHINE_CODE EM_MMIX
#define ELF_MAXPAGESIZE 0x100
#define TARGET_BIG_SYM bfd_elf64_mmix_vec
#define TARGET_BIG_NAME "elf64-mmix"
#define elf_info_to_howto_rel NULL
#define elf_info_to_howto mmix_info_to_howto_rela
#define elf_backend_relocate_section mmix_elf_relocate_section
#define elf_backend_gc_mark_hook mmix_elf_gc_mark_hook
#define elf_backend_link_output_symbol_hook \
mmix_elf_link_output_symbol_hook
#define elf_backend_add_symbol_hook mmix_elf_add_symbol_hook
#define elf_backend_check_relocs mmix_elf_check_relocs
#define elf_backend_symbol_processing mmix_elf_symbol_processing
#define bfd_elf64_bfd_is_local_label_name \
mmix_elf_is_local_label_name
#define elf_backend_may_use_rel_p 0
#define elf_backend_may_use_rela_p 1
#define elf_backend_default_use_rela_p 1
#define elf_backend_can_gc_sections 1
#define elf_backend_section_from_bfd_section \
mmix_elf_section_from_bfd_section
#define bfd_elf64_bfd_final_link mmix_elf_final_link
#include "elf64-target.h"