#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/cris.h"
static reloc_howto_type * cris_reloc_type_lookup
PARAMS ((bfd *abfd, bfd_reloc_code_real_type code));
static void cris_info_to_howto_rela
PARAMS ((bfd *, arelent *, Elf32_Internal_Rela *));
static boolean cris_elf_grok_prstatus
PARAMS ((bfd *abfd, Elf_Internal_Note *note));
static boolean cris_elf_grok_psinfo
PARAMS ((bfd *abfd, Elf_Internal_Note *note));
static boolean cris_elf_relocate_section
PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
static bfd_reloc_status_type cris_final_link_relocate
PARAMS ((reloc_howto_type *, bfd *, asection *, bfd_byte *,
Elf_Internal_Rela *, bfd_vma));
static boolean cris_elf_gc_sweep_hook
PARAMS ((bfd *, struct bfd_link_info *, asection *,
const Elf_Internal_Rela *));
static asection * cris_elf_gc_mark_hook
PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *,
struct elf_link_hash_entry *, Elf_Internal_Sym *));
static boolean cris_elf_object_p PARAMS ((bfd *));
static void cris_elf_final_write_processing PARAMS ((bfd *, boolean));
static boolean cris_elf_print_private_bfd_data PARAMS ((bfd *, PTR));
static boolean cris_elf_merge_private_bfd_data PARAMS ((bfd *, bfd *));
struct elf_cris_link_hash_entry;
static boolean elf_cris_discard_excess_dso_dynamics
PARAMS ((struct elf_cris_link_hash_entry *, PTR));
static boolean elf_cris_discard_excess_program_dynamics
PARAMS ((struct elf_cris_link_hash_entry *, PTR));
static boolean elf_cris_adjust_gotplt_to_got
PARAMS ((struct elf_cris_link_hash_entry *, PTR));
static boolean elf_cris_try_fold_plt_to_got
PARAMS ((struct elf_cris_link_hash_entry *, PTR));
static struct bfd_hash_entry *elf_cris_link_hash_newfunc
PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
static struct bfd_link_hash_table *elf_cris_link_hash_table_create
PARAMS ((bfd *));
static boolean elf_cris_adjust_dynamic_symbol
PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
static boolean cris_elf_check_relocs
PARAMS ((bfd *, struct bfd_link_info *, asection *,
const Elf_Internal_Rela *));
static boolean elf_cris_size_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
static boolean elf_cris_finish_dynamic_symbol
PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *,
Elf_Internal_Sym *));
static boolean elf_cris_finish_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
static void elf_cris_hide_symbol
PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *, boolean));
static enum elf_reloc_type_class elf_cris_reloc_type_class
PARAMS ((const Elf_Internal_Rela *));
static reloc_howto_type cris_elf_howto_table [] =
{
HOWTO (R_CRIS_NONE,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_NONE",
false,
0,
0,
false),
HOWTO (R_CRIS_8,
0,
0,
8,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_8",
false,
0x0000,
0x00ff,
false),
HOWTO (R_CRIS_16,
0,
1,
16,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_16",
false,
0x00000000,
0x0000ffff,
false),
HOWTO (R_CRIS_32,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_32",
false,
0x00000000,
0xffffffff,
false),
HOWTO (R_CRIS_8_PCREL,
0,
0,
8,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_8_PCREL",
false,
0x0000,
0x00ff,
true),
HOWTO (R_CRIS_16_PCREL,
0,
1,
16,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_16_PCREL",
false,
0x00000000,
0x0000ffff,
true),
HOWTO (R_CRIS_32_PCREL,
0,
2,
32,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_32_PCREL",
false,
0x00000000,
0xffffffff,
true),
HOWTO (R_CRIS_GNU_VTINHERIT,
0,
2,
0,
false,
0,
complain_overflow_dont,
NULL,
"R_CRIS_GNU_VTINHERIT",
false,
0,
0,
false),
HOWTO (R_CRIS_GNU_VTENTRY,
0,
2,
0,
false,
0,
complain_overflow_dont,
_bfd_elf_rel_vtable_reloc_fn,
"R_CRIS_GNU_VTENTRY",
false,
0,
0,
false),
HOWTO (R_CRIS_COPY,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_COPY",
false,
0,
0,
false),
HOWTO (R_CRIS_GLOB_DAT,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_GLOB_DAT",
false,
0,
0xffffffff,
false),
HOWTO (R_CRIS_JUMP_SLOT,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_JUMP_SLOT",
false,
0,
0,
false),
HOWTO (R_CRIS_RELATIVE,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_RELATIVE",
false,
0,
0xffffffff,
false),
HOWTO (R_CRIS_16_GOT,
0,
1,
16,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_16_GOT",
false,
0,
0xffff,
false),
HOWTO (R_CRIS_32_GOT,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_32_GOT",
false,
0,
0xffffffff,
false),
HOWTO (R_CRIS_16_GOTPLT,
0,
1,
16,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_16_GOTPLT",
false,
0,
0xffff,
false),
HOWTO (R_CRIS_32_GOTPLT,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_32_GOTPLT",
false,
0,
0xffffffff,
false),
HOWTO (R_CRIS_32_GOTREL,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_32_GOTREL",
false,
0,
0xffffffff,
false),
HOWTO (R_CRIS_32_PLT_GOTREL,
0,
2,
32,
false,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_32_PLT_GOTREL",
false,
0,
0xffffffff,
false),
HOWTO (R_CRIS_32_PLT_PCREL,
0,
2,
32,
true,
0,
complain_overflow_bitfield,
bfd_elf_generic_reloc,
"R_CRIS_32_PLT_PCREL",
false,
0,
0xffffffff,
true)
};
struct cris_reloc_map
{
bfd_reloc_code_real_type bfd_reloc_val;
unsigned int cris_reloc_val;
};
static const struct cris_reloc_map cris_reloc_map [] =
{
{ BFD_RELOC_NONE, R_CRIS_NONE },
{ BFD_RELOC_8, R_CRIS_8 },
{ BFD_RELOC_16, R_CRIS_16 },
{ BFD_RELOC_32, R_CRIS_32 },
{ BFD_RELOC_8_PCREL, R_CRIS_8_PCREL },
{ BFD_RELOC_16_PCREL, R_CRIS_16_PCREL },
{ BFD_RELOC_32_PCREL, R_CRIS_32_PCREL },
{ BFD_RELOC_VTABLE_INHERIT, R_CRIS_GNU_VTINHERIT },
{ BFD_RELOC_VTABLE_ENTRY, R_CRIS_GNU_VTENTRY },
{ BFD_RELOC_CRIS_COPY, R_CRIS_COPY },
{ BFD_RELOC_CRIS_GLOB_DAT, R_CRIS_GLOB_DAT },
{ BFD_RELOC_CRIS_JUMP_SLOT, R_CRIS_JUMP_SLOT },
{ BFD_RELOC_CRIS_RELATIVE, R_CRIS_RELATIVE },
{ BFD_RELOC_CRIS_16_GOT, R_CRIS_16_GOT },
{ BFD_RELOC_CRIS_32_GOT, R_CRIS_32_GOT },
{ BFD_RELOC_CRIS_16_GOTPLT, R_CRIS_16_GOTPLT },
{ BFD_RELOC_CRIS_32_GOTPLT, R_CRIS_32_GOTPLT },
{ BFD_RELOC_CRIS_32_GOTREL, R_CRIS_32_GOTREL },
{ BFD_RELOC_CRIS_32_PLT_GOTREL, R_CRIS_32_PLT_GOTREL },
{ BFD_RELOC_CRIS_32_PLT_PCREL, R_CRIS_32_PLT_PCREL }
};
static reloc_howto_type *
cris_reloc_type_lookup (abfd, code)
bfd * abfd ATTRIBUTE_UNUSED;
bfd_reloc_code_real_type code;
{
unsigned int i;
for (i = 0; i < sizeof (cris_reloc_map) / sizeof (cris_reloc_map[0]); i++)
if (cris_reloc_map [i].bfd_reloc_val == code)
return & cris_elf_howto_table [cris_reloc_map[i].cris_reloc_val];
return NULL;
}
static void
cris_info_to_howto_rela (abfd, cache_ptr, dst)
bfd * abfd ATTRIBUTE_UNUSED;
arelent * cache_ptr;
Elf32_Internal_Rela * dst;
{
unsigned int r_type;
r_type = ELF32_R_TYPE (dst->r_info);
BFD_ASSERT (r_type < (unsigned int) R_CRIS_max);
cache_ptr->howto = & cris_elf_howto_table [r_type];
}
static boolean
cris_elf_grok_prstatus (abfd, note)
bfd *abfd;
Elf_Internal_Note *note;
{
int offset;
size_t raw_size;
switch (note->descsz)
{
default:
return false;
case 214:
elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 22);
offset = 70;
raw_size = 140;
break;
}
return _bfd_elfcore_make_pseudosection (abfd, ".reg",
raw_size, note->descpos + offset);
}
static boolean
cris_elf_grok_psinfo (abfd, note)
bfd *abfd;
Elf_Internal_Note *note;
{
switch (note->descsz)
{
default:
return false;
case 124:
elf_tdata (abfd)->core_program
= _bfd_elfcore_strndup (abfd, note->descdata + 28, 16);
elf_tdata (abfd)->core_command
= _bfd_elfcore_strndup (abfd, note->descdata + 44, 80);
}
{
char *command = elf_tdata (abfd)->core_command;
int n = strlen (command);
if (0 < n && command[n - 1] == ' ')
command[n - 1] = '\0';
}
return true;
}
#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1"
#define PLT_ENTRY_SIZE 20
static const bfd_byte elf_cris_plt0_entry[PLT_ENTRY_SIZE] =
{
0xfc, 0xe1,
0x7e, 0x7e,
0x7f, 0x0d,
0, 0, 0, 0,
0x30, 0x7a,
0x7f, 0x0d,
0, 0, 0, 0,
0x30, 0x09,
};
static const bfd_byte elf_cris_plt_entry[PLT_ENTRY_SIZE] =
{
0x7f, 0x0d,
0, 0, 0, 0,
0x30, 0x09,
0x3f, 0x7e,
0, 0, 0, 0,
0x2f, 0xfe,
0xec, 0xff,
0xff, 0xff
};
static const bfd_byte elf_cris_pic_plt0_entry[PLT_ENTRY_SIZE] =
{
0xfc, 0xe1, 0x7e, 0x7e,
0x04, 0x01, 0x30, 0x7a,
0x08, 0x01, 0x30, 0x09,
0, 0, 0, 0, 0, 0, 0, 0,
};
static const bfd_byte elf_cris_pic_plt_entry[PLT_ENTRY_SIZE] =
{
0x6f, 0x0d,
0, 0, 0, 0,
0x30, 0x09,
0x3f, 0x7e,
0, 0, 0, 0,
0x2f, 0xfe,
0xec, 0xff,
0xff, 0xff
};
struct elf_cris_pcrel_relocs_copied
{
struct elf_cris_pcrel_relocs_copied *next;
asection *section;
bfd_size_type count;
};
struct elf_cris_link_hash_entry
{
struct elf_link_hash_entry root;
struct elf_cris_pcrel_relocs_copied *pcrel_relocs_copied;
bfd_signed_vma gotplt_refcount;
bfd_size_type gotplt_offset;
};
struct elf_cris_link_hash_table
{
struct elf_link_hash_table root;
bfd_size_type next_gotplt_entry;
};
#define elf_cris_link_hash_traverse(table, func, info) \
(elf_link_hash_traverse \
(&(table)->root, \
(boolean (*) PARAMS ((struct elf_link_hash_entry *, PTR))) (func), \
(info)))
#define elf_cris_hash_table(p) \
((struct elf_cris_link_hash_table *) (p)->hash)
static struct bfd_hash_entry *
elf_cris_link_hash_newfunc (entry, table, string)
struct bfd_hash_entry *entry;
struct bfd_hash_table *table;
const char *string;
{
struct elf_cris_link_hash_entry *ret =
(struct elf_cris_link_hash_entry *) entry;
if (ret == (struct elf_cris_link_hash_entry *) NULL)
ret = ((struct elf_cris_link_hash_entry *)
bfd_hash_allocate (table,
sizeof (struct elf_cris_link_hash_entry)));
if (ret == (struct elf_cris_link_hash_entry *) NULL)
return (struct bfd_hash_entry *) ret;
ret = ((struct elf_cris_link_hash_entry *)
_bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret,
table, string));
if (ret != (struct elf_cris_link_hash_entry *) NULL)
{
ret->pcrel_relocs_copied = NULL;
ret->gotplt_refcount = 0;
ret->gotplt_offset = 0;
}
return (struct bfd_hash_entry *) ret;
}
static struct bfd_link_hash_table *
elf_cris_link_hash_table_create (abfd)
bfd *abfd;
{
struct elf_cris_link_hash_table *ret;
bfd_size_type amt = sizeof (struct elf_cris_link_hash_table);
ret = ((struct elf_cris_link_hash_table *) bfd_malloc (amt));
if (ret == (struct elf_cris_link_hash_table *) NULL)
return NULL;
if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
elf_cris_link_hash_newfunc))
{
free (ret);
return NULL;
}
ret->next_gotplt_entry = 12;
return &ret->root.root;
}
static bfd_reloc_status_type
cris_final_link_relocate (howto, input_bfd, input_section, contents, rel,
relocation)
reloc_howto_type * howto;
bfd * input_bfd;
asection * input_section;
bfd_byte * contents;
Elf_Internal_Rela * rel;
bfd_vma relocation;
{
bfd_reloc_status_type r;
switch (ELF32_R_TYPE (rel->r_info))
{
case R_CRIS_16_GOTPLT:
case R_CRIS_16_GOT:
if ((bfd_signed_vma) relocation < 0)
return bfd_reloc_overflow;
break;
case R_CRIS_32_PLT_PCREL:
case R_CRIS_32_PCREL:
relocation -= 2;
case R_CRIS_8_PCREL:
case R_CRIS_16_PCREL:
relocation -= 2;
break;
default:
break;
}
r = _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset,
relocation, rel->r_addend);
return r;
}
static boolean
cris_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;
{
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;
local_got_offsets = elf_local_got_offsets (input_bfd);
symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (input_bfd);
relend = relocs + input_section->reloc_count;
sgot = NULL;
splt = NULL;
sreloc = NULL;
if (dynobj != NULL)
{
splt = bfd_get_section_by_name (dynobj, ".plt");
sgot = bfd_get_section_by_name (dynobj, ".got");
}
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 * symname = NULL;
int r_type;
r_type = ELF32_R_TYPE (rel->r_info);
if ( r_type == R_CRIS_GNU_VTINHERIT
|| r_type == R_CRIS_GNU_VTENTRY)
continue;
r_symndx = ELF32_R_SYM (rel->r_info);
howto = cris_elf_howto_table + r_type;
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);
symname = (bfd_elf_string_from_elf_section
(input_bfd, symtab_hdr->sh_link, sym->st_name));
if (symname == NULL)
symname = bfd_section_name (input_bfd, sec);
}
else
{
if (sym_hashes == NULL)
return false;
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;
symname = 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;
if ((sec->output_section == NULL
&& (sec->owner->flags & DYNAMIC) != 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
&& (r_type == R_CRIS_8
|| r_type == R_CRIS_16
|| r_type == R_CRIS_32
|| r_type == R_CRIS_8_PCREL
|| r_type == R_CRIS_16_PCREL
|| r_type == R_CRIS_32_PCREL)))
relocation = 0;
else if (sec->output_section != NULL)
relocation = (h->root.u.def.value
+ sec->output_section->vma
+ sec->output_offset);
else
{
(*_bfd_error_handler)
(_("%s: unresolvable relocation %s against symbol `%s' from %s section"),
bfd_archive_filename (input_bfd),
cris_elf_howto_table[r_type].name,
symname,
bfd_get_section_name (input_bfd, input_section));
bfd_set_error (bfd_error_bad_value);
return false;
}
}
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, symname, 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_CRIS_16_GOTPLT:
case R_CRIS_32_GOTPLT:
if (h != NULL
&& ((struct elf_cris_link_hash_entry *) h)->gotplt_offset != 0)
{
asection *sgotplt
= bfd_get_section_by_name (dynobj, ".got.plt");
bfd_vma got_offset;
BFD_ASSERT (h->dynindx != -1);
BFD_ASSERT (sgotplt != NULL);
got_offset
= ((struct elf_cris_link_hash_entry *) h)->gotplt_offset;
relocation = got_offset;
break;
}
if (h != NULL && h->got.offset == (bfd_vma) -1)
{
(*_bfd_error_handler)
(_("%s: No PLT nor GOT for relocation %s against symbol `%s' from %s section"),
bfd_archive_filename (input_bfd),
cris_elf_howto_table[r_type].name,
symname[0] != '\0' ? symname : _("[whose name is lost]"),
bfd_get_section_name (input_bfd, input_section));
bfd_set_error (bfd_error_bad_value);
return false;
}
case R_CRIS_16_GOT:
case R_CRIS_32_GOT:
{
bfd_vma off;
if (h != NULL)
{
off = h->got.offset;
BFD_ASSERT (off != (bfd_vma) -1);
if (!elf_hash_table (info)->dynamic_sections_created
|| (! info->shared && h->dynindx == -1)
|| (info->shared
&& (info->symbolic || h->dynindx == -1)
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
{
BFD_ASSERT (!elf_hash_table (info)->dynamic_sections_created
|| info->shared
|| (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) != 0
|| h->root.type == bfd_link_hash_undefweak);
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 *srelgot;
Elf_Internal_Rela outrel;
srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
BFD_ASSERT (srelgot != NULL);
outrel.r_offset = (sgot->output_section->vma
+ sgot->output_offset
+ off);
outrel.r_info = ELF32_R_INFO (0, R_CRIS_RELATIVE);
outrel.r_addend = relocation;
bfd_elf32_swap_reloca_out (output_bfd, &outrel,
(((Elf32_External_Rela *)
srelgot->contents)
+ srelgot->reloc_count));
++srelgot->reloc_count;
}
local_got_offsets[r_symndx] |= 1;
}
}
relocation = sgot->output_offset + off;
if (rel->r_addend != 0)
{
if (h == NULL)
(*_bfd_error_handler)
(_("%s: relocation %s with non-zero addend %d against local symbol from %s section"),
bfd_archive_filename (input_bfd),
cris_elf_howto_table[r_type].name,
rel->r_addend,
bfd_get_section_name (input_bfd, input_section));
else
(*_bfd_error_handler)
(_("%s: relocation %s with non-zero addend %d against symbol `%s' from %s section"),
bfd_archive_filename (input_bfd),
cris_elf_howto_table[r_type].name,
rel->r_addend,
symname[0] != '\0' ? symname : _("[whose name is lost]"),
bfd_get_section_name (input_bfd, input_section));
bfd_set_error (bfd_error_bad_value);
return false;
}
}
break;
case R_CRIS_32_GOTREL:
if (h != NULL && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
{
(*_bfd_error_handler)
(_("%s: relocation %s is not allowed for global symbol: `%s' from %s section"),
bfd_archive_filename (input_bfd),
cris_elf_howto_table[r_type].name,
symname,
bfd_get_section_name (input_bfd, input_section));
bfd_set_error (bfd_error_bad_value);
return false;
}
if (sgot == NULL)
{
(*_bfd_error_handler)
(_("%s: relocation %s in section %s with no GOT created"),
bfd_archive_filename (input_bfd),
cris_elf_howto_table[r_type].name,
bfd_get_section_name (input_bfd, input_section));
bfd_set_error (bfd_error_bad_value);
return false;
}
relocation -= sgot->output_section->vma;
break;
case R_CRIS_32_PLT_PCREL:
if (h == NULL || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
break;
if (h->plt.offset == (bfd_vma) -1
|| splt == NULL)
{
break;
}
relocation = (splt->output_section->vma
+ splt->output_offset
+ h->plt.offset);
break;
case R_CRIS_32_PLT_GOTREL:
relocation -= sgot->output_section->vma;
if (h == NULL || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
break;
if (h->plt.offset == (bfd_vma) -1
|| splt == NULL)
{
break;
}
relocation = (splt->output_section->vma
+ splt->output_offset
+ h->plt.offset
- sgot->output_section->vma);
break;
case R_CRIS_8_PCREL:
case R_CRIS_16_PCREL:
case R_CRIS_32_PCREL:
if (h == NULL || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
break;
case R_CRIS_8:
case R_CRIS_16:
case R_CRIS_32:
if (info->shared
&& r_symndx != 0
&& (input_section->flags & SEC_ALLOC) != 0
&& ((r_type != R_CRIS_8_PCREL
&& r_type != R_CRIS_16_PCREL
&& r_type != R_CRIS_32_PCREL)
|| (!info->symbolic
|| (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_REGULAR) == 0)))
{
Elf_Internal_Rela outrel;
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);
if (sreloc == NULL)
{
(*_bfd_error_handler)
(_("%s: Internal inconsistency; no relocation section %s"),
bfd_archive_filename (input_bfd),
name);
bfd_set_error (bfd_error_bad_value);
return false;
}
}
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_CRIS_32)
{
relocate = true;
outrel.r_info = ELF32_R_INFO (0, R_CRIS_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;
}
}
bfd_elf32_swap_reloca_out (output_bfd, &outrel,
(((Elf32_External_Rela *)
sreloc->contents)
+ sreloc->reloc_count));
++sreloc->reloc_count;
if (!relocate)
continue;
}
break;
}
r = cris_final_link_relocate (howto, input_bfd, input_section,
contents, rel, relocation);
if (r != bfd_reloc_ok)
{
const char * msg = (const char *) NULL;
switch (r)
{
case bfd_reloc_overflow:
r = info->callbacks->reloc_overflow
(info, symname, howto->name, (bfd_vma) 0,
input_bfd, input_section, rel->r_offset);
break;
case bfd_reloc_undefined:
r = info->callbacks->undefined_symbol
(info, symname, input_bfd, input_section, rel->r_offset,
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)
r = info->callbacks->warning
(info, msg, symname, input_bfd, input_section, rel->r_offset);
if (! r)
return false;
}
}
return true;
}
static boolean
elf_cris_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 = 2, plt_off2 = 10, plt_off3 = 16;
dynobj = elf_hash_table (info)->dynobj;
if (h->plt.offset != (bfd_vma) -1)
{
asection *splt;
asection *sgotplt;
asection *sgot;
asection *srela;
bfd_vma got_base;
bfd_vma gotplt_offset
= ((struct elf_cris_link_hash_entry *) h)->gotplt_offset;
Elf_Internal_Rela rela;
boolean has_gotplt = gotplt_offset != 0;
bfd_vma gotplt_index = gotplt_offset/4 - 3;
bfd_vma got_offset
= (has_gotplt
? gotplt_offset
: h->got.offset + elf_cris_hash_table(info)->next_gotplt_entry);
BFD_ASSERT (h->dynindx != -1);
splt = bfd_get_section_by_name (dynobj, ".plt");
sgot = bfd_get_section_by_name (dynobj, ".got");
sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
srela = bfd_get_section_by_name (dynobj, ".rela.plt");
BFD_ASSERT (splt != NULL && sgotplt != NULL
&& (! has_gotplt || srela != NULL));
got_base = sgotplt->output_section->vma + sgotplt->output_offset;
if (! info->shared)
{
memcpy (splt->contents + h->plt.offset, elf_cris_plt_entry,
PLT_ENTRY_SIZE);
bfd_put_32 (output_bfd, got_base + got_offset,
splt->contents + h->plt.offset + plt_off1);
}
else
{
memcpy (splt->contents + h->plt.offset, elf_cris_pic_plt_entry,
PLT_ENTRY_SIZE);
bfd_put_32 (output_bfd, got_offset,
splt->contents + h->plt.offset + plt_off1);
}
if (has_gotplt)
{
bfd_put_32 (output_bfd,
gotplt_index * sizeof (Elf32_External_Rela),
splt->contents + h->plt.offset + plt_off2);
bfd_put_32 (output_bfd, - (h->plt.offset + plt_off3 + 4),
splt->contents + h->plt.offset + plt_off3);
bfd_put_32 (output_bfd,
(splt->output_section->vma
+ splt->output_offset
+ h->plt.offset
+ 8),
sgotplt->contents + got_offset);
rela.r_offset = (sgotplt->output_section->vma
+ sgotplt->output_offset
+ got_offset);
rela.r_info = ELF32_R_INFO (h->dynindx, R_CRIS_JUMP_SLOT);
rela.r_addend = 0;
bfd_elf32_swap_reloca_out (output_bfd, &rela,
((Elf32_External_Rela *) srela->contents
+ gotplt_index));
}
if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
{
sym->st_shndx = SHN_UNDEF;
if ((h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR_NONWEAK)
== 0)
sym->st_value = 0;
}
}
if (h->got.offset != (bfd_vma) -1
&& (info->shared
|| (h->dynindx != -1
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
&& h->root.type != bfd_link_hash_undefweak)))
{
asection *sgot;
asection *srela;
Elf_Internal_Rela rela;
bfd_byte *where;
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));
where = sgot->contents + (h->got.offset &~ (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)))
{
rela.r_info = ELF32_R_INFO (0, R_CRIS_RELATIVE);
rela.r_addend = bfd_get_signed_32 (output_bfd, where);
}
else
{
bfd_put_32 (output_bfd, (bfd_vma) 0, where);
rela.r_info = ELF32_R_INFO (h->dynindx, R_CRIS_GLOB_DAT);
rela.r_addend = 0;
}
bfd_elf32_swap_reloca_out (output_bfd, &rela,
((Elf32_External_Rela *) srela->contents
+ srela->reloc_count));
++srela->reloc_count;
}
if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_COPY) != 0)
{
asection *s;
Elf_Internal_Rela rela;
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_CRIS_COPY);
rela.r_addend = 0;
bfd_elf32_swap_reloca_out (output_bfd, &rela,
((Elf32_External_Rela *) s->contents
+ s->reloc_count));
++s->reloc_count;
}
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 boolean
elf_cris_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;
asection *s;
bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
switch (dyn.d_tag)
{
default:
break;
case DT_PLTGOT:
s = bfd_get_section_by_name (output_bfd, ".got");
BFD_ASSERT (s != NULL);
dyn.d_un.d_ptr = s->vma;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_JMPREL:
s = bfd_get_section_by_name (output_bfd, ".rela.plt");
dyn.d_un.d_ptr = s != NULL ? s->vma : 0;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_PLTRELSZ:
s = bfd_get_section_by_name (output_bfd, ".rela.plt");
if (s == NULL)
dyn.d_un.d_val = 0;
else 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 (info->shared)
memcpy (splt->contents, elf_cris_pic_plt0_entry, PLT_ENTRY_SIZE);
else
{
memcpy (splt->contents, elf_cris_plt0_entry, PLT_ENTRY_SIZE);
bfd_put_32 (output_bfd,
sgot->output_section->vma + sgot->output_offset + 4,
splt->contents + 6);
bfd_put_32 (output_bfd,
sgot->output_section->vma + sgot->output_offset + 8,
splt->contents + 14);
elf_section_data (splt->output_section)->this_hdr.sh_entsize
= PLT_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;
}
static asection *
cris_elf_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_CRIS_GNU_VTINHERIT:
case R_CRIS_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 (sec->owner, sym->st_shndx);
return NULL;
}
static boolean
cris_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;
{
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_CRIS_16_GOT:
case R_CRIS_32_GOT:
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);
}
}
break;
}
local_got_reloc:
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_CRIS_16_GOTPLT:
case R_CRIS_32_GOTPLT:
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
goto local_got_reloc;
case R_CRIS_32_PLT_GOTREL:
if (local_got_refcounts != NULL)
local_got_refcounts[-1]--;
case R_CRIS_8_PCREL:
case R_CRIS_16_PCREL:
case R_CRIS_32_PCREL:
case R_CRIS_32_PLT_PCREL:
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 (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
&& h->plt.refcount > 0)
--h->plt.refcount;
}
break;
default:
break;
}
}
return true;
}
static boolean
elf_cris_adjust_gotplt_to_got (h, p)
struct elf_cris_link_hash_entry *h;
PTR p;
{
struct bfd_link_info *info = (struct bfd_link_info *) p;
bfd *dynobj = elf_hash_table (info)->dynobj;
BFD_ASSERT (dynobj != NULL);
if (h->root.root.type == bfd_link_hash_warning)
h = (struct elf_cris_link_hash_entry *) h->root.root.u.i.link;
if (h->gotplt_refcount <= 0)
return true;
if (h->root.got.refcount > 0)
{
h->root.got.refcount += h->gotplt_refcount;
h->gotplt_refcount = -1;
}
else
{
asection *sgot = bfd_get_section_by_name (dynobj, ".got");
asection *srelgot
= bfd_get_section_by_name (dynobj, ".rela.got");
h->root.got.refcount = h->gotplt_refcount;
h->gotplt_refcount = -1;
BFD_ASSERT (sgot != NULL && srelgot != NULL);
sgot->_raw_size += 4;
srelgot->_raw_size += sizeof (Elf32_External_Rela);
}
return true;
}
static boolean
elf_cris_try_fold_plt_to_got (h, p)
struct elf_cris_link_hash_entry *h;
PTR p;
{
struct bfd_link_info *info = (struct bfd_link_info *) p;
if (h->root.got.refcount <= 0 || h->root.plt.refcount <= 0)
return true;
BFD_ASSERT (h->gotplt_refcount <= h->root.plt.refcount);
if (h->gotplt_refcount == h->root.plt.refcount)
{
if (! elf_cris_adjust_gotplt_to_got (h, info))
return false;
h->root.plt.offset = (bfd_vma) -1;
}
return true;
}
static void
elf_cris_hide_symbol (info, h, force_local)
struct bfd_link_info *info;
struct elf_link_hash_entry *h;
boolean force_local;
{
elf_cris_adjust_gotplt_to_got ((struct elf_cris_link_hash_entry *) h, info);
_bfd_elf_link_hash_hide_symbol (info, h, force_local);
}
static boolean
elf_cris_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)
{
BFD_ASSERT ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0);
h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
h->plt.offset = (bfd_vma) -1;
return
elf_cris_adjust_gotplt_to_got ((struct
elf_cris_link_hash_entry *) h,
info);
}
if (! elf_cris_try_fold_plt_to_got ((struct elf_cris_link_hash_entry *)
h, info))
return false;
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)
{
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)
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;
}
if (h->got.refcount > 0)
{
h->got.refcount += h->plt.refcount;
BFD_ASSERT ((s->_raw_size & 3) == 0);
h->plt.offset = s->_raw_size;
BFD_ASSERT (((struct elf_cris_link_hash_entry *)
h)->gotplt_offset == 0);
s->_raw_size += PLT_ENTRY_SIZE;
return true;
}
h->plt.offset = s->_raw_size;
s->_raw_size += PLT_ENTRY_SIZE;
((struct elf_cris_link_hash_entry *) h)->gotplt_offset
= elf_cris_hash_table (info)->next_gotplt_entry;
elf_cris_hash_table (info)->next_gotplt_entry += 4;
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;
if ((h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0)
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 boolean
cris_elf_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, **sym_hashes_end;
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);
sym_hashes_end = sym_hashes + symtab_hdr->sh_size/sizeof (Elf32_External_Sym);
local_got_refcounts = elf_local_got_refcounts (abfd);
sgot = NULL;
srelgot = NULL;
sreloc = NULL;
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;
enum elf_cris_reloc_type r_type;
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];
r_type = ELF32_R_TYPE (rel->r_info);
switch (r_type)
{
case R_CRIS_16_GOT:
case R_CRIS_32_GOT:
case R_CRIS_32_GOTREL:
case R_CRIS_32_PLT_GOTREL:
case R_CRIS_32_PLT_PCREL:
case R_CRIS_16_GOTPLT:
case R_CRIS_32_GOTPLT:
if (dynobj == NULL)
{
elf_hash_table (info)->dynobj = dynobj = abfd;
if (!_bfd_elf_create_got_section (dynobj, info))
return false;
}
break;
default:
break;
}
switch (r_type)
{
case R_CRIS_16_GOTPLT:
case R_CRIS_32_GOTPLT:
case R_CRIS_16_GOT:
case R_CRIS_32_GOT:
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;
}
}
case R_CRIS_32_GOTREL:
case R_CRIS_32_PLT_GOTREL:
if (sgot == NULL)
sgot = bfd_get_section_by_name (dynobj, ".got");
if (local_got_refcounts == NULL)
{
bfd_size_type amt;
amt = symtab_hdr->sh_info + 1;
amt *= sizeof (bfd_signed_vma);
local_got_refcounts = ((bfd_signed_vma *) bfd_zalloc (abfd, amt));
if (local_got_refcounts == NULL)
return false;
local_got_refcounts++;
elf_local_got_refcounts (abfd) = local_got_refcounts;
}
break;
default:
break;
}
switch (r_type)
{
case R_CRIS_16_GOTPLT:
case R_CRIS_32_GOTPLT:
if (h != NULL)
{
((struct elf_cris_link_hash_entry *) h)->gotplt_refcount++;
goto handle_gotplt_reloc;
}
case R_CRIS_16_GOT:
case R_CRIS_32_GOT:
if (h != NULL)
{
if (h->got.refcount == 0)
{
if (h->dynindx == -1)
{
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[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_CRIS_32_GOTREL:
local_got_refcounts[-1]++;
break;
handle_gotplt_reloc:
case R_CRIS_32_PLT_GOTREL:
local_got_refcounts[-1]++;
case R_CRIS_32_PLT_PCREL:
if (h == NULL || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
continue;
h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
h->plt.refcount++;
break;
case R_CRIS_8:
case R_CRIS_16:
case R_CRIS_32:
if (info->shared
&& (sec->flags & SEC_ALLOC) != 0
&& (sec->flags & SEC_READONLY) != 0)
{
(*_bfd_error_handler)
(_("%s, section %s:\n relocation %s should not be used in a shared object; recompile with -fPIC"),
bfd_archive_filename (abfd),
sec->name,
cris_elf_howto_table[r_type].name);
}
case R_CRIS_8_PCREL:
case R_CRIS_16_PCREL:
case R_CRIS_32_PCREL:
if (h != NULL)
{
h->elf_link_hash_flags |= ELF_LINK_NON_GOT_REF;
if (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
h->plt.refcount++;
}
if (! info->shared)
break;
if ((sec->flags & SEC_ALLOC) == 0)
break;
if (r_type == R_CRIS_8_PCREL
|| r_type == R_CRIS_16_PCREL
|| r_type == R_CRIS_32_PCREL)
{
if (h == NULL || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
break;
if (info->symbolic
&& h->root.type != bfd_link_hash_defweak
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0)
break;
}
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)
info->flags |= DF_TEXTREL;
}
sreloc->_raw_size += sizeof (Elf32_External_Rela);
if ((r_type == R_CRIS_8_PCREL
|| r_type == R_CRIS_16_PCREL
|| r_type == R_CRIS_32_PCREL)
&& info->symbolic)
{
struct elf_cris_link_hash_entry *eh;
struct elf_cris_pcrel_relocs_copied *p;
eh = (struct elf_cris_link_hash_entry *) h;
for (p = eh->pcrel_relocs_copied; p != NULL; p = p->next)
if (p->section == sreloc)
break;
if (p == NULL)
{
p = ((struct elf_cris_pcrel_relocs_copied *)
bfd_alloc (dynobj, (bfd_size_type) sizeof *p));
if (p == NULL)
return false;
p->next = eh->pcrel_relocs_copied;
eh->pcrel_relocs_copied = p;
p->section = sreloc;
p->count = 0;
}
++p->count;
}
break;
case R_CRIS_GNU_VTINHERIT:
if (!_bfd_elf32_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
return false;
break;
case R_CRIS_GNU_VTENTRY:
if (!_bfd_elf32_gc_record_vtentry (abfd, sec, h, rel->r_addend))
return false;
break;
default:
bfd_set_error (bfd_error_bad_value);
return false;
}
}
return true;
}
static boolean
elf_cris_size_dynamic_sections (output_bfd, info)
bfd *output_bfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info;
{
bfd *dynobj;
asection *s;
boolean plt;
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
{
elf_cris_link_hash_traverse (elf_cris_hash_table (info),
elf_cris_adjust_gotplt_to_got,
(PTR) info);
s = bfd_get_section_by_name (dynobj, ".rela.got");
if (s != NULL)
s->_raw_size = 0;
}
if (info->shared)
elf_cris_link_hash_traverse (elf_cris_hash_table (info),
elf_cris_discard_excess_dso_dynamics,
(PTR) info);
else
elf_cris_link_hash_traverse (elf_cris_hash_table (info),
elf_cris_discard_excess_program_dynamics,
(PTR) info);
plt = false;
relocs = false;
for (s = dynobj->sections; s != NULL; s = s->next)
{
const char *name;
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
{
if (strcmp (name, ".rela.plt") != 0)
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;
info->flags |= DF_TEXTREL;
}
}
#undef add_dynamic_entry
return true;
}
static boolean
elf_cris_discard_excess_dso_dynamics (h, inf)
struct elf_cris_link_hash_entry *h;
PTR inf;
{
struct elf_cris_pcrel_relocs_copied *s;
struct bfd_link_info *info = (struct bfd_link_info *) inf;
if (h->root.root.type == bfd_link_hash_warning)
h = (struct elf_cris_link_hash_entry *) h->root.root.u.i.link;
if ((h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0
&& ((h->root.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0
|| info->symbolic))
{
for (s = h->pcrel_relocs_copied; s != NULL; s = s->next)
s->section->_raw_size -= s->count * sizeof (Elf32_External_Rela);
}
return true;
}
static boolean
elf_cris_discard_excess_program_dynamics (h, inf)
struct elf_cris_link_hash_entry *h;
PTR inf;
{
struct bfd_link_info *info = (struct bfd_link_info *) inf;
if (h->root.root.type == bfd_link_hash_warning)
h = (struct elf_cris_link_hash_entry *) h->root.root.u.i.link;
if ((h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0)
{
if (h->root.got.refcount > 0
&& elf_hash_table (info)->dynamic_sections_created)
{
bfd *dynobj = elf_hash_table (info)->dynobj;
asection *srelgot;
BFD_ASSERT (dynobj != NULL);
srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
BFD_ASSERT (srelgot != NULL);
srelgot->_raw_size -= sizeof (Elf32_External_Rela);
}
if (! info->export_dynamic
&& (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) == 0)
{
h->root.dynindx = -1;
_bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
h->root.dynstr_index);
}
}
return true;
}
static boolean
cris_elf_object_p (abfd)
bfd *abfd;
{
if ((elf_elfheader (abfd)->e_flags & EF_CRIS_UNDERSCORE))
return (bfd_get_symbol_leading_char (abfd) == '_');
else
return (bfd_get_symbol_leading_char (abfd) == 0);
}
static void
cris_elf_final_write_processing (abfd, linker)
bfd *abfd;
boolean linker ATTRIBUTE_UNUSED;
{
if (bfd_get_symbol_leading_char (abfd) == '_')
elf_elfheader (abfd)->e_flags |= EF_CRIS_UNDERSCORE;
else
elf_elfheader (abfd)->e_flags &= ~EF_CRIS_UNDERSCORE;
}
static boolean
cris_elf_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_CRIS_UNDERSCORE)
fprintf (file, _(" [symbols have a _ prefix]"));
fputc ('\n', file);
return true;
}
static boolean
cris_elf_merge_private_bfd_data (ibfd, obfd)
bfd *ibfd;
bfd *obfd;
{
flagword old_flags, new_flags;
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;
if (! elf_flags_init (obfd))
{
elf_flags_init (obfd) = true;
cris_elf_final_write_processing (obfd, false);
}
old_flags = elf_elfheader (obfd)->e_flags;
new_flags = elf_elfheader (ibfd)->e_flags;
if ((old_flags & EF_CRIS_UNDERSCORE) != (new_flags & EF_CRIS_UNDERSCORE))
{
(*_bfd_error_handler)
((new_flags & EF_CRIS_UNDERSCORE)
? _("%s: uses _-prefixed symbols, but writing file with non-prefixed symbols")
: _("%s: uses non-prefixed symbols, but writing file with _-prefixed symbols"),
bfd_archive_filename (ibfd));
bfd_set_error (bfd_error_bad_value);
return false;
}
return true;
}
static enum elf_reloc_type_class
elf_cris_reloc_type_class (rela)
const Elf_Internal_Rela *rela;
{
switch ((int) ELF32_R_TYPE (rela->r_info))
{
case R_CRIS_RELATIVE:
return reloc_class_relative;
case R_CRIS_JUMP_SLOT:
return reloc_class_plt;
case R_CRIS_COPY:
return reloc_class_copy;
default:
return reloc_class_normal;
}
}
#define ELF_ARCH bfd_arch_cris
#define ELF_MACHINE_CODE EM_CRIS
#define ELF_MAXPAGESIZE 0x2000
#define TARGET_LITTLE_SYM bfd_elf32_cris_vec
#define TARGET_LITTLE_NAME "elf32-cris"
#define elf_symbol_leading_char 0
#define elf_info_to_howto_rel NULL
#define elf_info_to_howto cris_info_to_howto_rela
#define elf_backend_relocate_section cris_elf_relocate_section
#define elf_backend_gc_mark_hook cris_elf_gc_mark_hook
#define elf_backend_gc_sweep_hook cris_elf_gc_sweep_hook
#define elf_backend_check_relocs cris_elf_check_relocs
#define elf_backend_grok_prstatus cris_elf_grok_prstatus
#define elf_backend_grok_psinfo cris_elf_grok_psinfo
#define elf_backend_can_gc_sections 1
#define elf_backend_can_refcount 1
#define elf_backend_object_p cris_elf_object_p
#define elf_backend_final_write_processing \
cris_elf_final_write_processing
#define bfd_elf32_bfd_print_private_bfd_data \
cris_elf_print_private_bfd_data
#define bfd_elf32_bfd_merge_private_bfd_data \
cris_elf_merge_private_bfd_data
#define bfd_elf32_bfd_reloc_type_lookup cris_reloc_type_lookup
#define bfd_elf32_bfd_link_hash_table_create \
elf_cris_link_hash_table_create
#define elf_backend_adjust_dynamic_symbol \
elf_cris_adjust_dynamic_symbol
#define elf_backend_size_dynamic_sections \
elf_cris_size_dynamic_sections
#define elf_backend_finish_dynamic_symbol \
elf_cris_finish_dynamic_symbol
#define elf_backend_finish_dynamic_sections \
elf_cris_finish_dynamic_sections
#define elf_backend_create_dynamic_sections \
_bfd_elf_create_dynamic_sections
#define bfd_elf32_bfd_final_link \
_bfd_elf32_gc_common_final_link
#define elf_backend_hide_symbol elf_cris_hide_symbol
#define elf_backend_reloc_type_class elf_cris_reloc_type_class
#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_plt_header_size PLT_ENTRY_SIZE
#define elf_backend_may_use_rel_p 0
#define elf_backend_may_use_rela_p 1
#define elf_backend_rela_normal 1
#include "elf32-target.h"
#define INCLUDED_TARGET_FILE
#undef TARGET_LITTLE_SYM
#undef TARGET_LITTLE_NAME
#undef elf_symbol_leading_char
#define TARGET_LITTLE_SYM bfd_elf32_us_cris_vec
#define TARGET_LITTLE_NAME "elf32-us-cris"
#define elf_symbol_leading_char '_'
#include "elf32-target.h"