]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
* elf32-m68hc1x.c: New file (from elf32-m68hc11.c and elf32-m68hc12.c)
authorStephane Carrez <stcarrez@nerim.fr>
Mon, 21 Apr 2003 13:22:14 +0000 (13:22 +0000)
committerStephane Carrez <stcarrez@nerim.fr>
Mon, 21 Apr 2003 13:22:14 +0000 (13:22 +0000)
(m68hc11_elf_hash_table_create): New function.
(elf32_m68hc11_link_hash_table_free): New function.
(stub_hash_newfunc): New function.
(m68hc11_add_stub): New function.
(elf32_m68hc11_add_symbol_hook): New function.
(elf32_m68hc11_setup_section_lists): New function.
(elf32_m68hc11_next_input_section): New function.
(elf32_m68hc11_size_stubs): New function.
(elf32_m68hc11_build_stubs): New function.
(m68hc11_get_relocation_value): New function.
(elf32_m68hc11_relocate_section): Call the above to redirect
some relocations to the trampoline code.
(m68hc11_elf_export_one_stub): New function.
(m68hc11_elf_set_symbol): New function.
(elf32_m68hc11_build_stubs): Call it via bfd_hash_traverse.
(m68hc11_elf_get_bank_parameters): Get parameters only when the info
is not yet initialized.

* elf32-m68hc1x.h: New file (from elf32-m68hc11.c and elf32-m68hc12.c)
(elf32_m68hc11_stub_hash_entry): New struct.
(m68hc11_page_info): Add trampoline handler address.
(m68hc11_elf_link_hash_table): Add stubs generation members.
(elf32_m68hc11_add_symbol_hook): Declare.
(elf32_m68hc11_setup_section_lists): Declare.
(elf32_m68hc11_size_stubs): Declare.
(elf32_m68hc11_build_stubs): Declare.

* elf32-m68hc11.c (m68hc11_elf_ignore_reloc): Move to elf32-m68hc1x.c.
(elf32_m68hc11_gc_mark_hook, elf32_m68hc11_gc_sweep_hook): Likewise.
(elf32_m68hc11_check_relocs, elf32_m68hc11_relocate_section): Ditto.
(_bfd_m68hc11_elf_set_private_flags): Ditto.
(_bfd_m68hc11_elf_merge_private_bfd_data): Ditto.
(_bfd_m68hc11_elf_print_private_bfd_data): Ditto.
(bfd_elf32_bfd_link_hash_table_create): Define.
(elf_backend_add_symbol_hook): Define.
(m68hc11_elf_bfd_link_hash_table_create): New function.
(m68hc11_elf_build_one_stub): New function.
(m68hc11_elf_size_one_stub): New function.
(m68hc11_elf_bfd_link_hash_table_create): Install the above.
(bfd_elf32_bfd_link_hash_table_create): Define.

* elf32-m68hc12.c (m68hc11_elf_ignore_reloc): Remove.
(m68hc12_addr_is_banked): Remove, use m68hc11_addr_is_banked.
(m68hc12_phys_addr): Ditto.
(m68hc12_phys_page): Ditto.
(m68hc12_elf_special_reloc): Move to elf32-m68hc1x.c.
(elf32_m68hc11_gc_mark_hook): Likewise.
(elf32_m68hc11_gc_sweep_hook): Likewise.
(elf32_m68hc11_check_relocs): Likewise.
(elf32_m68hc11_relocate_section): Likewise.
(_bfd_m68hc12_elf_set_private_flags): Likewise.
(_bfd_m68hc12_elf_merge_private_bfd_data): Likewise.
(_bfd_m68hc12_elf_print_private_bfd_data): Likewise.
(m68hc12_elf_build_one_stub): New function.
(m68hc12_elf_size_one_stub): New function.
(m68hc12_elf_bfd_link_hash_table_create): New function, use the above.
(elf_backend_add_symbol_hook): Define.
(elf_m68hc11_howto_table): Use TRUE for pcrel relocs; fix masks.

bfd/ChangeLog
bfd/elf32-m68hc11.c
bfd/elf32-m68hc12.c
bfd/elf32-m68hc1x.c [new file with mode: 0644]
bfd/elf32-m68hc1x.h [new file with mode: 0644]

index 000426784307485fbdaa94e5bdc541c5a807b2ab..ba50cbcf65153b91a8a8a49a842444149d5c99fb 100644 (file)
@@ -1,3 +1,65 @@
+2003-04-21  Stephane Carrez  <stcarrez@nerim.fr>
+
+       * elf32-m68hc1x.c: New file (from elf32-m68hc11.c and elf32-m68hc12.c)
+       (m68hc11_elf_hash_table_create): New function.
+       (elf32_m68hc11_link_hash_table_free): New function.
+       (stub_hash_newfunc): New function.
+       (m68hc11_add_stub): New function.
+       (elf32_m68hc11_add_symbol_hook): New function.
+       (elf32_m68hc11_setup_section_lists): New function.
+       (elf32_m68hc11_next_input_section): New function.
+       (elf32_m68hc11_size_stubs): New function.
+       (elf32_m68hc11_build_stubs): New function.
+       (m68hc11_get_relocation_value): New function.
+       (elf32_m68hc11_relocate_section): Call the above to redirect
+       some relocations to the trampoline code.
+       (m68hc11_elf_export_one_stub): New function.
+       (m68hc11_elf_set_symbol): New function.
+       (elf32_m68hc11_build_stubs): Call it via bfd_hash_traverse.
+       (m68hc11_elf_get_bank_parameters): Get parameters only when the info
+       is not yet initialized.
+
+       * elf32-m68hc1x.h: New file (from elf32-m68hc11.c and elf32-m68hc12.c)
+       (elf32_m68hc11_stub_hash_entry): New struct.
+       (m68hc11_page_info): Add trampoline handler address.
+       (m68hc11_elf_link_hash_table): Add stubs generation members.
+       (elf32_m68hc11_add_symbol_hook): Declare.
+       (elf32_m68hc11_setup_section_lists): Declare.
+       (elf32_m68hc11_size_stubs): Declare.
+       (elf32_m68hc11_build_stubs): Declare.
+       
+       * elf32-m68hc11.c (m68hc11_elf_ignore_reloc): Move to elf32-m68hc1x.c.
+       (elf32_m68hc11_gc_mark_hook, elf32_m68hc11_gc_sweep_hook): Likewise.
+       (elf32_m68hc11_check_relocs, elf32_m68hc11_relocate_section): Ditto.
+       (_bfd_m68hc11_elf_set_private_flags): Ditto.
+       (_bfd_m68hc11_elf_merge_private_bfd_data): Ditto.
+       (_bfd_m68hc11_elf_print_private_bfd_data): Ditto.
+       (bfd_elf32_bfd_link_hash_table_create): Define.
+       (elf_backend_add_symbol_hook): Define.
+       (m68hc11_elf_bfd_link_hash_table_create): New function.
+       (m68hc11_elf_build_one_stub): New function.
+       (m68hc11_elf_size_one_stub): New function.
+       (m68hc11_elf_bfd_link_hash_table_create): Install the above.
+       (bfd_elf32_bfd_link_hash_table_create): Define.
+
+       * elf32-m68hc12.c (m68hc11_elf_ignore_reloc): Remove.
+       (m68hc12_addr_is_banked): Remove, use m68hc11_addr_is_banked.
+       (m68hc12_phys_addr): Ditto.
+       (m68hc12_phys_page): Ditto.
+       (m68hc12_elf_special_reloc): Move to elf32-m68hc1x.c.
+       (elf32_m68hc11_gc_mark_hook): Likewise.
+       (elf32_m68hc11_gc_sweep_hook): Likewise.
+       (elf32_m68hc11_check_relocs): Likewise.
+       (elf32_m68hc11_relocate_section): Likewise.
+       (_bfd_m68hc12_elf_set_private_flags): Likewise.
+       (_bfd_m68hc12_elf_merge_private_bfd_data): Likewise.
+       (_bfd_m68hc12_elf_print_private_bfd_data): Likewise.
+       (m68hc12_elf_build_one_stub): New function.
+       (m68hc12_elf_size_one_stub): New function.
+       (m68hc12_elf_bfd_link_hash_table_create): New function, use the above.
+       (elf_backend_add_symbol_hook): Define.
+       (elf_m68hc11_howto_table): Use TRUE for pcrel relocs; fix masks.
+
 2003-04-18  Nick Clifton  <nickc@redhat.com>
 
        * format.c (bfd_check_format_matches): Only check associated
index 26fa3933a7401725142a0efad774831b3d2e3ecc..ebd9d42b95e8480fa9a75e49ccfc3ca8132d8b69 100644 (file)
@@ -24,29 +24,25 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "bfdlink.h"
 #include "libbfd.h"
 #include "elf-bfd.h"
+#include "elf32-m68hc1x.h"
 #include "elf/m68hc11.h"
+#include "opcode/m68hc11.h"
 
+/* Relocation functions.  */
 static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup
   PARAMS ((bfd *, bfd_reloc_code_real_type));
 static void m68hc11_info_to_howto_rel
   PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
 
-static bfd_reloc_status_type m68hc11_elf_ignore_reloc
-  PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
-
-/* GC mark and sweep.  */
-static asection *elf32_m68hc11_gc_mark_hook
-  PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *,
-          struct elf_link_hash_entry *, Elf_Internal_Sym *));
-static bfd_boolean elf32_m68hc11_gc_sweep_hook
-  PARAMS ((bfd *, struct bfd_link_info *, asection *,
-          const Elf_Internal_Rela *));
-static bfd_boolean elf32_m68hc11_check_relocs
-  PARAMS ((bfd *, struct bfd_link_info *, asection *,
-          const Elf_Internal_Rela *));
-static bfd_boolean elf32_m68hc11_relocate_section
-  PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
-          Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
+/* Trampoline generation.  */
+static bfd_boolean m68hc11_elf_size_one_stub
+  PARAMS((struct bfd_hash_entry *gen_entry, PTR in_arg));
+static bfd_boolean m68hc11_elf_build_one_stub
+  PARAMS((struct bfd_hash_entry *gen_entry, PTR in_arg));
+static struct bfd_link_hash_table* m68hc11_elf_bfd_link_hash_table_create
+  PARAMS ((bfd* abfd));
+
+/* Linker relaxation.  */
 static bfd_boolean m68hc11_elf_relax_section
   PARAMS ((bfd *, asection *, struct bfd_link_info *, bfd_boolean *));
 static void m68hc11_elf_relax_delete_bytes
@@ -56,18 +52,14 @@ static void m68hc11_relax_group
           unsigned long, unsigned long));
 static int compare_reloc PARAMS ((const void *, const void *));
 
-
-bfd_boolean _bfd_m68hc11_elf_merge_private_bfd_data PARAMS ((bfd *, bfd *));
-bfd_boolean _bfd_m68hc11_elf_set_private_flags PARAMS ((bfd *, flagword));
-bfd_boolean _bfd_m68hc11_elf_print_private_bfd_data PARAMS ((bfd *, PTR));
-
 /* Use REL instead of RELA to save space */
 #define USE_REL        1
 
-/* The Motorola 68HC11 microcontroler only addresses 64Kb.
+/* The Motorola 68HC11 microcontroller only addresses 64Kb but we also
+   support a memory bank switching mechanism similar to 68HC12.
    We must handle 8 and 16-bit relocations.  The 32-bit relocation
-   is defined but not used except by gas when -gstabs is used (which
-   is wrong).
+   are used for debugging sections (DWARF2) to represent a virtual
+   address.
    The 3-bit and 16-bit PC rel relocation is only used by 68HC12.  */
 static reloc_howto_type elf_m68hc11_howto_table[] = {
   /* This reloc does nothing.  */
@@ -367,25 +359,6 @@ bfd_elf32_bfd_reloc_type_lookup (abfd, code)
   return NULL;
 }
 
-/* This function is used for relocs which are only used for relaxing,
-   which the linker should otherwise ignore.  */
-
-static bfd_reloc_status_type
-m68hc11_elf_ignore_reloc (abfd, reloc_entry, symbol, data, input_section,
-                          output_bfd, error_message)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     arelent *reloc_entry;
-     asymbol *symbol ATTRIBUTE_UNUSED;
-     PTR data ATTRIBUTE_UNUSED;
-     asection *input_section;
-     bfd *output_bfd;
-     char **error_message ATTRIBUTE_UNUSED;
-{
-  if (output_bfd != NULL)
-    reloc_entry->address += input_section->output_offset;
-  return bfd_reloc_ok;
-}
-
 /* Set the howto pointer for an M68HC11 ELF reloc.  */
 
 static void
@@ -401,50 +374,106 @@ m68hc11_info_to_howto_rel (abfd, cache_ptr, dst)
   cache_ptr->howto = &elf_m68hc11_howto_table[r_type];
 }
 
