#include "bfd.h"
#include "sysdep.h"
#include "bfdlink.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf32-m68hc1x.h"
#include "elf/m68hc11.h"
#include "opcode/m68hc11.h"
#define m68hc12_stub_hash_lookup(table, string, create, copy) \
((struct elf32_m68hc11_stub_hash_entry *) \
bfd_hash_lookup ((table), (string), (create), (copy)))
static struct elf32_m68hc11_stub_hash_entry* m68hc12_add_stub
(const char *stub_name,
asection *section,
struct m68hc11_elf_link_hash_table *htab);
static struct bfd_hash_entry *stub_hash_newfunc
(struct bfd_hash_entry *, struct bfd_hash_table *, const char *);
static void m68hc11_elf_set_symbol (bfd* abfd, struct bfd_link_info *info,
const char* name, bfd_vma value,
asection* sec);
static bfd_boolean m68hc11_elf_export_one_stub
(struct bfd_hash_entry *gen_entry, void *in_arg);
static void scan_sections_for_abi (bfd*, asection*, PTR);
struct m68hc11_scan_param
{
struct m68hc11_page_info* pinfo;
bfd_boolean use_memory_banks;
};
struct m68hc11_elf_link_hash_table*
m68hc11_elf_hash_table_create (bfd *abfd)
{
struct m68hc11_elf_link_hash_table *ret;
bfd_size_type amt = sizeof (struct m68hc11_elf_link_hash_table);
ret = (struct m68hc11_elf_link_hash_table *) bfd_malloc (amt);
if (ret == (struct m68hc11_elf_link_hash_table *) NULL)
return NULL;
memset (ret, 0, amt);
if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
_bfd_elf_link_hash_newfunc))
{
free (ret);
return NULL;
}
amt = sizeof (struct bfd_hash_table);
ret->stub_hash_table = (struct bfd_hash_table*) bfd_malloc (amt);
if (ret->stub_hash_table == NULL)
{
free (ret);
return NULL;
}
if (!bfd_hash_table_init (ret->stub_hash_table, stub_hash_newfunc))
return NULL;
ret->stub_bfd = NULL;
ret->stub_section = 0;
ret->add_stub_section = NULL;
ret->sym_sec.abfd = NULL;
return ret;
}
void
m68hc11_elf_bfd_link_hash_table_free (struct bfd_link_hash_table *hash)
{
struct m68hc11_elf_link_hash_table *ret
= (struct m68hc11_elf_link_hash_table *) hash;
bfd_hash_table_free (ret->stub_hash_table);
free (ret->stub_hash_table);
_bfd_generic_link_hash_table_free (hash);
}
static struct bfd_hash_entry *
stub_hash_newfunc (struct bfd_hash_entry *entry, struct bfd_hash_table *table,
const char *string)
{
if (entry == NULL)
{
entry = bfd_hash_allocate (table,
sizeof (struct elf32_m68hc11_stub_hash_entry));
if (entry == NULL)
return entry;
}
entry = bfd_hash_newfunc (entry, table, string);
if (entry != NULL)
{
struct elf32_m68hc11_stub_hash_entry *eh;
eh = (struct elf32_m68hc11_stub_hash_entry *) entry;
eh->stub_sec = NULL;
eh->stub_offset = 0;
eh->target_value = 0;
eh->target_section = NULL;
}
return entry;
}
static struct elf32_m68hc11_stub_hash_entry *
m68hc12_add_stub (const char *stub_name, asection *section,
struct m68hc11_elf_link_hash_table *htab)
{
struct elf32_m68hc11_stub_hash_entry *stub_entry;
stub_entry = m68hc12_stub_hash_lookup (htab->stub_hash_table, stub_name,
TRUE, FALSE);
if (stub_entry == NULL)
{
(*_bfd_error_handler) (_("%B: cannot create stub entry %s"),
section->owner, stub_name);
return NULL;
}
if (htab->stub_section == 0)
{
htab->stub_section = (*htab->add_stub_section) (".tramp",
htab->tramp_section);
}
stub_entry->stub_sec = htab->stub_section;
stub_entry->stub_offset = 0;
return stub_entry;
}
bfd_boolean
elf32_m68hc11_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
Elf_Internal_Sym *sym,
const char **namep ATTRIBUTE_UNUSED,
flagword *flagsp ATTRIBUTE_UNUSED,
asection **secp ATTRIBUTE_UNUSED,
bfd_vma *valp ATTRIBUTE_UNUSED)
{
if (sym->st_other & STO_M68HC12_FAR)
{
struct elf_link_hash_entry *h;
h = (struct elf_link_hash_entry *)
bfd_link_hash_lookup (info->hash, "__far_trampoline",
FALSE, FALSE, FALSE);
if (h == NULL)
{
struct bfd_link_hash_entry* entry = NULL;
_bfd_generic_link_add_one_symbol (info, abfd,
"__far_trampoline",
BSF_GLOBAL,
bfd_und_section_ptr,
(bfd_vma) 0, (const char*) NULL,
FALSE, FALSE, &entry);
}
}
return TRUE;
}
int
elf32_m68hc11_setup_section_lists (bfd *output_bfd, struct bfd_link_info *info)
{
bfd *input_bfd;
unsigned int bfd_count;
int top_id, top_index;
asection *section;
asection **input_list, **list;
bfd_size_type amt;
asection *text_section;
struct m68hc11_elf_link_hash_table *htab;
htab = m68hc11_elf_hash_table (info);
if (htab->root.root.creator->flavour != bfd_target_elf_flavour)
return 0;
htab->tramp_section = 0;
text_section = 0;
for (input_bfd = info->input_bfds, bfd_count = 0, top_id = 0;
input_bfd != NULL;
input_bfd = input_bfd->link_next)
{
bfd_count += 1;
for (section = input_bfd->sections;
section != NULL;
section = section->next)
{
const char* name = bfd_get_section_name (input_bfd, section);
if (!strcmp (name, ".tramp"))
htab->tramp_section = section;
if (!strcmp (name, ".text"))
text_section = section;
if (top_id < section->id)
top_id = section->id;
}
}
htab->bfd_count = bfd_count;
if (htab->tramp_section == 0)
htab->tramp_section = text_section;
for (section = output_bfd->sections, top_index = 0;
section != NULL;
section = section->next)
{
if (top_index < section->index)
top_index = section->index;
}
htab->top_index = top_index;
amt = sizeof (asection *) * (top_index + 1);
input_list = (asection **) bfd_malloc (amt);
htab->input_list = input_list;
if (input_list == NULL)
return -1;
list = input_list + top_index;
do
*list = bfd_abs_section_ptr;
while (list-- != input_list);
for (section = output_bfd->sections;
section != NULL;
section = section->next)
{
if ((section->flags & SEC_CODE) != 0)
input_list[section->index] = NULL;
}
return 1;
}
bfd_boolean
elf32_m68hc11_size_stubs (bfd *output_bfd, bfd *stub_bfd,
struct bfd_link_info *info,
asection * (*add_stub_section) (const char*, asection*))
{
bfd *input_bfd;
asection *section;
Elf_Internal_Sym *local_syms, **all_local_syms;
unsigned int bfd_indx, bfd_count;
bfd_size_type amt;
asection *stub_sec;
struct m68hc11_elf_link_hash_table *htab = m68hc11_elf_hash_table (info);
htab->stub_bfd = stub_bfd;
htab->add_stub_section = add_stub_section;
for (input_bfd = info->input_bfds, bfd_count = 0;
input_bfd != NULL;
input_bfd = input_bfd->link_next)
{
bfd_count += 1;
}
amt = sizeof (Elf_Internal_Sym *) * bfd_count;
all_local_syms = (Elf_Internal_Sym **) bfd_zmalloc (amt);
if (all_local_syms == NULL)
return FALSE;
for (input_bfd = info->input_bfds, bfd_indx = 0;
input_bfd != NULL;
input_bfd = input_bfd->link_next, bfd_indx++)
{
Elf_Internal_Shdr *symtab_hdr;
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
if (symtab_hdr->sh_info == 0)
continue;
local_syms = (Elf_Internal_Sym *) symtab_hdr->contents;
if (local_syms == NULL)
{
local_syms = bfd_elf_get_elf_syms (input_bfd, symtab_hdr,
symtab_hdr->sh_info, 0,
NULL, NULL, NULL);
symtab_hdr->contents = (unsigned char *) local_syms;
}
if (local_syms == NULL)
{
free (all_local_syms);
return FALSE;
}
all_local_syms[bfd_indx] = local_syms;
}
for (input_bfd = info->input_bfds, bfd_indx = 0;
input_bfd != NULL;
input_bfd = input_bfd->link_next, bfd_indx++)
{
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Sym *local_syms;
struct elf_link_hash_entry ** sym_hashes;
sym_hashes = elf_sym_hashes (input_bfd);
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
if (symtab_hdr->sh_info == 0)
continue;
local_syms = all_local_syms[bfd_indx];
for (section = input_bfd->sections;
section != NULL;
section = section->next)
{
Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
if ((section->flags & SEC_RELOC) == 0
|| section->reloc_count == 0)
continue;
if (section->output_section == NULL
|| section->output_section->owner != output_bfd)
continue;
internal_relocs
= _bfd_elf_link_read_relocs (input_bfd, section, NULL,
(Elf_Internal_Rela *) NULL,
info->keep_memory);
if (internal_relocs == NULL)
goto error_ret_free_local;
irela = internal_relocs;
irelaend = irela + section->reloc_count;
for (; irela < irelaend; irela++)
{
unsigned int r_type, r_indx;
struct elf32_m68hc11_stub_hash_entry *stub_entry;
asection *sym_sec;
bfd_vma sym_value;
struct elf_link_hash_entry *hash;
const char *stub_name;
Elf_Internal_Sym *sym;
r_type = ELF32_R_TYPE (irela->r_info);
if (r_type != (unsigned int) R_M68HC11_16)
continue;
r_indx = ELF32_R_SYM (irela->r_info);
if (r_indx < symtab_hdr->sh_info)
{
Elf_Internal_Shdr *hdr;
bfd_boolean is_far;
sym = local_syms + r_indx;
is_far = (sym && (sym->st_other & STO_M68HC12_FAR));
if (!is_far)
continue;
hdr = elf_elfsections (input_bfd)[sym->st_shndx];
sym_sec = hdr->bfd_section;
stub_name = (bfd_elf_string_from_elf_section
(input_bfd, symtab_hdr->sh_link,
sym->st_name));
sym_value = sym->st_value;
hash = NULL;
}
else
{
int e_indx;
e_indx = r_indx - symtab_hdr->sh_info;
hash = (struct elf_link_hash_entry *)
(sym_hashes[e_indx]);
while (hash->root.type == bfd_link_hash_indirect
|| hash->root.type == bfd_link_hash_warning)
hash = ((struct elf_link_hash_entry *)
hash->root.u.i.link);
if (hash->root.type == bfd_link_hash_defined
|| hash->root.type == bfd_link_hash_defweak
|| hash->root.type == bfd_link_hash_new)
{
if (!(hash->other & STO_M68HC12_FAR))
continue;
}
else if (hash->root.type == bfd_link_hash_undefweak)
{
continue;
}
else if (hash->root.type == bfd_link_hash_undefined)
{
continue;
}
else
{
bfd_set_error (bfd_error_bad_value);
goto error_ret_free_internal;
}
sym_sec = hash->root.u.def.section;
sym_value = hash->root.u.def.value;
stub_name = hash->root.root.string;
}
if (!stub_name)
goto error_ret_free_internal;
stub_entry = m68hc12_stub_hash_lookup
(htab->stub_hash_table,
stub_name,
FALSE, FALSE);
if (stub_entry == NULL)
{
if (add_stub_section == 0)
continue;
stub_entry = m68hc12_add_stub (stub_name, section, htab);
if (stub_entry == NULL)
{
error_ret_free_internal:
if (elf_section_data (section)->relocs == NULL)
free (internal_relocs);
goto error_ret_free_local;
}
}
stub_entry->target_value = sym_value;
stub_entry->target_section = sym_sec;
}
if (elf_section_data (section)->relocs == NULL)
free (internal_relocs);
}
}
if (add_stub_section)
{
for (stub_sec = htab->stub_bfd->sections;
stub_sec != NULL;
stub_sec = stub_sec->next)
{
stub_sec->size = 0;
}
bfd_hash_traverse (htab->stub_hash_table, htab->size_one_stub, htab);
}
free (all_local_syms);
return TRUE;
error_ret_free_local:
free (all_local_syms);
return FALSE;
}
static bfd_boolean
m68hc11_elf_export_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
{
struct bfd_link_info *info;
struct m68hc11_elf_link_hash_table *htab;
struct elf32_m68hc11_stub_hash_entry *stub_entry;
char* name;
bfd_boolean result;
info = (struct bfd_link_info *) in_arg;
htab = m68hc11_elf_hash_table (info);
stub_entry = (struct elf32_m68hc11_stub_hash_entry *) gen_entry;
result = (* htab->build_one_stub) (gen_entry, in_arg);
name = alloca (strlen (stub_entry->root.string) + 16);
sprintf (name, "tramp.%s", stub_entry->root.string);
m68hc11_elf_set_symbol (htab->stub_bfd, info, name,
stub_entry->stub_offset,
stub_entry->stub_sec);
return result;
}
static void
m68hc11_elf_set_symbol (bfd *abfd, struct bfd_link_info *info,
const char *name, bfd_vma value, asection *sec)
{
struct elf_link_hash_entry *h;
h = (struct elf_link_hash_entry *)
bfd_link_hash_lookup (info->hash, name, FALSE, FALSE, FALSE);
if (h == NULL)
{
_bfd_generic_link_add_one_symbol (info, abfd,
name,
BSF_GLOBAL,
sec,
value,
(const char*) NULL,
TRUE, FALSE, NULL);
}
else
{
h->root.type = bfd_link_hash_defined;
h->root.u.def.value = value;
h->root.u.def.section = sec;
}
}
bfd_boolean
elf32_m68hc11_build_stubs (bfd *abfd, struct bfd_link_info *info)
{
asection *stub_sec;
struct bfd_hash_table *table;
struct m68hc11_elf_link_hash_table *htab;
struct m68hc11_scan_param param;
m68hc11_elf_get_bank_parameters (info);
htab = m68hc11_elf_hash_table (info);
for (stub_sec = htab->stub_bfd->sections;
stub_sec != NULL;
stub_sec = stub_sec->next)
{
bfd_size_type size;
size = stub_sec->size;
stub_sec->contents = (unsigned char *) bfd_zalloc (htab->stub_bfd, size);
if (stub_sec->contents == NULL && size != 0)
return FALSE;
stub_sec->size = 0;
}
table = htab->stub_hash_table;
bfd_hash_traverse (table, m68hc11_elf_export_one_stub, info);
param.use_memory_banks = FALSE;
param.pinfo = &htab->pinfo;
bfd_map_over_sections (abfd, scan_sections_for_abi, ¶m);
if (param.use_memory_banks)
{
m68hc11_elf_set_symbol (abfd, info, BFD_M68HC11_BANK_START_NAME,
htab->pinfo.bank_physical,
bfd_abs_section_ptr);
m68hc11_elf_set_symbol (abfd, info, BFD_M68HC11_BANK_VIRTUAL_NAME,
htab->pinfo.bank_virtual,
bfd_abs_section_ptr);
m68hc11_elf_set_symbol (abfd, info, BFD_M68HC11_BANK_SIZE_NAME,
htab->pinfo.bank_size,
bfd_abs_section_ptr);
}
return TRUE;
}
void
m68hc11_elf_get_bank_parameters (struct bfd_link_info *info)
{
unsigned i;
struct m68hc11_page_info *pinfo;
struct bfd_link_hash_entry *h;
pinfo = &m68hc11_elf_hash_table (info)->pinfo;
if (pinfo->bank_param_initialized)
return;
pinfo->bank_virtual = M68HC12_BANK_VIRT;
pinfo->bank_mask = M68HC12_BANK_MASK;
pinfo->bank_physical = M68HC12_BANK_BASE;
pinfo->bank_shift = M68HC12_BANK_SHIFT;
pinfo->bank_size = 1 << M68HC12_BANK_SHIFT;
h = bfd_link_hash_lookup (info->hash, BFD_M68HC11_BANK_START_NAME,
FALSE, FALSE, TRUE);
if (h != (struct bfd_link_hash_entry*) NULL
&& h->type == bfd_link_hash_defined)
pinfo->bank_physical = (h->u.def.value
+ h->u.def.section->output_section->vma
+ h->u.def.section->output_offset);
h = bfd_link_hash_lookup (info->hash, BFD_M68HC11_BANK_VIRTUAL_NAME,
FALSE, FALSE, TRUE);
if (h != (struct bfd_link_hash_entry*) NULL
&& h->type == bfd_link_hash_defined)
pinfo->bank_virtual = (h->u.def.value
+ h->u.def.section->output_section->vma
+ h->u.def.section->output_offset);
h = bfd_link_hash_lookup (info->hash, BFD_M68HC11_BANK_SIZE_NAME,
FALSE, FALSE, TRUE);
if (h != (struct bfd_link_hash_entry*) NULL
&& h->type == bfd_link_hash_defined)
pinfo->bank_size = (h->u.def.value
+ h->u.def.section->output_section->vma
+ h->u.def.section->output_offset);
pinfo->bank_shift = 0;
for (i = pinfo->bank_size; i != 0; i >>= 1)
pinfo->bank_shift++;
pinfo->bank_shift--;
pinfo->bank_mask = (1 << pinfo->bank_shift) - 1;
pinfo->bank_physical_end = pinfo->bank_physical + pinfo->bank_size;
pinfo->bank_param_initialized = 1;
h = bfd_link_hash_lookup (info->hash, "__far_trampoline", FALSE,
FALSE, TRUE);
if (h != (struct bfd_link_hash_entry*) NULL
&& h->type == bfd_link_hash_defined)
pinfo->trampoline_addr = (h->u.def.value
+ h->u.def.section->output_section->vma
+ h->u.def.section->output_offset);
}
int
m68hc11_addr_is_banked (struct m68hc11_page_info *pinfo, bfd_vma addr)
{
if (addr >= pinfo->bank_virtual)
return 1;
if (addr >= pinfo->bank_physical && addr <= pinfo->bank_physical_end)
return 1;
return 0;
}
bfd_vma
m68hc11_phys_addr (struct m68hc11_page_info *pinfo, bfd_vma addr)
{
if (addr < pinfo->bank_virtual)
return addr;
addr -= pinfo->bank_virtual;
addr &= pinfo->bank_mask;
addr += pinfo->bank_physical;
return addr;
}
bfd_vma
m68hc11_phys_page (struct m68hc11_page_info *pinfo, bfd_vma addr)
{
if (addr < pinfo->bank_virtual)
return 0;
addr -= pinfo->bank_virtual;
addr >>= pinfo->bank_shift;
addr &= 0x0ff;
return addr;
}
bfd_reloc_status_type
m68hc11_elf_ignore_reloc (bfd *abfd ATTRIBUTE_UNUSED,
arelent *reloc_entry,
asymbol *symbol ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED,
asection *input_section,
bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
if (output_bfd != NULL)
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
bfd_reloc_status_type
m68hc11_elf_special_reloc (bfd *abfd ATTRIBUTE_UNUSED,
arelent *reloc_entry,
asymbol *symbol,
void *data ATTRIBUTE_UNUSED,
asection *input_section,
bfd *output_bfd,
char **error_message ATTRIBUTE_UNUSED)
{
if (output_bfd != (bfd *) NULL
&& (symbol->flags & BSF_SECTION_SYM) == 0
&& (! reloc_entry->howto->partial_inplace
|| reloc_entry->addend == 0))
{
reloc_entry->address += input_section->output_offset;
return bfd_reloc_ok;
}
if (output_bfd != NULL)
return bfd_reloc_continue;
if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
return bfd_reloc_outofrange;
abort();
}
asection *
elf32_m68hc11_gc_mark_hook (asection *sec,
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 (ELF32_R_TYPE (rel->r_info))
{
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 (sec->owner, sym->st_shndx);
return NULL;
}
bfd_boolean
elf32_m68hc11_gc_sweep_hook (bfd *abfd ATTRIBUTE_UNUSED,
struct bfd_link_info *info ATTRIBUTE_UNUSED,
asection *sec ATTRIBUTE_UNUSED,
const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED)
{
return TRUE;
}
bfd_boolean
elf32_m68hc11_check_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;
struct elf_link_hash_entry ** sym_hashes_end;
const Elf_Internal_Rela * rel;
const Elf_Internal_Rela * rel_end;
if (info->relocatable)
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 (Elf32_External_Sym);
if (!elf_bad_symtab (abfd))
sym_hashes_end -= symtab_hdr->sh_info;
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 = ELF32_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
h = NULL;
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;
}
switch (ELF32_R_TYPE (rel->r_info))
{
case R_M68HC11_GNU_VTINHERIT:
if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
return FALSE;
break;
case R_M68HC11_GNU_VTENTRY:
if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
return FALSE;
break;
}
}
return TRUE;
}
static bfd_boolean
m68hc11_get_relocation_value (bfd *input_bfd, struct bfd_link_info *info,
asection *input_section,
asection **local_sections,
Elf_Internal_Sym *local_syms,
Elf_Internal_Rela *rel,
const char **name,
bfd_vma *relocation, bfd_boolean *is_far)
{
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
unsigned long r_symndx;
asection *sec;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
const char* stub_name = 0;
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (input_bfd);
r_symndx = ELF32_R_SYM (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 = (sec->output_section->vma
+ sec->output_offset
+ sym->st_value);
*is_far = (sym && (sym->st_other & STO_M68HC12_FAR));
if (*is_far)
stub_name = (bfd_elf_string_from_elf_section
(input_bfd, symtab_hdr->sh_link,
sym->st_name));
}
else
{
bfd_boolean unresolved_reloc, warned;
RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
r_symndx, symtab_hdr, sym_hashes,
h, sec, *relocation, unresolved_reloc, warned);
*is_far = (h && (h->other & STO_M68HC12_FAR));
stub_name = h->root.root.string;
}
if (h != NULL)
*name = h->root.root.string;
else
{
*name = (bfd_elf_string_from_elf_section
(input_bfd, symtab_hdr->sh_link, sym->st_name));
if (*name == NULL || **name == '\0')
*name = bfd_section_name (input_bfd, sec);
}
if (*is_far && ELF32_R_TYPE (rel->r_info) == R_M68HC11_16)
{
struct elf32_m68hc11_stub_hash_entry* stub;
struct m68hc11_elf_link_hash_table *htab;
htab = m68hc11_elf_hash_table (info);
stub = m68hc12_stub_hash_lookup (htab->stub_hash_table,
*name, FALSE, FALSE);
if (stub)
{
*relocation = stub->stub_offset
+ stub->stub_sec->output_section->vma
+ stub->stub_sec->output_offset;
*is_far = FALSE;
}
}
return TRUE;
}
bfd_boolean
elf32_m68hc11_relocate_section (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, *relend;
const char *name = NULL;
struct m68hc11_page_info *pinfo;
const struct elf_backend_data * const ebd = get_elf_backend_data (input_bfd);
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (input_bfd);
m68hc11_elf_get_bank_parameters (info);
pinfo = &m68hc11_elf_hash_table (info)->pinfo;
rel = relocs;
relend = relocs + input_section->reloc_count;
for (; rel < relend; rel++)
{
int r_type;
arelent arel;
reloc_howto_type *howto;
unsigned long r_symndx;
Elf_Internal_Sym *sym;
asection *sec;
bfd_vma relocation = 0;
bfd_reloc_status_type r = bfd_reloc_undefined;
bfd_vma phys_page;
bfd_vma phys_addr;
bfd_vma insn_addr;
bfd_vma insn_page;
bfd_boolean is_far = FALSE;
r_symndx = ELF32_R_SYM (rel->r_info);
r_type = ELF32_R_TYPE (rel->r_info);
if (r_type == R_M68HC11_GNU_VTENTRY
|| r_type == R_M68HC11_GNU_VTINHERIT )
continue;
if (info->relocatable)
{
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;
}
(*ebd->elf_info_to_howto_rel) (input_bfd, &arel, rel);
howto = arel.howto;
m68hc11_get_relocation_value (input_bfd, info, input_section,
local_sections, local_syms,
rel, &name, &relocation, &is_far);
phys_addr = m68hc11_phys_addr (pinfo, relocation + rel->r_addend);
phys_page = m68hc11_phys_page (pinfo, relocation + rel->r_addend);
switch (r_type)
{
case R_M68HC11_24:
bfd_put_16 (input_bfd, phys_addr,
(bfd_byte*) contents + rel->r_offset);
bfd_put_8 (input_bfd, phys_page,
(bfd_byte*) contents + rel->r_offset + 2);
r = bfd_reloc_ok;
r_type = R_M68HC11_NONE;
break;
case R_M68HC11_NONE:
r = bfd_reloc_ok;
break;
case R_M68HC11_LO16:
relocation = phys_addr;
break;
case R_M68HC11_PAGE:
relocation = phys_page;
break;
case R_M68HC11_16:
if (is_far)
{
const char* msg;
char* buf;
msg = _("Reference to the far symbol `%s' using a wrong "
"relocation may result in incorrect execution");
buf = alloca (strlen (msg) + strlen (name) + 10);
sprintf (buf, msg, name);
(* info->callbacks->warning)
(info, buf, name, input_bfd, NULL, rel->r_offset);
}
insn_addr = input_section->output_section->vma
+ input_section->output_offset
+ rel->r_offset;
insn_page = m68hc11_phys_page (pinfo, insn_addr);
if (m68hc11_addr_is_banked (pinfo, relocation + rel->r_addend)
&& m68hc11_addr_is_banked (pinfo, insn_addr)
&& phys_page != insn_page)
{
const char* msg;
char* buf;
msg = _("banked address [%lx:%04lx] (%lx) is not in the same bank "
"as current banked address [%lx:%04lx] (%lx)");
buf = alloca (strlen (msg) + 128);
sprintf (buf, msg, phys_page, phys_addr,
(long) (relocation + rel->r_addend),
insn_page, m68hc11_phys_addr (pinfo, insn_addr),
(long) (insn_addr));
if (!((*info->callbacks->warning)
(info, buf, name, input_bfd, input_section,
rel->r_offset)))
return FALSE;
break;
}
if (phys_page != 0 && insn_page == 0)
{
const char* msg;
char* buf;
msg = _("reference to a banked address [%lx:%04lx] in the "
"normal address space at %04lx");
buf = alloca (strlen (msg) + 128);
sprintf (buf, msg, phys_page, phys_addr, insn_addr);
if (!((*info->callbacks->warning)
(info, buf, name, input_bfd, input_section,
insn_addr)))
return FALSE;
relocation = phys_addr;
break;
}
if (m68hc11_addr_is_banked (pinfo, relocation + rel->r_addend))
relocation = phys_addr;
break;
}
if (r_type != R_M68HC11_NONE)
r = _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset,
relocation, rel->r_addend);
if (r != bfd_reloc_ok)
{
const char * msg = (const char *) 0;
switch (r)
{
case bfd_reloc_overflow:
if (!((*info->callbacks->reloc_overflow)
(info, NULL, name, howto->name, (bfd_vma) 0,
input_bfd, input_section, rel->r_offset)))
return FALSE;
break;
case bfd_reloc_undefined:
if (!((*info->callbacks->undefined_symbol)
(info, name, input_bfd, input_section,
rel->r_offset, TRUE)))
return FALSE;
break;
case bfd_reloc_outofrange:
msg = _ ("internal error: out of range error");
goto common_error;
case bfd_reloc_notsupported:
msg = _ ("internal error: unsupported relocation error");
goto common_error;
case bfd_reloc_dangerous:
msg = _ ("internal error: dangerous error");
goto common_error;
default:
msg = _ ("internal error: unknown error");
common_error:
if (!((*info->callbacks->warning)
(info, msg, name, input_bfd, input_section,
rel->r_offset)))
return FALSE;
break;
}
}
}
return TRUE;
}
bfd_boolean
_bfd_m68hc11_elf_set_private_flags (bfd *abfd, flagword flags)
{
BFD_ASSERT (!elf_flags_init (abfd)
|| elf_elfheader (abfd)->e_flags == flags);
elf_elfheader (abfd)->e_flags = flags;
elf_flags_init (abfd) = TRUE;
return TRUE;
}
bfd_boolean
_bfd_m68hc11_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
{
flagword old_flags;
flagword new_flags;
bfd_boolean ok = TRUE;
if (!_bfd_generic_verify_endian_match (ibfd, obfd))
return FALSE;
if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return TRUE;
new_flags = elf_elfheader (ibfd)->e_flags;
elf_elfheader (obfd)->e_flags |= new_flags & EF_M68HC11_ABI;
old_flags = elf_elfheader (obfd)->e_flags;
if (! elf_flags_init (obfd))
{
elf_flags_init (obfd) = TRUE;
elf_elfheader (obfd)->e_flags = new_flags;
elf_elfheader (obfd)->e_ident[EI_CLASS]
= elf_elfheader (ibfd)->e_ident[EI_CLASS];
if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
&& bfd_get_arch_info (obfd)->the_default)
{
if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
bfd_get_mach (ibfd)))
return FALSE;
}
return TRUE;
}
if ((new_flags & E_M68HC11_I32) != (old_flags & E_M68HC11_I32))
{
(*_bfd_error_handler)
(_("%B: linking files compiled for 16-bit integers (-mshort) "
"and others for 32-bit integers"), ibfd);
ok = FALSE;
}
if ((new_flags & E_M68HC11_F64) != (old_flags & E_M68HC11_F64))
{
(*_bfd_error_handler)
(_("%B: linking files compiled for 32-bit double (-fshort-double) "
"and others for 64-bit double"), ibfd);
ok = FALSE;
}
if (!EF_M68HC11_CAN_MERGE_MACH (new_flags, old_flags))
{
(*_bfd_error_handler)
(_("%B: linking files compiled for HCS12 with "
"others compiled for HC12"), ibfd);
ok = FALSE;
}
new_flags = ((new_flags & ~EF_M68HC11_MACH_MASK)
| (EF_M68HC11_MERGE_MACH (new_flags, old_flags)));
elf_elfheader (obfd)->e_flags = new_flags;
new_flags &= ~(EF_M68HC11_ABI | EF_M68HC11_MACH_MASK);
old_flags &= ~(EF_M68HC11_ABI | EF_M68HC11_MACH_MASK);
if (new_flags != old_flags)
{
(*_bfd_error_handler)
(_("%B: uses different e_flags (0x%lx) fields than previous modules (0x%lx)"),
ibfd, (unsigned long) new_flags, (unsigned long) old_flags);
ok = FALSE;
}
if (! ok)
{
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
return TRUE;
}
bfd_boolean
_bfd_m68hc11_elf_print_private_bfd_data (bfd *abfd, void *ptr)
{
FILE *file = (FILE *) ptr;
BFD_ASSERT (abfd != NULL && ptr != NULL);
_bfd_elf_print_private_bfd_data (abfd, ptr);
fprintf (file, _("private flags = %lx:"), elf_elfheader (abfd)->e_flags);
if (elf_elfheader (abfd)->e_flags & E_M68HC11_I32)
fprintf (file, _("[abi=32-bit int, "));
else
fprintf (file, _("[abi=16-bit int, "));
if (elf_elfheader (abfd)->e_flags & E_M68HC11_F64)
fprintf (file, _("64-bit double, "));
else
fprintf (file, _("32-bit double, "));
if (strcmp (bfd_get_target (abfd), "elf32-m68hc11") == 0)
fprintf (file, _("cpu=HC11]"));
else if (elf_elfheader (abfd)->e_flags & EF_M68HCS12_MACH)
fprintf (file, _("cpu=HCS12]"));
else
fprintf (file, _("cpu=HC12]"));
if (elf_elfheader (abfd)->e_flags & E_M68HC12_BANKS)
fprintf (file, _(" [memory=bank-model]"));
else
fprintf (file, _(" [memory=flat]"));
fputc ('\n', file);
return TRUE;
}
static void scan_sections_for_abi (bfd *abfd ATTRIBUTE_UNUSED,
asection *asect, void *arg)
{
struct m68hc11_scan_param* p = (struct m68hc11_scan_param*) arg;
if (asect->vma >= p->pinfo->bank_virtual)
p->use_memory_banks = TRUE;
}
void
elf32_m68hc11_post_process_headers (bfd *abfd, struct bfd_link_info *link_info)
{
struct m68hc11_scan_param param;
if (link_info == 0)
return;
m68hc11_elf_get_bank_parameters (link_info);
param.use_memory_banks = FALSE;
param.pinfo = &m68hc11_elf_hash_table (link_info)->pinfo;
bfd_map_over_sections (abfd, scan_sections_for_abi, ¶m);
if (param.use_memory_banks)
{
Elf_Internal_Ehdr * i_ehdrp;
i_ehdrp = elf_elfheader (abfd);
i_ehdrp->e_flags |= E_M68HC12_BANKS;
}
}