#define SH64_ELF
#include "bfd.h"
#include "sysdep.h"
#include "elf-bfd.h"
#include "../opcodes/sh64-opc.h"
#define DATALABEL_SUFFIX " DL"
struct sh64_find_section_vma_data
{
asection *section;
bfd_vma addr;
};
static boolean sh64_elf_copy_private_data PARAMS ((bfd *, bfd *));
static boolean sh64_elf_merge_private_data PARAMS ((bfd *, bfd *));
static boolean sh64_elf_fake_sections PARAMS ((bfd *, Elf_Internal_Shdr *,
asection *));
static boolean sh64_elf_set_private_flags PARAMS ((bfd *, flagword));
static boolean sh64_elf_set_mach_from_flags PARAMS ((bfd *));
static boolean shmedia_prepare_reloc
PARAMS ((struct bfd_link_info *, bfd *, asection *,
bfd_byte *, const Elf_Internal_Rela *, bfd_vma *));
static int sh64_elf_get_symbol_type PARAMS ((Elf_Internal_Sym *, int));
static boolean sh64_elf_add_symbol_hook
PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Sym *,
const char **, flagword *, asection **, bfd_vma *));
static boolean sh64_elf_link_output_symbol_hook
PARAMS ((bfd *, struct bfd_link_info *, const char *, Elf_Internal_Sym *,
asection *));
static boolean sh64_backend_section_from_shdr
PARAMS ((bfd *, Elf_Internal_Shdr *, char *));
static void sh64_elf_final_write_processing PARAMS ((bfd *, boolean));
static boolean sh64_bfd_elf_copy_private_section_data
PARAMS ((bfd *, asection *, bfd *, asection *));
static void sh64_find_section_for_address PARAMS ((bfd *, asection *, PTR));
#define sh_elf_copy_private_data sh64_elf_copy_private_data
#define sh_elf_merge_private_data sh64_elf_merge_private_data
#define sh_elf_set_private_flags sh64_elf_set_private_flags
#define bfd_elf32_bfd_set_private_flags sh64_elf_set_private_flags
#define sh_elf_set_mach_from_flags sh64_elf_set_mach_from_flags
#define elf_backend_sign_extend_vma 1
#define elf_backend_fake_sections sh64_elf_fake_sections
#define elf_backend_get_symbol_type sh64_elf_get_symbol_type
#define elf_backend_add_symbol_hook sh64_elf_add_symbol_hook
#define elf_backend_link_output_symbol_hook \
sh64_elf_link_output_symbol_hook
#define elf_backend_final_write_processing sh64_elf_final_write_processing
#define elf_backend_section_from_shdr sh64_backend_section_from_shdr
#define bfd_elf32_bfd_copy_private_section_data \
sh64_bfd_elf_copy_private_section_data
#define _bfd_sh_align_load_span(a,b,c,d,e,f,g,h,i,j) true
#ifndef ELF_ARCH
#define TARGET_BIG_SYM bfd_elf32_sh64_vec
#define TARGET_BIG_NAME "elf32-sh64"
#define TARGET_LITTLE_SYM bfd_elf32_sh64l_vec
#define TARGET_LITTLE_NAME "elf32-sh64l"
#define ELF_ARCH bfd_arch_sh
#define ELF_MACHINE_CODE EM_SH
#define ELF_MAXPAGESIZE 128
#define elf_symbol_leading_char '_'
#endif
#define GOT_BIAS (-((long)-32768))
#define INCLUDE_SHMEDIA
#include "elf32-sh.c"
static int crange_qsort_cmpb PARAMS ((const void *, const void *));
static int crange_qsort_cmpl PARAMS ((const void *, const void *));
static int crange_bsearch_cmpb PARAMS ((const void *, const void *));
static int crange_bsearch_cmpl PARAMS ((const void *, const void *));
static boolean sh64_address_in_cranges
PARAMS ((asection *cranges, bfd_vma, sh64_elf_crange *));
boolean
sh64_elf_fake_sections (output_bfd, elf_section_hdr, asect)
bfd *output_bfd ATTRIBUTE_UNUSED;
Elf_Internal_Shdr *elf_section_hdr;
asection *asect;
{
if (sh64_elf_section_data (asect) != NULL)
elf_section_hdr->sh_flags
|= sh64_elf_section_data (asect)->contents_flags;
if ((bfd_get_section_flags (output_bfd, asect) & SEC_SORT_ENTRIES) != 0
&& strcmp (bfd_get_section_name (output_bfd, asect),
SH64_CRANGES_SECTION_NAME) == 0)
elf_section_hdr->sh_type = SHT_SH5_CR_SORTED;
return true;
}
static boolean
sh64_elf_set_mach_from_flags (abfd)
bfd *abfd;
{
flagword flags = elf_elfheader (abfd)->e_flags;
asection *cranges;
switch (flags & EF_SH_MACH_MASK)
{
case EF_SH5:
bfd_default_set_arch_mach (abfd, bfd_arch_sh, bfd_mach_sh5);
break;
default:
bfd_set_error (bfd_error_wrong_format);
return false;
}
cranges = bfd_get_section_by_name (abfd, SH64_CRANGES_SECTION_NAME);
if (cranges != NULL
&& ! bfd_set_section_flags (abfd, cranges,
bfd_get_section_flags (abfd, cranges)
| SEC_DEBUGGING))
return false;
return true;
}
static boolean
sh64_elf_copy_private_data (ibfd, obfd)
bfd * ibfd;
bfd * obfd;
{
if ( bfd_get_flavour (ibfd) != bfd_target_elf_flavour
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return true;
BFD_ASSERT (!elf_flags_init (obfd)
|| (elf_elfheader (obfd)->e_flags
== elf_elfheader (ibfd)->e_flags));
elf_elfheader (obfd)->e_flags = elf_elfheader (ibfd)->e_flags;
return true;
}
static boolean
sh64_elf_merge_private_data (ibfd, obfd)
bfd *ibfd;
bfd *obfd;
{
flagword old_flags, new_flags;
if (_bfd_generic_verify_endian_match (ibfd, obfd) == false)
return false;
if ( bfd_get_flavour (ibfd) != bfd_target_elf_flavour
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return true;
if (bfd_get_arch_size (ibfd) != bfd_get_arch_size (obfd))
{
const char *msg;
if (bfd_get_arch_size (ibfd) == 32
&& bfd_get_arch_size (obfd) == 64)
msg = _("%s: compiled as 32-bit object and %s is 64-bit");
else if (bfd_get_arch_size (ibfd) == 64
&& bfd_get_arch_size (obfd) == 32)
msg = _("%s: compiled as 64-bit object and %s is 32-bit");
else
msg = _("%s: object size does not match that of target %s");
(*_bfd_error_handler) (msg, bfd_get_filename (ibfd),
bfd_get_filename (obfd));
bfd_set_error (bfd_error_wrong_format);
return false;
}
old_flags = elf_elfheader (obfd)->e_flags;
new_flags = elf_elfheader (ibfd)->e_flags;
if (! elf_flags_init (obfd))
{
elf_flags_init (obfd) = true;
elf_elfheader (obfd)->e_flags = old_flags = new_flags;
}
else if ((new_flags & EF_SH_MACH_MASK) != EF_SH5)
{
(*_bfd_error_handler)
("%s: uses non-SH64 instructions while previous modules use SH64 instructions",
bfd_get_filename (ibfd));
bfd_set_error (bfd_error_bad_value);
return false;
}
elf_elfheader (obfd)->e_flags = old_flags;
return sh64_elf_set_mach_from_flags (obfd);
}
boolean
sh64_backend_section_from_shdr (abfd, hdr, name)
bfd *abfd;
Elf_Internal_Shdr *hdr;
char *name;
{
flagword flags = 0;
switch (hdr->sh_type)
{
case SHT_SH5_CR_SORTED:
if (strcmp (name, SH64_CRANGES_SECTION_NAME) != 0)
return false;
flags = SEC_DEBUGGING | SEC_SORT_ENTRIES;
break;
default:
return false;
}
if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name))
return false;
if (flags
&& ! bfd_set_section_flags (abfd, hdr->bfd_section,
bfd_get_section_flags (abfd,
hdr->bfd_section)
| flags))
return false;
return true;
}
boolean
sh64_bfd_elf_copy_private_section_data (ibfd, isec, obfd, osec)
bfd *ibfd;
asection *isec;
bfd *obfd;
asection *osec;
{
struct sh64_section_data *sh64_sec_data;
if (ibfd->xvec->flavour != bfd_target_elf_flavour
|| obfd->xvec->flavour != bfd_target_elf_flavour)
return true;
if (! _bfd_elf_copy_private_section_data (ibfd, isec, obfd, osec))
return false;
sh64_sec_data = sh64_elf_section_data (isec);
if (sh64_sec_data == NULL)
{
sh64_sec_data = bfd_zmalloc (sizeof (struct sh64_section_data));
if (sh64_sec_data == NULL)
return false;
sh64_sec_data->contents_flags
= (elf_section_data (isec)->this_hdr.sh_flags
& (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED));
sh64_elf_section_data (osec) = sh64_sec_data;
}
return true;
}
static boolean
sh64_elf_set_private_flags (abfd, 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 sh64_elf_set_mach_from_flags (abfd);
}
static int
sh64_elf_get_symbol_type (elf_sym, type)
Elf_Internal_Sym * elf_sym;
int type;
{
if (ELF_ST_TYPE (elf_sym->st_info) == STT_DATALABEL)
return STT_DATALABEL;
return type;
}
static boolean
sh64_elf_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp)
bfd *abfd;
struct bfd_link_info *info;
const Elf_Internal_Sym *sym;
const char **namep;
flagword *flagsp ATTRIBUTE_UNUSED;
asection **secp;
bfd_vma *valp;
{
if (ELF_ST_TYPE (sym->st_info) == STT_DATALABEL)
{
struct elf_link_hash_entry *h;
flagword flags
= info->relocateable || info->emitrelocations
? BSF_GLOBAL : BSF_GLOBAL | BSF_INDIRECT;
char *dl_name
= bfd_malloc (strlen (*namep) + sizeof (DATALABEL_SUFFIX));
struct elf_link_hash_entry ** sym_hash = elf_sym_hashes (abfd);
BFD_ASSERT (sym_hash != NULL);
if (dl_name == NULL)
return false;
strcpy (dl_name, *namep);
strcat (dl_name, DATALABEL_SUFFIX);
h = (struct elf_link_hash_entry *)
bfd_link_hash_lookup (info->hash, dl_name, false, false, false);
if (h == NULL)
{
if (! _bfd_generic_link_add_one_symbol (info, abfd, dl_name,
flags, *secp, *valp,
*namep, false,
get_elf_backend_data (abfd)->collect,
(struct bfd_link_hash_entry **) &h))
{
free (dl_name);
return false;
}
h->elf_link_hash_flags &=~ ELF_LINK_NON_ELF;
h->type = STT_DATALABEL;
}
else
free (dl_name);
if (h->type != STT_DATALABEL
|| ((info->relocateable || info->emitrelocations)
&& h->root.type != bfd_link_hash_undefined)
|| (! info->relocateable && !info->emitrelocations
&& h->root.type != bfd_link_hash_indirect))
{
(*_bfd_error_handler)
(_("%s: encountered datalabel symbol in input"),
bfd_get_filename (abfd));
bfd_set_error (bfd_error_bad_value);
return false;
}
while (*sym_hash != NULL)
sym_hash++;
*sym_hash = h;
*namep = NULL;
}
return true;
}
boolean
sh64_elf_link_output_symbol_hook (abfd, info, cname, sym, input_sec)
bfd *abfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info;
const char *cname;
Elf_Internal_Sym *sym;
asection *input_sec ATTRIBUTE_UNUSED;
{
char *name = (char *) cname;
if (info->relocateable || info->emitrelocations)
{
if (ELF_ST_TYPE (sym->st_info) == STT_DATALABEL)
name[strlen (name) - strlen (DATALABEL_SUFFIX)] = 0;
}
return true;
}
static boolean
shmedia_prepare_reloc (info, abfd, input_section,
contents, rel, relocation)
struct bfd_link_info *info;
bfd *abfd;
asection *input_section;
bfd_byte *contents;
const Elf_Internal_Rela *rel;
bfd_vma *relocation;
{
bfd_vma disp, dropped;
switch (ELF32_R_TYPE (rel->r_info))
{
case R_SH_PT_16:
{
char *msg = NULL;
bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset);
if (insn & (1 << 10))
{
if ((insn & SHMEDIA_PTB_BIT) != 0
&& ((*relocation + rel->r_addend) & 1) != 0)
msg = _("PTB mismatch: a SHmedia address (bit 0 == 1)");
else if ((insn & SHMEDIA_PTB_BIT) == 0
&& ((*relocation + rel->r_addend) & 1) == 0)
msg = _("PTA mismatch: a SHcompact address (bit 0 == 0)");
if (msg != NULL
&& ! ((*info->callbacks->reloc_dangerous)
(info, msg, abfd, input_section,
rel->r_offset)))
return false;
}
else
{
if ((insn & SHMEDIA_PTB_BIT) != 0)
{
(*_bfd_error_handler)
(_("%s: GAS error: unexpected PTB insn with R_SH_PT_16"),
bfd_get_filename (input_section->owner));
return false;
}
if (((*relocation + rel->r_addend) & 1) == 0)
bfd_put_32 (abfd, insn | SHMEDIA_PTB_BIT,
contents + rel->r_offset);
}
}
case R_SH_SHMEDIA_CODE:
case R_SH_DIR5U:
case R_SH_DIR6S:
case R_SH_DIR6U:
case R_SH_DIR10S:
case R_SH_DIR10SW:
case R_SH_DIR10SL:
case R_SH_DIR10SQ:
case R_SH_IMMS16:
case R_SH_IMMU16:
case R_SH_IMM_LOW16:
case R_SH_IMM_LOW16_PCREL:
case R_SH_IMM_MEDLOW16:
case R_SH_IMM_MEDLOW16_PCREL:
case R_SH_IMM_MEDHI16:
case R_SH_IMM_MEDHI16_PCREL:
case R_SH_IMM_HI16:
case R_SH_IMM_HI16_PCREL:
case R_SH_64:
case R_SH_64_PCREL:
break;
default:
return false;
}
disp = (*relocation & 0xf);
dropped = 0;
switch (ELF32_R_TYPE (rel->r_info))
{
case R_SH_DIR10SW: dropped = disp & 1; break;
case R_SH_DIR10SL: dropped = disp & 3; break;
case R_SH_DIR10SQ: dropped = disp & 7; break;
}
if (dropped != 0)
{
(*_bfd_error_handler)
(_("%s: error: unaligned relocation type %d at %08x reloc %08x\n"),
bfd_get_filename (input_section->owner), ELF32_R_TYPE (rel->r_info),
(unsigned)rel->r_offset, (unsigned)relocation);
return false;
}
return true;
}
static void
sh64_find_section_for_address (abfd, section, data)
bfd *abfd ATTRIBUTE_UNUSED;
asection *section;
PTR data;
{
bfd_vma vma;
bfd_size_type size;
struct sh64_find_section_vma_data *fsec_datap
= (struct sh64_find_section_vma_data *) data;
if (fsec_datap->section)
return;
if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0)
return;
vma = bfd_get_section_vma (abfd, section);
if (fsec_datap->addr < vma)
return;
size
= section->_cooked_size ? section->_cooked_size : section->_raw_size;
if (fsec_datap->addr >= vma + size)
return;
fsec_datap->section = section;
}
static void
sh64_elf_final_write_processing (abfd, linker)
bfd * abfd;
boolean linker ATTRIBUTE_UNUSED;
{
bfd_vma ld_generated_cranges_size;
asection *cranges
= bfd_get_section_by_name (abfd, SH64_CRANGES_SECTION_NAME);
if (linker
&& cranges != NULL
&& elf_elfheader (abfd)->e_type != ET_EXEC
&& (ld_generated_cranges_size
= sh64_elf_section_data (cranges)->cranges_growth) != 0)
{
bfd_vma incoming_cranges_size
= ((cranges->_cooked_size != 0
? cranges->_cooked_size : cranges->_raw_size)
- ld_generated_cranges_size);
if (! bfd_set_section_contents (abfd, cranges,
cranges->contents
+ incoming_cranges_size,
cranges->output_offset
+ incoming_cranges_size,
ld_generated_cranges_size))
{
bfd_set_error (bfd_error_file_truncated);
(*_bfd_error_handler)
(_("%s: could not write out added .cranges entries"),
bfd_get_filename (abfd));
}
}
if (linker && elf_elfheader (abfd)->e_type == ET_EXEC)
{
struct sh64_find_section_vma_data fsec_data;
sh64_elf_crange dummy;
fsec_data.addr = elf_elfheader (abfd)->e_entry;
fsec_data.section = NULL;
bfd_map_over_sections (abfd, sh64_find_section_for_address,
(PTR) &fsec_data);
if (fsec_data.section
&& (sh64_get_contents_type (fsec_data.section,
elf_elfheader (abfd)->e_entry,
&dummy) == CRT_SH5_ISA32))
elf_elfheader (abfd)->e_entry |= 1;
if (cranges != NULL)
{
bfd_size_type cranges_size
= (cranges->_cooked_size != 0
? cranges->_cooked_size : cranges->_raw_size);
BFD_ASSERT (cranges->contents != NULL);
if (elf_section_data (cranges)->this_hdr.sh_type
!= SHT_SH5_CR_SORTED)
{
qsort (cranges->contents, cranges_size / SH64_CRANGE_SIZE,
SH64_CRANGE_SIZE,
bfd_big_endian (cranges->owner)
? crange_qsort_cmpb : crange_qsort_cmpl);
elf_section_data (cranges)->this_hdr.sh_type
= SHT_SH5_CR_SORTED;
}
if (! bfd_set_section_contents (abfd, cranges,
cranges->contents,
cranges->output_offset,
cranges_size))
{
bfd_set_error (bfd_error_file_truncated);
(*_bfd_error_handler)
(_("%s: could not write out sorted .cranges entries"),
bfd_get_filename (abfd));
}
}
}
}
static int
crange_qsort_cmpb (p1, p2)
const PTR p1;
const PTR p2;
{
bfd_vma a1 = bfd_getb32 (p1);
bfd_vma a2 = bfd_getb32 (p2);
if (a1 == a2)
return (char *) p1 - (char *) p2;
return a1 - a2;
}
static int
crange_qsort_cmpl (p1, p2)
const PTR p1;
const PTR p2;
{
bfd_vma a1 = (bfd_vma) bfd_getl32 (p1);
bfd_vma a2 = (bfd_vma) bfd_getl32 (p2);
if (a1 == a2)
return (char *) p1 - (char *) p2;
return a1 - a2;
}
static int
crange_bsearch_cmpb (p1, p2)
const PTR p1;
const PTR p2;
{
bfd_vma a1 = *(bfd_vma *) p1;
bfd_vma a2 = (bfd_vma) bfd_getb32 (p2);
bfd_size_type size
= (bfd_size_type) bfd_getb32 (SH64_CRANGE_CR_SIZE_OFFSET + (char *) p2);
if (a1 >= a2 + size)
return 1;
if (a1 < a2)
return -1;
return 0;
}
static int
crange_bsearch_cmpl (p1, p2)
const PTR p1;
const PTR p2;
{
bfd_vma a1 = *(bfd_vma *) p1;
bfd_vma a2 = (bfd_vma) bfd_getl32 (p2);
bfd_size_type size
= (bfd_size_type) bfd_getl32 (SH64_CRANGE_CR_SIZE_OFFSET + (char *) p2);
if (a1 >= a2 + size)
return 1;
if (a1 < a2)
return -1;
return 0;
}
static boolean
sh64_address_in_cranges (cranges, addr, rangep)
asection *cranges;
bfd_vma addr;
sh64_elf_crange *rangep;
{
bfd_byte *cranges_contents;
bfd_byte *found_rangep;
bfd_size_type cranges_size = bfd_section_size (cranges->owner, cranges);
if ((cranges_size % SH64_CRANGE_SIZE) != 0)
return false;
if (bfd_get_section_flags (cranges->owner, cranges) & SEC_RELOC)
return false;
if ((bfd_get_section_flags (cranges->owner, cranges) & SEC_IN_MEMORY)
&& elf_section_data (cranges)->this_hdr.sh_type == SHT_SH5_CR_SORTED)
cranges_contents = cranges->contents;
else
{
cranges_contents
= bfd_malloc (cranges->_cooked_size == 0
? cranges->_cooked_size : cranges->_raw_size);
if (cranges_contents == NULL)
return false;
if (! bfd_get_section_contents (cranges->owner, cranges,
cranges_contents, (file_ptr) 0,
cranges_size))
goto error_return;
if (elf_section_data (cranges)->this_hdr.sh_type
!= SHT_SH5_CR_SORTED)
qsort (cranges_contents, cranges_size / SH64_CRANGE_SIZE,
SH64_CRANGE_SIZE,
bfd_big_endian (cranges->owner)
? crange_qsort_cmpb : crange_qsort_cmpl);
cranges->contents = cranges_contents;
bfd_set_section_flags (cranges->owner, cranges,
bfd_get_section_flags (cranges->owner, cranges)
| SEC_IN_MEMORY);
elf_section_data (cranges)->this_hdr.sh_type = SHT_SH5_CR_SORTED;
}
found_rangep
= bsearch (&addr, cranges_contents, cranges_size / SH64_CRANGE_SIZE,
SH64_CRANGE_SIZE,
bfd_big_endian (cranges->owner)
? crange_bsearch_cmpb : crange_bsearch_cmpl);
if (found_rangep)
{
enum sh64_elf_cr_type cr_type
= bfd_get_16 (cranges->owner,
SH64_CRANGE_CR_TYPE_OFFSET + found_rangep);
bfd_vma cr_addr
= bfd_get_32 (cranges->owner,
SH64_CRANGE_CR_ADDR_OFFSET
+ (char *) found_rangep);
bfd_size_type cr_size
= bfd_get_32 (cranges->owner,
SH64_CRANGE_CR_SIZE_OFFSET
+ (char *) found_rangep);
rangep->cr_addr = cr_addr;
rangep->cr_size = cr_size;
rangep->cr_type = cr_type;
return true;
}
return false;
error_return:
free (cranges_contents);
return false;
}
enum sh64_elf_cr_type
sh64_get_contents_type (sec, addr, rangep)
asection *sec;
bfd_vma addr;
sh64_elf_crange *rangep;
{
asection *cranges;
if (bfd_get_flavour (sec->owner) == bfd_target_elf_flavour
&& elf_elfheader (sec->owner)->e_type == ET_EXEC)
{
rangep->cr_addr = bfd_get_section_vma (sec->owner, sec);
rangep->cr_size = bfd_section_size (sec->owner, sec);
rangep->cr_type = CRT_NONE;
}
else
return false;
if ((elf_section_data (sec)->this_hdr.sh_flags
& (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)) == 0)
{
enum sh64_elf_cr_type cr_type
= ((bfd_get_section_flags (sec->owner, sec) & SEC_CODE) != 0
? CRT_SH5_ISA16 : CRT_DATA);
rangep->cr_type = cr_type;
return cr_type;
}
if ((elf_section_data (sec)->this_hdr.sh_flags
& (SHF_SH5_ISA32 | SHF_SH5_ISA32_MIXED)) == SHF_SH5_ISA32)
{
rangep->cr_type = CRT_SH5_ISA32;
return CRT_SH5_ISA32;
}
cranges = bfd_get_section_by_name (sec->owner, SH64_CRANGES_SECTION_NAME);
if (cranges == NULL)
return CRT_NONE;
sh64_address_in_cranges (cranges, addr, rangep);
return rangep->cr_type;
}
boolean
sh64_address_is_shmedia (sec, addr)
asection *sec;
bfd_vma addr;
{
sh64_elf_crange dummy;
return sh64_get_contents_type (sec, addr, &dummy) == CRT_SH5_ISA32;
}