]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - bfd/elf32-m68hc12.c
* elf32-m68hc1x.c: New file (from elf32-m68hc11.c and elf32-m68hc12.c)
[thirdparty/binutils-gdb.git] / bfd / elf32-m68hc12.c
index 907159b01319b25f8772ad089b906adeffa0a489..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,50 +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 * abfd, bfd_reloc_code_real_type code));
+  PARAMS ((bfd *, bfd_reloc_code_real_type));
 static void m68hc11_info_to_howto_rel
-PARAMS ((bfd *, arelent *, Elf32_Internal_Rel *));
-
-static 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));
-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 ((bfd *abfd, struct bfd_link_info *info,
-         Elf_Internal_Rela *rel, struct elf_link_hash_entry *h,
-         Elf_Internal_Sym *sym));
-static boolean elf32_m68hc11_gc_sweep_hook
-PARAMS ((bfd *abfd, struct bfd_link_info *info,
-         asection *sec, const Elf_Internal_Rela *relocs));
-
-boolean _bfd_m68hc12_elf_merge_private_bfd_data PARAMS ((bfd*, bfd*));
-boolean _bfd_m68hc12_elf_set_private_flags PARAMS ((bfd*, flagword));
-boolean _bfd_m68hc12_elf_print_private_bfd_data PARAMS ((bfd*, PTR));
+  PARAMS ((bfd *, arelent *, 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 *));
 
 /* Use REL instead of RELA to save space */
-#define USE_REL
+#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
@@ -123,7 +106,7 @@ boolean _bfd_m68hc12_elf_print_private_bfd_data PARAMS ((bfd*, PTR));
 
 
    The 'call _foo' must be relocated with page 3 and 16-bit address