-static asection *
-elf32_m68hc11_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))
-       {
-       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;
+\f
+/* Far trampoline generation.  */
 
-           default:
-             break;
-           }
-       }
-    }
-  else
-    return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
+/* Build a 68HC11 trampoline stub.  */
+static bfd_boolean
+m68hc11_elf_build_one_stub (gen_entry, in_arg)
+     struct bfd_hash_entry *gen_entry;
+     PTR in_arg;
+{
+  struct elf32_m68hc11_stub_hash_entry *stub_entry;
+  struct bfd_link_info *info;
+  struct m68hc11_elf_link_hash_table *htab;
+  asection *stub_sec;
+  bfd *stub_bfd;
+  bfd_byte *loc;
+  bfd_vma sym_value, phys_page, phys_addr;
+
+  /* Massage our args to the form they really have.  */
+  stub_entry = (struct elf32_m68hc11_stub_hash_entry *) gen_entry;
+  info = (struct bfd_link_info *) in_arg;
+
+  htab = m68hc11_elf_hash_table (info);
+
+  stub_sec = stub_entry->stub_sec;
+
+  /* Make a note of the offset within the stubs for this entry.  */
+  stub_entry->stub_offset = stub_sec->_raw_size;
+  stub_sec->_raw_size += 10;
+  loc = stub_sec->contents + stub_entry->stub_offset;
+
+  stub_bfd = stub_sec->owner;
+
+  /* Create the trampoline call stub:
+
+     pshb
+     ldab #%page(symbol)
+     ldy #%addr(symbol)
+     jmp __trampoline
+
+  */
+  sym_value = (stub_entry->target_value
+               + stub_entry->target_section->output_offset
+               + stub_entry->target_section->output_section->vma);
+  phys_addr = m68hc11_phys_addr (&htab->pinfo, sym_value);
+  phys_page = m68hc11_phys_page (&htab->pinfo, sym_value);
+
+  /* pshb; ldab #%page(sym) */
+  bfd_put_8 (stub_bfd, 0x37, loc);
+  bfd_put_8 (stub_bfd, 0xC6, loc + 1);
+  bfd_put_8 (stub_bfd, phys_page, loc + 2);
+  loc += 3;
+
+  /* ldy #%addr(sym)  */
+  bfd_put_8 (stub_bfd, 0x18, loc);
+  bfd_put_8 (stub_bfd, 0xCE, loc + 1);
+  bfd_put_16 (stub_bfd, phys_addr, loc + 2);
+  loc += 4;
+
+  /* jmp __trampoline  */
+  bfd_put_8 (stub_bfd, 0x7E, loc);
+  bfd_put_16 (stub_bfd, htab->pinfo.trampoline_addr, loc + 1);
 
-  return NULL;
+  return TRUE;
 }
 
+/* As above, but don't actually build the stub.  Just bump offset so
+   we know stub section sizes.  */
+
 static bfd_boolean
-elf32_m68hc11_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;
+m68hc11_elf_size_one_stub (gen_entry, in_arg)
+     struct bfd_hash_entry *gen_entry;
+     PTR in_arg ATTRIBUTE_UNUSED;
 {
-  /* We don't use got and plt entries for 68hc11/68hc12.  */
+  struct elf32_m68hc11_stub_hash_entry *stub_entry;
+
+  /* Massage our args to the form they really have.  */
+  stub_entry = (struct elf32_m68hc11_stub_hash_entry *) gen_entry;
+
+  stub_entry->stub_sec->_raw_size += 10;
   return TRUE;
 }
 
+/* Create a 68HC11 ELF linker hash table.  */
+
+static struct bfd_link_hash_table *
+m68hc11_elf_bfd_link_hash_table_create (abfd)
+     bfd *abfd;
+{
+  struct m68hc11_elf_link_hash_table *ret;
+
+  ret = m68hc11_elf_hash_table_create (abfd);
+  if (ret == (struct m68hc11_elf_link_hash_table *) NULL)
+    return NULL;
+
+  ret->size_one_stub = m68hc11_elf_size_one_stub;
+  ret->build_one_stub = m68hc11_elf_build_one_stub;
+
+  return &ret->root.root;
+}
+
 \f
 /* 68HC11 Linker Relaxation.  */
 
@@ -1251,365 +1280,7 @@ m68hc11_elf_relax_delete_bytes (abfd, sec, addr, count)
     }
 }
 
-/* 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 bfd_boolean
-elf32_m68hc11_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;
-  struct elf_link_hash_entry ** 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_M68HC11_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_M68HC11_GNU_VTENTRY:
-          if (!_bfd_elf32_gc_record_vtentry (abfd, sec, h, rel->r_addend))
-            return FALSE;
-          break;
-        }
-    }
-
-  return TRUE;
-}
-
-/* Relocate a 68hc11/68hc12 ELF section.  */
-static bfd_boolean
-elf32_m68hc11_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, *relend;
-  const char *name;
-
-  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
-  sym_hashes = elf_sym_hashes (input_bfd);
-
-  rel = relocs;
-  relend = relocs + input_section->reloc_count;
-  for (; rel < relend; rel++)
-    {
-      int r_type;
-      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;
-
-      r_symndx = ELF32_R_SYM (rel->r_info);
-      r_type = ELF32_R_TYPE (rel->r_info);
-
-      if (r_type == R_M68HC11_GNU_VTENTRY
-          || r_type == R_M68HC11_GNU_VTINHERIT )
-        continue;
-
-      howto = elf_m68hc11_howto_table + r_type;
-
-      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.  */
-      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);
-       }
-      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;
-             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->callbacks->undefined_symbol)
-                   (info, h->root.root.string, input_bfd,
-                    input_section, rel->r_offset, TRUE)))
-               return FALSE;
-             relocation = 0;
-           }
-       }
-
-      if (h != NULL)
-       name = h->root.root.string;
-      else
-       {
-         name = (bfd_elf_string_from_elf_section
-                 (input_bfd, symtab_hdr->sh_link, sym->st_name));
-         if (name == NULL || *name == '\0')
-           name = bfd_section_name (input_bfd, sec);
-       }
-
-      r = _bfd_final_link_relocate (howto, input_bfd, input_section,
-                                    contents, rel->r_offset,
-                                    relocation, rel->r_addend);
-
-      if (r != bfd_reloc_ok)
-       {
-         const char * msg = (const char *) 0;
-
-         switch (r)
-           {
-           case bfd_reloc_overflow:
-             if (!((*info->callbacks->reloc_overflow)
-                   (info, name, howto->name, (bfd_vma) 0,
-                    input_bfd, input_section, rel->r_offset)))
-               return FALSE;
-             break;
-
-           case bfd_reloc_undefined:
-             if (!((*info->callbacks->undefined_symbol)
-                   (info, name, input_bfd, input_section,
-                    rel->r_offset, TRUE)))
-               return FALSE;
-             break;
-
-           case bfd_reloc_outofrange:
-             msg = _ ("internal error: out of range error");
-             goto common_error;
-
-           case bfd_reloc_notsupported:
-             msg = _ ("internal error: unsupported relocation error");
-             goto common_error;
-
-           case bfd_reloc_dangerous:
-             msg = _ ("internal error: dangerous error");
-             goto common_error;
-
-           default:
-             msg = _ ("internal error: unknown error");
-             /* fall through */
-
-           common_error:
-             if (!((*info->callbacks->warning)
-                   (info, msg, name, input_bfd, input_section,
-                    rel->r_offset)))
-               return FALSE;
-             break;
-           }
-       }
-    }
-
-  return TRUE;
-}
-
-
 \f
-/* Set and control ELF flags in ELF header.  */
-
-bfd_boolean
-_bfd_m68hc11_elf_set_private_flags (abfd, flags)
-     bfd *abfd;
-     flagword flags;
-{
-  BFD_ASSERT (!elf_flags_init (abfd)
-             || elf_elfheader (abfd)->e_flags == flags);
-
-  elf_elfheader (abfd)->e_flags = flags;
-  elf_flags_init (abfd) = TRUE;
-  return TRUE;
-}
-
-/* Merge backend specific data from an object file to the output
-   object file when linking.  */
-
-bfd_boolean
-_bfd_m68hc11_elf_merge_private_bfd_data (ibfd, obfd)
-     bfd *ibfd;
-     bfd *obfd;
-{
-  flagword old_flags;
-  flagword new_flags;
-  bfd_boolean ok = TRUE;
-
-  /* Check if we have the same endianess */
-  if (!_bfd_generic_verify_endian_match (ibfd, obfd))
-    return FALSE;
-
-  if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
-      || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
-    return TRUE;
-
-  new_flags = elf_elfheader (ibfd)->e_flags;
-  elf_elfheader (obfd)->e_flags |= new_flags & EF_M68HC11_ABI;
-  old_flags = elf_elfheader (obfd)->e_flags;
-
-  if (! elf_flags_init (obfd))
-    {
-      elf_flags_init (obfd) = TRUE;
-      elf_elfheader (obfd)->e_flags = new_flags;
-      elf_elfheader (obfd)->e_ident[EI_CLASS]
-       = elf_elfheader (ibfd)->e_ident[EI_CLASS];
-
-      if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
-         && bfd_get_arch_info (obfd)->the_default)
-       {
-         if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
-                                  bfd_get_mach (ibfd)))
-           return FALSE;
-       }
-
-      return TRUE;
-    }
-
-  /* Check ABI compatibility.  */
-  if ((new_flags & E_M68HC11_I32) != (old_flags & E_M68HC11_I32))
-    {
-      (*_bfd_error_handler)
-       (_("%s: linking files compiled for 16-bit integers (-mshort) "
-           "and others for 32-bit integers"),
-        bfd_archive_filename (ibfd));
-      ok = FALSE;
-    }
-  if ((new_flags & E_M68HC11_F64) != (old_flags & E_M68HC11_F64))
-    {
-      (*_bfd_error_handler)
-       (_("%s: linking files compiled for 32-bit double (-fshort-double) "
-           "and others for 64-bit double"),
-        bfd_archive_filename (ibfd));
-      ok = FALSE;
-    }
-  new_flags &= ~EF_M68HC11_ABI;
-  old_flags &= ~EF_M68HC11_ABI;
-
-  /* Warn about any other mismatches */
-  if (new_flags != old_flags)
-    {
-      (*_bfd_error_handler)
-       (_("%s: uses different e_flags (0x%lx) fields than previous modules (0x%lx)"),
-        bfd_archive_filename (ibfd), (unsigned long) new_flags,
-        (unsigned long) old_flags);
-      ok = FALSE;
-    }
-
-  if (! ok)
-    {
-      bfd_set_error (bfd_error_bad_value);
-      return FALSE;
-    }
-
-  return TRUE;
-}
-
-bfd_boolean
-_bfd_m68hc11_elf_print_private_bfd_data (abfd, ptr)
-     bfd *abfd;
-     PTR ptr;
-{
-  FILE *file = (FILE *) ptr;
-
-  BFD_ASSERT (abfd != NULL && ptr != NULL);
-
-  /* Print normal ELF private data.  */
-  _bfd_elf_print_private_bfd_data (abfd, ptr);
-
-  /* xgettext:c-format */
-  fprintf (file, _("private flags = %lx:"), elf_elfheader (abfd)->e_flags);
-
-  if (elf_elfheader (abfd)->e_flags & E_M68HC11_I32)
-    fprintf (file, _("[abi=32-bit int,"));
-  else
-    fprintf (file, _("[abi=16-bit int,"));
-
-  if (elf_elfheader (abfd)->e_flags & E_M68HC11_F64)
-    fprintf (file, _(" 64-bit double]"));
-  else
-    fprintf (file, _(" 32-bit double]"));
-
-  if (elf_elfheader (abfd)->e_flags & E_M68HC12_BANKS)
-    fprintf (file, _(" [memory=bank-model]"));
-  else
-    fprintf (file, _(" [memory=flat]"));
-
-  fputc ('\n', file);
-
-  return TRUE;
-}
-
-/* Below is the only difference between elf32-m68hc12.c and elf32-m68hc11.c.
-   The Motorola spec says to use a different Elf machine code.  */
 #define ELF_ARCH               bfd_arch_m68hc11
 #define ELF_MACHINE_CODE       EM_68HC11
 #define ELF_MAXPAGESIZE                0x1000
@@ -1624,9 +1295,15 @@ _bfd_m68hc11_elf_print_private_bfd_data (abfd, ptr)
 #define elf_backend_gc_sweep_hook    elf32_m68hc11_gc_sweep_hook
 #define elf_backend_check_relocs     elf32_m68hc11_check_relocs
 #define elf_backend_relocate_section elf32_m68hc11_relocate_section
+#define elf_backend_add_symbol_hook  elf32_m68hc11_add_symbol_hook
 #define elf_backend_object_p   0
 #define elf_backend_final_write_processing     0
 #define elf_backend_can_gc_sections            1
+
+#define bfd_elf32_bfd_link_hash_table_create \
+                                m68hc11_elf_bfd_link_hash_table_create
+#define bfd_elf32_bfd_link_hash_table_free \
+                               m68hc11_elf_bfd_link_hash_table_free
 #define bfd_elf32_bfd_merge_private_bfd_data \
                                        _bfd_m68hc11_elf_merge_private_bfd_data
 #define bfd_elf32_bfd_set_private_flags        _bfd_m68hc11_elf_set_private_flags
