/* CRIS-specific support for 32-bit ELF. Copyright 2000 Free Software Foundation, Inc. Contributed by Axis Communications AB. Written by Hans-Peter Nilsson, based on elf32-fr30.c This file is part of BFD, the Binary File Descriptor library. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "bfd.h" #include "sysdep.h" #include "libbfd.h" #include "elf-bfd.h" #include "elf/cris.h" /* Forward declarations. */ 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_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 ((bfd *, 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 *)); static reloc_howto_type cris_elf_howto_table [] = { /* This reloc does nothing. */ HOWTO (R_CRIS_NONE, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ false, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_CRIS_NONE", /* name */ false, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ false), /* pcrel_offset */ /* An 8 bit absolute relocation. */ HOWTO (R_CRIS_8, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ false, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_CRIS_8", /* name */ false, /* partial_inplace */ 0x0000, /* src_mask */ 0x00ff, /* dst_mask */ false), /* pcrel_offset */ /* A 16 bit absolute relocation. */ HOWTO (R_CRIS_16, /* type */ 0, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ false, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_CRIS_16", /* name */ false, /* partial_inplace */ 0x00000000, /* src_mask */ 0x0000ffff, /* dst_mask */ false), /* pcrel_offset */ /* A 32 bit absolute relocation. */ HOWTO (R_CRIS_32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ false, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_CRIS_32", /* name */ false, /* partial_inplace */ 0x00000000, /* src_mask */ 0xffffffff, /* dst_mask */ false), /* pcrel_offset */ /* An 8 bit absolute relocation. */ HOWTO (R_CRIS_8_PCREL, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ true, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_CRIS_8_PCREL", /* name */ false, /* partial_inplace */ 0x0000, /* src_mask */ 0x00ff, /* dst_mask */ false), /* pcrel_offset */ /* A 16 bit absolute relocation. */ HOWTO (R_CRIS_16_PCREL, /* type */ 0, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ true, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_CRIS_16", /* name */ false, /* partial_inplace */ 0x00000000, /* src_mask */ 0x0000ffff, /* dst_mask */ false), /* pcrel_offset */ /* A 32 bit absolute relocation. */ HOWTO (R_CRIS_32_PCREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ true, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_CRIS_32", /* name */ false, /* partial_inplace */ 0x00000000, /* src_mask */ 0xffffffff, /* dst_mask */ false), /* pcrel_offset */ /* GNU extension to record C++ vtable hierarchy */ HOWTO (R_CRIS_GNU_VTINHERIT, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ NULL, /* special_function */ "R_CRIS_GNU_VTINHERIT", /* name */ false, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ false), /* pcrel_offset */ /* GNU extension to record C++ vtable member usage */ HOWTO (R_CRIS_GNU_VTENTRY, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ _bfd_elf_rel_vtable_reloc_fn, /* special_function */ "R_CRIS_GNU_VTENTRY", /* name */ false, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ false) /* pcrel_offset */ }; /* Map BFD reloc types to CRIS ELF reloc types. */ 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 } }; 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 = 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; } /* Set the howto pointer for an CRIS ELF reloc. */ 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]; } /* Perform a single relocation. By default we use the standard BFD routines, but we might have to do a few relocs ourselves in the future. */ 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 = _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, relocation, rel->r_addend); return r; } /* Relocate an CRIS ELF section. See elf32-fr30.c, from where this was copied, for further comments. */ 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; { Elf_Internal_Shdr * symtab_hdr; struct elf_link_hash_entry ** sym_hashes; Elf_Internal_Rela * rel; Elf_Internal_Rela * relend; symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; sym_hashes = elf_sym_hashes (input_bfd); relend = relocs + input_section->reloc_count; /* It seems this can happen with erroneous or unsupported input (mixing a.out and elf in an archive, for example.) */ if (sym_hashes == NULL) return false; for (rel = relocs; rel < relend; rel ++) { reloc_howto_type * howto; unsigned long r_symndx; Elf_Internal_Sym * sym; asection * sec; struct elf_link_hash_entry * h; bfd_vma relocation; bfd_reloc_status_type r; const char * name = NULL; int r_type; 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); if (info->relocateable) { /* This is a relocateable link. We don't have to change anything, unless the reloc is against a section symbol, in which case we have to adjust according to where the section symbol winds up in the output section. */ if (r_symndx < symtab_hdr->sh_info) { sym = local_syms + r_symndx; if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) { sec = local_sections [r_symndx]; rel->r_addend += sec->output_offset + sym->st_value; } } continue; } /* This is a final link. */ howto = cris_elf_howto_table + ELF32_R_TYPE (rel->r_info); h = NULL; sym = NULL; sec = NULL; if (r_symndx < symtab_hdr->sh_info) { sym = local_syms + r_symndx; sec = local_sections [r_symndx]; relocation = (sec->output_section->vma + sec->output_offset + sym->st_value); name = bfd_elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link, sym->st_name); name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name; #if 0 fprintf (stderr, "local: sec: %s, sym: %s (%d), value: %x + %x + %x addend %x\n", sec->name, name, sym->st_name, sec->output_section->vma, sec->output_offset, sym->st_value, rel->r_addend); #endif } else { h = sym_hashes [r_symndx - symtab_hdr->sh_info]; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; name = h->root.root.string; if (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) { sec = h->root.u.def.section; relocation = (h->root.u.def.value + sec->output_section->vma + sec->output_offset); #if 0 fprintf (stderr, "defined: sec: %s, name: %s, value: %x + %x + %x gives: %x\n", sec->name, name, h->root.u.def.value, sec->output_section->vma, sec->output_offset, relocation); #endif } else if (h->root.type == bfd_link_hash_undefweak) { #if 0 fprintf (stderr, "undefined: sec: %s, name: %s\n", sec->name, name); #endif relocation = 0; } else if (info->shared && 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, true))) return false; #if 0 fprintf (stderr, "unknown: name: %s\n", name); #endif relocation = 0; } } 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, name, howto->name, (bfd_vma) 0, input_bfd, input_section, rel->r_offset); break; case bfd_reloc_undefined: r = info->callbacks->undefined_symbol (info, name, 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, name, input_bfd, input_section, rel->r_offset); if (! r) return false; } } return true; } /* Return the section that should be marked against GC for a given relocation. */ static asection * cris_elf_gc_mark_hook (abfd, info, rel, h, sym) bfd * abfd; struct bfd_link_info * info ATTRIBUTE_UNUSED; Elf_Internal_Rela * rel; struct elf_link_hash_entry * h; Elf_Internal_Sym * sym; { if (h != NULL) { switch (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 { if (!(elf_bad_symtab (abfd) && ELF_ST_BIND (sym->st_info) != STB_LOCAL) && ! ((sym->st_shndx <= 0 || sym->st_shndx >= SHN_LORESERVE) && sym->st_shndx != SHN_COMMON)) { return bfd_section_from_elf_index (abfd, sym->st_shndx); } } return NULL; } /* Update the got entry reference counts for the section being removed. */ 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; { return true; } /* Look through the relocs for a section during the first phase. Since we don't do .gots or .plts, we just need to consider the virtual table relocs for gc. */ static boolean cris_elf_check_relocs (abfd, info, sec, relocs) bfd *abfd; struct bfd_link_info *info; asection *sec; const Elf_Internal_Rela *relocs; { Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes, **sym_hashes_end; const Elf_Internal_Rela *rel; const Elf_Internal_Rela *rel_end; if (info->relocateable) return true; symtab_hdr = &elf_tdata (abfd)->symtab_hdr; sym_hashes = elf_sym_hashes (abfd); sym_hashes_end = sym_hashes + symtab_hdr->sh_size/sizeof (Elf32_External_Sym); if (!elf_bad_symtab (abfd)) sym_hashes_end -= symtab_hdr->sh_info; rel_end = relocs + sec->reloc_count; for (rel = relocs; rel < rel_end; rel++) { struct elf_link_hash_entry *h; unsigned long r_symndx; r_symndx = ELF32_R_SYM (rel->r_info); if (r_symndx < symtab_hdr->sh_info) h = NULL; else h = sym_hashes[r_symndx - symtab_hdr->sh_info]; switch (ELF32_R_TYPE (rel->r_info)) { /* This relocation describes the C++ object vtable hierarchy. Reconstruct it for later use during GC. */ case R_CRIS_GNU_VTINHERIT: if (!_bfd_elf32_gc_record_vtinherit (abfd, sec, h, rel->r_offset)) return false; break; /* This relocation describes which C++ vtable entries are actually used. Record for later use during GC. */ case R_CRIS_GNU_VTENTRY: if (!_bfd_elf32_gc_record_vtentry (abfd, sec, h, rel->r_addend)) return false; break; } } return true; } /* Reject a file depending on underscores on symbols. */ 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); } /* Mark presence or absence of leading underscore. */ 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; } /* Display the flags field. */ 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; } /* Don't mix files with and without a leading underscore. */ 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) == false) 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)) { /* This happens when ld starts out with a 'blank' output file. */ elf_flags_init (obfd) = true; /* Set flags according to current bfd_target. */ cris_elf_final_write_processing (obfd, false); } old_flags = elf_elfheader (obfd)->e_flags; new_flags = elf_elfheader (ibfd)->e_flags; /* Is this good or bad? We'll follow with other excluding 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_get_filename (ibfd)); bfd_set_error (bfd_error_bad_value); return false; } return true; } #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_can_gc_sections 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 /* Later, we my want to optimize RELA entries into REL entries for dynamic linking and libraries (if it's a win of any significance). Until then, take the easy route. */ #define elf_backend_may_use_rel_p 0 #define elf_backend_may_use_rela_p 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"