#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)
struct bpo_reloc_section_info
{
size_t first_base_plus_offset_reloc;
size_t n_bpo_relocs_this_section;
size_t bpo_index;
asection *bpo_greg_section;
};
struct bpo_reloc_request
{
bfd_vma value;
size_t regindex;
size_t offset;
size_t bpo_reloc_no;
boolean valid;
};
struct bpo_greg_section_info
{
size_t n_bpo_relocs;
size_t n_max_bpo_relocs;
size_t n_remaining_bpo_relocs_this_relaxation_round;
size_t n_allocated_bpo_gregs;
size_t *bpo_reloc_indexes;
struct bpo_reloc_request *reloc_request;
};
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_check_common_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 boolean mmix_elf_gc_sweep_hook
PARAMS ((bfd *, struct bfd_link_info *, asection *,
const Elf_Internal_Rela *));
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 *));
static int bpo_reloc_request_sort_fn PARAMS ((const PTR, const PTR));
static boolean mmix_elf_relax_section
PARAMS ((bfd *abfd, asection *sec, struct bfd_link_info *link_info,
boolean *again));
extern boolean mmix_elf_final_link PARAMS ((bfd *, struct bfd_link_info *));
extern void mmix_elf_symbol_processing PARAMS ((bfd *, asymbol *));
extern void mmix_dump_bpo_gregs
PARAMS ((struct bfd_link_info *, bfd_error_handler_type));
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,
0,
0xff,
false),
HOWTO (R_MMIX_16,
0,
1,
16,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_16",
false,
0,
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,
0,
0xffffffff,
false),
HOWTO (R_MMIX_64,
0,
4,
64,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_64",
false,
0,
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,
0,
0xff,
true),
HOWTO (R_MMIX_PC_16,
0,
1,
16,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_PC_16",
false,
0,
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,
0,
0xffffffff,
true),
HOWTO (R_MMIX_PC_64,
0,
4,
64,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_MMIX_PC_64",
false,
0,
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,
0,
0xff,
false),
HOWTO (R_MMIX_REG,
0,
1,
8,
false,
0,
complain_overflow_bitfield,
mmix_elf_reloc,
"R_MMIX_REG",
false,
0,
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,
0,
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_BASE_PLUS_OFFSET:
{
struct bpo_reloc_section_info *bpodata
= (struct bpo_reloc_section_info *)
elf_section_data (isec)->tdata;
asection *bpo_greg_section
= bpodata->bpo_greg_section;
struct bpo_greg_section_info *gregdata
= (struct bpo_greg_section_info *)
elf_section_data (bpo_greg_section)->tdata;
size_t bpo_index
= gregdata->bpo_reloc_indexes[bpodata->bpo_index++];
if (value != gregdata->reloc_request[bpo_index].value)
{
(*_bfd_error_handler)
(_("%s: Internal inconsistency error for value for\n\
linker-allocated global register: linked: 0x%lx%08lx != relaxed: 0x%lx%08lx\n"),
bfd_get_filename (isec->owner),
(unsigned long) (value >> 32), (unsigned long) value,
(unsigned long) (gregdata->reloc_request[bpo_index].value
>> 32),
(unsigned long) gregdata->reloc_request[bpo_index].value);
bfd_set_error (bfd_error_bad_value);
return bfd_reloc_overflow;
}
bfd_put_8 (abfd,
gregdata->reloc_request[bpo_index].regindex
+ bpo_greg_section->output_section->vma / 8,
datap);
bfd_put_8 (abfd,
gregdata->reloc_request[bpo_index].offset,
((unsigned char *) datap) + 1);
return bfd_reloc_ok;
}
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_BASE_PLUS_OFFSET:
if (symsec == NULL)
return bfd_reloc_undefined;
if (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)
{
if (symname == NULL || *symname == 0)
(*_bfd_error_handler)
(_("%s: base-plus-offset relocation against register symbol: (unknown) in %s"),
bfd_get_filename (input_section->owner),
bfd_get_section_name (symsec->owner, symsec));
else
(*_bfd_error_handler)
(_("%s: base-plus-offset relocation against register symbol: %s in %s"),
bfd_get_filename (input_section->owner), symname,
bfd_get_section_name (symsec->owner, symsec));
return bfd_reloc_overflow;
}
goto do_mmix_reloc;
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
{
if (symname == NULL || *symname == 0)
(*_bfd_error_handler)
(_("%s: register relocation against non-register symbol: (unknown) in %s"),
bfd_get_filename (input_section->owner),
bfd_get_section_name (symsec->owner, symsec));
else
(*_bfd_error_handler)
(_("%s: register relocation against non-register symbol: %s in %s"),
bfd_get_filename (input_section->owner), symname,
bfd_get_section_name (symsec->owner, symsec));
return bfd_reloc_overflow;
}
do_mmix_reloc:
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 boolean
mmix_elf_gc_sweep_hook (abfd, info, sec, relocs)
bfd *abfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
asection *sec ATTRIBUTE_UNUSED;
const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED;
{
struct bpo_reloc_section_info *bpodata
= (struct bpo_reloc_section_info *)
elf_section_data (sec)->tdata;
asection *allocated_gregs_section;
if (bpodata == NULL)
return true;
allocated_gregs_section = bpodata->bpo_greg_section;
((struct bpo_greg_section_info *)
elf_section_data (allocated_gregs_section)->tdata)
->n_bpo_relocs
-= bpodata->n_bpo_relocs_this_section;
return true;
}
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_common_relocs (abfd, info, sec, relocs)
bfd *abfd;
struct bfd_link_info *info;
asection *sec;
const Elf_Internal_Rela *relocs;
{
bfd *bpo_greg_owner = NULL;
asection *allocated_gregs_section = NULL;
struct bpo_greg_section_info *gregdata = NULL;
struct bpo_reloc_section_info *bpodata = NULL;
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
if (info->relocateable)
return true;
bpo_greg_owner = (bfd *) info->base_file;
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
{
switch (ELF64_R_TYPE (rel->r_info))
{
case R_MMIX_BASE_PLUS_OFFSET:
if (bpo_greg_owner == NULL)
{
bpo_greg_owner = abfd;
info->base_file = (PTR) bpo_greg_owner;
}
if (allocated_gregs_section == NULL)
allocated_gregs_section
= bfd_get_section_by_name (bpo_greg_owner,
MMIX_LD_ALLOCATED_REG_CONTENTS_SECTION_NAME);
if (allocated_gregs_section == NULL)
{
allocated_gregs_section
= bfd_make_section (bpo_greg_owner,
MMIX_LD_ALLOCATED_REG_CONTENTS_SECTION_NAME);
if (allocated_gregs_section == NULL
|| !bfd_set_section_flags (bpo_greg_owner,
allocated_gregs_section,
(SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_LINKER_CREATED))
|| !bfd_set_section_alignment (bpo_greg_owner,
allocated_gregs_section,
3))
return false;
gregdata = (struct bpo_greg_section_info *)
bfd_zalloc (bpo_greg_owner, sizeof (struct bpo_greg_section_info));
if (gregdata == NULL)
return false;
elf_section_data (allocated_gregs_section)->tdata = gregdata;
}
else if (gregdata == NULL)
gregdata = elf_section_data (allocated_gregs_section)->tdata;
if (bpodata == NULL)
{
bpodata = (struct bpo_reloc_section_info *)
bfd_alloc (bpo_greg_owner,
sizeof (struct bpo_reloc_section_info)
* (sec->reloc_count + 1));
if (bpodata == NULL)
return false;
elf_section_data (sec)->tdata = bpodata;
bpodata->first_base_plus_offset_reloc
= bpodata->bpo_index
= gregdata->n_max_bpo_relocs;
bpodata->bpo_greg_section
= allocated_gregs_section;
bpodata->n_bpo_relocs_this_section = 0;
}
bpodata->n_bpo_relocs_this_section++;
gregdata->n_max_bpo_relocs++;
gregdata->n_bpo_relocs
= gregdata->n_max_bpo_relocs;
break;
}
}
return true;
}
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);
if (!mmix_elf_check_common_relocs (abfd, info, sec, relocs))
return false;
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;
}
boolean
_bfd_mmix_check_all_relocs (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
asection *o;
for (o = abfd->sections; o != NULL; o = o->next)
{
Elf_Internal_Rela *internal_relocs;
boolean ok;
if ((o->flags & SEC_RELOC) == 0
|| o->reloc_count == 0
|| ((info->strip == strip_all || info->strip == strip_debugger)
&& (o->flags & SEC_DEBUGGING) != 0)
|| bfd_is_abs_section (o->output_section))
continue;
internal_relocs
= _bfd_elf64_link_read_relocs (abfd, o, (PTR) NULL,
(Elf_Internal_Rela *) NULL,
info->keep_memory);
if (internal_relocs == NULL)
return false;
ok = mmix_elf_check_common_relocs (abfd, info, o, internal_relocs);
if (! info->keep_memory)
free (internal_relocs);
if (! ok)
return false;
}
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;
if (info->base_file != NULL)
{
asection *greg_section
= bfd_get_section_by_name ((bfd *) info->base_file,
MMIX_LD_ALLOCATED_REG_CONTENTS_SECTION_NAME);
if (!bfd_set_section_contents (abfd,
greg_section->output_section,
greg_section->contents,
(file_ptr) greg_section->output_offset,
greg_section->_cooked_size))
return false;
}
return true;
}
boolean
_bfd_mmix_prepare_linker_allocated_gregs (abfd, info)
bfd *abfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info;
{
asection *bpo_gregs_section;
bfd *bpo_greg_owner;
struct bpo_greg_section_info *gregdata;
size_t n_gregs;
bfd_vma gregs_size;
size_t i;
size_t *bpo_reloc_indexes;
bpo_greg_owner = (bfd *) info->base_file;
if (bpo_greg_owner == NULL)
return true;
bpo_gregs_section
= bfd_get_section_by_name (bpo_greg_owner,
MMIX_LD_ALLOCATED_REG_CONTENTS_SECTION_NAME);
if (bpo_gregs_section == NULL)
return true;
gregdata = (struct bpo_greg_section_info *)
elf_section_data (bpo_gregs_section)->tdata;
if (gregdata == NULL)
return false;
n_gregs = gregdata->n_bpo_relocs;
gregdata->n_allocated_bpo_gregs = n_gregs;
gregdata->n_remaining_bpo_relocs_this_relaxation_round = n_gregs;
gregs_size = n_gregs * 8;
if (!bfd_set_section_size (bpo_greg_owner, bpo_gregs_section, gregs_size))
return false;
gregdata->reloc_request
= bfd_zalloc (bpo_greg_owner,
sizeof (struct bpo_reloc_request)
* gregdata->n_max_bpo_relocs);
gregdata->bpo_reloc_indexes
= bpo_reloc_indexes
= bfd_alloc (bpo_greg_owner,
gregdata->n_max_bpo_relocs
* sizeof (size_t));
if (bpo_reloc_indexes == NULL)
return false;
for (i = 0; i < gregdata->n_max_bpo_relocs; i++)
{
bpo_reloc_indexes[i] = i;
gregdata->reloc_request[i].bpo_reloc_no = i;
}
return true;
}
boolean
_bfd_mmix_finalize_linker_allocated_gregs (abfd, link_info)
bfd *abfd ATTRIBUTE_UNUSED;
struct bfd_link_info *link_info;
{
asection *bpo_gregs_section;
bfd *bpo_greg_owner;
struct bpo_greg_section_info *gregdata;
size_t n_gregs;
size_t i, j;
size_t lastreg;
bfd_byte *contents;
bpo_greg_owner = (bfd *) link_info->base_file;
if (bpo_greg_owner == NULL)
return true;
bpo_gregs_section
= bfd_get_section_by_name (bpo_greg_owner,
MMIX_LD_ALLOCATED_REG_CONTENTS_SECTION_NAME);
if (bpo_gregs_section == NULL)
return true;
gregdata = (struct bpo_greg_section_info *)
elf_section_data (bpo_gregs_section)->tdata;
if (gregdata == NULL)
return false;
n_gregs = gregdata->n_allocated_bpo_gregs;
bpo_gregs_section->contents
= contents = bfd_alloc (bpo_greg_owner, bpo_gregs_section->_cooked_size);
if (contents == NULL)
return false;
if (gregdata->n_remaining_bpo_relocs_this_relaxation_round
!= gregdata->n_bpo_relocs)
{
(*_bfd_error_handler)
(_("Internal inconsistency: remaining %u != max %u.\n\
Please report this bug."),
gregdata->n_remaining_bpo_relocs_this_relaxation_round,
gregdata->n_bpo_relocs);
return false;
}
for (lastreg = 255, i = 0, j = 0; j < n_gregs; i++)
if (gregdata->reloc_request[i].regindex != lastreg)
{
bfd_put_64 (bpo_greg_owner, gregdata->reloc_request[i].value,
contents + j * 8);
lastreg = gregdata->reloc_request[i].regindex;
j++;
}
return true;
}
static int
bpo_reloc_request_sort_fn (p1, p2)
const PTR p1;
const PTR p2;
{
const struct bpo_reloc_request *r1 = (const struct bpo_reloc_request *) p1;
const struct bpo_reloc_request *r2 = (const struct bpo_reloc_request *) p2;
if (r1->valid != r2->valid)
return r2->valid - r1->valid;
if (r1->value != r2->value)
return r1->value > r2->value ? 1 : -1;
return r1 > r2 ? 1 : (r1 < r2 ? -1 : 0);
}
void
mmix_dump_bpo_gregs (link_info, pf)
struct bfd_link_info *link_info;
bfd_error_handler_type pf;
{
bfd *bpo_greg_owner;
asection *bpo_gregs_section;
struct bpo_greg_section_info *gregdata;
unsigned int i;
if (link_info == NULL || link_info->base_file == NULL)
return;
bpo_greg_owner = (bfd *) link_info->base_file;
bpo_gregs_section
= bfd_get_section_by_name (bpo_greg_owner,
MMIX_LD_ALLOCATED_REG_CONTENTS_SECTION_NAME);
if (bpo_gregs_section == NULL)
return;
gregdata = (struct bpo_greg_section_info *)
elf_section_data (bpo_gregs_section)->tdata;
if (gregdata == NULL)
return;
if (pf == NULL)
pf = _bfd_error_handler;
(*pf) (" n_bpo_relocs: %u\n n_max_bpo_relocs: %u\n n_remain...round: %u\n\
n_allocated_bpo_gregs: %u\n", gregdata->n_bpo_relocs,
gregdata->n_max_bpo_relocs,
gregdata->n_remaining_bpo_relocs_this_relaxation_round,
gregdata->n_allocated_bpo_gregs);
if (gregdata->reloc_request)
for (i = 0; i < gregdata->n_max_bpo_relocs; i++)
(*pf) ("%4u (%4u)/%4u#%u: 0x%08lx%08lx r: %3u o: %3u\n",
i,
gregdata->bpo_reloc_indexes != NULL
? gregdata->bpo_reloc_indexes[i] : -1,
gregdata->reloc_request[i].bpo_reloc_no,
gregdata->reloc_request[i].valid,
(unsigned long) (gregdata->reloc_request[i].value >> 32),
(unsigned long) gregdata->reloc_request[i].value,
gregdata->reloc_request[i].regindex,
gregdata->reloc_request[i].offset);
}
static boolean
mmix_elf_relax_section (abfd, sec, link_info, again)
bfd *abfd;
asection *sec;
struct bfd_link_info *link_info;
boolean *again;
{
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Shdr *shndx_hdr;
Elf_Internal_Rela *internal_relocs;
Elf_Internal_Rela *free_relocs = NULL;
Elf_Internal_Rela *irel, *irelend;
asection *bpo_gregs_section = NULL;
struct bpo_greg_section_info *gregdata;
struct bpo_reloc_section_info *bpodata
= (struct bpo_reloc_section_info *)
elf_section_data (sec)->tdata;
size_t bpono;
bfd *bpo_greg_owner;
Elf64_External_Sym *extsyms = NULL;
Elf64_External_Sym *free_extsyms = NULL;
Elf_External_Sym_Shndx *shndx_buf = NULL;
*again = false;
if (sec->_cooked_size == 0)
sec->_cooked_size = sec->_raw_size;
if (link_info->relocateable
|| (sec->flags & SEC_RELOC) == 0
|| sec->reloc_count == 0
|| (sec->flags & SEC_CODE) == 0
|| (sec->flags & SEC_LINKER_CREATED) != 0
|| bpodata == NULL)
return true;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
bpo_greg_owner = (bfd *) link_info->base_file;
bpo_gregs_section = bpodata->bpo_greg_section;
gregdata = (struct bpo_greg_section_info *)
elf_section_data (bpo_gregs_section)->tdata;
bpono = bpodata->first_base_plus_offset_reloc;
internal_relocs
= _bfd_elf64_link_read_relocs (abfd, sec, (PTR) NULL,
(Elf_Internal_Rela *) NULL,
link_info->keep_memory);
if (internal_relocs == NULL)
goto error_return;
if (! link_info->keep_memory)
free_relocs = internal_relocs;
irelend = internal_relocs + sec->reloc_count;
for (irel = internal_relocs; irel < irelend; irel++)
{
bfd_vma symval;
if (ELF64_R_TYPE (irel->r_info) != (int) R_MMIX_BASE_PLUS_OFFSET)
continue;
if (extsyms == NULL)
{
if (symtab_hdr->contents != NULL)
extsyms = (Elf64_External_Sym *) symtab_hdr->contents;
else
{
bfd_size_type amt;
amt = symtab_hdr->sh_info;
amt *= sizeof (Elf64_External_Sym);
extsyms = (Elf64_External_Sym *) bfd_malloc (amt);
if (extsyms == NULL)
goto error_return;
free_extsyms = extsyms;
if (bfd_seek (abfd, symtab_hdr->sh_offset, SEEK_SET) != 0
|| bfd_bread ((PTR) extsyms, amt, abfd) != amt)
goto error_return;
symtab_hdr->contents = (bfd_byte *) extsyms;
}
if (shndx_hdr->sh_size != 0)
{
bfd_size_type amt;
amt = symtab_hdr->sh_info;
amt *= sizeof (Elf_External_Sym_Shndx);
shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
if (shndx_buf == NULL)
goto error_return;
if (bfd_seek (abfd, shndx_hdr->sh_offset, SEEK_SET) != 0
|| bfd_bread ((PTR) shndx_buf, amt, abfd) != amt)
goto error_return;
shndx_hdr->contents = (bfd_byte *) shndx_buf;
}
}
if (ELF64_R_SYM (irel->r_info) < symtab_hdr->sh_info)
{
Elf64_External_Sym *esym;
Elf_External_Sym_Shndx *shndx;
Elf_Internal_Sym isym;
asection *sym_sec;
esym = extsyms + ELF64_R_SYM (irel->r_info);
shndx = shndx_buf + (shndx_buf
? ELF64_R_SYM (irel->r_info) : 0);
bfd_elf64_swap_symbol_in (abfd, esym, shndx, &isym);
if (isym.st_shndx == SHN_UNDEF)
sym_sec = bfd_und_section_ptr;
else if (isym.st_shndx == SHN_ABS)
sym_sec = bfd_abs_section_ptr;
else if (isym.st_shndx == SHN_COMMON)
sym_sec = bfd_com_section_ptr;
else
sym_sec = bfd_section_from_elf_index (abfd, isym.st_shndx);
symval = (isym.st_value
+ sym_sec->output_section->vma
+ sym_sec->output_offset);
}
else
{
unsigned long indx;
struct elf_link_hash_entry *h;
indx = ELF64_R_SYM (irel->r_info) - symtab_hdr->sh_info;
h = elf_sym_hashes (abfd)[indx];
BFD_ASSERT (h != NULL);
if (h->root.type != bfd_link_hash_defined
&& h->root.type != bfd_link_hash_defweak)
{
gregdata->n_remaining_bpo_relocs_this_relaxation_round--;
bpono++;
continue;
}
symval = (h->root.u.def.value
+ h->root.u.def.section->output_section->vma
+ h->root.u.def.section->output_offset);
}
gregdata->reloc_request[gregdata->bpo_reloc_indexes[bpono]].value
= symval + irel->r_addend;
gregdata->reloc_request[gregdata->bpo_reloc_indexes[bpono++]].valid = true;
gregdata->n_remaining_bpo_relocs_this_relaxation_round--;
}
if (gregdata->n_remaining_bpo_relocs_this_relaxation_round == 0)
{
size_t i;
bfd_vma prev_base;
size_t regindex;
gregdata->n_remaining_bpo_relocs_this_relaxation_round
= gregdata->n_bpo_relocs;
qsort ((PTR) gregdata->reloc_request,
gregdata->n_max_bpo_relocs,
sizeof (struct bpo_reloc_request),
bpo_reloc_request_sort_fn);
for (i = 0; i < gregdata->n_max_bpo_relocs; i++)
if (gregdata->bpo_reloc_indexes[gregdata->reloc_request[i].bpo_reloc_no]
!= i)
{
gregdata->bpo_reloc_indexes[gregdata->reloc_request[i].bpo_reloc_no]
= i;
*again = true;
}
for (i = 0, regindex = 0, prev_base = gregdata->reloc_request[0].value;
i < gregdata->n_bpo_relocs;
i++)
{
if (gregdata->reloc_request[i].value > prev_base + 255)
{
regindex++;
prev_base = gregdata->reloc_request[i].value;
}
gregdata->reloc_request[i].regindex = regindex;
gregdata->reloc_request[i].offset
= gregdata->reloc_request[i].value - prev_base;
}
if (gregdata->n_allocated_bpo_gregs != regindex + 1)
{
gregdata->n_allocated_bpo_gregs = regindex + 1;
*again = true;
}
bpo_gregs_section->_cooked_size = (regindex + 1) * 8;
}
if (free_relocs != NULL)
free (free_relocs);
if (shndx_buf != NULL)
{
shndx_hdr->contents = NULL;
free (shndx_buf);
}
if (free_extsyms != NULL)
{
if (! link_info->keep_memory)
{
symtab_hdr->contents = NULL;
free (free_extsyms);
}
}
return true;
error_return:
if (free_relocs != NULL)
free (free_relocs);
if (shndx_buf != NULL)
{
shndx_hdr->contents = NULL;
free (shndx_buf);
}
if (free_extsyms != NULL)
{
symtab_hdr->contents = NULL;
free (free_extsyms);
}
return false;
}
#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_gc_sweep_hook mmix_elf_gc_sweep_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
#define bfd_elf64_bfd_relax_section mmix_elf_relax_section
#include "elf64-target.h"