index 219fd6b0da331a371d0f452fa0c5f13f746defee..54fdca2827753e02dadc7dbd2cec4aab6668d1c9 100644 (file)
@@ -1,5 +1,5 @@
 /* Motorola 68HC12-specific support for 32-bit ELF
-   Copyright 1999, 2000, 2002 Free Software Foundation, Inc.
+   Copyright 1999, 2000, 2002, 2003 Free Software Foundation, Inc.
    Contributed by Stephane Carrez (stcarrez@nerim.fr)
    (Heavily copied from the D10V port by Martin Hunt (hunt@cygnus.com))
 
@@ -21,49 +21,33 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include "bfd.h"
 #include "sysdep.h"
+#include "bfdlink.h"
 #include "libbfd.h"
 #include "elf-bfd.h"
+#include "elf32-m68hc1x.h"
 #include "elf/m68hc11.h"
 #include "opcode/m68hc11.h"
 
+/* Relocation functions.  */
 static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup
   PARAMS ((bfd *, bfd_reloc_code_real_type));
 static void m68hc11_info_to_howto_rel
   PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
 
-static bfd_reloc_status_type m68hc11_elf_ignore_reloc
-  PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
-static bfd_reloc_status_type m68hc12_elf_special_reloc
-  PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
-static int m68hc12_addr_is_banked PARAMS ((bfd_vma));
-static bfd_vma m68hc12_phys_addr PARAMS ((bfd_vma));
-static bfd_vma m68hc12_phys_page PARAMS ((bfd_vma));
-
-/* GC mark and sweep.  */
-static asection *elf32_m68hc11_gc_mark_hook
-  PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *,
-          struct elf_link_hash_entry *, Elf_Internal_Sym *));
-static bfd_boolean elf32_m68hc11_gc_sweep_hook
-  PARAMS ((bfd *, struct bfd_link_info *, asection *,
-          const Elf_Internal_Rela *));
+/* Trampoline generation.  */
+static bfd_boolean m68hc12_elf_size_one_stub
+  PARAMS((struct bfd_hash_entry *gen_entry, PTR in_arg));
+static bfd_boolean m68hc12_elf_build_one_stub
+  PARAMS((struct bfd_hash_entry *gen_entry, PTR in_arg));
+static struct bfd_link_hash_table* m68hc12_elf_bfd_link_hash_table_create
+  PARAMS((bfd*));
 
 static bfd_boolean m68hc12_elf_set_mach_from_flags PARAMS ((bfd *));
 
-bfd_boolean _bfd_m68hc12_elf_merge_private_bfd_data PARAMS ((bfd *, bfd *));
-bfd_boolean _bfd_m68hc12_elf_set_private_flags PARAMS ((bfd *, flagword));
-bfd_boolean _bfd_m68hc12_elf_print_private_bfd_data PARAMS ((bfd *, PTR));
-
-
-
 /* Use REL instead of RELA to save space */
 #define USE_REL        1
 
-/* The Motorola 68HC11 microcontroler only addresses 64Kb.
-   We must handle 8 and 16-bit relocations.  The 32-bit relocation
-   is defined but not used except by gas when -gstabs is used (which
-   is wrong).
-
-   The 68HC12 microcontroler has a memory bank switching system
+/* The 68HC12 microcontroler has a memory bank switching system
    with a 16Kb window in the 64Kb address space.  The extended memory
    is mapped in the 16Kb window (at 0x8000).  The page register controls
    which 16Kb bank is mapped.  The call/rtc instructions take care of
@@ -199,7 +183,7 @@ static reloc_howto_type elf_m68hc11_howto_table[] = {
         FALSE,                 /* partial_inplace */
         0x00ff,                /* src_mask */
         0x00ff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        TRUE),                 /* pcrel_offset */
 
   /* A 16 bit absolute relocation */
   HOWTO (R_M68HC11_16,         /* type */
@@ -209,7 +193,7 @@ static reloc_howto_type elf_m68hc11_howto_table[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont /*bitfield */ , /* complain_on_overflow */
-        m68hc12_elf_special_reloc,     /* special_function */
+        bfd_elf_generic_reloc, /* special_function */
         "R_M68HC12_16",        /* name */
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
@@ -260,7 +244,7 @@ static reloc_howto_type elf_m68hc11_howto_table[] = {
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        TRUE),                 /* pcrel_offset */
 
   /* GNU extension to record C++ vtable hierarchy */
   HOWTO (R_M68HC11_GNU_VTINHERIT,      /* type */
@@ -295,16 +279,16 @@ static reloc_howto_type elf_m68hc11_howto_table[] = {
   /* A 24 bit relocation */
   HOWTO (R_M68HC11_24,         /* type */
         0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
         24,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        m68hc12_elf_special_reloc,     /* special_function */
+        m68hc11_elf_special_reloc,     /* special_function */
         "R_M68HC12_24",        /* name */
         FALSE,                 /* partial_inplace */
-        0xffff,                /* src_mask */
-        0xffff,                /* dst_mask */
+        0xffffff,              /* src_mask */
+        0xffffff,              /* dst_mask */
         FALSE),                /* pcrel_offset */
 
   /* A 16-bit low relocation */
@@ -315,7 +299,7 @@ static reloc_howto_type elf_m68hc11_howto_table[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        m68hc12_elf_special_reloc,/* special_function */
+        m68hc11_elf_special_reloc,/* special_function */
         "R_M68HC12_LO16",      /* name */
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
@@ -330,7 +314,7 @@ static reloc_howto_type elf_m68hc11_howto_table[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
-        m68hc12_elf_special_reloc,/* special_function */
+        m68hc11_elf_special_reloc,/* special_function */
         "R_M68HC12_PAGE",      /* name */
         FALSE,                 /* partial_inplace */
         0x00ff,                /* src_mask */
@@ -423,217 +407,112 @@ bfd_elf32_bfd_reloc_type_lookup (abfd, code)
   return NULL;
 }
 
-/* This function is used for relocs which are only used for relaxing,
-   which the linker should otherwise ignore.  */
+/* Set the howto pointer for an M68HC11 ELF reloc.  */
 
-static bfd_reloc_status_type
-m68hc11_elf_ignore_reloc (abfd, reloc_entry, symbol, data, input_section,
-                          output_bfd, error_message)
+static void
+m68hc11_info_to_howto_rel (abfd, cache_ptr, dst)
      bfd *abfd ATTRIBUTE_UNUSED;
-     arelent *reloc_entry;
-     asymbol *symbol ATTRIBUTE_UNUSED;
-     PTR data ATTRIBUTE_UNUSED;
-     asection *input_section;
-     bfd *output_bfd;
-     char **error_message ATTRIBUTE_UNUSED;
+     arelent *cache_ptr;
+     Elf_Internal_Rela *dst;
 {
-  if (output_bfd != NULL)
-    reloc_entry->address += input_section->output_offset;
-  return bfd_reloc_ok;
-}
+  unsigned int r_type;
 
-static int
-m68hc12_addr_is_banked (addr)
-     bfd_vma addr;
-{
-   return (addr >= M68HC12_BANK_VIRT) ? 1 : 0;
+  r_type = ELF32_R_TYPE (dst->r_info);
+  BFD_ASSERT (r_type < (unsigned int) R_M68HC11_max);
+  cache_ptr->howto = &elf_m68hc11_howto_table[r_type];
 }
 
-/* Return the physical address seen by the processor, taking
-   into account banked memory.  */
-static bfd_vma
-m68hc12_phys_addr (addr)
-     bfd_vma addr;
-{
-  if (addr < M68HC12_BANK_VIRT)
-    return addr;
-
-  /* Map the address to the memory bank.  */
-  addr -= M68HC12_BANK_VIRT;
-  addr &= M68HC12_BANK_MASK;
-  addr += M68HC12_BANK_BASE;
-  return addr;
-}
+\f
+/* Far trampoline generation.  */
 
-/* Return the page number corresponding to an address in banked memory.  */
-static bfd_vma
-m68hc12_phys_page (addr)
-     bfd_vma addr;
+/* Build a 68HC12 trampoline stub.  */
+static bfd_boolean
+m68hc12_elf_build_one_stub (gen_entry, in_arg)
+     struct bfd_hash_entry *gen_entry;
+     PTR in_arg;
 {
-  if (addr < M68HC12_BANK_VIRT)
-    return 0;
-
-  /* Map the address to the memory bank.  */
-  addr -= M68HC12_BANK_VIRT;
-  addr >>= M68HC12_BANK_SHIFT;
-  addr &= M68HC12_BANK_PAGE_MASK;
-  return addr;
-}
+  struct elf32_m68hc11_stub_hash_entry *stub_entry;
+  struct bfd_link_info *info;
+  struct m68hc11_elf_link_hash_table *htab;
+  asection *stub_sec;
+  bfd *stub_bfd;
+  bfd_byte *loc;
+  bfd_vma sym_value, phys_page, phys_addr;
 
-static bfd_reloc_status_type
-m68hc12_elf_special_reloc (abfd, reloc_entry, symbol, data, input_section,
-                           output_bfd, error_message)
-     bfd *abfd;
-     arelent *reloc_entry;
-     asymbol *symbol;
-     PTR data;
-     asection *input_section;
-     bfd *output_bfd;
-     char **error_message ATTRIBUTE_UNUSED;
-{
-  reloc_howto_type *howto;
-  bfd_vma relocation;
-  bfd_vma phys_addr;
-  bfd_vma phys_page;
-  bfd_vma insn_page;
-  bfd_vma insn_addr;
-
-  if (output_bfd != (bfd *) NULL
-      && (symbol->flags & BSF_SECTION_SYM) == 0
-      && (! reloc_entry->howto->partial_inplace
-         || reloc_entry->addend == 0))
-    {
-      reloc_entry->address += input_section->output_offset;
-      return bfd_reloc_ok;
-    }
+  /* Massage our args to the form they really have.  */
+  stub_entry = (struct elf32_m68hc11_stub_hash_entry *) gen_entry;
+  info = (struct bfd_link_info *) in_arg;
 
-  if (output_bfd != NULL)
-    return bfd_reloc_continue;
+  htab = m68hc11_elf_hash_table (info);
 
-  if (reloc_entry->address > input_section->_cooked_size)
-    return bfd_reloc_outofrange;
+  stub_sec = stub_entry->stub_sec;
 
-  /* Compute relocation.  */
-  relocation = (symbol->value
-               + symbol->section->output_section->vma
-               + symbol->section->output_offset);
-  relocation += reloc_entry->addend;
-  relocation += bfd_get_16 (abfd, (bfd_byte*) data + reloc_entry->address);
+  /* Make a note of the offset within the stubs for this entry.  */
+  stub_entry->stub_offset = stub_sec->_raw_size;
+  stub_sec->_raw_size += 7;
+  loc = stub_sec->contents + stub_entry->stub_offset;
 
-  /* Do the memory bank mapping.  */
-  phys_addr = m68hc12_phys_addr (relocation);
-  phys_page = m68hc12_phys_page (relocation);
+  stub_bfd = stub_sec->owner;
 
-  howto = reloc_entry->howto;
-  if (howto->complain_on_overflow != complain_overflow_dont
-      && (phys_addr & (((bfd_vma) -1) << 16)))
-     return bfd_reloc_overflow;
+  /* Create the trampoline call stub:
 
-  switch (howto->type)
-    {
-    case R_M68HC11_16:
-          /* Get virtual address of instruction having the relocation.  */
-       insn_addr = input_section->output_section->vma
-          + input_section->output_offset
-          + reloc_entry->address;
-
-      insn_page = m68hc12_phys_page (insn_addr);
-
-      if (m68hc12_addr_is_banked (relocation)
-          && m68hc12_addr_is_banked (insn_addr)
-          && phys_page != insn_page)
-         {
-            *error_message = _("address is not in the same bank");
-            return bfd_reloc_dangerous;
-         }
-      if (m68hc12_addr_is_banked (relocation)
-          && !m68hc12_addr_is_banked (insn_addr))
-         {
-            *error_message = _("reference to a banked address in "
-                               "the normal address space");
-            return bfd_reloc_dangerous;
-         }
-
-    case R_M68HC11_LO16:
-      bfd_put_16 (abfd, phys_addr, (bfd_byte*) data + reloc_entry->address);
-      break;
+     ldy #%addr(symbol)
+     call %page(symbol), __trampoline
 
-    case R_M68HC11_24:
-      bfd_put_16 (abfd, phys_addr, (bfd_byte*) data + reloc_entry->address);
-      bfd_put_8 (abfd, phys_page, (bfd_byte*) data + reloc_entry->address + 2);
-      break;
+  */
+  sym_value = (stub_entry->target_value
+               + stub_entry->target_section->output_offset
+               + stub_entry->target_section->output_section->vma);
+  phys_addr = m68hc11_phys_addr (&htab->pinfo, sym_value);
+  phys_page = m68hc11_phys_page (&htab->pinfo, sym_value);
 
-    case R_M68HC11_PAGE:
-      bfd_put_8 (abfd, phys_page, (bfd_byte*) data + reloc_entry->address);
-      break;
+  /* ldy #%page(sym) */
+  bfd_put_8 (stub_bfd, 0xCD, loc);
+  bfd_put_16 (stub_bfd, phys_addr, loc + 1);
+  loc += 3;
 
-    default:
-       abort ();
-       break;
-    }
+  /* call %page(sym), __trampoline  */
+  bfd_put_8 (stub_bfd, 0x4a, loc);
+  bfd_put_16 (stub_bfd, htab->pinfo.trampoline_addr, loc + 1);
+  bfd_put_8 (stub_bfd, phys_page, loc + 3);
 
-  return bfd_reloc_ok;
+  return TRUE;
 }
 
-/* Set the howto pointer for an M68HC11 ELF reloc.  */
+/* As above, but don't actually build the stub.  Just bump offset so
+   we know stub section sizes.  */
 
-static void
-m68hc11_info_to_howto_rel (abfd, cache_ptr, dst)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     arelent *cache_ptr;
-     Elf_Internal_Rela *dst;
+static bfd_boolean
+m68hc12_elf_size_one_stub (gen_entry, in_arg)
+     struct bfd_hash_entry *gen_entry;
+     PTR in_arg ATTRIBUTE_UNUSED;
 {
-  unsigned int r_type;
+  struct elf32_m68hc11_stub_hash_entry *stub_entry;
 
-  r_type = ELF32_R_TYPE (dst->r_info);
-  BFD_ASSERT (r_type < (unsigned int) R_M68HC11_max);
-  cache_ptr->howto = &elf_m68hc11_howto_table[r_type];
+  /* Massage our args to the form they really have.  */
+  stub_entry = (struct elf32_m68hc11_stub_hash_entry *) gen_entry;
+
+  stub_entry->stub_sec->_raw_size += 7;
+  return TRUE;
 }
 
-static asection *
-elf32_m68hc11_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;
+/* Create a 68HC12 ELF linker hash table.  */
+
+static struct bfd_link_hash_table *
+m68hc12_elf_bfd_link_hash_table_create (abfd)
+     bfd *abfd;
 {
-  if (h != NULL)
-    {
-      switch (ELF32_R_TYPE (rel->r_info))
-       {
-       default:
-         switch (h->root.type)
-           {
-           case bfd_link_hash_defined:
-           case bfd_link_hash_defweak:
-             return h->root.u.def.section;
-
-           case bfd_link_hash_common:
-             return h->root.u.c.p->section;
-
-           default:
-             break;
-           }
-       }
-    }
-  else
-    return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
+  struct m68hc11_elf_link_hash_table *ret;
 
-  return NULL;
-}
+  ret = m68hc11_elf_hash_table_create (abfd);
+  if (ret == (struct m68hc11_elf_link_hash_table *) NULL)
+    return NULL;
 
-static bfd_boolean
-elf32_m68hc11_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;
-{
-  /* We don't use got and plt entries for 68hc11/68hc12.  */
-  return TRUE;
-}
+  ret->size_one_stub = m68hc12_elf_size_one_stub;
+  ret->build_one_stub = m68hc12_elf_build_one_stub;
 
+  return &ret->root.root;
+}
 \f
 static bfd_boolean
 m68hc12_elf_set_mach_from_flags (abfd)
@@ -659,149 +538,6 @@ m68hc12_elf_set_mach_from_flags (abfd)
   return TRUE;
 }
 
-/* Set and control ELF flags in ELF header.  */
-
-bfd_boolean
-_bfd_m68hc12_elf_set_private_flags (abfd, flags)
-     bfd *abfd;
-     flagword flags;
-{
-  BFD_ASSERT (!elf_flags_init (abfd)
-             || elf_elfheader (abfd)->e_flags == flags);
-
-  elf_elfheader (abfd)->e_flags = flags;
-  elf_flags_init (abfd) = TRUE;
-  return m68hc12_elf_set_mach_from_flags (abfd);
-}
-
-/* Merge backend specific data from an object file to the output
-   object file when linking.  */
-
-bfd_boolean
-_bfd_m68hc12_elf_merge_private_bfd_data (ibfd, obfd)
-     bfd *ibfd;
-     bfd *obfd;
-{
-  flagword old_flags;
-  flagword new_flags;
-  bfd_boolean ok = TRUE;
-
-  /* Check if we have the same endianess */
-  if (!_bfd_generic_verify_endian_match (ibfd, obfd))
-    return FALSE;
-
-  if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
-      || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
-    return TRUE;
-
-  new_flags = elf_elfheader (ibfd)->e_flags;
-  old_flags = elf_elfheader (obfd)->e_flags;
-
-  if (! elf_flags_init (obfd))
-    {
-      elf_flags_init (obfd) = TRUE;
-      elf_elfheader (obfd)->e_flags = new_flags;
-      elf_elfheader (obfd)->e_ident[EI_CLASS]
-       = elf_elfheader (ibfd)->e_ident[EI_CLASS];
-
-      if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
-         && bfd_get_arch_info (obfd)->the_default)
-       {
-         if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
-                                  bfd_get_mach (ibfd)))
-           return FALSE;
-       }
-
-      return TRUE;
-    }
-
-  /* Check ABI compatibility.  */
-  if ((new_flags & E_M68HC11_I32) != (old_flags & E_M68HC11_I32))
-    {
-      (*_bfd_error_handler)
-       (_("%s: linking files compiled for 16-bit integers (-mshort) "
-           "and others for 32-bit integers"),
-        bfd_archive_filename (ibfd));
-      ok = FALSE;
-    }
-  if ((new_flags & E_M68HC11_F64) != (old_flags & E_M68HC11_F64))
-    {
-      (*_bfd_error_handler)
-       (_("%s: linking files compiled for 32-bit double (-fshort-double) "
-           "and others for 64-bit double"),
-        bfd_archive_filename (ibfd));
-      ok = FALSE;
-    }
-
-  /* Processor compatibility.  */
-  if (!EF_M68HC11_CAN_MERGE_MACH (new_flags, old_flags))
-    {
-      (*_bfd_error_handler)
-       (_("%s: linking files compiled for HCS12 with "
-           "others compiled for HC12"),
-        bfd_archive_filename (ibfd));
-      ok = FALSE;
-    }
-  new_flags = ((new_flags & ~EF_M68HC11_MACH_MASK)
-               | (EF_M68HC11_MERGE_MACH (new_flags, old_flags)));
-
-  elf_elfheader (obfd)->e_flags = new_flags;
-
-  /* Warn about any other mismatches */
-  new_flags &= ~(EF_M68HC11_ABI | EF_M68HC11_MACH_MASK);
-  old_flags &= ~(EF_M68HC11_ABI | EF_M68HC11_MACH_MASK);
-  if (new_flags != old_flags)
-    {
-      (*_bfd_error_handler)
-       (_("%s: uses different e_flags (0x%lx) fields than previous modules (0x%lx)"),
-        bfd_archive_filename (ibfd), (unsigned long) new_flags,
-        (unsigned long) old_flags);
-      ok = FALSE;
-    }
-
-  if (! ok)
-    {
-      bfd_set_error (bfd_error_bad_value);
-      return FALSE;
-    }
-
-  return TRUE;
-}
-
-bfd_boolean
-_bfd_m68hc12_elf_print_private_bfd_data (abfd, ptr)
-     bfd *abfd;
-     PTR ptr;
-{
-  FILE *file = (FILE *) ptr;
-
-  BFD_ASSERT (abfd != NULL && ptr != NULL);
-
-  /* Print normal ELF private data.  */
-  _bfd_elf_print_private_bfd_data (abfd, ptr);
-
-  /* xgettext:c-format */
-  fprintf (file, _("private flags = %lx:"), elf_elfheader (abfd)->e_flags);
-
-  if (elf_elfheader (abfd)->e_flags & E_M68HC11_I32)
-    fprintf (file, _("[abi=32-bit int,"));
-  else
-    fprintf (file, _("[abi=16-bit int,"));
-
-  if (elf_elfheader (abfd)->e_flags & E_M68HC11_F64)
-    fprintf (file, _(" 64-bit double,"));
-  else
-    fprintf (file, _(" 32-bit double,"));
-
-  if (elf_elfheader (abfd)->e_flags & EF_M68HCS12_MACH)
-    fprintf (file, _(" cpu=HCS12]"));
-  else
-    fprintf (file, _(" cpu=HC12]"));
-  fputc ('\n', file);
-
-  return TRUE;
-}
-
 #define ELF_ARCH               bfd_arch_m68hc12
 #define ELF_MACHINE_CODE       EM_68HC12
 #define ELF_MAXPAGESIZE                0x1000