-   mapped at 0x8000.  
+   mapped at 0x8000.
 
    The 3-bit and 16-bit PC rel relocation is only used by 68HC12.  */
 static reloc_howto_type elf_m68hc11_howto_table[] = {
@@ -132,90 +115,90 @@ static reloc_howto_type elf_m68hc11_howto_table[] = {
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         32,                    /* bitsize */
-        false,                 /* pc_relative */
+        FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont,/* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_M68HC12_NONE",      /* name */
-        false,                 /* partial_inplace */
+        FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0,                     /* dst_mask */
-        false),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* A 8 bit absolute relocation */
   HOWTO (R_M68HC11_8,          /* type */
         0,                     /* rightshift */
         0,                     /* size (0 = byte, 1 = short, 2 = long) */
         8,                     /* bitsize */
-        false,                 /* pc_relative */
+        FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_bitfield,    /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_M68HC12_8",         /* name */
-        false,                 /* partial_inplace */
+        FALSE,                 /* partial_inplace */
         0x00ff,                /* src_mask */
         0x00ff,                /* dst_mask */
-        false),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* A 8 bit absolute relocation (upper address) */
   HOWTO (R_M68HC11_HI8,                /* type */
         8,                     /* rightshift */
         0,                     /* size (0 = byte, 1 = short, 2 = long) */
         8,                     /* bitsize */
-        false,                 /* pc_relative */
+        FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_bitfield,    /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_M68HC12_HI8",       /* name */
-        false,                 /* partial_inplace */
+        FALSE,                 /* partial_inplace */
         0x00ff,                /* src_mask */
         0x00ff,                /* dst_mask */
-        false),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* A 8 bit absolute relocation (upper address) */
   HOWTO (R_M68HC11_LO8,                /* type */
         0,                     /* rightshift */
         0,                     /* size (0 = byte, 1 = short, 2 = long) */
         8,                     /* bitsize */
-        false,                 /* pc_relative */
+        FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_M68HC12_LO8",       /* name */
-        false,                 /* partial_inplace */
+        FALSE,                 /* partial_inplace */
         0x00ff,                /* src_mask */
         0x00ff,                /* dst_mask */
-        false),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* A 8 bit PC-rel relocation */
   HOWTO (R_M68HC11_PCREL_8,    /* type */
         0,                     /* rightshift */
         0,                     /* size (0 = byte, 1 = short, 2 = long) */
         8,                     /* bitsize */
-        true,                  /* pc_relative */
+        TRUE,                  /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_bitfield,    /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_M68HC12_PCREL_8",   /* name */
-        false,                 /* partial_inplace */
+        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 */
         0,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
-        false,                 /* pc_relative */
+        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 */
+        FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        false),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* A 32 bit absolute relocation.  This one is never used for the
      code relocation.  It's used by gas for -gstabs generation.  */
@@ -223,120 +206,120 @@ static reloc_howto_type elf_m68hc11_howto_table[] = {
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         32,                    /* bitsize */
-        false,                 /* pc_relative */
+        FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_bitfield,    /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_M68HC12_32",        /* name */
-        false,                 /* partial_inplace */
+        FALSE,                 /* partial_inplace */
         0xffffffff,            /* src_mask */
         0xffffffff,            /* dst_mask */
-        false),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* A 3 bit absolute relocation */
   HOWTO (R_M68HC11_3B,         /* type */
         0,                     /* rightshift */
         0,                     /* size (0 = byte, 1 = short, 2 = long) */
         3,                     /* bitsize */
-        false,                 /* pc_relative */
+        FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_bitfield,    /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_M68HC12_4B",        /* name */
-        false,                 /* partial_inplace */
+        FALSE,                 /* partial_inplace */
         0x003,                 /* src_mask */
         0x003,                 /* dst_mask */
-        false),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* A 16 bit PC-rel relocation */
   HOWTO (R_M68HC11_PCREL_16,   /* type */
         0,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
-        true,                  /* pc_relative */
+        TRUE,                  /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_M68HC12_PCREL_16",  /* name */
-        false,                 /* partial_inplace */
+        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 */
         0,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
         0,                     /* bitsize */
-        false,                 /* pc_relative */
+        FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
         NULL,                  /* special_function */
         "R_M68HC11_GNU_VTINHERIT",     /* name */
-        false,                 /* partial_inplace */
+        FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0,                     /* dst_mask */
-        false),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* GNU extension to record C++ vtable member usage */
   HOWTO (R_M68HC11_GNU_VTENTRY,        /* type */
         0,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
         0,                     /* bitsize */
-        false,                 /* pc_relative */
+        FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
         _bfd_elf_rel_vtable_reloc_fn,  /* special_function */
         "R_M68HC11_GNU_VTENTRY",       /* name */
-        false,                 /* partial_inplace */
+        FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0,                     /* dst_mask */
-        false),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* 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 */
+        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 */
-        false),                /* pcrel_offset */
-  
+        FALSE,                 /* partial_inplace */
+        0xffffff,              /* src_mask */
+        0xffffff,              /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
   /* A 16-bit low relocation */
   HOWTO (R_M68HC11_LO16,        /* type */
         0,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
-        false,                 /* pc_relative */
+        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 */
+        FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        false),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* A page relocation */
   HOWTO (R_M68HC11_PAGE,        /* type */
         0,                     /* rightshift */
         0,                     /* size (0 = byte, 1 = short, 2 = long) */
         8,                     /* bitsize */
-        false,                 /* pc_relative */
+        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 */
+        FALSE,                 /* partial_inplace */
         0x00ff,                /* src_mask */
         0x00ff,                /* dst_mask */
-        false),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   EMPTY_HOWTO (14),
   EMPTY_HOWTO (15),
@@ -344,36 +327,36 @@ static reloc_howto_type elf_m68hc11_howto_table[] = {
   EMPTY_HOWTO (17),
   EMPTY_HOWTO (18),
   EMPTY_HOWTO (19),
-  
+
   /* Mark beginning of a jump instruction (any form).  */
   HOWTO (R_M68HC11_RL_JUMP,    /* type */
         0,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
         0,                     /* bitsize */
-        false,                 /* pc_relative */
+        FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
         m68hc11_elf_ignore_reloc,      /* special_function */
         "R_M68HC12_RL_JUMP",   /* name */
-        true,                  /* partial_inplace */
+        TRUE,                  /* partial_inplace */
         0,                     /* src_mask */
         0,                     /* dst_mask */
-        true),                 /* pcrel_offset */
+        TRUE),                 /* pcrel_offset */
 
   /* Mark beginning of Gcc relaxation group instruction.  */
   HOWTO (R_M68HC11_RL_GROUP,   /* type */
         0,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
         0,                     /* bitsize */
-        false,                 /* pc_relative */
+        FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont,        /* complain_on_overflow */
         m68hc11_elf_ignore_reloc,      /* special_function */
         "R_M68HC12_RL_GROUP",  /* name */
-        true,                  /* partial_inplace */
+        TRUE,                  /* partial_inplace */
         0,                     /* src_mask */
         0,                     /* dst_mask */
-        true),                 /* pcrel_offset */
+        TRUE),                 /* pcrel_offset */
 };
 
 /* Map BFD reloc types to M68HC11 ELF reloc types.  */
