#include "bfd.h"
#include "sysdep.h"
#include "bfdlink.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/m68k.h"
static reloc_howto_type *reloc_type_lookup
PARAMS ((bfd *, bfd_reloc_code_real_type));
static void rtype_to_howto
PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
static struct bfd_hash_entry *elf_m68k_link_hash_newfunc
PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
static struct bfd_link_hash_table *elf_m68k_link_hash_table_create
PARAMS ((bfd *));
static bfd_boolean elf_m68k_check_relocs
PARAMS ((bfd *, struct bfd_link_info *, asection *,
const Elf_Internal_Rela *));
static asection *elf_m68k_gc_mark_hook
PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *,
struct elf_link_hash_entry *, Elf_Internal_Sym *));
static bfd_boolean elf_m68k_gc_sweep_hook
PARAMS ((bfd *, struct bfd_link_info *, asection *,
const Elf_Internal_Rela *));
static bfd_boolean elf_m68k_adjust_dynamic_symbol
PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
static bfd_boolean elf_m68k_size_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
static bfd_boolean elf_m68k_relocate_section
PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
static bfd_boolean elf_m68k_finish_dynamic_symbol
PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *,
Elf_Internal_Sym *));
static bfd_boolean elf_m68k_finish_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
static bfd_boolean elf32_m68k_set_private_flags
PARAMS ((bfd *, flagword));
static bfd_boolean elf32_m68k_merge_private_bfd_data
PARAMS ((bfd *, bfd *));
static bfd_boolean elf32_m68k_print_private_bfd_data
PARAMS ((bfd *, PTR));
static enum elf_reloc_type_class elf32_m68k_reloc_type_class
PARAMS ((const Elf_Internal_Rela *));
static reloc_howto_type howto_table[] = {
HOWTO(R_68K_NONE, 0, 0, 0, FALSE,0, complain_overflow_dont, bfd_elf_generic_reloc, "R_68K_NONE", FALSE, 0, 0x00000000,FALSE),
HOWTO(R_68K_32, 0, 2,32, FALSE,0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_68K_32", FALSE, 0, 0xffffffff,FALSE),
HOWTO(R_68K_16, 0, 1,16, FALSE,0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_68K_16", FALSE, 0, 0x0000ffff,FALSE),
HOWTO(R_68K_8, 0, 0, 8, FALSE,0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_68K_8", FALSE, 0, 0x000000ff,FALSE),
HOWTO(R_68K_PC32, 0, 2,32, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_68K_PC32", FALSE, 0, 0xffffffff,TRUE),
HOWTO(R_68K_PC16, 0, 1,16, TRUE, 0, complain_overflow_signed, bfd_elf_generic_reloc, "R_68K_PC16", FALSE, 0, 0x0000ffff,TRUE),
HOWTO(R_68K_PC8, 0, 0, 8, TRUE, 0, complain_overflow_signed, bfd_elf_generic_reloc, "R_68K_PC8", FALSE, 0, 0x000000ff,TRUE),
HOWTO(R_68K_GOT32, 0, 2,32, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_68K_GOT32", FALSE, 0, 0xffffffff,TRUE),
HOWTO(R_68K_GOT16, 0, 1,16, TRUE, 0, complain_overflow_signed, bfd_elf_generic_reloc, "R_68K_GOT16", FALSE, 0, 0x0000ffff,TRUE),
HOWTO(R_68K_GOT8, 0, 0, 8, TRUE, 0, complain_overflow_signed, bfd_elf_generic_reloc, "R_68K_GOT8", FALSE, 0, 0x000000ff,TRUE),
HOWTO(R_68K_GOT32O, 0, 2,32, FALSE,0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_68K_GOT32O", FALSE, 0, 0xffffffff,FALSE),
HOWTO(R_68K_GOT16O, 0, 1,16, FALSE,0, complain_overflow_signed, bfd_elf_generic_reloc, "R_68K_GOT16O", FALSE, 0, 0x0000ffff,FALSE),
HOWTO(R_68K_GOT8O, 0, 0, 8, FALSE,0, complain_overflow_signed, bfd_elf_generic_reloc, "R_68K_GOT8O", FALSE, 0, 0x000000ff,FALSE),
HOWTO(R_68K_PLT32, 0, 2,32, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_68K_PLT32", FALSE, 0, 0xffffffff,TRUE),
HOWTO(R_68K_PLT16, 0, 1,16, TRUE, 0, complain_overflow_signed, bfd_elf_generic_reloc, "R_68K_PLT16", FALSE, 0, 0x0000ffff,TRUE),
HOWTO(R_68K_PLT8, 0, 0, 8, TRUE, 0, complain_overflow_signed, bfd_elf_generic_reloc, "R_68K_PLT8", FALSE, 0, 0x000000ff,TRUE),
HOWTO(R_68K_PLT32O, 0, 2,32, FALSE,0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_68K_PLT32O", FALSE, 0, 0xffffffff,FALSE),
HOWTO(R_68K_PLT16O, 0, 1,16, FALSE,0, complain_overflow_signed, bfd_elf_generic_reloc, "R_68K_PLT16O", FALSE, 0, 0x0000ffff,FALSE),
HOWTO(R_68K_PLT8O, 0, 0, 8, FALSE,0, complain_overflow_signed, bfd_elf_generic_reloc, "R_68K_PLT8O", FALSE, 0, 0x000000ff,FALSE),
HOWTO(R_68K_COPY, 0, 0, 0, FALSE,0, complain_overflow_dont, bfd_elf_generic_reloc, "R_68K_COPY", FALSE, 0, 0xffffffff,FALSE),
HOWTO(R_68K_GLOB_DAT, 0, 2,32, FALSE,0, complain_overflow_dont, bfd_elf_generic_reloc, "R_68K_GLOB_DAT", FALSE, 0, 0xffffffff,FALSE),
HOWTO(R_68K_JMP_SLOT, 0, 2,32, FALSE,0, complain_overflow_dont, bfd_elf_generic_reloc, "R_68K_JMP_SLOT", FALSE, 0, 0xffffffff,FALSE),
HOWTO(R_68K_RELATIVE, 0, 2,32, FALSE,0, complain_overflow_dont, bfd_elf_generic_reloc, "R_68K_RELATIVE", FALSE, 0, 0xffffffff,FALSE),
HOWTO (R_68K_GNU_VTINHERIT,
0,
2,
0,
FALSE,
0,
complain_overflow_dont,
NULL,
"R_68K_GNU_VTINHERIT",
FALSE,
0,
0,
FALSE),
HOWTO (R_68K_GNU_VTENTRY,
0,
2,
0,
FALSE,
0,
complain_overflow_dont,
_bfd_elf_rel_vtable_reloc_fn,
"R_68K_GNU_VTENTRY",
FALSE,
0,
0,
FALSE),
};
static void
rtype_to_howto (abfd, cache_ptr, dst)
bfd *abfd ATTRIBUTE_UNUSED;
arelent *cache_ptr;
Elf_Internal_Rela *dst;
{
BFD_ASSERT (ELF32_R_TYPE(dst->r_info) < (unsigned int) R_68K_max);
cache_ptr->howto = &howto_table[ELF32_R_TYPE(dst->r_info)];
}
#define elf_info_to_howto rtype_to_howto
static const struct
{
bfd_reloc_code_real_type bfd_val;
int elf_val;
} reloc_map[] = {
{ BFD_RELOC_NONE, R_68K_NONE },
{ BFD_RELOC_32, R_68K_32 },
{ BFD_RELOC_16, R_68K_16 },
{ BFD_RELOC_8, R_68K_8 },
{ BFD_RELOC_32_PCREL, R_68K_PC32 },
{ BFD_RELOC_16_PCREL, R_68K_PC16 },
{ BFD_RELOC_8_PCREL, R_68K_PC8 },
{ BFD_RELOC_32_GOT_PCREL, R_68K_GOT32 },
{ BFD_RELOC_16_GOT_PCREL, R_68K_GOT16 },
{ BFD_RELOC_8_GOT_PCREL, R_68K_GOT8 },
{ BFD_RELOC_32_GOTOFF, R_68K_GOT32O },
{ BFD_RELOC_16_GOTOFF, R_68K_GOT16O },
{ BFD_RELOC_8_GOTOFF, R_68K_GOT8O },
{ BFD_RELOC_32_PLT_PCREL, R_68K_PLT32 },
{ BFD_RELOC_16_PLT_PCREL, R_68K_PLT16 },
{ BFD_RELOC_8_PLT_PCREL, R_68K_PLT8 },
{ BFD_RELOC_32_PLTOFF, R_68K_PLT32O },
{ BFD_RELOC_16_PLTOFF, R_68K_PLT16O },
{ BFD_RELOC_8_PLTOFF, R_68K_PLT8O },
{ BFD_RELOC_NONE, R_68K_COPY },
{ BFD_RELOC_68K_GLOB_DAT, R_68K_GLOB_DAT },
{ BFD_RELOC_68K_JMP_SLOT, R_68K_JMP_SLOT },
{ BFD_RELOC_68K_RELATIVE, R_68K_RELATIVE },
{ BFD_RELOC_CTOR, R_68K_32 },
{ BFD_RELOC_VTABLE_INHERIT, R_68K_GNU_VTINHERIT },
{ BFD_RELOC_VTABLE_ENTRY, R_68K_GNU_VTENTRY },
};
static reloc_howto_type *
reloc_type_lookup (abfd, code)
bfd *abfd ATTRIBUTE_UNUSED;
bfd_reloc_code_real_type code;
{
unsigned int i;
for (i = 0; i < sizeof (reloc_map) / sizeof (reloc_map[0]); i++)
{
if (reloc_map[i].bfd_val == code)
return &howto_table[reloc_map[i].elf_val];
}
return 0;
}
#define bfd_elf32_bfd_reloc_type_lookup reloc_type_lookup
#define ELF_ARCH bfd_arch_m68k
#define ELF_DYNAMIC_INTERPRETER "/usr/lib/libc.so.1"
#define PLT_ENTRY_SIZE 20
static const bfd_byte elf_m68k_plt0_entry[PLT_ENTRY_SIZE] =
{
0x2f, 0x3b, 0x01, 0x70,
0, 0, 0, 0,
0x4e, 0xfb, 0x01, 0x71,
0, 0, 0, 0,
0, 0, 0, 0
};
static const bfd_byte elf_m68k_plt_entry[PLT_ENTRY_SIZE] =
{
0x4e, 0xfb, 0x01, 0x71,
0, 0, 0, 0,
0x2f, 0x3c,
0, 0, 0, 0,
0x60, 0xff,
0, 0, 0, 0
};
#define CPU32_FLAG(abfd) (elf_elfheader (abfd)->e_flags & EF_CPU32)
#define PLT_CPU32_ENTRY_SIZE 24
static const bfd_byte elf_cpu32_plt0_entry[PLT_CPU32_ENTRY_SIZE] =
{
0x2f, 0x3b, 0x01, 0x70,
0, 0, 0, 0,
0x22, 0x7b, 0x01, 0x70,
0, 0, 0, 0,
0x4e, 0xd1,
0, 0, 0, 0,
0, 0
};
static const bfd_byte elf_cpu32_plt_entry[PLT_CPU32_ENTRY_SIZE] =
{
0x22, 0x7b, 0x01, 0x70,
0, 0, 0, 0,
0x4e, 0xd1,
0x2f, 0x3c,
0, 0, 0, 0,
0x60, 0xff,
0, 0, 0, 0,
0, 0
};
struct elf_m68k_pcrel_relocs_copied
{
struct elf_m68k_pcrel_relocs_copied *next;
asection *section;
bfd_size_type count;
};
struct elf_m68k_link_hash_entry
{
struct elf_link_hash_entry root;
struct elf_m68k_pcrel_relocs_copied *pcrel_relocs_copied;
};
struct elf_m68k_link_hash_table
{
struct elf_link_hash_table root;
struct sym_sec_cache sym_sec;
};
static bfd_boolean elf_m68k_discard_copies
PARAMS ((struct elf_m68k_link_hash_entry *, PTR));
#define elf_m68k_link_hash_traverse(table, func, info) \
(elf_link_hash_traverse \
(&(table)->root, \
(bfd_boolean (*) PARAMS ((struct elf_link_hash_entry *, PTR))) (func), \
(info)))
#define elf_m68k_hash_table(p) \
((struct elf_m68k_link_hash_table *) (p)->hash)
static struct bfd_hash_entry *
elf_m68k_link_hash_newfunc (entry, table, string)
struct bfd_hash_entry *entry;
struct bfd_hash_table *table;
const char *string;
{
struct elf_m68k_link_hash_entry *ret =
(struct elf_m68k_link_hash_entry *) entry;
if (ret == (struct elf_m68k_link_hash_entry *) NULL)
ret = ((struct elf_m68k_link_hash_entry *)
bfd_hash_allocate (table,
sizeof (struct elf_m68k_link_hash_entry)));
if (ret == (struct elf_m68k_link_hash_entry *) NULL)
return (struct bfd_hash_entry *) ret;
ret = ((struct elf_m68k_link_hash_entry *)
_bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret,
table, string));
if (ret != (struct elf_m68k_link_hash_entry *) NULL)
{
ret->pcrel_relocs_copied = NULL;
}
return (struct bfd_hash_entry *) ret;
}
static struct bfd_link_hash_table *
elf_m68k_link_hash_table_create (abfd)
bfd *abfd;
{
struct elf_m68k_link_hash_table *ret;
bfd_size_type amt = sizeof (struct elf_m68k_link_hash_table);
ret = (struct elf_m68k_link_hash_table *) bfd_malloc (amt);
if (ret == (struct elf_m68k_link_hash_table *) NULL)
return NULL;
if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
elf_m68k_link_hash_newfunc))
{
free (ret);
return NULL;
}
ret->sym_sec.abfd = NULL;
return &ret->root.root;
}
static bfd_boolean
elf32_m68k_set_private_flags (abfd, flags)
bfd *abfd;
flagword flags;
{
elf_elfheader (abfd)->e_flags = flags;
elf_flags_init (abfd) = TRUE;
return TRUE;
}
static bfd_boolean
elf32_m68k_merge_private_bfd_data (ibfd, obfd)
bfd *ibfd;
bfd *obfd;
{
flagword out_flags;
flagword in_flags;
if ( bfd_get_flavour (ibfd) != bfd_target_elf_flavour
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return TRUE;
in_flags = elf_elfheader (ibfd)->e_flags;
out_flags = elf_elfheader (obfd)->e_flags;
if (!elf_flags_init (obfd))
{
elf_flags_init (obfd) = TRUE;
elf_elfheader (obfd)->e_flags = in_flags;
}
return TRUE;
}
static bfd_boolean
elf32_m68k_print_private_bfd_data (abfd, ptr)
bfd *abfd;
PTR 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 & EF_CPU32)
fprintf (file, _(" [cpu32]"));
if (elf_elfheader (abfd)->e_flags & EF_M68000)
fprintf (file, _(" [m68000]"));
fputc ('\n', file);
return TRUE;
}
static bfd_boolean
elf_m68k_check_relocs (abfd, info, sec, relocs)
bfd *abfd;
struct bfd_link_info *info;
asection *sec;
const Elf_Internal_Rela *relocs;
{
bfd *dynobj;
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
bfd_signed_vma *local_got_refcounts;
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
asection *sgot;
asection *srelgot;
asection *sreloc;
if (info->relocateable)
return TRUE;
dynobj = elf_hash_table (info)->dynobj;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
local_got_refcounts = elf_local_got_refcounts (abfd);
sgot = NULL;
srelgot = NULL;
sreloc = NULL;
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
{
unsigned long r_symndx;
struct elf_link_hash_entry *h;
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];
switch (ELF32_R_TYPE (rel->r_info))
{
case R_68K_GOT8:
case R_68K_GOT16:
case R_68K_GOT32:
if (h != NULL
&& strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
break;
case R_68K_GOT8O:
case R_68K_GOT16O:
case R_68K_GOT32O:
if (dynobj == NULL)
{
elf_hash_table (info)->dynobj = dynobj = abfd;
if (!_bfd_elf_create_got_section (dynobj, info))
return FALSE;
}
if (sgot == NULL)
{
sgot = bfd_get_section_by_name (dynobj, ".got");
BFD_ASSERT (sgot != NULL);
}
if (srelgot == NULL
&& (h != NULL || info->shared))
{
srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
if (srelgot == NULL)
{
srelgot = bfd_make_section (dynobj, ".rela.got");
if (srelgot == NULL
|| !bfd_set_section_flags (dynobj, srelgot,
(SEC_ALLOC
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_LINKER_CREATED
| SEC_READONLY))
|| !bfd_set_section_alignment (dynobj, srelgot, 2))
return FALSE;
}
}
if (h != NULL)
{
if (h->got.refcount == 0)
{
if (h->dynindx == -1
&& (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
{
if (!bfd_elf32_link_record_dynamic_symbol (info, h))
return FALSE;
}
sgot->_raw_size += 4;
srelgot->_raw_size += sizeof (Elf32_External_Rela);
}
h->got.refcount++;
}
else
{
if (local_got_refcounts == NULL)
{
bfd_size_type size;
size = symtab_hdr->sh_info;
size *= sizeof (bfd_signed_vma);
local_got_refcounts = ((bfd_signed_vma *)
bfd_zalloc (abfd, size));
if (local_got_refcounts == NULL)
return FALSE;
elf_local_got_refcounts (abfd) = local_got_refcounts;
}
if (local_got_refcounts[r_symndx] == 0)
{
sgot->_raw_size += 4;
if (info->shared)
{
srelgot->_raw_size += sizeof (Elf32_External_Rela);
}
}
local_got_refcounts[r_symndx]++;
}
break;
case R_68K_PLT8:
case R_68K_PLT16:
case R_68K_PLT32:
if (h == NULL)
continue;
h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
h->plt.refcount++;
break;
case R_68K_PLT8O:
case R_68K_PLT16O:
case R_68K_PLT32O:
if (h == NULL)
{
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
if (h->dynindx == -1
&& (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
{
if (!bfd_elf32_link_record_dynamic_symbol (info, h))
return FALSE;
}
h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
h->plt.refcount++;
break;
case R_68K_PC8:
case R_68K_PC16:
case R_68K_PC32:
if (!(info->shared
&& (sec->flags & SEC_ALLOC) != 0
&& h != NULL
&& (!info->symbolic
|| h->root.type == bfd_link_hash_defweak
|| (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0)))
{
if (h != NULL)
{
h->plt.refcount++;
}
break;
}
case R_68K_8:
case R_68K_16:
case R_68K_32:
if (h != NULL)
{
h->plt.refcount++;
}
if (info->shared
&& (sec->flags & SEC_ALLOC) != 0)
{
if (sreloc == NULL)
{
const char *name;
name = (bfd_elf_string_from_elf_section
(abfd,
elf_elfheader (abfd)->e_shstrndx,
elf_section_data (sec)->rel_hdr.sh_name));
if (name == NULL)
return FALSE;
BFD_ASSERT (strncmp (name, ".rela", 5) == 0
&& strcmp (bfd_get_section_name (abfd, sec),
name + 5) == 0);
sreloc = bfd_get_section_by_name (dynobj, name);
if (sreloc == NULL)
{
sreloc = bfd_make_section (dynobj, name);
if (sreloc == NULL
|| !bfd_set_section_flags (dynobj, sreloc,
(SEC_ALLOC
| SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
| SEC_LINKER_CREATED
| SEC_READONLY))
|| !bfd_set_section_alignment (dynobj, sreloc, 2))
return FALSE;
}
}
if (sec->flags & SEC_READONLY
&& !(ELF32_R_TYPE (rel->r_info) == R_68K_PC8
|| ELF32_R_TYPE (rel->r_info) == R_68K_PC16
|| ELF32_R_TYPE (rel->r_info) == R_68K_PC32))
info->flags |= DF_TEXTREL;
sreloc->_raw_size += sizeof (Elf32_External_Rela);
if (ELF32_R_TYPE (rel->r_info) == R_68K_PC8
|| ELF32_R_TYPE (rel->r_info) == R_68K_PC16
|| ELF32_R_TYPE (rel->r_info) == R_68K_PC32)
{
struct elf_m68k_pcrel_relocs_copied *p;
struct elf_m68k_pcrel_relocs_copied **head;
if (h != NULL)
{
struct elf_m68k_link_hash_entry *eh
= (struct elf_m68k_link_hash_entry *) h;
head = &eh->pcrel_relocs_copied;
}
else
{
asection *s;
s = (bfd_section_from_r_symndx
(abfd, &elf_m68k_hash_table (info)->sym_sec,
sec, r_symndx));
if (s == NULL)
return FALSE;
head = ((struct elf_m68k_pcrel_relocs_copied **)
&elf_section_data (s)->local_dynrel);
}
for (p = *head; p != NULL; p = p->next)
if (p->section == sreloc)
break;
if (p == NULL)
{
p = ((struct elf_m68k_pcrel_relocs_copied *)
bfd_alloc (dynobj, (bfd_size_type) sizeof *p));
if (p == NULL)
return FALSE;
p->next = *head;
*head = p;
p->section = sreloc;
p->count = 0;
}
++p->count;
}
}
break;
case R_68K_GNU_VTINHERIT:
if (!_bfd_elf32_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
return FALSE;
break;
case R_68K_GNU_VTENTRY:
if (!_bfd_elf32_gc_record_vtentry (abfd, sec, h, rel->r_addend))
return FALSE;
break;
default:
break;
}
}
return TRUE;
}
static asection *
elf_m68k_gc_mark_hook (sec, info, rel, h, sym)
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))
{
case R_68K_GNU_VTINHERIT:
case R_68K_GNU_VTENTRY:
break;
default:
switch (h->root.type)
{
default:
break;
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;
}
}
}
else
return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
return NULL;
}
static bfd_boolean
elf_m68k_gc_sweep_hook (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;
bfd_signed_vma *local_got_refcounts;
const Elf_Internal_Rela *rel, *relend;
unsigned long r_symndx;
struct elf_link_hash_entry *h;
bfd *dynobj;
asection *sgot;
asection *srelgot;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
local_got_refcounts = elf_local_got_refcounts (abfd);
dynobj = elf_hash_table (info)->dynobj;
if (dynobj == NULL)
return TRUE;
sgot = bfd_get_section_by_name (dynobj, ".got");
srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
relend = relocs + sec->reloc_count;
for (rel = relocs; rel < relend; rel++)
{
switch (ELF32_R_TYPE (rel->r_info))
{
case R_68K_GOT8:
case R_68K_GOT16:
case R_68K_GOT32:
case R_68K_GOT8O:
case R_68K_GOT16O:
case R_68K_GOT32O:
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx >= symtab_hdr->sh_info)
{
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
if (h->got.refcount > 0)
{
--h->got.refcount;
if (h->got.refcount == 0)
{
sgot->_raw_size -= 4;
srelgot->_raw_size -= sizeof (Elf32_External_Rela);
}
}
}
else if (local_got_refcounts != NULL)
{
if (local_got_refcounts[r_symndx] > 0)
{
--local_got_refcounts[r_symndx];
if (local_got_refcounts[r_symndx] == 0)
{
sgot->_raw_size -= 4;
if (info->shared)
srelgot->_raw_size -= sizeof (Elf32_External_Rela);
}
}
}
break;
case R_68K_PLT8:
case R_68K_PLT16:
case R_68K_PLT32:
case R_68K_PLT8O:
case R_68K_PLT16O:
case R_68K_PLT32O:
case R_68K_PC8:
case R_68K_PC16:
case R_68K_PC32:
case R_68K_8:
case R_68K_16:
case R_68K_32:
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx >= symtab_hdr->sh_info)
{
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
if (h->plt.refcount > 0)
--h->plt.refcount;
}
break;
default:
break;
}
}
return TRUE;
}
static bfd_boolean
elf_m68k_adjust_dynamic_symbol (info, h)
struct bfd_link_info *info;
struct elf_link_hash_entry *h;
{
bfd *dynobj;
asection *s;
unsigned int power_of_two;
dynobj = elf_hash_table (info)->dynobj;
BFD_ASSERT (dynobj != NULL
&& ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT)
|| h->weakdef != NULL
|| ((h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_DYNAMIC) != 0
&& (h->elf_link_hash_flags
& ELF_LINK_HASH_REF_REGULAR) != 0
&& (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0)));
if (h->type == STT_FUNC
|| (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
{
if (! info->shared
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0
&& (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) == 0
&& h->dynindx == -1)
{
BFD_ASSERT ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0);
h->plt.offset = (bfd_vma) -1;
return TRUE;
}
if (h->plt.refcount <= 0)
{
h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
h->plt.offset = (bfd_vma) -1;
return TRUE;
}
if (h->dynindx == -1
&& (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
{
if (! bfd_elf32_link_record_dynamic_symbol (info, h))
return FALSE;
}
s = bfd_get_section_by_name (dynobj, ".plt");
BFD_ASSERT (s != NULL);
if (s->_raw_size == 0)
{
if (CPU32_FLAG (dynobj))
s->_raw_size += PLT_CPU32_ENTRY_SIZE;
else
s->_raw_size += PLT_ENTRY_SIZE;
}
if (!info->shared
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
{
h->root.u.def.section = s;
h->root.u.def.value = s->_raw_size;
}
h->plt.offset = s->_raw_size;
if (CPU32_FLAG (dynobj))
s->_raw_size += PLT_CPU32_ENTRY_SIZE;
else
s->_raw_size += PLT_ENTRY_SIZE;
s = bfd_get_section_by_name (dynobj, ".got.plt");
BFD_ASSERT (s != NULL);
s->_raw_size += 4;
s = bfd_get_section_by_name (dynobj, ".rela.plt");
BFD_ASSERT (s != NULL);
s->_raw_size += sizeof (Elf32_External_Rela);
return TRUE;
}
h->plt.offset = (bfd_vma) -1;
if (h->weakdef != NULL)
{
BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined
|| h->weakdef->root.type == bfd_link_hash_defweak);
h->root.u.def.section = h->weakdef->root.u.def.section;
h->root.u.def.value = h->weakdef->root.u.def.value;
return TRUE;
}
if (info->shared)
return TRUE;
s = bfd_get_section_by_name (dynobj, ".dynbss");
BFD_ASSERT (s != NULL);
if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
{
asection *srel;
srel = bfd_get_section_by_name (dynobj, ".rela.bss");
BFD_ASSERT (srel != NULL);
srel->_raw_size += sizeof (Elf32_External_Rela);
h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_COPY;
}
power_of_two = bfd_log2 (h->size);
if (power_of_two > 3)
power_of_two = 3;
s->_raw_size = BFD_ALIGN (s->_raw_size,
(bfd_size_type) (1 << power_of_two));
if (power_of_two > bfd_get_section_alignment (dynobj, s))
{
if (!bfd_set_section_alignment (dynobj, s, power_of_two))
return FALSE;
}
h->root.u.def.section = s;
h->root.u.def.value = s->_raw_size;
s->_raw_size += h->size;
return TRUE;
}
static bfd_boolean
elf_m68k_size_dynamic_sections (output_bfd, info)
bfd *output_bfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info;
{
bfd *dynobj;
asection *s;
bfd_boolean plt;
bfd_boolean relocs;
dynobj = elf_hash_table (info)->dynobj;
BFD_ASSERT (dynobj != NULL);
if (elf_hash_table (info)->dynamic_sections_created)
{
if (!info->shared)
{
s = bfd_get_section_by_name (dynobj, ".interp");
BFD_ASSERT (s != NULL);
s->_raw_size = sizeof ELF_DYNAMIC_INTERPRETER;
s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER;
}
}
else
{
s = bfd_get_section_by_name (dynobj, ".rela.got");
if (s != NULL)
s->_raw_size = 0;
}
if (info->shared)
elf_m68k_link_hash_traverse (elf_m68k_hash_table (info),
elf_m68k_discard_copies,
(PTR) info);
plt = FALSE;
relocs = FALSE;
for (s = dynobj->sections; s != NULL; s = s->next)
{
const char *name;
bfd_boolean strip;
if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
name = bfd_get_section_name (dynobj, s);
strip = FALSE;
if (strcmp (name, ".plt") == 0)
{
if (s->_raw_size == 0)
{
strip = TRUE;
}
else
{
plt = TRUE;
}
}
else if (strncmp (name, ".rela", 5) == 0)
{
if (s->_raw_size == 0)
{
strip = TRUE;
}
else
{
relocs = TRUE;
s->reloc_count = 0;
}
}
else if (strncmp (name, ".got", 4) != 0)
{
continue;
}
if (strip)
{
_bfd_strip_section_from_output (info, s);
continue;
}
s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->_raw_size);
if (s->contents == NULL && s->_raw_size != 0)
return FALSE;
}
if (elf_hash_table (info)->dynamic_sections_created)
{
#define add_dynamic_entry(TAG, VAL) \
bfd_elf32_add_dynamic_entry (info, (bfd_vma) (TAG), (bfd_vma) (VAL))
if (!info->shared)
{
if (!add_dynamic_entry (DT_DEBUG, 0))
return FALSE;
}
if (plt)
{
if (!add_dynamic_entry (DT_PLTGOT, 0)
|| !add_dynamic_entry (DT_PLTRELSZ, 0)
|| !add_dynamic_entry (DT_PLTREL, DT_RELA)
|| !add_dynamic_entry (DT_JMPREL, 0))
return FALSE;
}
if (relocs)
{
if (!add_dynamic_entry (DT_RELA, 0)
|| !add_dynamic_entry (DT_RELASZ, 0)
|| !add_dynamic_entry (DT_RELAENT, sizeof (Elf32_External_Rela)))
return FALSE;
}
if ((info->flags & DF_TEXTREL) != 0)
{
if (!add_dynamic_entry (DT_TEXTREL, 0))
return FALSE;
}
}
#undef add_dynamic_entry
return TRUE;
}
static bfd_boolean
elf_m68k_discard_copies (h, inf)
struct elf_m68k_link_hash_entry *h;
PTR inf;
{
struct bfd_link_info *info = (struct bfd_link_info *) inf;
struct elf_m68k_pcrel_relocs_copied *s;
if (h->root.root.type == bfd_link_hash_warning)
h = (struct elf_m68k_link_hash_entry *) h->root.root.u.i.link;
if ((h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
|| (!info->symbolic
&& (h->root.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0))
{
if ((info->flags & DF_TEXTREL) == 0)
{
for (s = h->pcrel_relocs_copied; s != NULL; s = s->next)
if ((s->section->flags & SEC_READONLY) != 0)
{
info->flags |= DF_TEXTREL;
break;
}
}
return TRUE;
}
for (s = h->pcrel_relocs_copied; s != NULL; s = s->next)
s->section->_raw_size -= s->count * sizeof (Elf32_External_Rela);
return TRUE;
}
static bfd_boolean
elf_m68k_relocate_section (output_bfd, info, input_bfd, input_section,
contents, relocs, local_syms, local_sections)
bfd *output_bfd;
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;
{
bfd *dynobj;
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
bfd_vma *local_got_offsets;
asection *sgot;
asection *splt;
asection *sreloc;
Elf_Internal_Rela *rel;
Elf_Internal_Rela *relend;
if (info->relocateable)
return TRUE;
dynobj = elf_hash_table (info)->dynobj;
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (input_bfd);
local_got_offsets = elf_local_got_offsets (input_bfd);
sgot = NULL;
splt = NULL;
sreloc = NULL;
rel = relocs;
relend = relocs + input_section->reloc_count;
for (; rel < relend; rel++)
{
int r_type;
reloc_howto_type *howto;
unsigned long r_symndx;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
asection *sec;
bfd_vma relocation;
bfd_reloc_status_type r;
r_type = ELF32_R_TYPE (rel->r_info);
if (r_type < 0 || r_type >= (int) R_68K_max)
{
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
howto = howto_table + r_type;
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 = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel);
}
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;
if (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
{
sec = h->root.u.def.section;
if (((r_type == R_68K_PLT8
|| r_type == R_68K_PLT16
|| r_type == R_68K_PLT32
|| r_type == R_68K_PLT8O
|| r_type == R_68K_PLT16O
|| r_type == R_68K_PLT32O)
&& h->plt.offset != (bfd_vma) -1
&& elf_hash_table (info)->dynamic_sections_created)
|| ((r_type == R_68K_GOT8O
|| r_type == R_68K_GOT16O
|| r_type == R_68K_GOT32O
|| ((r_type == R_68K_GOT8
|| r_type == R_68K_GOT16
|| r_type == R_68K_GOT32)
&& strcmp (h->root.root.string,
"_GLOBAL_OFFSET_TABLE_") != 0))
&& elf_hash_table (info)->dynamic_sections_created
&& (! info->shared
|| (! info->symbolic && h->dynindx != -1)
|| (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0))
|| (info->shared
&& ((! info->symbolic && h->dynindx != -1)
|| (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0)
&& ((input_section->flags & SEC_ALLOC) != 0
|| ((input_section->flags & SEC_DEBUGGING) != 0
&& (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_DYNAMIC) != 0))
&& (r_type == R_68K_8
|| r_type == R_68K_16
|| r_type == R_68K_32
|| r_type == R_68K_PC8
|| r_type == R_68K_PC16
|| r_type == R_68K_PC32)))
{
relocation = 0;
}
else
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
&& (!info->symbolic || info->allow_shlib_undefined)
&& !info->no_undefined
&& ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
relocation = 0;
else
{
if (!(info->callbacks->undefined_symbol
(info, h->root.root.string, input_bfd,
input_section, rel->r_offset,
(!info->shared || info->no_undefined
|| ELF_ST_VISIBILITY (h->other)))))
return FALSE;
relocation = 0;
}
}
switch (r_type)
{
case R_68K_GOT8:
case R_68K_GOT16:
case R_68K_GOT32:
if (h != NULL
&& strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
break;
case R_68K_GOT8O:
case R_68K_GOT16O:
case R_68K_GOT32O:
{
bfd_vma off;
if (sgot == NULL)
{
sgot = bfd_get_section_by_name (dynobj, ".got");
BFD_ASSERT (sgot != NULL);
}
if (h != NULL)
{
off = h->got.offset;
BFD_ASSERT (off != (bfd_vma) -1);
if (!elf_hash_table (info)->dynamic_sections_created
|| (info->shared
&& (info->symbolic || h->dynindx == -1)
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
{
if ((off & 1) != 0)
off &= ~1;
else
{
bfd_put_32 (output_bfd, relocation,
sgot->contents + off);
h->got.offset |= 1;
}
}
}
else
{
BFD_ASSERT (local_got_offsets != NULL
&& local_got_offsets[r_symndx] != (bfd_vma) -1);
off = local_got_offsets[r_symndx];
if ((off & 1) != 0)
off &= ~1;
else
{
bfd_put_32 (output_bfd, relocation, sgot->contents + off);
if (info->shared)
{
asection *s;
Elf_Internal_Rela outrel;
bfd_byte *loc;
s = bfd_get_section_by_name (dynobj, ".rela.got");
BFD_ASSERT (s != NULL);
outrel.r_offset = (sgot->output_section->vma
+ sgot->output_offset
+ off);
outrel.r_info = ELF32_R_INFO (0, R_68K_RELATIVE);
outrel.r_addend = relocation;
loc = s->contents;
loc += s->reloc_count++ * sizeof (Elf32_External_Rela);
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
}
local_got_offsets[r_symndx] |= 1;
}
}
relocation = sgot->output_offset + off;
if (r_type == R_68K_GOT8O
|| r_type == R_68K_GOT16O
|| r_type == R_68K_GOT32O)
{
rel->r_addend = 0;
}
else
relocation += sgot->output_section->vma;
}
break;
case R_68K_PLT8:
case R_68K_PLT16:
case R_68K_PLT32:
if (h == NULL)
break;
if (h->plt.offset == (bfd_vma) -1
|| !elf_hash_table (info)->dynamic_sections_created)
{
break;
}
if (splt == NULL)
{
splt = bfd_get_section_by_name (dynobj, ".plt");
BFD_ASSERT (splt != NULL);
}
relocation = (splt->output_section->vma
+ splt->output_offset
+ h->plt.offset);
break;
case R_68K_PLT8O:
case R_68K_PLT16O:
case R_68K_PLT32O:
BFD_ASSERT (h != NULL && h->plt.offset != (bfd_vma) -1);
if (splt == NULL)
{
splt = bfd_get_section_by_name (dynobj, ".plt");
BFD_ASSERT (splt != NULL);
}
relocation = h->plt.offset;
rel->r_addend = 0;
break;
case R_68K_PC8:
case R_68K_PC16:
case R_68K_PC32:
if (h == NULL
|| (info->shared
&& (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0))
break;
case R_68K_8:
case R_68K_16:
case R_68K_32:
if (info->shared
&& r_symndx != 0
&& (input_section->flags & SEC_ALLOC) != 0
&& ((r_type != R_68K_PC8
&& r_type != R_68K_PC16
&& r_type != R_68K_PC32)
|| (!info->symbolic
|| (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0)))
{
Elf_Internal_Rela outrel;
bfd_byte *loc;
bfd_boolean skip, relocate;
if (sreloc == NULL)
{
const char *name;
name = (bfd_elf_string_from_elf_section
(input_bfd,
elf_elfheader (input_bfd)->e_shstrndx,
elf_section_data (input_section)->rel_hdr.sh_name));
if (name == NULL)
return FALSE;
BFD_ASSERT (strncmp (name, ".rela", 5) == 0
&& strcmp (bfd_get_section_name (input_bfd,
input_section),
name + 5) == 0);
sreloc = bfd_get_section_by_name (dynobj, name);
BFD_ASSERT (sreloc != NULL);
}
skip = FALSE;
relocate = FALSE;
outrel.r_offset =
_bfd_elf_section_offset (output_bfd, info, input_section,
rel->r_offset);
if (outrel.r_offset == (bfd_vma) -1)
skip = TRUE;
else if (outrel.r_offset == (bfd_vma) -2)
skip = TRUE, relocate = TRUE;
outrel.r_offset += (input_section->output_section->vma
+ input_section->output_offset);
if (skip)
memset (&outrel, 0, sizeof outrel);
else if (h != NULL
&& ((! info->symbolic && h->dynindx != -1)
|| (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0))
{
BFD_ASSERT (h->dynindx != -1);
outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
outrel.r_addend = relocation + rel->r_addend;
}
else
{
if (r_type == R_68K_32)
{
relocate = TRUE;
outrel.r_info = ELF32_R_INFO (0, R_68K_RELATIVE);
outrel.r_addend = relocation + rel->r_addend;
}
else
{
long indx;
if (h == NULL)
sec = local_sections[r_symndx];
else
{
BFD_ASSERT (h->root.type == bfd_link_hash_defined
|| (h->root.type
== bfd_link_hash_defweak));
sec = h->root.u.def.section;
}
if (sec != NULL && bfd_is_abs_section (sec))
indx = 0;
else if (sec == NULL || sec->owner == NULL)
{
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
else
{
asection *osec;
osec = sec->output_section;
indx = elf_section_data (osec)->dynindx;
BFD_ASSERT (indx > 0);
}
outrel.r_info = ELF32_R_INFO (indx, r_type);
outrel.r_addend = relocation + rel->r_addend;
}
}
loc = sreloc->contents;
loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rela);
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
if (!relocate)
continue;
}
break;
case R_68K_GNU_VTINHERIT:
case R_68K_GNU_VTENTRY:
continue;
default:
break;
}
r = _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset,
relocation, rel->r_addend);
if (r != bfd_reloc_ok)
{
switch (r)
{
default:
case bfd_reloc_outofrange:
abort ();
case bfd_reloc_overflow:
{
const char *name;
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)
return FALSE;
if (*name == '\0')
name = bfd_section_name (input_bfd, sec);
}
if (!(info->callbacks->reloc_overflow
(info, name, howto->name, (bfd_vma) 0,
input_bfd, input_section, rel->r_offset)))
return FALSE;
}
break;
}
}
}
return TRUE;
}
static bfd_boolean
elf_m68k_finish_dynamic_symbol (output_bfd, info, h, sym)
bfd *output_bfd;
struct bfd_link_info *info;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
{
bfd *dynobj;
int plt_off1, plt_off2, plt_off3;
dynobj = elf_hash_table (info)->dynobj;
if (h->plt.offset != (bfd_vma) -1)
{
asection *splt;
asection *sgot;
asection *srela;
bfd_vma plt_index;
bfd_vma got_offset;
Elf_Internal_Rela rela;
bfd_byte *loc;
BFD_ASSERT (h->dynindx != -1);
splt = bfd_get_section_by_name (dynobj, ".plt");
sgot = bfd_get_section_by_name (dynobj, ".got.plt");
srela = bfd_get_section_by_name (dynobj, ".rela.plt");
BFD_ASSERT (splt != NULL && sgot != NULL && srela != NULL);
if ( CPU32_FLAG (output_bfd))
plt_index = h->plt.offset / PLT_CPU32_ENTRY_SIZE - 1;
else
plt_index = h->plt.offset / PLT_ENTRY_SIZE - 1;
got_offset = (plt_index + 3) * 4;
if ( CPU32_FLAG (output_bfd))
{
memcpy (splt->contents + h->plt.offset, elf_cpu32_plt_entry,
PLT_CPU32_ENTRY_SIZE);
plt_off1 = 4;
plt_off2 = 12;
plt_off3 = 18;
}
else
{
memcpy (splt->contents + h->plt.offset, elf_m68k_plt_entry,
PLT_ENTRY_SIZE);
plt_off1 = 4;
plt_off2 = 10;
plt_off3 = 16;
}
bfd_put_32 (output_bfd,
(sgot->output_section->vma
+ sgot->output_offset
+ got_offset
- (splt->output_section->vma
+ h->plt.offset + 2)),
splt->contents + h->plt.offset + plt_off1);
bfd_put_32 (output_bfd, plt_index * sizeof (Elf32_External_Rela),
splt->contents + h->plt.offset + plt_off2);
bfd_put_32 (output_bfd, - (h->plt.offset + plt_off3),
splt->contents + h->plt.offset + plt_off3);
bfd_put_32 (output_bfd,
(splt->output_section->vma
+ splt->output_offset
+ h->plt.offset
+ 8),
sgot->contents + got_offset);
rela.r_offset = (sgot->output_section->vma
+ sgot->output_offset
+ got_offset);
rela.r_info = ELF32_R_INFO (h->dynindx, R_68K_JMP_SLOT);
rela.r_addend = 0;
loc = srela->contents + plt_index * sizeof (Elf32_External_Rela);
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
{
sym->st_shndx = SHN_UNDEF;
}
}
if (h->got.offset != (bfd_vma) -1)
{
asection *sgot;
asection *srela;
Elf_Internal_Rela rela;
bfd_byte *loc;
sgot = bfd_get_section_by_name (dynobj, ".got");
srela = bfd_get_section_by_name (dynobj, ".rela.got");
BFD_ASSERT (sgot != NULL && srela != NULL);
rela.r_offset = (sgot->output_section->vma
+ sgot->output_offset
+ (h->got.offset &~ (bfd_vma) 1));
if (info->shared
&& (info->symbolic || h->dynindx == -1)
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))
{
rela.r_info = ELF32_R_INFO (0, R_68K_RELATIVE);
rela.r_addend = bfd_get_signed_32 (output_bfd,
(sgot->contents
+ (h->got.offset &~ (bfd_vma) 1)));
}
else
{
bfd_put_32 (output_bfd, (bfd_vma) 0,
sgot->contents + (h->got.offset &~ (bfd_vma) 1));
rela.r_info = ELF32_R_INFO (h->dynindx, R_68K_GLOB_DAT);
rela.r_addend = 0;
}
loc = srela->contents;
loc += srela->reloc_count++ * sizeof (Elf32_External_Rela);
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
}
if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_COPY) != 0)
{
asection *s;
Elf_Internal_Rela rela;
bfd_byte *loc;
BFD_ASSERT (h->dynindx != -1
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak));
s = bfd_get_section_by_name (h->root.u.def.section->owner,
".rela.bss");
BFD_ASSERT (s != NULL);
rela.r_offset = (h->root.u.def.value
+ h->root.u.def.section->output_section->vma
+ h->root.u.def.section->output_offset);
rela.r_info = ELF32_R_INFO (h->dynindx, R_68K_COPY);
rela.r_addend = 0;
loc = s->contents + s->reloc_count++ * sizeof (Elf32_External_Rela);
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
}
if (strcmp (h->root.root.string, "_DYNAMIC") == 0
|| strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
sym->st_shndx = SHN_ABS;
return TRUE;
}
static bfd_boolean
elf_m68k_finish_dynamic_sections (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
bfd *dynobj;
asection *sgot;
asection *sdyn;
dynobj = elf_hash_table (info)->dynobj;
sgot = bfd_get_section_by_name (dynobj, ".got.plt");
BFD_ASSERT (sgot != NULL);
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
if (elf_hash_table (info)->dynamic_sections_created)
{
asection *splt;
Elf32_External_Dyn *dyncon, *dynconend;
splt = bfd_get_section_by_name (dynobj, ".plt");
BFD_ASSERT (splt != NULL && sdyn != NULL);
dyncon = (Elf32_External_Dyn *) sdyn->contents;
dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->_raw_size);
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
const char *name;
asection *s;
bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
switch (dyn.d_tag)
{
default:
break;
case DT_PLTGOT:
name = ".got";
goto get_vma;
case DT_JMPREL:
name = ".rela.plt";
get_vma:
s = bfd_get_section_by_name (output_bfd, name);
BFD_ASSERT (s != NULL);
dyn.d_un.d_ptr = s->vma;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_PLTRELSZ:
s = bfd_get_section_by_name (output_bfd, ".rela.plt");
BFD_ASSERT (s != NULL);
if (s->_cooked_size != 0)
dyn.d_un.d_val = s->_cooked_size;
else
dyn.d_un.d_val = s->_raw_size;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_RELASZ:
s = bfd_get_section_by_name (output_bfd, ".rela.plt");
if (s != NULL)
{
if (s->_cooked_size != 0)
dyn.d_un.d_val -= s->_cooked_size;
else
dyn.d_un.d_val -= s->_raw_size;
}
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
}
}
if (splt->_raw_size > 0)
{
if (!CPU32_FLAG (output_bfd))
{
memcpy (splt->contents, elf_m68k_plt0_entry, PLT_ENTRY_SIZE);
bfd_put_32 (output_bfd,
(sgot->output_section->vma
+ sgot->output_offset + 4
- (splt->output_section->vma + 2)),
splt->contents + 4);
bfd_put_32 (output_bfd,
(sgot->output_section->vma
+ sgot->output_offset + 8
- (splt->output_section->vma + 10)),
splt->contents + 12);
elf_section_data (splt->output_section)->this_hdr.sh_entsize
= PLT_ENTRY_SIZE;
}
else
{
memcpy (splt->contents, elf_cpu32_plt0_entry, PLT_CPU32_ENTRY_SIZE);
bfd_put_32 (output_bfd,
(sgot->output_section->vma
+ sgot->output_offset + 4
- (splt->output_section->vma + 2)),
splt->contents + 4);
bfd_put_32 (output_bfd,
(sgot->output_section->vma
+ sgot->output_offset + 8
- (splt->output_section->vma + 10)),
splt->contents + 12);
elf_section_data (splt->output_section)->this_hdr.sh_entsize
= PLT_CPU32_ENTRY_SIZE;
}
}
}
if (sgot->_raw_size > 0)
{
if (sdyn == NULL)
bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents);
else
bfd_put_32 (output_bfd,
sdyn->output_section->vma + sdyn->output_offset,
sgot->contents);
bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 4);
bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 8);
}
elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4;
return TRUE;
}
bfd_boolean
bfd_m68k_elf32_create_embedded_relocs (abfd, info, datasec, relsec, errmsg)
bfd *abfd;
struct bfd_link_info *info;
asection *datasec;
asection *relsec;
char **errmsg;
{
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Sym *isymbuf = NULL;
Elf_Internal_Rela *internal_relocs = NULL;
Elf_Internal_Rela *irel, *irelend;
bfd_byte *p;
bfd_size_type amt;
BFD_ASSERT (! info->relocateable);
*errmsg = NULL;
if (datasec->reloc_count == 0)
return TRUE;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
internal_relocs = (_bfd_elf32_link_read_relocs
(abfd, datasec, (PTR) NULL, (Elf_Internal_Rela *) NULL,
info->keep_memory));
if (internal_relocs == NULL)
goto error_return;
amt = (bfd_size_type) datasec->reloc_count * 12;
relsec->contents = (bfd_byte *) bfd_alloc (abfd, amt);
if (relsec->contents == NULL)
goto error_return;
p = relsec->contents;
irelend = internal_relocs + datasec->reloc_count;
for (irel = internal_relocs; irel < irelend; irel++, p += 12)
{
asection *targetsec;
if (ELF32_R_TYPE (irel->r_info) != (int) R_68K_32)
{
*errmsg = _("unsupported reloc type");
bfd_set_error (bfd_error_bad_value);
goto error_return;
}
if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
{
Elf_Internal_Sym *isym;
if (isymbuf == NULL)
{
isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
if (isymbuf == NULL)
isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
symtab_hdr->sh_info, 0,
NULL, NULL, NULL);
if (isymbuf == NULL)
goto error_return;
}
isym = isymbuf + ELF32_R_SYM (irel->r_info);
targetsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
}
else
{
unsigned long indx;
struct elf_link_hash_entry *h;
indx = ELF32_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)
targetsec = h->root.u.def.section;
else
targetsec = NULL;
}
bfd_put_32 (abfd, irel->r_offset + datasec->output_offset, p);
memset (p + 4, 0, 8);
if (targetsec != NULL)
strncpy (p + 4, targetsec->output_section->name, 8);
}
if (isymbuf != NULL && symtab_hdr->contents != (unsigned char *) isymbuf)
free (isymbuf);
if (internal_relocs != NULL
&& elf_section_data (datasec)->relocs != internal_relocs)
free (internal_relocs);
return TRUE;
error_return:
if (isymbuf != NULL && symtab_hdr->contents != (unsigned char *) isymbuf)
free (isymbuf);
if (internal_relocs != NULL
&& elf_section_data (datasec)->relocs != internal_relocs)
free (internal_relocs);
return FALSE;
}
static enum elf_reloc_type_class
elf32_m68k_reloc_type_class (rela)
const Elf_Internal_Rela *rela;
{
switch ((int) ELF32_R_TYPE (rela->r_info))
{
case R_68K_RELATIVE:
return reloc_class_relative;
case R_68K_JMP_SLOT:
return reloc_class_plt;
case R_68K_COPY:
return reloc_class_copy;
default:
return reloc_class_normal;
}
}
#define TARGET_BIG_SYM bfd_elf32_m68k_vec
#define TARGET_BIG_NAME "elf32-m68k"
#define ELF_MACHINE_CODE EM_68K
#define ELF_MAXPAGESIZE 0x2000
#define elf_backend_create_dynamic_sections \
_bfd_elf_create_dynamic_sections
#define bfd_elf32_bfd_link_hash_table_create \
elf_m68k_link_hash_table_create
#define bfd_elf32_bfd_final_link _bfd_elf32_gc_common_final_link
#define elf_backend_check_relocs elf_m68k_check_relocs
#define elf_backend_adjust_dynamic_symbol \
elf_m68k_adjust_dynamic_symbol
#define elf_backend_size_dynamic_sections \
elf_m68k_size_dynamic_sections
#define elf_backend_relocate_section elf_m68k_relocate_section
#define elf_backend_finish_dynamic_symbol \
elf_m68k_finish_dynamic_symbol
#define elf_backend_finish_dynamic_sections \
elf_m68k_finish_dynamic_sections
#define elf_backend_gc_mark_hook elf_m68k_gc_mark_hook
#define elf_backend_gc_sweep_hook elf_m68k_gc_sweep_hook
#define bfd_elf32_bfd_merge_private_bfd_data \
elf32_m68k_merge_private_bfd_data
#define bfd_elf32_bfd_set_private_flags \
elf32_m68k_set_private_flags
#define bfd_elf32_bfd_print_private_bfd_data \
elf32_m68k_print_private_bfd_data
#define elf_backend_reloc_type_class elf32_m68k_reloc_type_class
#define elf_backend_can_gc_sections 1
#define elf_backend_can_refcount 1
#define elf_backend_want_got_plt 1
#define elf_backend_plt_readonly 1
#define elf_backend_want_plt_sym 0
#define elf_backend_got_header_size 12
#define elf_backend_rela_normal 1
#include "elf32-target.h"