@@ -813,15 +549,22 @@ _bfd_m68hc12_elf_print_private_bfd_data (abfd, ptr)
 #define elf_info_to_howto_rel  m68hc11_info_to_howto_rel
 #define elf_backend_gc_mark_hook     elf32_m68hc11_gc_mark_hook
 #define elf_backend_gc_sweep_hook    elf32_m68hc11_gc_sweep_hook
+#define elf_backend_check_relocs     elf32_m68hc11_check_relocs
+#define elf_backend_relocate_section elf32_m68hc11_relocate_section
 #define elf_backend_object_p           m68hc12_elf_set_mach_from_flags
 #define elf_backend_final_write_processing     0
-/* Disabled as this backend uses the generic linker.  */
-#define elf_backend_can_gc_sections            0
-
+#define elf_backend_can_gc_sections            1
+#define elf_backend_post_process_headers     elf32_m68hc11_post_process_headers
+#define elf_backend_add_symbol_hook  elf32_m68hc11_add_symbol_hook
+
+#define bfd_elf32_bfd_link_hash_table_create \
+                                m68hc12_elf_bfd_link_hash_table_create
+#define bfd_elf32_bfd_link_hash_table_free \
+                               m68hc11_elf_bfd_link_hash_table_free
 #define bfd_elf32_bfd_merge_private_bfd_data \
-                                       _bfd_m68hc12_elf_merge_private_bfd_data
-#define bfd_elf32_bfd_set_private_flags        _bfd_m68hc12_elf_set_private_flags
+                                       _bfd_m68hc11_elf_merge_private_bfd_data
+#define bfd_elf32_bfd_set_private_flags        _bfd_m68hc11_elf_set_private_flags
 #define bfd_elf32_bfd_print_private_bfd_data \
-                                       _bfd_m68hc12_elf_print_private_bfd_data
+                                       _bfd_m68hc11_elf_print_private_bfd_data
 
 #include "elf32-target.h"