@@ -424,165 +407,13 @@ 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;
-}
-
-static int
-m68hc12_addr_is_banked (addr)
-     bfd_vma addr;
-{
-   return (addr >= M68HC12_BANK_VIRT) ? 1 : 0;
-}
-
-/* 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;
-}
-
-/* Return the page number corresponding to an address in banked memory.  */
-static bfd_vma
-m68hc12_phys_page (addr)
-     bfd_vma addr;
-{
-  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;
-}
-
-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;
-    }
-
-  if (output_bfd != NULL)
-    return bfd_reloc_continue;
-
-  if (reloc_entry->address > input_section->_cooked_size)
-    return bfd_reloc_outofrange;
-
-  /* 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);
-
-  /* Do the memory bank mapping.  */
-  phys_addr = m68hc12_phys_addr (relocation);
-  phys_page = m68hc12_phys_page (relocation);
-
-  howto = reloc_entry->howto;
-  if (howto->complain_on_overflow != complain_overflow_dont
-      && (phys_addr & (((bfd_vma) -1) << 16)))
-     return bfd_reloc_overflow;
-
-  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;
-
-    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;
-
-    case R_M68HC11_PAGE:
-      bfd_put_8 (abfd, phys_page, (bfd_byte*) data + reloc_entry->address);
-      break;
-
-    default:
-       abort ();
-       break;
-    }
-  
-  return bfd_reloc_ok;
-}
-
 /* Set the howto pointer for an M68HC11 ELF reloc.  */
 
 static void
 m68hc11_info_to_howto_rel (abfd, cache_ptr, dst)
      bfd *abfd ATTRIBUTE_UNUSED;
      arelent *cache_ptr;
-     Elf32_Internal_Rel *dst;
+     Elf_Internal_Rela *dst;
 {
   unsigned int r_type;
 
@@ -591,186 +422,122 @@ 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 (abfd, info, rel, h, sym)
-     bfd *abfd;
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
-     Elf_Internal_Rela *rel;
-     struct elf_link_hash_entry *h;
-     Elf_Internal_Sym *sym;
-{
-  if (h != NULL)
-    {
-      switch (ELF32_R_TYPE (rel->r_info))
-       {
-       default:
-         switch (h->root.type)
-           {
-           case bfd_link_hash_defined:
-           case bfd_link_hash_defweak:
-             return h->root.u.def.section;
-
-           case bfd_link_hash_common:
-             return h->root.u.c.p->section;
-
-           default:
-             break;
-           }
-       }
-    }
-  else
-    {
-      if (!(elf_bad_symtab (abfd)
-           && ELF_ST_BIND (sym->st_info) != STB_LOCAL)
-         && !((sym->st_shndx <= 0 || sym->st_shndx >= SHN_LORESERVE)
-              && sym->st_shndx != SHN_COMMON))
-       {
-         return bfd_section_from_elf_index (abfd, sym->st_shndx);
-       }
-    }
-  return NULL;
-}
-
-static 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;
-}
-
 \f
-/* Set and control ELF flags in ELF header.  */
+/* Far trampoline generation.  */
 
-boolean
-_bfd_m68hc12_elf_set_private_flags (abfd, flags)
-     bfd *abfd;
-     flagword flags;
+/* 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;
 {
-  BFD_ASSERT (!elf_flags_init (abfd)
-             || elf_elfheader (abfd)->e_flags == flags);
+  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;
 
-  elf_elfheader (abfd)->e_flags = flags;
-  elf_flags_init (abfd) = true;
-  return true;
-}
+  /* 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;
 
-/* Merge backend specific data from an object file to the output
-   object file when linking.  */
+  htab = m68hc11_elf_hash_table (info);
 