diff --git a/bfd/elf32-m68hc1x.c b/bfd/elf32-m68hc1x.c
new file mode 100644 (file)
index 0000000..0696a36
--- /dev/null
@@ -0,0 +1,1508 @@
+/* Motorola 68HC11/HC12-specific support for 32-bit ELF
+   Copyright 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+   Contributed by Stephane Carrez (stcarrez@nerim.fr)
+
+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 "bfdlink.h"
+#include "libbfd.h"
+#include "elf-bfd.h"
+#include "elf32-m68hc1x.h"
+#include "elf/m68hc11.h"
+#include "opcode/m68hc11.h"
+
+
+#define m68hc12_stub_hash_lookup(table, string, create, copy) \
+  ((struct elf32_m68hc11_stub_hash_entry *) \
+   bfd_hash_lookup ((table), (string), (create), (copy)))
+
+static struct elf32_m68hc11_stub_hash_entry* m68hc12_add_stub
+  PARAMS((const char *stub_name,
+          asection *section,
+          struct m68hc11_elf_link_hash_table *htab));
+
+static struct bfd_hash_entry *stub_hash_newfunc
+  PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
+
+static void m68hc11_elf_set_symbol
+  PARAMS ((bfd* abfd, struct bfd_link_info *info,
+           const char* name, bfd_vma value, asection* sec));
+
+static bfd_boolean m68hc11_elf_export_one_stub
+  PARAMS((struct bfd_hash_entry *gen_entry, PTR in_arg));
+
+static bfd_boolean m68hc11_get_relocation_value
+  PARAMS ((bfd* abfd,
+           struct bfd_link_info* info,
+           asection **local_sections,
+           Elf_Internal_Sym* local_syms,
+           Elf_Internal_Rela* rel,
+           const char** name,
+           bfd_vma* relocation,
+           bfd_boolean* is_far));
+
+static void scan_sections_for_abi PARAMS ((bfd*, asection*, PTR));
+
+struct m68hc11_scan_param
+{
+   struct m68hc11_page_info* pinfo;
+   bfd_boolean use_memory_banks;
+};
+
+
+/* Create a 68HC11/68HC12 ELF linker hash table.  */
+
+struct m68hc11_elf_link_hash_table*
+m68hc11_elf_hash_table_create (abfd)
+     bfd *abfd;
+{
+  struct m68hc11_elf_link_hash_table *ret;
+  bfd_size_type amt = sizeof (struct m68hc11_elf_link_hash_table);
+
+  ret = (struct m68hc11_elf_link_hash_table *) bfd_zalloc (abfd, amt);
+  if (ret == (struct m68hc11_elf_link_hash_table *) NULL)
+    return NULL;
+
+  if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
+                                      _bfd_elf_link_hash_newfunc))
+    {
+      bfd_release (abfd, ret);
+      return NULL;
+    }
+
+  /* Init the stub hash table too.  */
+  amt = sizeof (struct bfd_hash_table);
+  ret->stub_hash_table = (struct bfd_hash_table*) bfd_malloc (amt);
+  if (ret->stub_hash_table == NULL)
+    {
+      bfd_release (abfd, ret);
+      return NULL;
+    }
+  if (!bfd_hash_table_init (ret->stub_hash_table, stub_hash_newfunc))
+    return NULL;
+
+  ret->stub_bfd = NULL;
+  ret->stub_section = 0;
+  ret->add_stub_section = NULL;
+  ret->sym_sec.abfd = NULL;
+
+  return ret;
+}
+
+/* Free the derived linker hash table.  */
+
+void
+m68hc11_elf_bfd_link_hash_table_free (hash)
+     struct bfd_link_hash_table *hash;
+{
+  struct m68hc11_elf_link_hash_table *ret
+    = (struct m68hc11_elf_link_hash_table *) hash;
+
+  bfd_hash_table_free (ret->stub_hash_table);
+  free (ret->stub_hash_table);
+  _bfd_generic_link_hash_table_free (hash);
+}
+
+/* Assorted hash table functions.  */
+
+/* Initialize an entry in the stub hash table.  */
+
+static struct bfd_hash_entry *
+stub_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
+{
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (entry == NULL)
+    {
+      entry = bfd_hash_allocate (table,
+                                sizeof (struct elf32_m68hc11_stub_hash_entry));
+      if (entry == NULL)
+       return entry;
+    }
+
+  /* Call the allocation method of the superclass.  */
+  entry = bfd_hash_newfunc (entry, table, string);
+  if (entry != NULL)
+    {
+      struct elf32_m68hc11_stub_hash_entry *eh;
+
+      /* Initialize the local fields.  */
+      eh = (struct elf32_m68hc11_stub_hash_entry *) entry;
+      eh->stub_sec = NULL;
+      eh->stub_offset = 0;
+      eh->target_value = 0;
+      eh->target_section = NULL;
+    }
+
+  return entry;
+}
+
+/* Add a new stub entry to the stub hash.  Not all fields of the new
+   stub entry are initialised.  */
+
+static struct elf32_m68hc11_stub_hash_entry *
+m68hc12_add_stub (stub_name, section, htab)
+     const char *stub_name;
+     asection *section;
+     struct m68hc11_elf_link_hash_table *htab;
+{
+  struct elf32_m68hc11_stub_hash_entry *stub_entry;
+
+  /* Enter this entry into the linker stub hash table.  */
+  stub_entry = m68hc12_stub_hash_lookup (htab->stub_hash_table, stub_name,
+                                         TRUE, FALSE);
+  if (stub_entry == NULL)
+    {
+      (*_bfd_error_handler) (_("%s: cannot create stub entry %s"),
+                            bfd_archive_filename (section->owner),
+                            stub_name);
+      return NULL;
+    }
+
+  if (htab->stub_section == 0)
+    {
+      htab->stub_section = (*htab->add_stub_section) (".tramp",
+                                                      htab->tramp_section);
+    }
+
+  stub_entry->stub_sec = htab->stub_section;
+  stub_entry->stub_offset = 0;
+  return stub_entry;
+}
+
+/* Hook called by the linker routine which adds symbols from an object
+   file.  We use it for identify far symbols and force a loading of
+   the trampoline handler.  */
+
+bfd_boolean
+elf32_m68hc11_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     const Elf_Internal_Sym *sym;
+     const char **namep ATTRIBUTE_UNUSED;
+     flagword *flagsp ATTRIBUTE_UNUSED;
+     asection **secp ATTRIBUTE_UNUSED;
+     bfd_vma *valp ATTRIBUTE_UNUSED;
+{
+  if (sym->st_other & STO_M68HC12_FAR)
+    {
+      struct elf_link_hash_entry *h;
+
+      h = (struct elf_link_hash_entry *)
+       bfd_link_hash_lookup (info->hash, "__far_trampoline",
+                              FALSE, FALSE, FALSE);
+      if (h == NULL)
+        {
+          struct bfd_link_hash_entry* entry = NULL;
+
+          _bfd_generic_link_add_one_symbol (info, abfd,
+                                            "__far_trampoline",
+                                            BSF_GLOBAL,
+                                            bfd_und_section_ptr,
+                                            (bfd_vma) 0, (const char*) NULL,
+                                            FALSE, FALSE, &entry);
+        }
+
+    }
+  return TRUE;
+}
+
+/* External entry points for sizing and building linker stubs.  */
+
+/* Set up various things so that we can make a list of input sections
+   for each output section included in the link.  Returns -1 on error,
+   0 when no stubs will be needed, and 1 on success.  */
+
+int
+elf32_m68hc11_setup_section_lists (output_bfd, info)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+{
+  bfd *input_bfd;
+  unsigned int bfd_count;
+  int top_id, top_index;
+  asection *section;
+  asection **input_list, **list;
+  bfd_size_type amt;
+  asection *text_section;
+  struct m68hc11_elf_link_hash_table *htab;
+
+  htab = m68hc11_elf_hash_table (info);
+
+  if (htab->root.root.creator->flavour != bfd_target_elf_flavour)
+    return 0;
+
+  /* Count the number of input BFDs and find the top input section id.
+     Also search for an existing ".tramp" section so that we know
+     where generated trampolines must go.  Default to ".text" if we
+     can't find it.  */
+  htab->tramp_section = 0;
+  text_section = 0;
+  for (input_bfd = info->input_bfds, bfd_count = 0, top_id = 0;
+       input_bfd != NULL;
+       input_bfd = input_bfd->link_next)
+    {
+      bfd_count += 1;
+      for (section = input_bfd->sections;
+          section != NULL;
+          section = section->next)
+       {
+          const char* name = bfd_get_section_name (input_bfd, section);
+
+          if (!strcmp (name, ".tramp"))
+            htab->tramp_section = section;
+
+          if (!strcmp (name, ".text"))
+            text_section = section;
+
+         if (top_id < section->id)
+           top_id = section->id;
+       }
+    }
+  htab->bfd_count = bfd_count;
+  if (htab->tramp_section == 0)
+    htab->tramp_section = text_section;
+
+  /* We can't use output_bfd->section_count here to find the top output
+     section index as some sections may have been removed, and
+     _bfd_strip_section_from_output doesn't renumber the indices.  */
+  for (section = output_bfd->sections, top_index = 0;
+       section != NULL;
+       section = section->next)
+    {
+      if (top_index < section->index)
+       top_index = section->index;
+    }
+
+  htab->top_index = top_index;
+  amt = sizeof (asection *) * (top_index + 1);
+  input_list = (asection **) bfd_malloc (amt);
+  htab->input_list = input_list;
+  if (input_list == NULL)
+    return -1;
+
+  /* For sections we aren't interested in, mark their entries with a
+     value we can check later.  */
+  list = input_list + top_index;
+  do
+    *list = bfd_abs_section_ptr;
+  while (list-- != input_list);
+
+  for (section = output_bfd->sections;
+       section != NULL;
+       section = section->next)
+    {
+      if ((section->flags & SEC_CODE) != 0)
+       input_list[section->index] = NULL;
+    }
+
+  return 1;
+}
+
+/* Determine and set the size of the stub section for a final link.
+
+   The basic idea here is to examine all the relocations looking for
+   PC-relative calls to a target that is unreachable with a "bl"
+   instruction.  */
+
+bfd_boolean
+elf32_m68hc11_size_stubs (output_bfd, stub_bfd, info, add_stub_section)
+     bfd *output_bfd;
+     bfd *stub_bfd;
+     struct bfd_link_info *info;
+     asection * (*add_stub_section) PARAMS ((const char *, asection *));
+{
+  bfd *input_bfd;
+  asection *section;
+  Elf_Internal_Sym *local_syms, **all_local_syms;
+  unsigned int bfd_indx, bfd_count;
+  bfd_size_type amt;
+  asection *stub_sec;
+
+  struct m68hc11_elf_link_hash_table *htab = m68hc11_elf_hash_table (info);
+
+  /* Stash our params away.  */
+  htab->stub_bfd = stub_bfd;
+  htab->add_stub_section = add_stub_section;
+
+  /* Count the number of input BFDs and find the top input section id.  */
+  for (input_bfd = info->input_bfds, bfd_count = 0;
+       input_bfd != NULL;
+       input_bfd = input_bfd->link_next)
+    {
+      bfd_count += 1;
+    }
+
+  /* We want to read in symbol extension records only once.  To do this
+     we need to read in the local symbols in parallel and save them for
+     later use; so hold pointers to the local symbols in an array.  */
+  amt = sizeof (Elf_Internal_Sym *) * bfd_count;
+  all_local_syms = (Elf_Internal_Sym **) bfd_zmalloc (amt);
+  if (all_local_syms == NULL)
+    return FALSE;
+
+  /* Walk over all the input BFDs, swapping in local symbols.  */
+  for (input_bfd = info->input_bfds, bfd_indx = 0;
+       input_bfd != NULL;
+       input_bfd = input_bfd->link_next, bfd_indx++)
+    {
+      Elf_Internal_Shdr *symtab_hdr;
+      Elf_Internal_Shdr *shndx_hdr;
+      Elf_Internal_Sym *isym;
+      Elf32_External_Sym *extsyms, *esym, *end_sy;
+      Elf_External_Sym_Shndx *shndx_buf, *shndx;
+      bfd_size_type sec_size;
+
+      /* We'll need the symbol table in a second.  */
+      symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+      if (symtab_hdr->sh_info == 0)
+       continue;
+
+      /* We need an array of the local symbols attached to the input bfd.
+        Unfortunately, we're going to have to read & swap them in.  */
+      sec_size = symtab_hdr->sh_info;
+      sec_size *= sizeof (Elf_Internal_Sym);
+      local_syms = (Elf_Internal_Sym *) bfd_malloc (sec_size);
+      if (local_syms == NULL)
+       goto error_ret_free_local;
+
+      all_local_syms[bfd_indx] = local_syms;
+      sec_size = symtab_hdr->sh_info;
+      sec_size *= sizeof (Elf32_External_Sym);
+
+      /* Get the cached copy.  */
+      if (symtab_hdr->contents != NULL)
+        extsyms = (Elf32_External_Sym *) symtab_hdr->contents;
+      else
+        {
+          /* Go get them off disk.  */
+          bfd_size_type amt = symtab_hdr->sh_size;
+          extsyms = (Elf32_External_Sym *) bfd_malloc (amt);
+          if (extsyms == NULL)
+            goto error_ret_free_local;
+
+          if (bfd_seek (input_bfd, symtab_hdr->sh_offset, SEEK_SET) != 0
+              || bfd_bread ((PTR) extsyms, amt, input_bfd) != amt)
+            {
+            error_ret_free_ext_syms:
+              free (extsyms);
+              goto error_ret_free_local;
+            }
+        }
+      shndx_buf = NULL;
+      shndx_hdr = &elf_tdata (input_bfd)->symtab_shndx_hdr;
+      if (shndx_hdr->sh_size != 0)
+        {
+          bfd_size_type amt;
+
+          amt = symtab_hdr->sh_info * sizeof (Elf_External_Sym_Shndx);
+          shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
+          if (shndx_buf == NULL)
+            goto error_ret_free_ext_syms;
+          if (bfd_seek (input_bfd, shndx_hdr->sh_offset, SEEK_SET) != 0
+              || bfd_bread ((PTR) shndx_buf, amt, input_bfd) != amt)
+            {
+              free (shndx_buf);
+              goto error_ret_free_ext_syms;
+            }
+          shndx_hdr->contents = (PTR) shndx_buf;
+        }
+
+      /* Swap the local symbols in.  */
+      for (esym = extsyms, end_sy = esym + symtab_hdr->sh_info,
+            isym = local_syms, shndx = shndx_buf;
+          esym < end_sy;
+          esym++, isym++, shndx = (shndx ? shndx + 1 : NULL))
+       bfd_elf32_swap_symbol_in (input_bfd, esym, shndx, isym);
+
+      /* Now we can free the external symbols.  */
+      free (shndx_buf);
+    }
+
+  for (input_bfd = info->input_bfds, bfd_indx = 0;
+       input_bfd != NULL;
+       input_bfd = input_bfd->link_next, bfd_indx++)
+    {
+      Elf_Internal_Shdr *symtab_hdr;
+      Elf_Internal_Sym *local_syms;
+      struct elf_link_hash_entry ** sym_hashes;
+
+      sym_hashes = elf_sym_hashes (input_bfd);
+
+      /* We'll need the symbol table in a second.  */
+      symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+      if (symtab_hdr->sh_info == 0)
+        continue;
+
+      local_syms = all_local_syms[bfd_indx];
+
+      /* Walk over each section attached to the input bfd.  */
+      for (section = input_bfd->sections;
+           section != NULL;
+           section = section->next)
+        {
+          Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
+
+          /* If there aren't any relocs, then there's nothing more
+             to do.  */
+          if ((section->flags & SEC_RELOC) == 0
+              || section->reloc_count == 0)
+            continue;
+
+          /* If this section is a link-once section that will be
+             discarded, then don't create any stubs.  */
+          if (section->output_section == NULL
+              || section->output_section->owner != output_bfd)
+            continue;
+
+          /* Get the relocs.  */
+          internal_relocs
+            = _bfd_elf32_link_read_relocs (input_bfd, section, NULL,
+                                           (Elf_Internal_Rela *) NULL,
+                                           info->keep_memory);
+          if (internal_relocs == NULL)
+            goto error_ret_free_local;
+
+          /* Now examine each relocation.  */
+          irela = internal_relocs;
+          irelaend = irela + section->reloc_count;
+          for (; irela < irelaend; irela++)
+            {
+              unsigned int r_type, r_indx;
+              struct elf32_m68hc11_stub_hash_entry *stub_entry;
+              asection *sym_sec;
+              bfd_vma sym_value;
+              struct elf_link_hash_entry *hash;
+              const char *stub_name;
+              Elf_Internal_Sym *sym;
+
+              r_type = ELF32_R_TYPE (irela->r_info);
+
+              /* Only look at 16-bit relocs.  */
+              if (r_type != (unsigned int) R_M68HC11_16)
+                continue;
+
+              /* Now determine the call target, its name, value,
+                 section.  */
+              r_indx = ELF32_R_SYM (irela->r_info);
+              if (r_indx < symtab_hdr->sh_info)
+                {
+                  /* It's a local symbol.  */
+                  Elf_Internal_Shdr *hdr;
+                  bfd_boolean is_far;
+
+                  sym = local_syms + r_indx;
+                  hdr = elf_elfsections (input_bfd)[sym->st_shndx];
+                  sym_sec = hdr->bfd_section;
+                  is_far = (sym && (sym->st_other & STO_M68HC12_FAR));
+                  if (!is_far)
+                    continue;
+                  stub_name = (bfd_elf_string_from_elf_section
+                               (input_bfd, symtab_hdr->sh_link,
+                                sym->st_name));
+                  sym_value = sym->st_value;
+                  hash = NULL;
+                }
+              else
+                {
+                  /* It's an external symbol.  */
+                  int e_indx;
+
+                  e_indx = r_indx - symtab_hdr->sh_info;
+                  hash = (struct elf_link_hash_entry *)
+                    (sym_hashes[e_indx]);
+
+                  while (hash->root.type == bfd_link_hash_indirect
+                         || hash->root.type == bfd_link_hash_warning)
+                    hash = ((struct elf_link_hash_entry *)
+                            hash->root.u.i.link);
+
+                  if (hash->root.type == bfd_link_hash_defined
+                      || hash->root.type == bfd_link_hash_defweak)
+                    {
+                      if (!(hash->other & STO_M68HC12_FAR))
+                        continue;
+                    }
+                  else if (hash->root.type == bfd_link_hash_undefweak)
+                    {
+                      continue;
+                    }
+                  else if (hash->root.type == bfd_link_hash_undefined)
+                    {
+                      continue;
+                    }
+                  else
+                    {
+                      bfd_set_error (bfd_error_bad_value);
+                      goto error_ret_free_internal;
+                    }
+                  sym_sec = hash->root.u.def.section;
+                  sym_value = hash->root.u.def.value;
+                  stub_name = hash->root.root.string;
+                }
+
+              if (!stub_name)
+                goto error_ret_free_internal;
+
+              stub_entry = m68hc12_stub_hash_lookup
+                (htab->stub_hash_table,
+                 stub_name,
+                 FALSE, FALSE);
+              if (stub_entry == NULL)
+                {
+                  if (add_stub_section == 0)
+                    continue;
+
+                  stub_entry = m68hc12_add_stub (stub_name, section, htab);
+                  if (stub_entry == NULL)
+                    {
+                    error_ret_free_internal:
+                      if (elf_section_data (section)->relocs == NULL)
+                        free (internal_relocs);
+                      goto error_ret_free_local;
+                    }
+                }
+
+              stub_entry->target_value = sym_value;
+              stub_entry->target_section = sym_sec;
+            }
+
+          /* We're done with the internal relocs, free them.  */
+          if (elf_section_data (section)->relocs == NULL)
+            free (internal_relocs);
+        }
+    }
+
+  if (add_stub_section)
+    {
+      /* OK, we've added some stubs.  Find out the new size of the
+         stub sections.  */
+      for (stub_sec = htab->stub_bfd->sections;
+           stub_sec != NULL;
+           stub_sec = stub_sec->next)
+        {
+          stub_sec->_raw_size = 0;
+          stub_sec->_cooked_size = 0;
+        }
+
+      bfd_hash_traverse (htab->stub_hash_table, htab->size_one_stub, htab);
+    }
+  free (htab->all_local_syms);
+  return TRUE;
+
+ error_ret_free_local:
+  free (htab->all_local_syms);
+  return FALSE;
+}
+
+/* Export the trampoline addresses in the symbol table.  */
+static bfd_boolean
+m68hc11_elf_export_one_stub (gen_entry, in_arg)
+     struct bfd_hash_entry *gen_entry;
+     PTR in_arg;
+{
+  struct bfd_link_info *info;
+  struct m68hc11_elf_link_hash_table *htab;
+  struct elf32_m68hc11_stub_hash_entry *stub_entry;
+  char* name;
+  bfd_boolean result;
+
+  info = (struct bfd_link_info *) in_arg;
+  htab = m68hc11_elf_hash_table (info);
+
+  /* Massage our args to the form they really have.  */
+  stub_entry = (struct elf32_m68hc11_stub_hash_entry *) gen_entry;
+
+  /* Generate the trampoline according to HC11 or HC12.  */
+  result = (* htab->build_one_stub) (gen_entry, in_arg);
+
+  /* Make a printable name that does not conflict with the real function.  */
+  name = alloca (strlen (stub_entry->root.string) + 16);
+  sprintf (name, "tramp.%s", stub_entry->root.string);
+
+  /* Export the symbol for debugging/disassembling.  */
+  m68hc11_elf_set_symbol (htab->stub_bfd, info, name,
+                          stub_entry->stub_offset,
+                          stub_entry->stub_sec);
+  return result;
+}
+
+/* Export a symbol or set its value and section.  */
+static void
+m68hc11_elf_set_symbol (abfd, info, name, value, sec)
+     bfd* abfd;
+     struct bfd_link_info *info;
+     const char* name;
+     bfd_vma value;
+     asection* sec;
+{
+  struct elf_link_hash_entry *h;
+
+  h = (struct elf_link_hash_entry *)
+    bfd_link_hash_lookup (info->hash, name, FALSE, FALSE, FALSE);
+  if (h == NULL)
+    {
+      _bfd_generic_link_add_one_symbol (info, abfd,
+                                        name,
+                                        BSF_GLOBAL,
+                                        sec,
+                                        value,
+                                        (const char*) NULL,
+                                        TRUE, FALSE, NULL);
+    }
+  else
+    {
+      h->root.type = bfd_link_hash_defined;
+      h->root.u.def.value = value;
+      h->root.u.def.section = sec;
+    }
+}
+
+
+/* Build all the stubs associated with the current output file.  The
+   stubs are kept in a hash table attached to the main linker hash
+   table.  This function is called via m68hc12elf_finish in the
+   linker.  */
+
+bfd_boolean
+elf32_m68hc11_build_stubs (abfd, info)
+     bfd* abfd;
+     struct bfd_link_info *info;
+{
+  asection *stub_sec;
+  struct bfd_hash_table *table;
+  struct m68hc11_elf_link_hash_table *htab;
+  struct m68hc11_scan_param param;
+
+  m68hc11_elf_get_bank_parameters (info);
+  htab = m68hc11_elf_hash_table (info);
+
+  for (stub_sec = htab->stub_bfd->sections;
+       stub_sec != NULL;
+       stub_sec = stub_sec->next)
+    {
+      bfd_size_type size;
+
+      /* Allocate memory to hold the linker stubs.  */
+      size = stub_sec->_raw_size;
+      stub_sec->contents = (unsigned char *) bfd_zalloc (htab->stub_bfd, size);
+      if (stub_sec->contents == NULL && size != 0)
+       return FALSE;
+      stub_sec->_raw_size = 0;
+    }
+
+  /* Build the stubs as directed by the stub hash table.  */
+  table = htab->stub_hash_table;
+  bfd_hash_traverse (table, m68hc11_elf_export_one_stub, info);
+  
+  /* Scan the output sections to see if we use the memory banks.
+     If so, export the symbols that define how the memory banks
+     are mapped.  This is used by gdb and the simulator to obtain
+     the information.  It can be used by programs to burn the eprom
+     at the good addresses.  */
+  param.use_memory_banks = FALSE;
+  param.pinfo = &htab->pinfo;
+  bfd_map_over_sections (abfd, scan_sections_for_abi, &param);
+  if (param.use_memory_banks)
+    {
+      m68hc11_elf_set_symbol (abfd, info, BFD_M68HC11_BANK_START_NAME,
+                              htab->pinfo.bank_physical,
+                              bfd_abs_section_ptr);
+      m68hc11_elf_set_symbol (abfd, info, BFD_M68HC11_BANK_VIRTUAL_NAME,
+                              htab->pinfo.bank_virtual,
+                              bfd_abs_section_ptr);
+      m68hc11_elf_set_symbol (abfd, info, BFD_M68HC11_BANK_SIZE_NAME,
+                              htab->pinfo.bank_size,
+                              bfd_abs_section_ptr);
+    }
+
+  return TRUE;
+}
+
+void
+m68hc11_elf_get_bank_parameters (info)
+     struct bfd_link_info *info;
+{
+  unsigned i;
+  struct m68hc11_page_info *pinfo;
+  struct bfd_link_hash_entry *h;
+
+  pinfo = &m68hc11_elf_hash_table (info)->pinfo;
+  if (pinfo->bank_param_initialized)
+    return;
+
+  pinfo->bank_virtual = M68HC12_BANK_VIRT;
+  pinfo->bank_mask = M68HC12_BANK_MASK;
+  pinfo->bank_physical = M68HC12_BANK_BASE;
+  pinfo->bank_shift = M68HC12_BANK_SHIFT;
+  pinfo->bank_size = 1 << M68HC12_BANK_SHIFT;
+
+  h = bfd_link_hash_lookup (info->hash, BFD_M68HC11_BANK_START_NAME,
+                            FALSE, FALSE, TRUE);
+  if (h != (struct bfd_link_hash_entry*) NULL
+      && h->type == bfd_link_hash_defined)
+    pinfo->bank_physical = (h->u.def.value
+                            + h->u.def.section->output_section->vma
+                            + h->u.def.section->output_offset);
+
+  h = bfd_link_hash_lookup (info->hash, BFD_M68HC11_BANK_VIRTUAL_NAME,
+                            FALSE, FALSE, TRUE);
+  if (h != (struct bfd_link_hash_entry*) NULL
+      && h->type == bfd_link_hash_defined)
+    pinfo->bank_virtual = (h->u.def.value
+                           + h->u.def.section->output_section->vma
+                           + h->u.def.section->output_offset);
+
+  h = bfd_link_hash_lookup (info->hash, BFD_M68HC11_BANK_SIZE_NAME,
+                            FALSE, FALSE, TRUE);
+  if (h != (struct bfd_link_hash_entry*) NULL
+      && h->type == bfd_link_hash_defined)
+    pinfo->bank_size = (h->u.def.value
+                        + h->u.def.section->output_section->vma
+                        + h->u.def.section->output_offset);
+
+  pinfo->bank_shift = 0;
+  for (i = pinfo->bank_size; i != 0; i >>= 1)
+    pinfo->bank_shift++;
+  pinfo->bank_shift--;
+  pinfo->bank_mask = (1 << pinfo->bank_shift) - 1;
+  pinfo->bank_physical_end = pinfo->bank_physical + pinfo->bank_size;
+  pinfo->bank_param_initialized = 1;
+
+  h = bfd_link_hash_lookup (info->hash, "__far_trampoline", FALSE,
+                            FALSE, TRUE);
+  if (h != (struct bfd_link_hash_entry*) NULL
+      && h->type == bfd_link_hash_defined)
+    pinfo->trampoline_addr = (h->u.def.value
+                              + h->u.def.section->output_section->vma
+                              + h->u.def.section->output_offset);
+}
+
+/* Return 1 if the address is in banked memory.
+   This can be applied to a virtual address and to a physical address.  */
+int
+m68hc11_addr_is_banked (pinfo, addr)
+     struct m68hc11_page_info *pinfo;
+     bfd_vma addr;
+{
+  if (addr >= pinfo->bank_virtual)
+    return 1;
+
+  if (addr >= pinfo->bank_physical && addr <= pinfo->bank_physical_end)
+    return 1;
+
+  return 0;
+}
+
+/* Return the physical address seen by the processor, taking
+   into account banked memory.  */
+bfd_vma
+m68hc11_phys_addr (pinfo, addr)
+     struct m68hc11_page_info *pinfo;
+     bfd_vma addr;
+{
+  if (addr < pinfo->bank_virtual)
+    return addr;
+
+  /* Map the address to the memory bank.  */
+  addr -= pinfo->bank_virtual;
+  addr &= pinfo->bank_mask;
+  addr += pinfo->bank_physical;
+  return addr;
+}
+
+/* Return the page number corresponding to an address in banked memory.  */
+bfd_vma
+m68hc11_phys_page (pinfo, addr)
+     struct m68hc11_page_info *pinfo;
+     bfd_vma addr;
+{
+  if (addr < pinfo->bank_virtual)
+    return 0;
+
+  /* Map the address to the memory bank.  */
+  addr -= pinfo->bank_virtual;
+  addr >>= pinfo->bank_shift;
+  addr &= 0x0ff;
+  return addr;
+}
+
+/* This function is used for relocs which are only used for relaxing,
+   which the linker should otherwise ignore.  */
+
+bfd_reloc_status_type
+m68hc11_elf_ignore_reloc (abfd, reloc_entry, symbol, data, input_section,
+                          output_bfd, error_message)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     arelent *reloc_entry;
+     asymbol *symbol ATTRIBUTE_UNUSED;
+     PTR data ATTRIBUTE_UNUSED;
+     asection *input_section;
+     bfd *output_bfd;
+     char **error_message ATTRIBUTE_UNUSED;
+{
+  if (output_bfd != NULL)
+    reloc_entry->address += input_section->output_offset;
+  return bfd_reloc_ok;
+}
+
+bfd_reloc_status_type
+m68hc11_elf_special_reloc (abfd, reloc_entry, symbol, data, input_section,
+                           output_bfd, error_message)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     arelent *reloc_entry;
+     asymbol *symbol;
+     PTR data ATTRIBUTE_UNUSED;
+     asection *input_section;
+     bfd *output_bfd;
+     char **error_message ATTRIBUTE_UNUSED;
+{
+  if (output_bfd != (bfd *) NULL
+      && (symbol->flags & BSF_SECTION_SYM) == 0
+      && (! reloc_entry->howto->partial_inplace
+         || reloc_entry->addend == 0))
+    {
+      reloc_entry->address += input_section->output_offset;
+      return bfd_reloc_ok;
+    }
+
+  if (output_bfd != NULL)
+    return bfd_reloc_continue;
+
+  if (reloc_entry->address > input_section->_cooked_size)
+    return bfd_reloc_outofrange;
+
+  abort();
+}
+
+asection *
+elf32_m68hc11_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))
+       {
+       default:
+         switch (h->root.type)
+           {
+           case bfd_link_hash_defined:
+           case bfd_link_hash_defweak:
+             return h->root.u.def.section;
+
+           case bfd_link_hash_common:
+             return h->root.u.c.p->section;
+
+           default:
+             break;
+           }
+       }
+    }
+  else
+    return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
+
+  return NULL;
+}
+
+bfd_boolean
+elf32_m68hc11_gc_sweep_hook (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;
+{
+  /* We don't use got and plt entries for 68hc11/68hc12.  */
+  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.  */
+
+bfd_boolean
+elf32_m68hc11_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;
+  struct elf_link_hash_entry ** 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_M68HC11_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_M68HC11_GNU_VTENTRY:
+          if (!_bfd_elf32_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+            return FALSE;
+          break;
+        }
+    }
+
+  return TRUE;
+}
+
+static bfd_boolean
+m68hc11_get_relocation_value (abfd, info, local_sections, local_syms,
+                              rel, name,
+                              relocation, is_far)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     asection **local_sections;
+     Elf_Internal_Sym* local_syms;
+     Elf_Internal_Rela* rel;
+     const char** name;
+     bfd_vma* relocation;
+     bfd_boolean* is_far;
+{
+  Elf_Internal_Shdr *symtab_hdr;
+  struct elf_link_hash_entry **sym_hashes;
+  unsigned long r_symndx;
+  asection *sec;
+  struct elf_link_hash_entry *h;
+  Elf_Internal_Sym *sym;
+  const char* stub_name = 0;
+
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  sym_hashes = elf_sym_hashes (abfd);
+
+  r_symndx = ELF32_R_SYM (rel->r_info);
+
+  /* This is a final link.  */
+  h = NULL;
+  sym = NULL;
+  sec = NULL;
+  if (r_symndx < symtab_hdr->sh_info)
+    {
+      sym = local_syms + r_symndx;
+      sec = local_sections[r_symndx];
+      *relocation = (sec->output_section->vma
+                     + sec->output_offset
+                     + sym->st_value);
+      *is_far = (sym && (sym->st_other & STO_M68HC12_FAR));
+      if (*is_far)
+        stub_name = (bfd_elf_string_from_elf_section
+                     (abfd, symtab_hdr->sh_link,
+                      sym->st_name));
+    }
+  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;
+          *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->callbacks->undefined_symbol)
+                (info, h->root.root.string, abfd,
+                 sec, rel->r_offset, TRUE)))
+            return FALSE;
+          *relocation = 0;
+        }
+      *is_far = (h && (h->other & STO_M68HC12_FAR));
+      stub_name = h->root.root.string;
+    }
+
+  if (h != NULL)
+    *name = h->root.root.string;
+  else
+    {
+      *name = (bfd_elf_string_from_elf_section
+               (abfd, symtab_hdr->sh_link, sym->st_name));
+      if (*name == NULL || **name == '\0')
+        *name = bfd_section_name (input_bfd, sec);
+    }
+
+  if (*is_far && ELF32_R_TYPE (rel->r_info) == R_M68HC11_16)
+    {
+      struct elf32_m68hc11_stub_hash_entry* stub;
+      struct m68hc11_elf_link_hash_table *htab;
+
+      htab = m68hc11_elf_hash_table (info);
+      stub = m68hc12_stub_hash_lookup (htab->stub_hash_table,
+                                       *name, FALSE, FALSE);
+      if (stub)
+        {
+          *relocation = stub->stub_offset
+            + stub->stub_sec->output_section->vma
+            + stub->stub_sec->output_offset;
+          *is_far = FALSE;
+        }
+    }
+  return TRUE;
+}
+
+/* Relocate a 68hc11/68hc12 ELF section.  */
+bfd_boolean
+elf32_m68hc11_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, *relend;
+  const char *name;
+  struct m68hc11_page_info *pinfo;
+  struct elf_backend_data * const ebd = get_elf_backend_data (input_bfd);
+
+  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+  sym_hashes = elf_sym_hashes (input_bfd);
+
+  /* Get memory bank parameters.  */
+  m68hc11_elf_get_bank_parameters (info);
+  pinfo = &m68hc11_elf_hash_table (info)->pinfo;
+
+  rel = relocs;
+  relend = relocs + input_section->reloc_count;
+  for (; rel < relend; rel++)
+    {
+      int r_type;
+      arelent arel;
+      reloc_howto_type *howto;
+      unsigned long r_symndx;
+      Elf_Internal_Sym *sym;
+      asection *sec;
+      bfd_vma relocation;
+      bfd_reloc_status_type r = bfd_reloc_undefined;
+      bfd_vma phys_page;
+      bfd_vma phys_addr;
+      bfd_vma insn_addr;
+      bfd_vma insn_page;
+      bfd_boolean is_far;
+
+      r_symndx = ELF32_R_SYM (rel->r_info);
+      r_type = ELF32_R_TYPE (rel->r_info);
+
+      if (r_type == R_M68HC11_GNU_VTENTRY
+          || r_type == R_M68HC11_GNU_VTINHERIT )
+        continue;
+
+      if (info->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;
+       }
+      (*ebd->elf_info_to_howto_rel) (input_bfd, &arel, rel);
+      howto = arel.howto;
+
+      m68hc11_get_relocation_value (input_bfd, info,
+                                    local_sections, local_syms,
+                                    rel, &name, &relocation, &is_far);
+
+      /* Do the memory bank mapping.  */
+      phys_addr = m68hc11_phys_addr (pinfo, relocation + rel->r_addend);
+      phys_page = m68hc11_phys_page (pinfo, relocation + rel->r_addend);
+      switch (r_type)
+        {
+        case R_M68HC11_24:
+          /* Reloc used by 68HC12 call instruction.  */
+          bfd_put_16 (input_bfd, phys_addr,
+                      (bfd_byte*) contents + rel->r_offset);
+          bfd_put_8 (input_bfd, phys_page,
+                     (bfd_byte*) contents + rel->r_offset + 2);
+          r = bfd_reloc_ok;
+          r_type = R_M68HC11_NONE;
+          break;
+
+        case R_M68HC11_NONE:
+          r = bfd_reloc_ok;
+          break;
+
+        case R_M68HC11_LO16:
+          /* Reloc generated by %addr(expr) gas to obtain the
+             address as mapped in the memory bank window.  */
+          relocation = phys_addr;
+          break;
+
+        case R_M68HC11_PAGE:
+          /* Reloc generated by %page(expr) gas to obtain the
+             page number associated with the address.  */
+          relocation = phys_page;
+          break;
+
+        case R_M68HC11_16:
+          /* Get virtual address of instruction having the relocation.  */
+          if (is_far)
+            {
+              const char* msg;
+              char* buf;
+              msg = _("Reference to the far symbol `%s' using a wrong "
+                      "relocation may result in incorrect execution");
+              buf = alloca (strlen (msg) + strlen (name) + 10);
+              sprintf (buf, msg, name);
+              
+              (* info->callbacks->warning)
+                (info, buf, name, input_bfd, NULL, rel->r_offset);
+            }
+
+          /* Get virtual address of instruction having the relocation.  */
+          insn_addr = input_section->output_section->vma
+            + input_section->output_offset
+            + rel->r_offset;
+
+          insn_page = m68hc11_phys_page (pinfo, insn_addr);
+
+          if (m68hc11_addr_is_banked (pinfo, relocation + rel->r_addend)
+              && m68hc11_addr_is_banked (pinfo, insn_addr)
+              && phys_page != insn_page)
+            {
+              const char* msg;
+              char* buf;
+
+              msg = _("banked address [%lx:%04lx] (%lx) is not in the same bank "
+                      "as current banked address [%lx:%04lx] (%lx)");
+
+              buf = alloca (strlen (msg) + 128);
+              sprintf (buf, msg, phys_page, phys_addr,
+                       (long) (relocation + rel->r_addend),
+                       insn_page, m68hc11_phys_addr (pinfo, insn_addr),
+                       (long) (insn_addr));
+              if (!((*info->callbacks->warning)
+                    (info, buf, name, input_bfd, input_section,
+                     rel->r_offset)))
+                return FALSE;
+              break;
+            }
+          if (phys_page != 0 && insn_page == 0)
+            {
+              const char* msg;
+              char* buf;
+
+              msg = _("reference to a banked address [%lx:%04lx] in the "
+                      "normal address space at %04lx");
+
+              buf = alloca (strlen (msg) + 128);
+              sprintf (buf, msg, phys_page, phys_addr, insn_addr);
+              if (!((*info->callbacks->warning)
+                    (info, buf, name, input_bfd, input_section,
+                     insn_addr)))
+                return FALSE;
+
+              relocation = phys_addr;
+              break;
+            }
+
+          /* If this is a banked address use the phys_addr so that
+             we stay in the banked window.  */
+          if (m68hc11_addr_is_banked (pinfo, relocation + rel->r_addend))
+            relocation = phys_addr;
+          break;
+        }
+      if (r_type != R_M68HC11_NONE)
+        r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                      contents, rel->r_offset,
+                                      relocation, rel->r_addend);
+
+      if (r != bfd_reloc_ok)
+       {
+         const char * msg = (const char *) 0;
+
+         switch (r)
+           {
+           case bfd_reloc_overflow:
+             if (!((*info->callbacks->reloc_overflow)
+                   (info, name, howto->name, (bfd_vma) 0,
+                    input_bfd, input_section, rel->r_offset)))
+               return FALSE;
+             break;
+
+           case bfd_reloc_undefined:
+             if (!((*info->callbacks->undefined_symbol)
+                   (info, name, input_bfd, input_section,
+                    rel->r_offset, TRUE)))
+               return FALSE;
+             break;
+
+           case bfd_reloc_outofrange:
+             msg = _ ("internal error: out of range error");
+             goto common_error;
+
+           case bfd_reloc_notsupported:
+             msg = _ ("internal error: unsupported relocation error");
+             goto common_error;
+
+           case bfd_reloc_dangerous:
+             msg = _ ("internal error: dangerous error");
+             goto common_error;
+
+           default:
+             msg = _ ("internal error: unknown error");
+             /* fall through */
+
+           common_error:
+             if (!((*info->callbacks->warning)
+                   (info, msg, name, input_bfd, input_section,
+                    rel->r_offset)))
+               return FALSE;
+             break;
+           }
+       }
+    }
+
+  return TRUE;
+}
+
+
+\f
+/* Set and control ELF flags in ELF header.  */
+
+bfd_boolean
+_bfd_m68hc11_elf_set_private_flags (abfd, flags)
+     bfd *abfd;
+     flagword flags;
+{
+  BFD_ASSERT (!elf_flags_init (abfd)
+             || elf_elfheader (abfd)->e_flags == flags);
+
+  elf_elfheader (abfd)->e_flags = flags;
+  elf_flags_init (abfd) = TRUE;
+  return TRUE;
+}
+
+/* Merge backend specific data from an object file to the output
+   object file when linking.  */
+
+bfd_boolean
+_bfd_m68hc11_elf_merge_private_bfd_data (ibfd, obfd)
+     bfd *ibfd;
+     bfd *obfd;
+{
+  flagword old_flags;
+  flagword new_flags;
+  bfd_boolean ok = TRUE;
+
+  /* Check if we have the same endianess */
+  if (!_bfd_generic_verify_endian_match (ibfd, obfd))
+    return FALSE;
+
+  if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
+      || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
+    return TRUE;
+
+  new_flags = elf_elfheader (ibfd)->e_flags;
+  elf_elfheader (obfd)->e_flags |= new_flags & EF_M68HC11_ABI;
+  old_flags = elf_elfheader (obfd)->e_flags;
+
+  if (! elf_flags_init (obfd))
+    {
+      elf_flags_init (obfd) = TRUE;
+      elf_elfheader (obfd)->e_flags = new_flags;
+      elf_elfheader (obfd)->e_ident[EI_CLASS]
+       = elf_elfheader (ibfd)->e_ident[EI_CLASS];
+
+      if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
+         && bfd_get_arch_info (obfd)->the_default)
+       {
+         if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
+                                  bfd_get_mach (ibfd)))
+           return FALSE;
+       }
+
+      return TRUE;
+    }
+
+  /* Check ABI compatibility.  */
+  if ((new_flags & E_M68HC11_I32) != (old_flags & E_M68HC11_I32))
+    {
+      (*_bfd_error_handler)
+       (_("%s: linking files compiled for 16-bit integers (-mshort) "
+           "and others for 32-bit integers"),
+        bfd_archive_filename (ibfd));
+      ok = FALSE;
+    }
+  if ((new_flags & E_M68HC11_F64) != (old_flags & E_M68HC11_F64))
+    {
+      (*_bfd_error_handler)
+       (_("%s: linking files compiled for 32-bit double (-fshort-double) "
+           "and others for 64-bit double"),
+        bfd_archive_filename (ibfd));
+      ok = FALSE;
+    }
+  new_flags &= ~EF_M68HC11_ABI;
+  old_flags &= ~EF_M68HC11_ABI;
+
+  /* Warn about any other mismatches */
+  if (new_flags != old_flags)
+    {
+      (*_bfd_error_handler)
+       (_("%s: uses different e_flags (0x%lx) fields than previous modules (0x%lx)"),
+        bfd_archive_filename (ibfd), (unsigned long) new_flags,
+        (unsigned long) old_flags);
+      ok = FALSE;
+    }
+
+  if (! ok)
+    {
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+bfd_boolean
+_bfd_m68hc11_elf_print_private_bfd_data (abfd, ptr)
+     bfd *abfd;
+     PTR ptr;
+{
+  FILE *file = (FILE *) ptr;
+
+  BFD_ASSERT (abfd != NULL && ptr != NULL);
+
+  /* Print normal ELF private data.  */
+  _bfd_elf_print_private_bfd_data (abfd, ptr);
+
+  /* xgettext:c-format */
+  fprintf (file, _("private flags = %lx:"), elf_elfheader (abfd)->e_flags);
+
+  if (elf_elfheader (abfd)->e_flags & E_M68HC11_I32)
+    fprintf (file, _("[abi=32-bit int, "));
+  else
+    fprintf (file, _("[abi=16-bit int, "));
+
+  if (elf_elfheader (abfd)->e_flags & E_M68HC11_F64)
+    fprintf (file, _("64-bit double, "));
+  else
+    fprintf (file, _("32-bit double, "));
+
+  if (strcmp (bfd_get_target (abfd), "elf32-m68hc11") == 0)
+    fprintf (file, _("cpu=HC11]"));
+  else if (elf_elfheader (abfd)->e_flags & EF_M68HCS12_MACH)
+    fprintf (file, _("cpu=HCS12]"));
+  else
+    fprintf (file, _("cpu=HC12]"));    
+
+  if (elf_elfheader (abfd)->e_flags & E_M68HC12_BANKS)
+    fprintf (file, _(" [memory=bank-model]"));
+  else
+    fprintf (file, _(" [memory=flat]"));
+
+  fputc ('\n', file);
+
+  return TRUE;
+}
+
+static void scan_sections_for_abi (abfd, asect, arg)
+     bfd* abfd ATTRIBUTE_UNUSED;
+     asection* asect;
+     PTR arg;
+{
+  struct m68hc11_scan_param* p = (struct m68hc11_scan_param*) arg;
+
+  if (asect->vma >= p->pinfo->bank_virtual)
+    p->use_memory_banks = TRUE;
+}
+  
+/* Tweak the OSABI field of the elf header.  */
+
+void
+elf32_m68hc11_post_process_headers (abfd, link_info)
+     bfd *abfd;
+     struct bfd_link_info *link_info;
+{
+  struct m68hc11_scan_param param;
+
+  if (link_info == 0)
+    return;
+
+  m68hc11_elf_get_bank_parameters (link_info);
+
+  param.use_memory_banks = FALSE;
+  param.pinfo = &m68hc11_elf_hash_table (link_info)->pinfo;
+  bfd_map_over_sections (abfd, scan_sections_for_abi, &param);
+  if (param.use_memory_banks)
+    {
+      Elf_Internal_Ehdr * i_ehdrp;
+
+      i_ehdrp = elf_elfheader (abfd);
+      i_ehdrp->e_flags |= E_M68HC12_BANKS;
+    }
+}
+
diff --git a/bfd/elf32-m68hc1x.h b/bfd/elf32-m68hc1x.h
new file mode 100644 (file)
index 0000000..e70c172
--- /dev/null
@@ -0,0 +1,205 @@
+/* Motorola 68HC11/68HC12-specific support for 32-bit ELF
+   Copyright 2003 Free Software Foundation, Inc.
+   Contributed by Stephane Carrez (stcarrez@nerim.fr)
+
+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.  */
+
+#ifndef _ELF32_M68HC1X_H
+#define _ELF32_M68HC1X_H
+
+#include "elf-bfd.h"
+#include "bfdlink.h"
+#include "elf/m68hc11.h"
+
+/* Name of symbols exported by HC11/HC12 linker when there is a memory
+   bank window.  */
+#define BFD_M68HC11_BANK_START_NAME   "__bank_start"
+#define BFD_M68HC11_BANK_SIZE_NAME    "__bank_size"
+#define BFD_M68HC11_BANK_VIRTUAL_NAME "__bank_virtual"
+
+/* Set and control ELF flags in ELF header.  */
+extern bfd_boolean _bfd_m68hc11_elf_merge_private_bfd_data PARAMS ((bfd*,bfd*));
+extern bfd_boolean _bfd_m68hc11_elf_set_private_flags PARAMS ((bfd*,flagword));
+extern bfd_boolean _bfd_m68hc11_elf_print_private_bfd_data PARAMS ((bfd*,PTR));
+
+/* This hash entry is used to record a trampoline that must be generated
+   to call a far function using a normal calling convention ('jsr').
+   The trampoline is used when a pointer to a far function is used.
+   It takes care of installing the proper memory bank as well as creating
+   the 'call/rtc' calling convention.  */
+struct elf32_m68hc11_stub_hash_entry {
+
+  /* Base hash table entry structure.  */
+  struct bfd_hash_entry root;
+
+  /* The stub section.  */
+  asection *stub_sec;
+
+  /* Offset within stub_sec of the beginning of this stub.  */
+  bfd_vma stub_offset;
+
+  /* Given the symbol's value and its section we can determine its final
+     value when building the stubs (so the stub knows where to jump.  */
+  bfd_vma target_value;
+  asection *target_section;
+};
+
+/* Placeholder for the parameters to compute memory page and physical address.
+   The following formulas are used:
+
+   sym > bank_virtual =>
+     %addr(sym) = (((sym - bank_virtual) & bank_mask) + bank_physical
+     %page(sym) = (((sym - bank_virtual) >> bank_shift) % 256
+
+   sym < bank_virtual =>
+     %addr(sym) = sym
+     %page(sym) = 0
+
+
+   These parameters are obtained from the symbol table by looking
+   at the following:
+
+   __bank_start         Symbol marking the start of memory bank window
+                        (bank_physical)
+   __bank_virtual       Logical address of symbols for which the transformation
+                        must be computed
+   __bank_page_size     Size in bytes of page size (this is *NOT* the memory
+                        bank window size and the window size is always
+                        less or equal to the page size)
+
+   For 68HC12, the window is at 0x8000 and the page size is 16K (full window).
+   For 68HC11 this is board specific (implemented by external hardware).
+
+*/
+struct m68hc11_page_info
+{
+  bfd_vma bank_virtual;
+  bfd_vma bank_physical;
+  bfd_vma bank_physical_end;
+  bfd_vma bank_mask;
+  bfd_vma bank_size;
+  int bank_shift;
+  int bank_param_initialized;
+  bfd_vma trampoline_addr;
+};
+
+struct m68hc11_elf_link_hash_table
+{
+  struct elf_link_hash_table root;
+  struct m68hc11_page_info pinfo;
+
+  /* The stub hash table.  */
+  struct bfd_hash_table* stub_hash_table;
+
+  /* Linker stub bfd.  */
+  bfd *stub_bfd;
+
+  asection* stub_section;
+  asection* tramp_section;
+
+  /* Linker call-backs.  */
+  asection * (*add_stub_section) PARAMS ((const char *, asection *));
+
+  /* Assorted information used by elf32_hppa_size_stubs.  */
+  unsigned int bfd_count;
+  int top_index;
+  asection **input_list;
+  Elf_Internal_Sym **all_local_syms;
+
+  /* Small local sym to section mapping cache.  */
+  struct sym_sec_cache sym_sec;
+
+  bfd_boolean (* size_one_stub) PARAMS((struct bfd_hash_entry*, PTR));
+  bfd_boolean (* build_one_stub) PARAMS((struct bfd_hash_entry*, PTR));
+};
+
+/* Get the Sparc64 ELF linker hash table from a link_info structure.  */
+
+#define m68hc11_elf_hash_table(p) \
+  ((struct m68hc11_elf_link_hash_table *) ((p)->hash))
+
+/* Create a 68HC11/68HC12 ELF linker hash table.  */
+
+extern struct m68hc11_elf_link_hash_table* m68hc11_elf_hash_table_create
+  PARAMS ((bfd*));
+extern void m68hc11_elf_bfd_link_hash_table_free
+  PARAMS ((struct bfd_link_hash_table*));
+
+extern void m68hc11_elf_get_bank_parameters
+  PARAMS ((struct bfd_link_info*));
+
+/* Return 1 if the address is in banked memory.
+   This can be applied to a virtual address and to a physical address.  */
+extern int m68hc11_addr_is_banked
+  PARAMS ((struct m68hc11_page_info*, bfd_vma));
+
+/* Return the physical address seen by the processor, taking
+   into account banked memory.  */
+extern bfd_vma m68hc11_phys_addr
+  PARAMS ((struct m68hc11_page_info*, bfd_vma));
+
+/* Return the page number corresponding to an address in banked memory.  */
+extern bfd_vma m68hc11_phys_page
+  PARAMS ((struct m68hc11_page_info*, bfd_vma));
+
+bfd_reloc_status_type m68hc11_elf_ignore_reloc
+  PARAMS ((bfd *abfd, arelent *reloc_entry,
+           asymbol *symbol, PTR data, asection *input_section,
+           bfd *output_bfd, char **error_message));
+bfd_reloc_status_type m68hc11_elf_special_reloc
+  PARAMS ((bfd *abfd, arelent *reloc_entry,
+           asymbol *symbol, PTR data, asection *input_section,
+           bfd *output_bfd, char **error_message));
+
+/* GC mark and sweep.  */
+asection *elf32_m68hc11_gc_mark_hook
+  PARAMS ((asection *sec, struct bfd_link_info *info,
+           Elf_Internal_Rela *rel, struct elf_link_hash_entry *h,
+           Elf_Internal_Sym *sym));
+bfd_boolean elf32_m68hc11_gc_sweep_hook
+  PARAMS ((bfd *abfd, struct bfd_link_info *info,
+           asection *sec, const Elf_Internal_Rela *relocs));
+bfd_boolean elf32_m68hc11_check_relocs
+  PARAMS ((bfd * abfd, struct bfd_link_info * info,
+           asection * sec, const Elf_Internal_Rela * relocs));
+bfd_boolean elf32_m68hc11_relocate_section
+  PARAMS ((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_boolean elf32_m68hc11_add_symbol_hook
+  PARAMS ((bfd *abfd, struct bfd_link_info *info,
+           const Elf_Internal_Sym *sym, const char **namep,
+           flagword *flagsp, asection **secp,
+           bfd_vma *valp));
+
+/* Tweak the OSABI field of the elf header.  */
+
+extern void elf32_m68hc11_post_process_headers
+  PARAMS ((bfd*, struct bfd_link_info*));
+
+int elf32_m68hc11_setup_section_lists
+  PARAMS ((bfd *, struct bfd_link_info *));
+
+bfd_boolean elf32_m68hc11_size_stubs
+  PARAMS ((bfd *, bfd *, struct bfd_link_info *,
+          asection * (*) PARAMS ((const char *, asection *))));
+
+bfd_boolean elf32_m68hc11_build_stubs
+  PARAMS ((bfd* abfd, struct bfd_link_info *));
+#endif