-boolean
-_bfd_m68hc12_elf_merge_private_bfd_data (ibfd, obfd)
-     bfd *ibfd;
-     bfd *obfd;
-{
-  flagword old_flags;
-  flagword new_flags;
-  boolean ok = true;
+  stub_sec = stub_entry->stub_sec;
 
-  /* Check if we have the same endianess */
-  if (_bfd_generic_verify_endian_match (ibfd, obfd) == false)
-    return false;
+  /* 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;
 
-  if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
-      || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
-    return true;
+  stub_bfd = stub_sec->owner;
 
-  new_flags = elf_elfheader (ibfd)->e_flags;
-  elf_elfheader (obfd)->e_flags |= new_flags & EF_M68HC11_ABI;
-  old_flags = elf_elfheader (obfd)->e_flags;
+  /* Create the trampoline call stub:
 
-  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;
-    }
+     ldy #%addr(symbol)
+     call %page(symbol), __trampoline
 
-  /* 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;
+  */
+  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);
 
-  /* 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;
-    }
+  /* ldy #%page(sym) */
+  bfd_put_8 (stub_bfd, 0xCD, loc);
+  bfd_put_16 (stub_bfd, phys_addr, loc + 1);
+  loc += 3;
 
-  if (! ok)
-    {
-      bfd_set_error (bfd_error_bad_value);
-      return false;
-    }
+  /* 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 true;
+  return TRUE;
 }
 
-boolean
-_bfd_m68hc12_elf_print_private_bfd_data (abfd, ptr)
-     bfd *abfd;
-     PTR ptr;
+/* As above, but don't actually build the stub.  Just bump offset so
+   we know stub section sizes.  */
+
+static bfd_boolean
+m68hc12_elf_size_one_stub (gen_entry, in_arg)
+     struct bfd_hash_entry *gen_entry;
+     PTR in_arg ATTRIBUTE_UNUSED;
 {
-  FILE *file = (FILE *) ptr;
+  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;
 
-  BFD_ASSERT (abfd != NULL && ptr != NULL);
+  stub_entry->stub_sec->_raw_size += 7;
+  return TRUE;
+}
 
-  /* Print normal ELF private data.  */
-  _bfd_elf_print_private_bfd_data (abfd, ptr);
+/* Create a 68HC12 ELF linker hash table.  */
 
-  /* xgettext:c-format */
-  fprintf (file, _("private flags = %lx:"), elf_elfheader (abfd)->e_flags);
+static struct bfd_link_hash_table *
+m68hc12_elf_bfd_link_hash_table_create (abfd)
+     bfd *abfd;
+{
+  struct m68hc11_elf_link_hash_table *ret;
 
-  if (elf_elfheader (abfd)->e_flags & E_M68HC11_I32)
-    fprintf (file, _("[abi=32-bit int,"));
-  else
-    fprintf (file, _("[abi=16-bit int,"));
+  ret = m68hc11_elf_hash_table_create (abfd);
+  if (ret == (struct m68hc11_elf_link_hash_table *) NULL)
+    return NULL;
 
-  if (elf_elfheader (abfd)->e_flags & E_M68HC11_F64)
-    fprintf (file, _(" 64-bit double]"));
-  else
-    fprintf (file, _(" 32-bit double]"));
+  ret->size_one_stub = m68hc12_elf_size_one_stub;
+  ret->build_one_stub = m68hc12_elf_build_one_stub;
 
-  fputc ('\n', file);
+  return &ret->root.root;
+}
+\f
+static bfd_boolean
+m68hc12_elf_set_mach_from_flags (abfd)
+     bfd *abfd;
+{
+  flagword flags = elf_elfheader (abfd)->e_flags;
 
-  return true;
+  switch (flags & EF_M68HC11_MACH_MASK)
+    {
+    case EF_M68HC12_MACH:
+      bfd_default_set_arch_mach (abfd, bfd_arch_m68hc12, bfd_mach_m6812);
+      break;
+    case EF_M68HCS12_MACH:
+      bfd_default_set_arch_mach (abfd, bfd_arch_m68hc12, bfd_mach_m6812s);
+      break;
+    case EF_M68HC11_GENERIC:
+      bfd_default_set_arch_mach (abfd, bfd_arch_m68hc12,
+                                 bfd_mach_m6812_default);
+      break;
+    default:
+      return FALSE;
+    }
+  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_m68hc12
 #define ELF_MACHINE_CODE       EM_68HC12
 #define ELF_MAXPAGESIZE                0x1000
@@ -782,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_object_p   0
+#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"