Add support for PIC and APCS-FLOAT type binaries.
authorNick Clifton <nickc@redhat.com>
Sat, 22 Nov 1997 23:17:39 +0000 (23:17 +0000)
committerNick Clifton <nickc@redhat.com>
Sat, 22 Nov 1997 23:17:39 +0000 (23:17 +0000)
bfd/ChangeLog
bfd/coff-arm.c

index 288bdaa30eff3be660460887ac0b3b7cbbf4a700..d5460a6ed637bbd029dc14e9ed91e6aaa2bd6bd3 100644 (file)
@@ -1,3 +1,9 @@
+Sat Nov 22 15:16:00 1997  Nick Clifton  <nickc@cygnus.com>
+
+       * coff-arm.c: Add support for PIC and APCS-FLOAT type binaries.
+
+       * coffcode.h: Add support for PIC and APCS-FLOAT type binaries.
+
 Sat Nov 22 16:06:56 1997  Klaus Kaempf  <kkaempf@progis.de>
 
        * evax-emh.c (_bfd_evax_write_emh): Use alloca instead of strdup.
index 432d363cf19ecb224adb6be1176a3be25dd03c17..b167764659fc57ddb25836f53f7b4919d70d0934 100644 (file)
@@ -32,6 +32,24 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include "libcoff.h"
 
+#define APCS_26_FLAG(       abfd )     (coff_data (abfd)->flags & F_APCS_26)
+#define APCS_FLOAT_FLAG(    abfd )     (coff_data (abfd)->flags & F_APCS_FLOAT)
+#define PIC_FLAG(           abfd )     (coff_data (abfd)->flags & F_PIC)
+#define APCS_SET(           abfd )     (coff_data (abfd)->flags & F_APCS_SET)
+#define SET_APCS_FLAGS(     abfd, flgs)        (coff_data (abfd)->flags = \
+                                       (coff_data (abfd)->flags & ~ (F_APCS_26 | F_APCS_FLOAT | F_PIC)) \
+                                        | (flgs | F_APCS_SET))
+#define INTERWORK_FLAG(     abfd )     (coff_data (abfd)->flags & F_INTERWORK)
+#define INTERWORK_SET(      abfd )     (coff_data (abfd)->flags & F_INTERWORK_SET)
+#define SET_INTERWORK_FLAG( abfd, flg )        (coff_data (abfd)->flags = \
+                                       (coff_data (abfd)->flags & ~ F_INTERWORK) \
+                                        | (flg | F_INTERWORK_SET))
+
+
+static boolean coff_arm_relocate_section
+  PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
+           struct internal_reloc *, struct internal_syment *, asection **));
+
 static bfd_reloc_status_type
 aoutarm_fix_pcrel_26_done PARAMS ((bfd *, arelent *, asymbol *, PTR,
                                  asection *, bfd *, char **));
@@ -40,12 +58,38 @@ static bfd_reloc_status_type
 aoutarm_fix_pcrel_26 PARAMS ((bfd *, arelent *, asymbol *, PTR,
                             asection *, bfd *, char **));
 
+static bfd_reloc_status_type
+coff_thumb_pcrel_23 PARAMS ((bfd *, arelent *, asymbol *, PTR,
+                             asection *, bfd *, char **));
+
+static bfd_reloc_status_type
+coff_thumb_pcrel_12 PARAMS ((bfd *, arelent *, asymbol *, PTR,
+                             asection *, bfd *, char **));
+
+static bfd_reloc_status_type
+coff_thumb_pcrel_9 PARAMS ((bfd *, arelent *, asymbol *, PTR,
+                            asection *, bfd *, char **));
+
+static bfd_reloc_status_type
+coff_arm_reloc PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *,
+                       char **));
 
-static bfd_reloc_status_type coff_arm_reloc 
-  PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
-static boolean coff_arm_adjust_symndx
-  PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *,
-          struct internal_reloc *, boolean *));
+static boolean
+coff_arm_adjust_symndx PARAMS ((bfd *, struct bfd_link_info *, bfd *,
+                               asection *, struct internal_reloc *,
+                               boolean *));
+
+/* The linker script knows the section names for placement.
+   The entry_names are used to do simple name mangling on the stubs.
+   Given a function name, and its type, the stub can be found. The
+   name can be changed. The only requirement is the %s be present.
+   */
+   
+#define THUMB2ARM_GLUE_SECTION_NAME ".glue_7t"
+#define THUMB2ARM_GLUE_ENTRY_NAME   "__%s_from_thumb"
+
+#define ARM2THUMB_GLUE_SECTION_NAME ".glue_7"
+#define ARM2THUMB_GLUE_ENTRY_NAME   "__%s_from_arm"
 
 /* Used by the assembler. */
 static bfd_reloc_status_type
@@ -108,27 +152,48 @@ coff_arm_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd,
   return bfd_reloc_continue;
 }
 
+#define TARGET_UNDERSCORE '_'
+
 #ifndef PCRELOFFSET
 #define PCRELOFFSET true
 #endif
 
+/* These most certainly belong somewhere else. Just had to get rid of
+   the manifest constants in the code. */
+
+#define ARM_8        0
+#define ARM_16       1
+#define ARM_32       2
+#define ARM_26       3
+#define ARM_DISP8    4
+#define ARM_DISP16   5
+#define ARM_DISP32   6
+#define ARM_26D      7
+/* 8 is unused */
+#define ARM_NEG16    9
+#define ARM_NEG32   10
+#define ARM_RVA32   11
+#define ARM_THUMB9  12
+#define ARM_THUMB12 13
+#define ARM_THUMB23 14
+
 static reloc_howto_type aoutarm_std_reloc_howto[] = 
 {
   /* type              rs size bsz  pcrel bitpos ovrf                     sf name     part_inpl readmask  setmask    pcdone */
-  HOWTO(0,                     /* type */
-       0,                      /* rs */
+  HOWTO(ARM_8,                 /* type */
+       0,                      /* rightshift */
        0,                      /* size */
-       8,                      /* bsz */
-       false,                  /* pcrel */
+       8,                      /* bitsize */
+       false,                  /* pc_relative */
        0,                      /* bitpos */
-       complain_overflow_bitfield, /* ovf */
-       coff_arm_reloc,         /* sf */
-       "8",                    /*name */
-        true,                  /* partial */
-       0x000000ff,             /*read mask */
-       0x000000ff,             /* setmask */
-       PCRELOFFSET             /* pcdone */),
-  HOWTO(1,  
+       complain_overflow_bitfield, /* complain_on_overflow */
+       coff_arm_reloc,         /* special_function */
+       "ARM_8",                /* name */
+        true,                  /* partial_inplace */
+       0x000000ff,             /* src_mask */
+       0x000000ff,             /* dst_mask */
+       PCRELOFFSET             /* pcrel_offset */),
+  HOWTO(ARM_16,  
        0, 
        1, 
        16, 
@@ -136,12 +201,12 @@ static reloc_howto_type aoutarm_std_reloc_howto[] =
        0,
        complain_overflow_bitfield,
        coff_arm_reloc,
-       "16", 
+       "ARM_16", 
        true,
        0x0000ffff,
        0x0000ffff, 
        PCRELOFFSET),
-  HOWTO( 2, 
+  HOWTO(ARM_32, 
        0,
        2, 
        32,
@@ -149,12 +214,12 @@ static reloc_howto_type aoutarm_std_reloc_howto[] =
        0,
        complain_overflow_bitfield,
        coff_arm_reloc,
-       "32",
+       "ARM_32",
         true,
        0xffffffff,
        0xffffffff,
        PCRELOFFSET),
-  HOWTO( 3,
+  HOWTO(ARM_26,
        2,
        2,
        26,
@@ -162,12 +227,12 @@ static reloc_howto_type aoutarm_std_reloc_howto[] =
        0,
        complain_overflow_signed,
        aoutarm_fix_pcrel_26 ,
-       "ARM26",
+       "ARM_26",
        false,
        0x00ffffff,
        0x00ffffff, 
        PCRELOFFSET),
-  HOWTO( 4,        
+  HOWTO(ARM_DISP8,        
        0,
        0,
        8, 
@@ -175,12 +240,12 @@ static reloc_howto_type aoutarm_std_reloc_howto[] =
        0,
        complain_overflow_signed, 
        coff_arm_reloc,
-       "DISP8",  
+       "ARM_DISP8",  
        true,
        0x000000ff,
        0x000000ff,
        true),
-  HOWTO( 5
+  HOWTO( ARM_DISP16
        0,
        1,
        16,
@@ -188,12 +253,12 @@ static reloc_howto_type aoutarm_std_reloc_howto[] =
        0,
        complain_overflow_signed, 
        coff_arm_reloc,
-       "DISP16",
+       "ARM_DISP16",
        true,
        0x0000ffff,
        0x0000ffff,
        true),
-  HOWTO( 6,
+  HOWTO( ARM_DISP32,
        0,
        2,
        32,
@@ -201,12 +266,12 @@ static reloc_howto_type aoutarm_std_reloc_howto[] =
        0,
        complain_overflow_signed, 
        coff_arm_reloc,
-       "DISP32",
+       "ARM_DISP32",
        true,
        0xffffffff,
        0xffffffff,
        true),
-  HOWTO( 7,  
+  HOWTO( ARM_26D,  
        2, 
        2,
        26,
@@ -214,13 +279,14 @@ static reloc_howto_type aoutarm_std_reloc_howto[] =
        0,
        complain_overflow_signed,
        aoutarm_fix_pcrel_26_done, 
-       "ARM26D",
+       "ARM_26D",
        true,
        0x00ffffff,
        0x0,
        false),
+  /* 8 is unused */
   {-1},
-  HOWTO( 9,
+  HOWTO( ARM_NEG16,
        0,
        -1,
        16,
@@ -228,12 +294,12 @@ static reloc_howto_type aoutarm_std_reloc_howto[] =
        0, 
        complain_overflow_bitfield,
        coff_arm_reloc,
-       "NEG16",
+       "ARM_NEG16",
         true, 
        0x0000ffff,
        0x0000ffff, 
        false),
-  HOWTO( 10
+  HOWTO( ARM_NEG32
        0, 
        -2,
        32,
@@ -241,12 +307,12 @@ static reloc_howto_type aoutarm_std_reloc_howto[] =
        0,
        complain_overflow_bitfield,
        coff_arm_reloc,
-       "NEG32",
+       "ARM_NEG32",
         true,
        0xffffffff,
        0xffffffff,
        false),
-  HOWTO( 11
+  HOWTO( ARM_RVA32
        0,
        2, 
        32,
@@ -254,11 +320,50 @@ static reloc_howto_type aoutarm_std_reloc_howto[] =
        0,
        complain_overflow_bitfield,
        coff_arm_reloc,
-       "rva32",
+       "ARM_RVA32",
         true,
        0xffffffff,
        0xffffffff,
        PCRELOFFSET),
+  HOWTO( ARM_THUMB9,
+       1,
+       1,
+       9,
+       true,
+       0,
+       complain_overflow_signed,
+       coff_thumb_pcrel_9 ,
+       "ARM_THUMB9",
+       false,
+       0x000000ff,
+       0x000000ff, 
+       PCRELOFFSET),
+  HOWTO( ARM_THUMB12,
+       1,
+       1,
+       12,
+       true,
+       0,
+       complain_overflow_signed,
+       coff_thumb_pcrel_12 ,
+       "ARM_THUMB12",
+       false,
+       0x000007ff,
+       0x000007ff, 
+       PCRELOFFSET),
+  HOWTO( ARM_THUMB23,
+       1,
+       2,
+       23,
+       true,
+       0,
+       complain_overflow_signed,
+       coff_thumb_pcrel_23 ,
+       "ARM_THUMB23",
+       false,
+       0x07ff07ff,
+       0x07ff07ff, 
+       PCRELOFFSET),
 };
 #ifdef COFF_WITH_PE
 /* Return true if this relocation should
@@ -291,7 +396,7 @@ coff_arm_rtype_to_howto (abfd, sec, rel, h, sym, addendp)
 
   howto = aoutarm_std_reloc_howto + rel->r_type;
 
-  if (rel->r_type == 11)
+  if (rel->r_type == ARM_RVA32)
     {
       *addendp -= pe_data(sec->output_section->owner)->pe_opthdr.ImageBase;
     }
@@ -301,14 +406,14 @@ coff_arm_rtype_to_howto (abfd, sec, rel, h, sym, addendp)
      ARM26 to ARM26D relocs if possible.  We return a fake howto in
      this case without pcrel_offset set, and adjust the addend to
      compensate.  */
-  if (rel->r_type == 3
+  if (rel->r_type == ARM_26
       && h != NULL
       && (h->root.type == bfd_link_hash_defined
          || h->root.type == bfd_link_hash_defweak)
       && h->root.u.def.section->output_section == sec->output_section)
     {
       static reloc_howto_type fake_arm26_reloc = 
-       HOWTO (3,
+       HOWTO (ARM_26,
               2,
               2,
               26,
@@ -316,14 +421,14 @@ coff_arm_rtype_to_howto (abfd, sec, rel, h, sym, addendp)
               0,
               complain_overflow_signed,
               aoutarm_fix_pcrel_26 ,
-              "ARM26",
+              "ARM_26",
               false,
               0x00ffffff,
               0x00ffffff, 
               false);
 
       *addendp -= rel->r_vaddr - sec->vma;
-      return &fake_arm26_reloc;
+      return & fake_arm26_reloc;
     }
 
   return howto;
@@ -384,6 +489,7 @@ aoutarm_fix_pcrel_26 (abfd, reloc_entry, symbol, data, input_section,
   relocation -= input_section->output_section->vma;
   relocation -= input_section->output_offset;
   relocation -= addr;
+  
   if (relocation & 3)
     return bfd_reloc_overflow;
 
@@ -402,11 +508,178 @@ aoutarm_fix_pcrel_26 (abfd, reloc_entry, symbol, data, input_section,
 
   /* Now the ARM magic... Change the reloc type so that it is marked as done.
      Strictly this is only necessary if we are doing a partial relocation.  */
-  reloc_entry->howto = &aoutarm_std_reloc_howto[7];
+  reloc_entry->howto = &aoutarm_std_reloc_howto[ARM_26D];
+
+  return flag;
+}
+
+typedef enum {bunknown, b9, b12, b23} thumb_pcrel_branchtype;
+
+static bfd_reloc_status_type
+coff_thumb_pcrel_common (abfd, reloc_entry, symbol, data, input_section,
+                    output_bfd, error_message, btype)
+     bfd *abfd;
+     arelent *reloc_entry;
+     asymbol *symbol;
+     PTR data;
+     asection *input_section;
+     bfd *output_bfd;
+     char **error_message;
+     thumb_pcrel_branchtype btype;
+{
+  bfd_vma relocation;
+  bfd_size_type addr = reloc_entry->address;
+  long target = bfd_get_32 (abfd, (bfd_byte *) data + addr);
+  bfd_reloc_status_type flag = bfd_reloc_ok;
+  bfd_vma dstmsk;
+  bfd_vma offmsk;
+  bfd_vma signbit;
+
+  /* NOTE: This routine is currently used by GAS, but not by the link
+     phase.  */
+
+  switch (btype)
+    {
+    case b9:
+      dstmsk  = 0x000000ff;
+      offmsk  = 0x000001fe;
+      signbit = 0x00000100;
+      break;
+
+    case b12:
+      dstmsk  = 0x000007ff;
+      offmsk  = 0x00000ffe;
+      signbit = 0x00000800;
+      break;
+
+    case b23:
+      dstmsk  = 0x07ff07ff;
+      offmsk  = 0x007fffff;
+      signbit = 0x00400000;
+      break;
+
+    default:
+      abort ();
+    }
+  
+  /* If this is an undefined symbol, return error */
+  if (symbol->section == &bfd_und_section
+      && (symbol->flags & BSF_WEAK) == 0)
+    return output_bfd ? bfd_reloc_continue : bfd_reloc_undefined;
+
+  /* If the sections are different, and we are doing a partial relocation,
+     just ignore it for now.  */
+  if (symbol->section->name != input_section->name
+      && output_bfd != (bfd *)NULL)
+    return bfd_reloc_continue;
+
+  switch (btype)
+    {
+    case b9:
+    case b12:
+      relocation = ((target & dstmsk) << 1);
+      break;
+
+    case b23:
+      if (bfd_big_endian (abfd))
+       relocation = ((target & 0x7ff) << 1)  | ((target & 0x07ff0000) >> 4);
+      else
+       relocation = ((target & 0x7ff) << 12) | ((target & 0x07ff0000) >> 15);
+      break;
+    }
+
+  relocation = (relocation ^ signbit) - signbit; /* Sign extend */
+  relocation += symbol->value;
+  relocation += symbol->section->output_section->vma;
+  relocation += symbol->section->output_offset;
+  relocation += reloc_entry->addend;
+  relocation -= input_section->output_section->vma;
+  relocation -= input_section->output_offset;
+  relocation -= addr;
+
+  if (relocation & 1)
+    return bfd_reloc_overflow;
+
+  /* Check for overflow */
+  if (relocation & signbit)
+    {
+      if ((relocation & ~offmsk) != ~offmsk)
+       flag = bfd_reloc_overflow;
+    }
+  else if (relocation & ~offmsk)
+    flag = bfd_reloc_overflow;
+
+  target &= ~dstmsk;
+  switch (btype)
+   {
+   case b9:
+   case b12:
+     target |= (relocation >> 1);
+     break;
+
+   case b23:
+     if (bfd_big_endian (abfd))
+       target |= ((relocation & 0xfff) >> 1)  | ((relocation << 4)  & 0x07ff0000);
+     else
+       target |= ((relocation & 0xffe) << 15) | ((relocation >> 12) & 0x7ff);
+     break;
+   }
 
+  bfd_put_32 (abfd, target, (bfd_byte *) data + addr);
+
+  /* Now the ARM magic... Change the reloc type so that it is marked as done.
+     Strictly this is only necessary if we are doing a partial relocation.  */
+  reloc_entry->howto = & aoutarm_std_reloc_howto [ARM_26D];
+  
+  /* TODO: We should possibly have DONE entries for the THUMB PCREL relocations */
   return flag;
 }
 
+static bfd_reloc_status_type
+coff_thumb_pcrel_23 (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;
+{
+  return coff_thumb_pcrel_common (abfd, reloc_entry, symbol, data,
+                                  input_section, output_bfd, error_message, b23);
+}
+
+static bfd_reloc_status_type
+coff_thumb_pcrel_12 (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;
+{
+  return coff_thumb_pcrel_common (abfd, reloc_entry, symbol, data,
+                                  input_section, output_bfd, error_message, b12);
+}
+
+static bfd_reloc_status_type
+coff_thumb_pcrel_9 (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;
+{
+  return coff_thumb_pcrel_common (abfd, reloc_entry, symbol, data,
+                                  input_section, output_bfd, error_message, b9);
+}
+
 
 static CONST struct reloc_howto_struct *
 arm_reloc_type_lookup(abfd,code)
@@ -425,13 +698,17 @@ arm_reloc_type_lookup(abfd,code)
 
   switch (code)
     {
-      ASTD (BFD_RELOC_16, 1);
-      ASTD (BFD_RELOC_32, 2);
-      ASTD (BFD_RELOC_ARM_PCREL_BRANCH, 3);
-      ASTD (BFD_RELOC_8_PCREL, 4);
-      ASTD (BFD_RELOC_16_PCREL, 5);
-      ASTD (BFD_RELOC_32_PCREL, 6);
-      ASTD (BFD_RELOC_RVA, 11);
+      ASTD (BFD_RELOC_8,                    ARM_8);
+      ASTD (BFD_RELOC_16,                   ARM_16);
+      ASTD (BFD_RELOC_32,                   ARM_32);
+      ASTD (BFD_RELOC_ARM_PCREL_BRANCH,     ARM_26);
+      ASTD (BFD_RELOC_8_PCREL,              ARM_DISP8);
+      ASTD (BFD_RELOC_16_PCREL,             ARM_DISP16);
+      ASTD (BFD_RELOC_32_PCREL,             ARM_DISP32);
+      ASTD (BFD_RELOC_RVA,                  ARM_RVA32);
+      ASTD (BFD_RELOC_THUMB_PCREL_BRANCH9,  ARM_THUMB9);
+      ASTD (BFD_RELOC_THUMB_PCREL_BRANCH12, ARM_THUMB12);
+      ASTD (BFD_RELOC_THUMB_PCREL_BRANCH23, ARM_THUMB23);
     default: return (CONST struct reloc_howto_struct *) 0;
     }
 }
@@ -447,175 +724,1293 @@ arm_reloc_type_lookup(abfd,code)
 #define BADMAG(x) ARMBADMAG(x)
 #define ARM 1                  /* Customize coffcode.h */
 
+/* The set of global variables that mark the total size of each kind
+   of glue required. */
+long int global_thumb_glue_size = 0;
+long int global_arm_glue_size = 0;
+
+bfd* bfd_of_glue_owner = 0;
+
+/* some typedefs for holding instructions */
+typedef unsigned long int insn32;
+typedef unsigned short int insn16;
+
+\f
+/* The thumb form of a long branch is a bit finicky, because the offset
+   encoding is split over two fields, each in it's own instruction. They
+   can occur in any order. So given a thumb form of long branch, and an 
+   offset, insert the offset into the thumb branch and return finished
+   instruction. 
+
+   It takes two thumb instructions to encode the target address. Each has 
+   11 bits to invest. The upper 11 bits are stored in one (identifed by
+   H-0.. see below), the lower 11 bits are stored in the other (identified 
+   by H-1). 
+
+   Combine together and shifted left by 1 (it's a half word address) and 
+   there you have it.
+
+     Op: 1111 = F,
+     H-0, upper address-0 = 000
+     Op: 1111 = F,
+     H-1, lower address-0 = 800
+
+   They can be ordered either way, but the arm tools I've seen always put 
+   the lower one first. It probably doesn't matter. krk@cygnus.com
+
+   XXX:  Actually the order does matter.  The second instruction (H-1)
+   moves the computed address into the PC, so it must be the second one
+   in the sequence.  The problem, however is that whilst little endian code
+   stores the instructions in HI then LOW order, big endian code does the
+   reverse.  nickc@cygnus.com  */
+
+#define LOW_HI_ORDER 0xF800F000
+#define HI_LOW_ORDER 0xF000F800
+
+static insn32
+insert_thumb_branch (br_insn, rel_off)
+     insn32 br_insn;
+     int rel_off;
+{
+  unsigned int low_bits;
+  unsigned int high_bits;
 
-/* We use the special COFF backend linker.  */
-#define coff_relocate_section _bfd_coff_generic_relocate_section
-
-/* When doing a relocateable link, we want to convert ARM26 relocs
-   into ARM26D relocs.  */
 
-static boolean
-coff_arm_adjust_symndx (obfd, info, ibfd, sec, irel, adjustedp)
-     bfd *obfd;
-     struct bfd_link_info *info;
-     bfd *ibfd;
-     asection *sec;
-     struct internal_reloc *irel;
-     boolean *adjustedp;
-{
-  if (irel->r_type == 3)
-    {
-      struct coff_link_hash_entry *h;
+  BFD_ASSERT((rel_off & 1) != 1);
 
-      h = obj_coff_sym_hashes (ibfd)[irel->r_symndx];
-      if (h != NULL
-         && (h->root.type == bfd_link_hash_defined
-             || h->root.type == bfd_link_hash_defweak)
-         && h->root.u.def.section->output_section == sec->output_section)
-       irel->r_type = 7;
-    }
-  *adjustedp = false;
-  return true;
-}
+  rel_off >>= 1;                              /* half word aligned address */
+  low_bits = rel_off & 0x000007FF;            /* the bottom 11 bits */
+  high_bits = ( rel_off >> 11 ) & 0x000007FF; /* the top 11 bits */
 
+  if ((br_insn & LOW_HI_ORDER) == LOW_HI_ORDER)
+    br_insn = LOW_HI_ORDER | (low_bits << 16) | high_bits;
+  else if ((br_insn & HI_LOW_ORDER) == HI_LOW_ORDER)
+    br_insn = HI_LOW_ORDER | (high_bits << 16) | low_bits;
+  else
+    abort(); /* error - not a valid branch instruction form */
 
-#define APCS_FLAG( abfd )              (coff_data (abfd)->flags & F_APCS_26)
-#define APCS_SET(  abfd )              (coff_data (abfd)->flags & F_APCS_SET)
-#define SET_APCS_FLAG( abfd, flg )     (coff_data (abfd)->flags = (coff_data (abfd)->flags & ~ F_APCS_26) | (flg | F_APCS_SET))
+  /* FIXME: abort is probably not the right call. krk@cygnus.com */
 
-/* Called when merging the private data areas of two BFDs.
-   This is important as it allows us to detect if we are
-   attempting to merge binaries compiled for different ARM
-   targets, eg different CPUs or differents APCS's.     */
+  return br_insn;
+}
 
-static boolean
-coff_arm_bfd_merge_private_bfd_data (ibfd, obfd)
-     bfd *   ibfd;
-     bfd *   obfd;
+\f
+static struct coff_link_hash_entry *
+find_thumb_glue (info, name, input_bfd)
+     struct bfd_link_info * info;
+     char *                 name;
+     bfd *                  input_bfd;
 {
-  BFD_ASSERT (ibfd != NULL && obfd != NULL);
+  char *tmp_name = 0;
 
-  if (ibfd == obfd)
-    return true;
+  struct coff_link_hash_entry *myh = 0;
 
-  /* If the two formats are different we cannot check anything */
-  if (ibfd->xvec != obfd->xvec)
-    return true;
+  tmp_name = ((char *)
+        bfd_malloc (strlen (name) + strlen (THUMB2ARM_GLUE_ENTRY_NAME)));
 
-  /* Verify that the APCS is the same for the two BFDs */
-  if (APCS_SET (ibfd))
-    {
-      if (APCS_SET (obfd))
-       {
-         /* If the src and dest have different APCS flag bits set, fail */
-         if (APCS_FLAG (obfd) != APCS_FLAG (ibfd))
-           {
-             _bfd_error_handler
-               ("%s: ERROR: compiled for APCS-%d whereas target %s uses APCS-%d",
-                bfd_get_filename (ibfd), APCS_FLAG (ibfd) ? 26 : 32,
-                bfd_get_filename (obfd), APCS_FLAG (obfd) ? 26 : 32
-                );
+  BFD_ASSERT (tmp_name);
 
-             bfd_set_error (bfd_error_wrong_format);
-             return false;
-           }
-       }
-      else
-       {
-         SET_APCS_FLAG (obfd, APCS_FLAG (ibfd));
-         
-         /* Set up the arch and fields as well as these are probably wrong */
-         bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), bfd_get_mach (ibfd));
-       }
+  sprintf (tmp_name, THUMB2ARM_GLUE_ENTRY_NAME, name);
+  
+  myh = coff_link_hash_lookup (coff_hash_table (info),
+                              tmp_name,
+                              false, false, true);
+  if (myh == NULL)
+    {
+      _bfd_error_handler ("%s: unable to find THUMB glue '%s' for `%s'",
+                         bfd_get_filename (input_bfd), tmp_name, name);
     }
   
-  return true;
-}
-
+  free (tmp_name);
 
-/* Display the flags field */
-
-static boolean
-coff_arm_bfd_print_private_bfd_data (abfd, ptr)
-     bfd *   abfd;
-     PTR     ptr;
-{
-  FILE * file = (FILE *) ptr;
-  
-  BFD_ASSERT (abfd != NULL && ptr != NULL)
-  
-  fprintf (file, "private flags = %x", coff_data( abfd )->flags);
-  
-  if (APCS_SET (abfd))
-    fprintf (file, ": [APCS-%d]", APCS_FLAG( abfd ) ? 26 : 32);
-  
-  fputc ('\n', file);
-  
-  return true;
+  return myh;
 }
 
-
-/* Copies the given flags into the coff_tdata.flags field.
-   Typically these flags come from the f_flags[] field of
-   the COFF filehdr structure, which contains important,
-   target specific information.                       */
-
-boolean
-coff_arm_bfd_set_private_flags (abfd, flags)
-       bfd *      abfd;
-       flagword   flags;
+static struct coff_link_hash_entry *
+find_arm_glue (info, name, input_bfd)
+     struct bfd_link_info * info;
+     char *                 name;
+     bfd *                  input_bfd;
 {
-  int flag;
+  char *tmp_name = 0;
+  struct coff_link_hash_entry *myh = 0;
 
-  BFD_ASSERT (abfd != NULL);
+  tmp_name = ((char *)
+             bfd_malloc (strlen (name) + strlen (ARM2THUMB_GLUE_ENTRY_NAME)));
 
-  flag = (flags & F_APCS26) ? F_APCS_26 : 0;
+  BFD_ASSERT (tmp_name);
 
-  /* Make sure that the APCS field has not been initialised to the opposite value */
-  if (APCS_SET (abfd) && (APCS_FLAG (abfd) != flag))
-    return false;
+  sprintf (tmp_name, ARM2THUMB_GLUE_ENTRY_NAME, name);
+  
+  myh = coff_link_hash_lookup (coff_hash_table (info),
+                              tmp_name,
+                              false, false, true);
 
-  SET_APCS_FLAG (abfd, flag);
+  if (myh == NULL)
+    {
+      _bfd_error_handler ("%s: unable to find ARM glue '%s' for `%s'",
+                         bfd_get_filename (input_bfd), tmp_name, name);
+    }
   
-  return true;
-}
+  free (tmp_name);
 
+  return myh;
+}
 
-/* Copy the important parts of the target specific data
-   from one instance of a BFD to another.            */
+/*
+  ARM->Thumb glue:
+
+       .arm
+       __func_from_arm:
+            ldr r12, __func_addr
+            bx  r12
+       __func_addr:
+            .word func    @ behave as if you saw a ARM_32 reloc
+
+   ldr ip,8 <__func_addr> e59fc000
+   bx  ip                 e12fff1c
+   .word func             00000001
+
+*/
+
+#define ARM2THUMB_GLUE_SIZE 12
+static insn32 
+        a2t1_ldr_insn = 0xe59fc000,
+       a2t2_bx_r12_insn = 0xe12fff1c,
+       a2t3_func_addr_insn = 0x00000001;
+
+/*
+   Thumb->ARM:
+
+   .thumb
+   .align 2
+      __func_from_thumb:
+          bx pc
+          nop
+   .arm
+      __func_change_to_arm:
+           b func
+
+
+   bx  pc    4778
+   nop       0000
+   b   func  eafffffe
+
+*/
+
+#define THUMB2ARM_GLUE_SIZE 8
+static insn16
+        t2a1_bx_pc_insn    = 0x4778,
+        t2a2_noop_insn     = 0x46c0;
+static insn32
+        t2a3_b_insn        = 0xea000000;
+
+/* TODO:
+     We should really create new local (static) symbols in destination
+     object for each stub we create.  We should also create local
+     (static) symbols within the stubs when switching between ARM and
+     Thumb code.  This will ensure that the debugger and disassembler
+     can present a better view of stubs.
+
+     We can treat stubs like literal sections, and for the THUMB9 ones
+     (short addressing range) we should be able to insert the stubs
+     between sections. i.e. the simplest approach (since relocations
+     are done on a section basis) is to dump the stubs at the end of
+     processing a section. That way we can always try and minimise the
+     offset to and from a stub. However, this does not map well onto
+     the way that the linker/BFD does its work: mapping all input
+     sections to output sections via the linker script before doing
+     all the processing.
+
+     Unfortunately it may be easier to just to disallow short range
+     Thumb->ARM stubs (i.e. no conditional inter-working branches,
+     only branch-and-link (BL) calls.  This will simplify the processing
+     since we can then put all of the stubs into their own section.
+
+  TODO:
+     On a different subject, rather than complaining when a
+     branch cannot fit in the number of bits available for the
+     instruction we should generate a trampoline stub (needed to
+     address the complete 32bit address space).  */
+
+/* The standard COFF backend linker does not cope with the special 
+   Thumb BRANCH23 relocation.  The alternative would be to split the
+   BRANCH23 into seperate HI23 and LO23 relocations. However, it is a
+   bit simpler simply providing our own relocation driver. */
+
+/* The reloc processing routine for the ARM/Thumb COFF linker.  NOTE:
+   This code is a very slightly modified copy of
+   _bfd_coff_generic_relocate_section.  It would be a much more
+   maintainable solution to have a MACRO that could be expanded within
+   _bfd_coff_generic_relocate_section that would only be provided for
+   ARM/Thumb builds.  It is only the code marked THUMBEXTENSION that
+   is different from the original.  */
 
 static boolean
-coff_arm_bfd_copy_private_bfd_data (src, dest)
-     bfd *  src;
-     bfd *  dest;
+coff_arm_relocate_section (output_bfd, info, input_bfd, input_section,
+                           contents, relocs, syms, sections)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+     bfd *input_bfd;
+     asection *input_section;
+     bfd_byte *contents;
+     struct internal_reloc *relocs;
+     struct internal_syment *syms;
+     asection **sections;
 {
-  BFD_ASSERT (src != NULL && dest != NULL);
-
-  if (src == dest)
-    return true;
+  struct internal_reloc *rel;
+  struct internal_reloc *relend;
 
-  /* If the destination is not in the same format as the source, do not do the copy */
-  if (src->xvec != dest->xvec)
-    return true;
+  rel = relocs;
+  relend = rel + input_section->reloc_count;
 
-  /* copy the flags field */
-  if (APCS_SET (src))
+  for (; rel < relend; rel++)
     {
-      if (APCS_SET (dest))
+      int done = 0;
+      long symndx;
+      struct coff_link_hash_entry *h;
+      struct internal_syment *sym;
+      bfd_vma addend;
+      bfd_vma val;
+      reloc_howto_type *howto;
+      bfd_reloc_status_type rstat;
+      bfd_vma h_val;
+
+      symndx = rel->r_symndx;
+
+      if (symndx == -1)
        {
-         /* If the src and dest have different APCS flag bits set, fail */
-         if (APCS_FLAG (dest) != APCS_FLAG (src))
-           return false;
+         h = NULL;
+         sym = NULL;
        }
       else
-       SET_APCS_FLAG (dest, APCS_FLAG (src));
-    }
+       {    
+         h = obj_coff_sym_hashes (input_bfd)[symndx];
+         sym = syms + symndx;
+       }
 
-  return true;
-}
+      /* COFF treats common symbols in one of two ways.  Either the
+         size of the symbol is included in the section contents, or it
+         is not.  We assume that the size is not included, and force
+         the rtype_to_howto function to adjust the addend as needed.  */
 
+      if (sym != NULL && sym->n_scnum != 0)
+       addend = - sym->n_value;
+      else
+       addend = 0;
+
+
+      howto = bfd_coff_rtype_to_howto (input_bfd, input_section, rel, h,
+                                      sym, &addend);
+      if (howto == NULL)
+       return false;
+
+      /* If we are doing a relocateable link, then we can just ignore
+         a PC relative reloc that is pcrel_offset.  It will already
+         have the correct value.  If this is not a relocateable link,
+         then we should ignore the symbol value.  */
+      if (howto->pc_relative && howto->pcrel_offset)
+        {
+          if (info->relocateable)
+            continue;
+          if (sym != NULL && sym->n_scnum != 0)
+            addend += sym->n_value;
+        }
+
+      /* If we are doing a relocateable link, then we can just ignore
+         a PC relative reloc that is pcrel_offset.  It will already
+         have the correct value.  */
+      if (info->relocateable
+          && howto->pc_relative
+          && howto->pcrel_offset)
+        continue;
+
+      val = 0;
+
+      if (h == NULL)
+       {
+         asection *sec;
 
-#define coff_adjust_symndx                     coff_arm_adjust_symndx
+         if (symndx == -1)
+           {
+             sec = bfd_abs_section_ptr;
+             val = 0;
+           }
+         else
+           {
+             sec = sections[symndx];
+              val = (sec->output_section->vma
+                    + sec->output_offset
+                    + sym->n_value
+                    - sec->vma);
+           }
+       }
+      else
+       {
+#if 1 /* THUMBEXTENSION */
+          /* We don't output the stubs if we are generating a
+             relocatable output file, since we may as well leave the
+             stub generation to the final linker pass. If we fail to
+            verify that the name is defined, we'll try to build stubs
+            for an undefined name... */
+          if (! info->relocateable
+             && (   h->root.type == bfd_link_hash_defined
+                 || h->root.type == bfd_link_hash_defweak))
+            {
+             asection *   sec;
+             asection *   h_sec = h->root.u.def.section;
+             const char * name  = h->root.root.string;
+             
+             /* h locates the symbol referenced in the reloc.  */
+             h_val = (h->root.u.def.value
+                      + h_sec->output_section->vma
+                      + h_sec->output_offset);
+
+              if (howto->type == ARM_26)
+                {
+                  if (   h->class == C_THUMBSTATFUNC
+                     || h->class == C_THUMBEXTFUNC)
+                   {
+                     /* Arm code calling a Thumb function */
+                     signed long int                final_disp;
+                     unsigned long int              tmp;
+                     long int                       my_offset;
+                     long int                       offset;
+                     asection *                     s = 0;
+                     unsigned long int              return_address;
+                     long int                       ret_offset;
+                     long int                       disp;
+                     struct coff_link_hash_entry *  myh; 
+
+                     myh = find_arm_glue (info, name, input_bfd);
+                     if (myh == NULL)
+                       return false;
+
+                     my_offset = myh->root.u.def.value;
+
+                     s = bfd_get_section_by_name (bfd_of_glue_owner, 
+                                                 ARM2THUMB_GLUE_SECTION_NAME);
+                     BFD_ASSERT (s != NULL);
+                     BFD_ASSERT (s->contents != NULL);
+
+                     if ((my_offset & 0x01) == 0x01)
+                       {
+                         if (INTERWORK_SET (h_sec->owner) && ! INTERWORK_FLAG (h_sec->owner))
+                           _bfd_error_handler ("%s: warning: interworking not enabled.",
+                                               bfd_get_filename (h_sec->owner));
+                         
+                         --my_offset;
+                         myh->root.u.def.value = my_offset;
+
+                         bfd_put_32 (output_bfd, a2t1_ldr_insn, s->contents + my_offset);
+                         
+                         bfd_put_32 (output_bfd, a2t2_bx_r12_insn, s->contents + my_offset + 4);
+                         
+                         /* It's a thumb address.  Add the low order bit.  */
+                         bfd_put_32 (output_bfd, h_val | a2t3_func_addr_insn, s->contents + my_offset + 8);
+                       }
+
+                     BFD_ASSERT (my_offset <= global_arm_glue_size);
+
+                     tmp = bfd_get_32 (input_bfd, contents + rel->r_vaddr - input_section->vma);
+                     
+                     tmp = tmp & 0xFF000000;
+                     
+                     /* Somehow these are both 4 too far, so subtract 8. */
+                     ret_offset =
+                       s->output_offset
+                       + my_offset 
+                       + s->output_section->vma
+                       - (input_section->output_offset
+                          + input_section->output_section->vma 
+                          + rel->r_vaddr)
+                       - 8;
+
+                     tmp = tmp | ((ret_offset >> 2) & 0x00FFFFFF);
+                     
+                     bfd_put_32 (output_bfd, tmp, contents + rel->r_vaddr - input_section->vma);
+                     
+                     done = 1;
+                   }
+                }
+             
+             /* Note: We used to check for ARM_THUMB9 and ARM_THUMB12 */
+              else if (howto->type == ARM_THUMB23)
+                {
+                  if (   h->class == C_EXT 
+                     || h->class == C_STAT
+                     || h->class == C_LABEL)
+                   {
+                     /* Thumb code calling an ARM function */
+                     unsigned long int              return_address;
+                     signed long int                final_disp;
+                     asection *                     s = 0;
+                     long int                       my_offset;
+                     unsigned long int              tmp;
+                     long int                       ret_offset;
+                     struct coff_link_hash_entry *  myh;
+
+                     myh = find_thumb_glue (info, name, input_bfd);
+                     if (myh == NULL)
+                       return false;
+
+                     my_offset = myh->root.u.def.value;
+                     
+                     s = bfd_get_section_by_name (bfd_of_glue_owner, 
+                                                  THUMB2ARM_GLUE_SECTION_NAME);
+                     
+                     BFD_ASSERT (s != NULL);
+                     BFD_ASSERT (s->contents != NULL);
+                     
+                     if ((my_offset & 0x01) == 0x01)
+                       {
+                         if (INTERWORK_SET (h_sec->owner) && ! INTERWORK_FLAG (h_sec->owner))
+                           _bfd_error_handler ("%s: warning: interworking not enabled.",
+                                               bfd_get_filename (h_sec->owner));
+                         
+                         -- my_offset;
+                         myh->root.u.def.value = my_offset;
+
+                         bfd_put_16 (output_bfd, t2a1_bx_pc_insn,
+                                    s->contents + my_offset);
+                     
+                         bfd_put_16 (output_bfd, t2a2_noop_insn,
+                                    s->contents + my_offset + 2);
+                     
+                         ret_offset =
+                           ((signed)h_val) - 
+                           ((signed)(s->output_offset
+                                     + my_offset 
+                                     + s->output_section->vma))
+                           - 12;
+                     
+                         t2a3_b_insn |= ((ret_offset >> 2) & 0x00FFFFFF);
+                         
+                         bfd_put_32 (output_bfd, t2a3_b_insn, s->contents + my_offset + 4);
+                       }
+
+                     BFD_ASSERT (my_offset <= global_thumb_glue_size);
+
+                     /* Now go back and fix up the original bl insn to point here.  */
+                     ret_offset =
+                       s->output_offset
+                       + my_offset
+                       - (input_section->output_offset
+                          + rel->r_vaddr)
+                       -4;
+                     
+                     tmp = bfd_get_32 (input_bfd, contents + rel->r_vaddr - input_section->vma);
+                     
+                     bfd_put_32 (output_bfd,
+                                 insert_thumb_branch (tmp, ret_offset),
+                                 contents + rel->r_vaddr - input_section->vma);
+                     
+                     done = 1;
+                    }
+                }
+            }
+         
+          /* If the relocation type and destination symbol does not
+             fall into one of the above categories, then we can just
+             perform a direct link. */
+
+         if (done)
+           rstat = bfd_reloc_ok;
+         else 
+#endif /* THUMBEXTENSION */
+           if (   h->root.type == bfd_link_hash_defined
+               || h->root.type == bfd_link_hash_defweak)
+           {
+             asection *sec;
+
+             sec = h->root.u.def.section;
+             val = (h->root.u.def.value
+                    + sec->output_section->vma
+                    + sec->output_offset);
+             }
+
+         else if (! info->relocateable)
+           {
+             if (! ((*info->callbacks->undefined_symbol)
+                    (info, h->root.root.string, input_bfd, input_section,
+                     rel->r_vaddr - input_section->vma)))
+               return false;
+           }
+       }
+
+      if (info->base_file)
+       {
+         /* Emit a reloc if the backend thinks it needs it. */
+         if (sym && pe_data(output_bfd)->in_reloc_p(output_bfd, howto))
+           {
+             /* relocation to a symbol in a section which
+                isn't absolute - we output the address here 
+                to a file */
+             bfd_vma addr = rel->r_vaddr 
+               - input_section->vma 
+               + input_section->output_offset 
+                 + input_section->output_section->vma;
+             if (coff_data(output_bfd)->pe)
+               addr -= pe_data(output_bfd)->pe_opthdr.ImageBase;
+             /* FIXME: Shouldn't 4 be sizeof (addr)?  */
+             fwrite (&addr, 1,4, (FILE *) info->base_file);
+           }
+       }
+  
+#if 1 /* THUMBEXTENSION */
+      if (done)
+       ;
+      /* Only perform this fix during the final link, not a relocatable link.  nickc@cygnus.com  */
+      else if (! info->relocateable
+              && howto->type == ARM_THUMB23)
+        {
+          /* This is pretty much a copy of what the default
+             _bfd_final_link_relocate and _bfd_relocate_contents
+             routines do to perform a relocation, with special
+             processing for the split addressing of the Thumb BL
+             instruction.  Again, it would probably be simpler adding a
+             ThumbBRANCH23 specific macro expansion into the default
+             code.  */
+         
+          bfd_vma address = rel->r_vaddr - input_section->vma;
+         
+          if (address > input_section->_raw_size)
+            rstat = bfd_reloc_outofrange;
+          else
+            {
+              bfd_vma         relocation       = val + addend;
+             int             size             = bfd_get_reloc_size (howto);
+             boolean         overflow         = false;
+             bfd_byte *      location         = contents + address;
+             bfd_vma         x                = bfd_get_32 (input_bfd, location);
+             bfd_vma         src_mask         = 0x007FFFFE;
+             bfd_signed_vma  reloc_signed_max = (1 << (howto->bitsize - 1)) - 1;
+             bfd_signed_vma  reloc_signed_min = ~reloc_signed_max;
+             bfd_vma         check;
+             bfd_signed_vma  signed_check;
+             bfd_vma         add;
+             bfd_signed_vma  signed_add;
+
+             BFD_ASSERT (size == 4);
+             
+              /* howto->pc_relative should be TRUE for type 14 BRANCH23 */
+              relocation -= (input_section->output_section->vma
+                             + input_section->output_offset);
+             
+              /* howto->pcrel_offset should be TRUE for type 14 BRANCH23 */
+              relocation -= address;
+             
+             /* No need to negate the relocation with BRANCH23. */
+             /* howto->complain_on_overflow == complain_overflow_signed for BRANCH23.  */
+             /* howto->rightshift == 1 */
+             /* Drop unwanted bits from the value we are relocating to.  */
+             
+             check = relocation >> howto->rightshift;
+               
+             /* If this is a signed value, the rightshift just dropped
+                leading 1 bits (assuming twos complement).  */
+             if ((bfd_signed_vma) relocation >= 0)
+               signed_check = check;
+             else
+               signed_check = (check
+                               | ((bfd_vma) - 1
+                                  & ~((bfd_vma) - 1 >> howto->rightshift)));
+             
+             /* Get the value from the object file.  */
+             if (bfd_big_endian (input_bfd))
+               {
+                 add = (((x) & 0x07ff0000) >> 4) | (((x) & 0x7ff) << 1);
+               }
+             else
+               {
+                 add = ((((x) & 0x7ff) << 12) | (((x) & 0x07ff0000) >> 15));
+               }
+
+             /* Get the value from the object file with an appropriate sign.
+                The expression involving howto->src_mask isolates the upper
+                bit of src_mask.  If that bit is set in the value we are
+                adding, it is negative, and we subtract out that number times
+                two.  If src_mask includes the highest possible bit, then we
+                can not get the upper bit, but that does not matter since
+                signed_add needs no adjustment to become negative in that
+                case.  */
+             
+             signed_add = add;
+             
+             if ((add & (((~ src_mask) >> 1) & src_mask)) != 0)
+               signed_add -= (((~ src_mask) >> 1) & src_mask) << 1;
+             
+             /* Add the value from the object file, shifted so that it is a
+                straight number.  */
+             /* howto->bitpos == 0 */
+             
+             signed_check += signed_add;
+             relocation += signed_add;
+
+             BFD_ASSERT (howto->complain_on_overflow == complain_overflow_signed);
+
+             /* Assumes two's complement.  */
+             if (   signed_check > reloc_signed_max
+                 || signed_check < reloc_signed_min)
+               overflow = true;
+             
+             /* Put RELOCATION into the correct bits:  */
+             
+             if (bfd_big_endian (input_bfd))
+               {
+                 relocation = (((relocation & 0xffe) >> 1)  | ((relocation << 4) & 0x07ff0000));
+               }
+             else
+               {
+                 relocation = (((relocation & 0xffe) << 15) | ((relocation >> 12) & 0x7ff));
+               }
+             
+             /* Add RELOCATION to the correct bits of X:  */
+             x = ((x & ~howto->dst_mask) | relocation);
+
+             /* Put the relocated value back in the object file:  */
+             bfd_put_32 (input_bfd, x, location);
+
+             rstat = overflow ? bfd_reloc_overflow : bfd_reloc_ok;
+            }
+        }
+      else
+#endif /* THUMBEXTENSION */
+        rstat = _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                          contents,
+                                          rel->r_vaddr - input_section->vma,
+                                          val, addend);
+#if 1 /* THUMBEXTENSION */
+      /* FIXME: 
+        Is this the best way to fix up thumb addresses? krk@cygnus.com
+        Probably not, but it works, and if it works it don't need fixing!  nickc@cygnus.com */
+      /* Only perform this fix during the final link, not a relocatable link.  nickc@cygnus.com  */
+      if (! info->relocateable
+         && rel->r_type == ARM_32)
+       {
+         /* Determine if we need to set the bottom bit of a relocated address
+            because the address is the address of a Thumb code symbol.  */
+            
+         int patchit = false;
+         
+         if (h != NULL
+             && (   h->class == C_THUMBSTATFUNC
+                 || h->class == C_THUMBEXTFUNC))
+           {
+             patchit = true;
+           }
+         else if (sym != NULL
+                  && sym->n_scnum > N_UNDEF)
+           {
+             /* No hash entry - use the symbol instead.  */
+
+             if (   sym->n_sclass == C_THUMBSTATFUNC
+                 || sym->n_sclass == C_THUMBEXTFUNC)
+               patchit = true;
+           }
+                       
+         if (patchit)
+           {
+             bfd_byte * location = contents + rel->r_vaddr - input_section->vma;
+             bfd_vma    x        = bfd_get_32 (input_bfd, location);
+
+             bfd_put_32 (input_bfd, x | 1, location);
+           }
+       }
+#endif /* THUMBEXTENSION */      
+      
+      switch (rstat)
+       {
+       default:
+         abort ();
+       case bfd_reloc_ok:
+         break;
+       case bfd_reloc_outofrange:
+         (*_bfd_error_handler)
+           ("%s: bad reloc address 0x%lx in section `%s'",
+            bfd_get_filename (input_bfd),
+            (unsigned long) rel->r_vaddr,
+            bfd_get_section_name (input_bfd, input_section));
+         return false;
+       case bfd_reloc_overflow:
+         {
+           const char *name;
+           char buf[SYMNMLEN + 1];
+
+           if (symndx == -1)
+             name = "*ABS*";
+           else if (h != NULL)
+             name = h->root.root.string;
+           else
+             {
+               name = _bfd_coff_internal_syment_name (input_bfd, sym, buf);
+               if (name == NULL)
+                 return false;
+             }
+
+           if (! ((*info->callbacks->reloc_overflow)
+                  (info, name, howto->name, (bfd_vma) 0, input_bfd,
+                   input_section, rel->r_vaddr - input_section->vma)))
+             return false;
+         }
+       }
+    }
+
+  return true;
+}
+
+boolean
+arm_allocate_interworking_sections (info) 
+     struct bfd_link_info *info;
+{
+  asection *s;
+  bfd_byte *foo;
+  static char test_char = '1';
+
+  if ( global_arm_glue_size != 0 )
+    {
+      BFD_ASSERT (bfd_of_glue_owner != NULL);
+      
+      s = bfd_get_section_by_name (bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME);
+
+      BFD_ASSERT (s != NULL);
+      
+      foo = (bfd_byte *) bfd_alloc(bfd_of_glue_owner, global_arm_glue_size);
+      memset(foo, test_char, global_arm_glue_size);
+      
+      s->_raw_size = s->_cooked_size = global_arm_glue_size;
+      s->contents = foo;
+    }
+
+  if (global_thumb_glue_size != 0)
+    {
+      BFD_ASSERT (bfd_of_glue_owner != NULL);
+      
+      s = bfd_get_section_by_name (bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME);
+
+      BFD_ASSERT (s != NULL);
+      
+      foo = (bfd_byte *) bfd_alloc(bfd_of_glue_owner, global_thumb_glue_size);
+      memset(foo, test_char, global_thumb_glue_size);
+      
+      s->_raw_size = s->_cooked_size = global_thumb_glue_size;
+      s->contents = foo;
+    }
+
+  return true;
+}
+
+static void
+record_arm_to_thumb_glue (info, h)
+     struct bfd_link_info *        info;
+     struct coff_link_hash_entry * h;
+{
+  const char *                   name = h->root.root.string;
+  register asection *            s;
+  char *                         tmp_name;
+  struct coff_link_hash_entry *  myh;
+
+  
+  BFD_ASSERT (bfd_of_glue_owner != NULL);
+
+  s = bfd_get_section_by_name (bfd_of_glue_owner, 
+                              ARM2THUMB_GLUE_SECTION_NAME);
+
+  BFD_ASSERT (s != NULL);
+
+  tmp_name = ((char *)
+             bfd_malloc (strlen (name) + strlen (ARM2THUMB_GLUE_ENTRY_NAME)));
+
+  BFD_ASSERT (tmp_name);
+
+  sprintf (tmp_name, ARM2THUMB_GLUE_ENTRY_NAME, name);
+  
+  myh = coff_link_hash_lookup (coff_hash_table (info),
+                              tmp_name,
+                              false, false, true);
+  
+  if (myh != NULL)
+    {
+      free (tmp_name);
+      return; /* we've already seen this guy */
+    }
+
+
+  /* The only trick here is using global_arm_glue_size as the value. Even
+     though the section isn't allocated yet, this is where we will be putting
+     it.  */
+
+  bfd_coff_link_add_one_symbol (info, bfd_of_glue_owner, tmp_name,
+                               BSF_EXPORT | BSF_GLOBAL, 
+                               s, global_arm_glue_size + 1,
+                               NULL, true, false, 
+                               (struct bfd_link_hash_entry **) & myh);
+  
+  global_arm_glue_size += ARM2THUMB_GLUE_SIZE;
+
+  free (tmp_name);
+
+  return;
+}
+
+static void
+record_thumb_to_arm_glue (info, h)
+     struct bfd_link_info *        info;
+     struct coff_link_hash_entry * h;
+{
+  const char *                   name = h->root.root.string;
+  register asection *            s;
+  char *                         tmp_name;
+  struct coff_link_hash_entry *  myh;
+
+  
+  BFD_ASSERT (bfd_of_glue_owner != NULL);
+
+  s = bfd_get_section_by_name (bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME);
+
+  BFD_ASSERT (s != NULL);
+
+  tmp_name = (char *) bfd_malloc (strlen (name) + strlen (THUMB2ARM_GLUE_ENTRY_NAME));
+
+  BFD_ASSERT (tmp_name);
+
+  sprintf (tmp_name, THUMB2ARM_GLUE_ENTRY_NAME, name);
+
+  myh = coff_link_hash_lookup (coff_hash_table (info),
+                              tmp_name,
+                              false, false, true);
+  
+  if (myh != NULL)
+    {
+      free (tmp_name);
+      return; /* we've already seen this guy */
+    }
+
+  bfd_coff_link_add_one_symbol (info, bfd_of_glue_owner, tmp_name,
+                               BSF_LOCAL, s, global_thumb_glue_size + 1,
+                               NULL, true, false, 
+                               (struct bfd_link_hash_entry **) & myh);
+  
+  /* if we mark it 'thumb', the disassembler will do a better job */
+  myh->class = C_THUMBEXTFUNC;
+
+  free (tmp_name);
+
+#define CHANGE_TO_ARM "__%s_change_to_arm"
+  
+  tmp_name = ((char *)
+             bfd_malloc (strlen (name) + strlen (CHANGE_TO_ARM)));
+
+  BFD_ASSERT (tmp_name);
+
+  sprintf (tmp_name, CHANGE_TO_ARM, name);
+
+  myh = NULL;
+
+  /* now allocate another symbol to switch back to arm mode */
+  bfd_coff_link_add_one_symbol (info, bfd_of_glue_owner, tmp_name,
+                               BSF_LOCAL, s, global_thumb_glue_size + 4,
+                               NULL, true, false, 
+                               (struct bfd_link_hash_entry **) & myh);
+
+  free (tmp_name);  
+
+  global_thumb_glue_size += THUMB2ARM_GLUE_SIZE;
+
+  return;
+}
+
+boolean
+arm_process_before_allocation (abfd, info)
+     bfd *                   abfd;
+     struct bfd_link_info *  info;
+{
+  asection * sec;
+
+  /* If we are only performing a partial link do not bother
+     to construct any glue.  */
+  if (info->relocateable)
+    return true;
+  
+  /* Here we have a bfd that is to be included on the link.  We have a hook
+     to do reloc rummaging, before section sizes are nailed down.  */
+
+  _bfd_coff_get_external_symbols (abfd);
+
+  /* Rummage around all the relocs and map the glue vectors.  */
+  sec = abfd->sections;
+
+  if (sec == NULL)
+    return true;
+
+  for (; sec != NULL; sec = sec->next)
+    {
+      struct internal_reloc * i;
+      struct internal_reloc * rel;
+      
+      if (sec->reloc_count == 0) 
+       continue;
+
+      /* Load the relocs.  */
+      /* FIXME: there may be a storage leak here. */
+      
+      i = _bfd_coff_read_internal_relocs (abfd, sec, 1, 0, 0, 0);
+    
+      BFD_ASSERT (i != 0);
+
+      for (rel = i; rel < i + sec->reloc_count; ++rel) 
+       {
+         unsigned short                 r_type  = rel->r_type;
+         long                           symndx;
+         struct coff_link_hash_entry *  h;
+
+         symndx = rel->r_symndx;
+
+         /* If the relocation is not against a symbol it cannot concern us.  */
+         if (symndx == -1)
+           continue;
+
+         h = obj_coff_sym_hashes (abfd)[symndx];
+
+         /* If the relocation is against a static symbol it must be within
+            the current section and so canot be a cross ARM/Thumb relocation.  */
+         if (h == NULL)
+           continue;
+
+         switch (r_type)
+           {
+           case ARM_26:
+             /* This one is a call from arm code.  We need to look up
+                the target of the call. If it is a thumb target, we
+                insert glue.  */
+             
+             if (h->class == C_THUMBEXTFUNC)
+               {
+                 record_arm_to_thumb_glue (info, h);
+               }
+             break;
+             
+           case ARM_THUMB23:
+             /* This one is a call from thumb code.  We used to look
+                for ARM_THUMB9 and ARM_THUMB12 as well.  We need to look
+                up the target of the call. If it is an arm target, we
+                insert glue.  */
+             
+             switch (h->class)
+               {
+               case C_EXT:
+               case C_STAT:
+               case C_LABEL:
+                 record_thumb_to_arm_glue (info, h);
+                 break;
+               default:
+                 ;
+               }
+             break;
+             
+           default:
+             break;
+           }
+       }
+    }
+
+  return true;
+}
+
+#define coff_relocate_section coff_arm_relocate_section
+
+/* When doing a relocateable link, we want to convert ARM26 relocs
+   into ARM26D relocs.  */
+
+static boolean
+coff_arm_adjust_symndx (obfd, info, ibfd, sec, irel, adjustedp)
+     bfd *obfd;
+     struct bfd_link_info *info;
+     bfd *ibfd;
+     asection *sec;
+     struct internal_reloc *irel;
+     boolean *adjustedp;
+{
+  if (irel->r_type == 3)
+    {
+      struct coff_link_hash_entry *h;
+
+      h = obj_coff_sym_hashes (ibfd)[irel->r_symndx];
+      if (h != NULL
+         && (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+         && h->root.u.def.section->output_section == sec->output_section)
+       irel->r_type = 7;
+    }
+  *adjustedp = false;
+  return true;
+}
+
+
+/* Called when merging the private data areas of two BFDs.
+   This is important as it allows us to detect if we are
+   attempting to merge binaries compiled for different ARM
+   targets, eg different CPUs or differents APCS's.     */
+
+static boolean
+coff_arm_bfd_merge_private_bfd_data (ibfd, obfd)
+     bfd *   ibfd;
+     bfd *   obfd;
+{
+  BFD_ASSERT (ibfd != NULL && obfd != NULL);
+
+  if (ibfd == obfd)
+    return true;
+
+  /* If the two formats are different we cannot merge anything.  */
+  if (ibfd->xvec != obfd->xvec)
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return false;
+    }
+
+  /* Verify that the APCS is the same for the two BFDs */
+  if (APCS_SET (ibfd))
+    {
+      if (APCS_SET (obfd))
+       {
+         /* If the src and dest have different APCS flag bits set, fail.  */
+         if (APCS_26_FLAG (obfd) != APCS_26_FLAG (ibfd))
+           {
+             _bfd_error_handler
+               ("%s: ERROR: compiled for APCS-%d whereas target %s uses APCS-%d",
+                bfd_get_filename (ibfd), APCS_26_FLAG (ibfd) ? 26 : 32,
+                bfd_get_filename (obfd), APCS_26_FLAG (obfd) ? 26 : 32
+                );
+
+             bfd_set_error (bfd_error_wrong_format);
+             return false;
+           }
+         
+         if (APCS_FLOAT_FLAG (obfd) != APCS_FLOAT_FLAG (ibfd))
+           {
+             _bfd_error_handler
+               ("%s: ERROR: passes floats in %s registers whereas target %s uses %s registers",
+                bfd_get_filename (ibfd), APCS_FLOAT_FLAG (ibfd) ? "float" : "integer",
+                bfd_get_filename (obfd), APCS_FLOAT_FLAG (obfd) ? "float" : "integer"
+                );
+
+             bfd_set_error (bfd_error_wrong_format);
+             return false;
+           }
+         
+         if (PIC_FLAG (obfd) != PIC_FLAG (ibfd))
+           {
+             _bfd_error_handler
+               ("%s: ERROR: compiled as %s code, whereas target %s is %s",
+                bfd_get_filename (ibfd), PIC_FLAG (ibfd) ? "position independent" : "absoluste position",
+                bfd_get_filename (obfd), PIC_FLAG (obfd) ? "position independent" : "absoluste position"
+                );
+
+             bfd_set_error (bfd_error_wrong_format);
+             return false;
+           }
+       }
+      else
+       {
+         SET_APCS_FLAGS (obfd, APCS_26_FLAG (ibfd) | APCS_FLOAT_FLAG (ibfd) | PIC_FLAG (ibfd));
+         
+         /* Set up the arch and fields as well as these are probably wrong.  */
+         bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), bfd_get_mach (ibfd));
+       }
+    }
+  
+  /* Check the interworking support.  */
+  if (INTERWORK_SET (ibfd))
+    {
+      if (INTERWORK_SET (obfd))
+       {
+         /* If the src and dest differ in their interworking issue a warning.  */
+         if (INTERWORK_FLAG (obfd) != INTERWORK_FLAG (ibfd))
+           {
+             _bfd_error_handler
+               ("Warning: input file %s %s interworking, whereas %s does%s",
+                bfd_get_filename (ibfd),
+                INTERWORK_FLAG (ibfd) ? "supports" : "does not support",
+                bfd_get_filename (obfd),
+                INTERWORK_FLAG (obfd) ? "." : " not."
+                );
+           }
+       }
+      else
+       {
+         SET_INTERWORK_FLAG (obfd, INTERWORK_FLAG (ibfd));
+       }
+    }
+
+  return true;
+}
+
+
+/* Display the flags field */
+
+static boolean
+coff_arm_bfd_print_private_bfd_data (abfd, ptr)
+     bfd *   abfd;
+     PTR     ptr;
+{
+  FILE * file = (FILE *) ptr;
+  
+  BFD_ASSERT (abfd != NULL && ptr != NULL)
+  
+  fprintf (file, "private flags = %x", coff_data( abfd )->flags);
+  
+  if (APCS_SET (abfd))
+    fprintf (file, ": [APCS-%d] [floats passed in %s registers] [%s]",
+            APCS_26_FLAG (abfd) ? 26 : 32,
+            APCS_FLOAT_FLAG (abfd) ? "float" : "integer",
+            PIC_FLAG (abfd) ? "position independent" : "absolute position"
+            );
+  
+  if (INTERWORK_SET (abfd))
+    fprintf (file, ": [interworking %ssupported]",
+            INTERWORK_FLAG (abfd) ? "" : "not " );
+  
+  fputc ('\n', file);
+  
+  return true;
+}
+
+
+/* Copies the given flags into the coff_tdata.flags field.
+   Typically these flags come from the f_flags[] field of
+   the COFF filehdr structure, which contains important,
+   target specific information.  */
+
+static boolean
+coff_arm_bfd_set_private_flags (abfd, flags)
+       bfd *      abfd;
+       flagword   flags;
+{
+  int flag;
+
+  BFD_ASSERT (abfd != NULL);
+
+  flag = (flags & F_APCS26) ? F_APCS_26 : 0;
+  
+  /* Make sure that the APCS field has not been initialised to the opposite value.  */
+  if (APCS_SET (abfd)
+      && (   (APCS_26_FLAG    (abfd) != flag)
+         || (APCS_FLOAT_FLAG (abfd) != (flags & F_APCS_FLOAT))
+         || (PIC_FLAG        (abfd) != (flags & F_PIC))
+         ))
+    return false;
+
+  flag |= (flags & (F_APCS_FLOAT | F_PIC));
+  
+  SET_APCS_FLAGS (abfd, flag);
+
+  flag = (flags & F_INTERWORK);
+  
+  /* If either the flags or the BFD do support interworking then do not set the interworking flag.  */
+  if (INTERWORK_SET (abfd) && (INTERWORK_FLAG (abfd) != flag))
+    flag = 0;
+
+  SET_INTERWORK_FLAG (abfd, flag);
+
+  return true;
+}
+
+
+/* Copy the important parts of the target specific data
+   from one instance of a BFD to another.  */
+
+static boolean
+coff_arm_bfd_copy_private_bfd_data (src, dest)
+     bfd *  src;
+     bfd *  dest;
+{
+  BFD_ASSERT (src != NULL && dest != NULL);
+
+  if (src == dest)
+    return true;
+
+  /* If the destination is not in the same format as the source, do not do the copy.  */
+  if (src->xvec != dest->xvec)
+    return true;
+
+  /* copy the flags field */
+  if (APCS_SET (src))
+    {
+      if (APCS_SET (dest))
+       {
+         /* If the src and dest have different APCS flag bits set, fail.  */
+         if (APCS_26_FLAG (dest) != APCS_26_FLAG (src))
+           return false;
+         
+         if (APCS_FLOAT_FLAG (dest) != APCS_FLOAT_FLAG (src))
+           return false;
+         
+         if (PIC_FLAG (dest) != PIC_FLAG (src))
+           return false;
+       }
+      else
+       SET_APCS_FLAGS (dest, APCS_26_FLAG (src) | APCS_FLOAT_FLAG (src) | PIC_FLAG (src));
+    }
+
+  if (INTERWORK_SET (src))
+    {
+      if (INTERWORK_SET (dest))
+       {
+         /* If the src and dest have different interworking flags then turn off the interworking bit.  */
+         if (INTERWORK_FLAG (dest) != INTERWORK_FLAG (src))
+           SET_INTERWORK_FLAG (dest, 0);
+       }
+      else
+       {
+         SET_INTERWORK_FLAG (dest, INTERWORK_FLAG (src));
+       }
+    }
+
+  return true;
+}
+
+/* Note:  the definitions here of LOCAL_LABEL_PREFIX and USER_LABEL_PREIFX
+ *must* match the definitions on gcc/config/arm/semi.h.  */
+#define LOCAL_LABEL_PREFIX "."
+#define USER_LABEL_PREFIX "_"
+
+static boolean
+coff_arm_is_local_label_name (abfd, name)
+     bfd *        abfd;
+     const char * name;
+{
+#ifdef LOCAL_LABEL_PREFIX
+  /* If there is a prefix for local labels then look for this.
+     If the prefix exists, but it is empty, then ignore the test. */
+  
+  if (LOCAL_LABEL_PREFIX[0] != 0)
+    {
+      if (strncmp (name, LOCAL_LABEL_PREFIX, strlen (LOCAL_LABEL_PREFIX)) == 0)
+       return true;
+    }
+#endif
+#ifdef USER_LABEL_PREFIX
+  if (USER_LABEL_PREFIX[0] != 0)
+    {
+      if (strncmp (name, USER_LABEL_PREFIX, strlen (USER_LABEL_PREFIX)) == 0)
+       return false;
+    }
+#endif
+  
+  /* devo/gcc/config/dbxcoff.h defines ASM_OUTPUT_SOURCE_LINE to generate local line numbers as .LM<number>, so treat these as local.  */
+  
+  switch (name[0])
+    {
+    case 'L': return true;
+    case '.': return (name[1] == 'L' && name[2] == 'M') ? true : false;
+    default:  return false;     /* Cannot make our minds up - default to false so that it will not be stripped by accident.  */ 
+    }
+}
+
+#define coff_bfd_is_local_label_name           coff_arm_is_local_label_name
+#define coff_adjust_symndx                     coff_arm_adjust_symndx
+#define coff_bfd_final_link                    coff_arm_bfd_final_link 
 #define coff_bfd_merge_private_bfd_data                coff_arm_bfd_merge_private_bfd_data
 #define coff_bfd_print_private_bfd_data                coff_arm_bfd_print_private_bfd_data
 #define coff_bfd_set_private_flags              coff_arm_bfd_set_private_flags
@@ -623,6 +2018,643 @@ coff_arm_bfd_copy_private_bfd_data (src, dest)
 
 #include "coffcode.h"
 
+static boolean
+arm_do_last (abfd)
+     bfd * abfd;
+{
+  if (abfd == bfd_of_glue_owner)
+    return true;
+  else
+    return false;
+}
+
+static bfd *
+arm_get_last()
+{
+  return bfd_of_glue_owner;
+}
+
+/* This piece of machinery exists only to guarantee that the bfd that holds
+   the glue section is written last. 
+
+   This does depend on bfd_make_section attaching a new section to the
+   end of the section list for the bfd. 
+
+   This is otherwise intended to be functionally the same as 
+   cofflink.c:_bfd_coff_final_link(). It is specifically different only 
+   where the ARM_HACKS macro modifies the code. It is left in as a 
+   precise form of comment. krk@cygnus.com  */
+
+#define ARM_HACKS
+
+
+/* Do the final link step.  */
+
+boolean
+coff_arm_bfd_final_link (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  bfd_size_type symesz;
+  struct coff_final_link_info finfo;
+  boolean debug_merge_allocated;
+  asection *o;
+  struct bfd_link_order *p;
+  size_t max_sym_count;
+  size_t max_lineno_count;
+  size_t max_reloc_count;
+  size_t max_output_reloc_count;
+  size_t max_contents_size;
+  file_ptr rel_filepos;
+  unsigned int relsz;
+  file_ptr line_filepos;
+  unsigned int linesz;
+  bfd *sub;
+  bfd_byte *external_relocs = NULL;
+  char strbuf[STRING_SIZE_SIZE];
+
+  symesz = bfd_coff_symesz (abfd);
+
+  finfo.info = info;
+  finfo.output_bfd = abfd;
+  finfo.strtab = NULL;
+  finfo.section_info = NULL;
+  finfo.last_file_index = -1;
+  finfo.last_bf_index = -1;
+  finfo.internal_syms = NULL;
+  finfo.sec_ptrs = NULL;
+  finfo.sym_indices = NULL;
+  finfo.outsyms = NULL;
+  finfo.linenos = NULL;
+  finfo.contents = NULL;
+  finfo.external_relocs = NULL;
+  finfo.internal_relocs = NULL;
+  debug_merge_allocated = false;
+
+  coff_data (abfd)->link_info = info;
+
+  finfo.strtab = _bfd_stringtab_init ();
+  if (finfo.strtab == NULL)
+    goto error_return;
+
+  if (! coff_debug_merge_hash_table_init (&finfo.debug_merge))
+    goto error_return;
+  debug_merge_allocated = true;
+
+  /* Compute the file positions for all the sections.  */
+  if (! abfd->output_has_begun)
+    bfd_coff_compute_section_file_positions (abfd);
+
+  /* Count the line numbers and relocation entries required for the
+     output file.  Set the file positions for the relocs.  */
+  rel_filepos = obj_relocbase (abfd);
+  relsz = bfd_coff_relsz (abfd);
+  max_contents_size = 0;
+  max_lineno_count = 0;
+  max_reloc_count = 0;
+
+  for (o = abfd->sections; o != NULL; o = o->next)
+    {
+      o->reloc_count = 0;
+      o->lineno_count = 0;
+      for (p = o->link_order_head; p != NULL; p = p->next)
+       {
+
+         if (p->type == bfd_indirect_link_order)
+           {
+             asection *sec;
+
+             sec = p->u.indirect.section;
+
+             /* Mark all sections which are to be included in the
+                link.  This will normally be every section.  We need
+                to do this so that we can identify any sections which
+                the linker has decided to not include.  */
+             sec->linker_mark = true;
+
+             if (info->strip == strip_none
+                 || info->strip == strip_some)
+               o->lineno_count += sec->lineno_count;
+
+             if (info->relocateable)
+               o->reloc_count += sec->reloc_count;
+
+             if (sec->_raw_size > max_contents_size)
+               max_contents_size = sec->_raw_size;
+             if (sec->lineno_count > max_lineno_count)
+               max_lineno_count = sec->lineno_count;
+             if (sec->reloc_count > max_reloc_count)
+               max_reloc_count = sec->reloc_count;
+           }
+         else if (info->relocateable
+                  && (p->type == bfd_section_reloc_link_order
+                      || p->type == bfd_symbol_reloc_link_order))
+           ++o->reloc_count;
+       }
+      if (o->reloc_count == 0)
+       o->rel_filepos = 0;
+      else
+       {
+         o->flags |= SEC_RELOC;
+         o->rel_filepos = rel_filepos;
+         rel_filepos += o->reloc_count * relsz;
+       }
+    }
+
+  /* If doing a relocateable link, allocate space for the pointers we
+     need to keep.  */
+  if (info->relocateable)
+    {
+      unsigned int i;
+
+      /* We use section_count + 1, rather than section_count, because
+         the target_index fields are 1 based.  */
+      finfo.section_info =
+       ((struct coff_link_section_info *)
+        bfd_malloc ((abfd->section_count + 1)
+                    * sizeof (struct coff_link_section_info)));
+      if (finfo.section_info == NULL)
+       goto error_return;
+      for (i = 0; i <= abfd->section_count; i++)
+       {
+         finfo.section_info[i].relocs = NULL;
+         finfo.section_info[i].rel_hashes = NULL;
+       }
+    }
+
+  /* We now know the size of the relocs, so we can determine the file
+     positions of the line numbers.  */
+  line_filepos = rel_filepos;
+  linesz = bfd_coff_linesz (abfd);
+  max_output_reloc_count = 0;
+  for (o = abfd->sections; o != NULL; o = o->next)
+    {
+      if (o->lineno_count == 0)
+       o->line_filepos = 0;
+      else
+       {
+         o->line_filepos = line_filepos;
+         line_filepos += o->lineno_count * linesz;
+       }
+
+      if (o->reloc_count != 0)
+       {
+         /* We don't know the indices of global symbols until we have
+             written out all the local symbols.  For each section in
+             the output file, we keep an array of pointers to hash
+             table entries.  Each entry in the array corresponds to a
+             reloc.  When we find a reloc against a global symbol, we
+             set the corresponding entry in this array so that we can
+             fix up the symbol index after we have written out all the
+             local symbols.
+
+            Because of this problem, we also keep the relocs in
+            memory until the end of the link.  This wastes memory,
+            but only when doing a relocateable link, which is not the
+            common case.  */
+         BFD_ASSERT (info->relocateable);
+         finfo.section_info[o->target_index].relocs =
+           ((struct internal_reloc *)
+            bfd_malloc (o->reloc_count * sizeof (struct internal_reloc)));
+         finfo.section_info[o->target_index].rel_hashes =
+           ((struct coff_link_hash_entry **)
+            bfd_malloc (o->reloc_count
+                    * sizeof (struct coff_link_hash_entry *)));
+         if (finfo.section_info[o->target_index].relocs == NULL
+             || finfo.section_info[o->target_index].rel_hashes == NULL)
+           goto error_return;
+
+         if (o->reloc_count > max_output_reloc_count)
+           max_output_reloc_count = o->reloc_count;
+       }
+
+      /* Reset the reloc and lineno counts, so that we can use them to
+        count the number of entries we have output so far.  */
+      o->reloc_count = 0;
+      o->lineno_count = 0;
+    }
+
+  obj_sym_filepos (abfd) = line_filepos;
+
+  /* Figure out the largest number of symbols in an input BFD.  Take
+     the opportunity to clear the output_has_begun fields of all the
+     input BFD's.  */
+  max_sym_count = 0;
+  for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
+    {
+      size_t sz;
+
+      sub->output_has_begun = false;
+      sz = obj_raw_syment_count (sub);
+      if (sz > max_sym_count)
+       max_sym_count = sz;
+    }
+
+  /* Allocate some buffers used while linking.  */
+  finfo.internal_syms = ((struct internal_syment *)
+                        bfd_malloc (max_sym_count
+                                    * sizeof (struct internal_syment)));
+  finfo.sec_ptrs = (asection **) bfd_malloc (max_sym_count
+                                            * sizeof (asection *));
+  finfo.sym_indices = (long *) bfd_malloc (max_sym_count * sizeof (long));
+  finfo.outsyms = ((bfd_byte *)
+                  bfd_malloc ((size_t) ((max_sym_count + 1) * symesz)));
+  finfo.linenos = (bfd_byte *) bfd_malloc (max_lineno_count
+                                      * bfd_coff_linesz (abfd));
+  finfo.contents = (bfd_byte *) bfd_malloc (max_contents_size);
+  finfo.external_relocs = (bfd_byte *) bfd_malloc (max_reloc_count * relsz);
+  if (! info->relocateable)
+    finfo.internal_relocs = ((struct internal_reloc *)
+                            bfd_malloc (max_reloc_count
+                                        * sizeof (struct internal_reloc)));
+  if ((finfo.internal_syms == NULL && max_sym_count > 0)
+      || (finfo.sec_ptrs == NULL && max_sym_count > 0)
+      || (finfo.sym_indices == NULL && max_sym_count > 0)
+      || finfo.outsyms == NULL
+      || (finfo.linenos == NULL && max_lineno_count > 0)
+      || (finfo.contents == NULL && max_contents_size > 0)
+      || (finfo.external_relocs == NULL && max_reloc_count > 0)
+      || (! info->relocateable
+         && finfo.internal_relocs == NULL
+         && max_reloc_count > 0))
+    goto error_return;
+
+  /* We now know the position of everything in the file, except that
+     we don't know the size of the symbol table and therefore we don't
+     know where the string table starts.  We just build the string
+     table in memory as we go along.  We process all the relocations
+     for a single input file at once.  */
+  obj_raw_syment_count (abfd) = 0;
+
+  if (coff_backend_info (abfd)->_bfd_coff_start_final_link)
+    {
+      if (! bfd_coff_start_final_link (abfd, info))
+       goto error_return;
+    }
+
+  for (o = abfd->sections; o != NULL; o = o->next)
+    {
+      for (p = o->link_order_head; p != NULL; p = p->next)
+       {
+         if (p->type == bfd_indirect_link_order
+             && (bfd_get_flavour (p->u.indirect.section->owner)
+                 == bfd_target_coff_flavour))
+           {
+             sub = p->u.indirect.section->owner;
+#ifdef ARM_HACKS
+             if (! sub->output_has_begun && !arm_do_last(sub))
+#else
+             if (! sub->output_has_begun)
+#endif
+               {
+                 if (! _bfd_coff_link_input_bfd (&finfo, sub))
+                   goto error_return;
+                 sub->output_has_begun = true;
+               }
+           }
+         else if (p->type == bfd_section_reloc_link_order
+                  || p->type == bfd_symbol_reloc_link_order)
+           {
+             if (! _bfd_coff_reloc_link_order (abfd, &finfo, o, p))
+               goto error_return;
+           }
+         else
+           {
+             if (! _bfd_default_link_order (abfd, info, o, p))
+               goto error_return;
+           }
+       }
+    }
+
+#ifdef ARM_HACKS
+  {
+    extern bfd* arm_get_last();
+    bfd* last_one = arm_get_last();
+    if (last_one)
+      {
+       if (! _bfd_coff_link_input_bfd (&finfo, last_one))
+         goto error_return;
+      }
+    last_one->output_has_begun = true;
+  }
+#endif
+
+  /* Free up the buffers used by _bfd_coff_link_input_bfd.  */
+
+  coff_debug_merge_hash_table_free (&finfo.debug_merge);
+  debug_merge_allocated = false;
+
+  if (finfo.internal_syms != NULL)
+    {
+      free (finfo.internal_syms);
+      finfo.internal_syms = NULL;
+    }
+  if (finfo.sec_ptrs != NULL)
+    {
+      free (finfo.sec_ptrs);
+      finfo.sec_ptrs = NULL;
+    }
+  if (finfo.sym_indices != NULL)
+    {
+      free (finfo.sym_indices);
+      finfo.sym_indices = NULL;
+    }
+  if (finfo.linenos != NULL)
+    {
+      free (finfo.linenos);
+      finfo.linenos = NULL;
+    }
+  if (finfo.contents != NULL)
+    {
+      free (finfo.contents);
+      finfo.contents = NULL;
+    }
+  if (finfo.external_relocs != NULL)
+    {
+      free (finfo.external_relocs);
+      finfo.external_relocs = NULL;
+    }
+  if (finfo.internal_relocs != NULL)
+    {
+      free (finfo.internal_relocs);
+      finfo.internal_relocs = NULL;
+    }
+
+  /* The value of the last C_FILE symbol is supposed to be the symbol
+     index of the first external symbol.  Write it out again if
+     necessary.  */
+  if (finfo.last_file_index != -1
+      && (unsigned int) finfo.last_file.n_value != obj_raw_syment_count (abfd))
+    {
+      finfo.last_file.n_value = obj_raw_syment_count (abfd);
+      bfd_coff_swap_sym_out (abfd, (PTR) &finfo.last_file,
+                            (PTR) finfo.outsyms);
+      if (bfd_seek (abfd,
+                   (obj_sym_filepos (abfd)
+                    + finfo.last_file_index * symesz),
+                   SEEK_SET) != 0
+         || bfd_write (finfo.outsyms, symesz, 1, abfd) != symesz)
+       return false;
+    }
+
+  /* Write out the global symbols.  */
+  finfo.failed = false;
+  coff_link_hash_traverse (coff_hash_table (info), _bfd_coff_write_global_sym,
+                          (PTR) &finfo);
+  if (finfo.failed)
+    goto error_return;
+
+  /* The outsyms buffer is used by _bfd_coff_write_global_sym.  */
+  if (finfo.outsyms != NULL)
+    {
+      free (finfo.outsyms);
+      finfo.outsyms = NULL;
+    }
+
+  if (info->relocateable)
+    {
+      /* Now that we have written out all the global symbols, we know
+        the symbol indices to use for relocs against them, and we can
+        finally write out the relocs.  */
+      external_relocs = ((bfd_byte *)
+                        bfd_malloc (max_output_reloc_count * relsz));
+      if (external_relocs == NULL)
+       goto error_return;
+
+      for (o = abfd->sections; o != NULL; o = o->next)
+       {
+         struct internal_reloc *irel;
+         struct internal_reloc *irelend;
+         struct coff_link_hash_entry **rel_hash;
+         bfd_byte *erel;
+
+         if (o->reloc_count == 0)
+           continue;
+
+         irel = finfo.section_info[o->target_index].relocs;
+         irelend = irel + o->reloc_count;
+         rel_hash = finfo.section_info[o->target_index].rel_hashes;
+         erel = external_relocs;
+         for (; irel < irelend; irel++, rel_hash++, erel += relsz)
+           {
+             if (*rel_hash != NULL)
+               {
+                 BFD_ASSERT ((*rel_hash)->indx >= 0);
+                 irel->r_symndx = (*rel_hash)->indx;
+               }
+             bfd_coff_swap_reloc_out (abfd, (PTR) irel, (PTR) erel);
+           }
+
+         if (bfd_seek (abfd, o->rel_filepos, SEEK_SET) != 0
+             || bfd_write ((PTR) external_relocs, relsz, o->reloc_count,
+                           abfd) != relsz * o->reloc_count)
+           goto error_return;
+       }
+
+      free (external_relocs);
+      external_relocs = NULL;
+    }
+
+  /* Free up the section information.  */
+  if (finfo.section_info != NULL)
+    {
+      unsigned int i;
+
+      for (i = 0; i < abfd->section_count; i++)
+       {
+         if (finfo.section_info[i].relocs != NULL)
+           free (finfo.section_info[i].relocs);
+         if (finfo.section_info[i].rel_hashes != NULL)
+           free (finfo.section_info[i].rel_hashes);
+       }
+      free (finfo.section_info);
+      finfo.section_info = NULL;
+    }
+
+  /* If we have optimized stabs strings, output them.  */
+  if (coff_hash_table (info)->stab_info != NULL)
+    {
+      if (! _bfd_write_stab_strings (abfd, &coff_hash_table (info)->stab_info))
+       return false;
+    }
+
+  /* Write out the string table.  */
+  if (obj_raw_syment_count (abfd) != 0)
+    {
+      if (bfd_seek (abfd,
+                   (obj_sym_filepos (abfd)
+                    + obj_raw_syment_count (abfd) * symesz),
+                   SEEK_SET) != 0)
+       return false;
+
+#if STRING_SIZE_SIZE == 4
+      bfd_h_put_32 (abfd,
+                   _bfd_stringtab_size (finfo.strtab) + STRING_SIZE_SIZE,
+                   (bfd_byte *) strbuf);
+#else
+ #error Change bfd_h_put_32
+#endif
+
+      if (bfd_write (strbuf, 1, STRING_SIZE_SIZE, abfd) != STRING_SIZE_SIZE)
+       return false;
+
+      if (! _bfd_stringtab_emit (abfd, finfo.strtab))
+       return false;
+    }
+
+  _bfd_stringtab_free (finfo.strtab);
+
+  /* Setting bfd_get_symcount to 0 will cause write_object_contents to
+     not try to write out the symbols.  */
+  bfd_get_symcount (abfd) = 0;
+
+  return true;
+
+ error_return:
+  if (debug_merge_allocated)
+    coff_debug_merge_hash_table_free (&finfo.debug_merge);
+  if (finfo.strtab != NULL)
+    _bfd_stringtab_free (finfo.strtab);
+  if (finfo.section_info != NULL)
+    {
+      unsigned int i;
+
+      for (i = 0; i < abfd->section_count; i++)
+       {
+         if (finfo.section_info[i].relocs != NULL)
+           free (finfo.section_info[i].relocs);
+         if (finfo.section_info[i].rel_hashes != NULL)
+           free (finfo.section_info[i].rel_hashes);
+       }
+      free (finfo.section_info);
+    }
+  if (finfo.internal_syms != NULL)
+    free (finfo.internal_syms);
+  if (finfo.sec_ptrs != NULL)
+    free (finfo.sec_ptrs);
+  if (finfo.sym_indices != NULL)
+    free (finfo.sym_indices);
+  if (finfo.outsyms != NULL)
+    free (finfo.outsyms);
+  if (finfo.linenos != NULL)
+    free (finfo.linenos);
+  if (finfo.contents != NULL)
+    free (finfo.contents);
+  if (finfo.external_relocs != NULL)
+    free (finfo.external_relocs);
+  if (finfo.internal_relocs != NULL)
+    free (finfo.internal_relocs);
+  if (external_relocs != NULL)
+    free (external_relocs);
+  return false;
+}
+
+static void
+arm_bfd_coff_swap_sym_in (abfd, ext1, in1)
+     bfd            *abfd;
+     PTR ext1;
+     PTR in1;
+{
+  SYMENT *ext = (SYMENT *)ext1;
+  struct internal_syment      *in = (struct internal_syment *)in1;
+  flagword flags;
+  register asection *s;
+
+  if( ext->e.e_name[0] == 0) {
+    in->_n._n_n._n_zeroes = 0;
+    in->_n._n_n._n_offset = bfd_h_get_32(abfd, (bfd_byte *) ext->e.e.e_offset);
+  }
+  else {
+#if SYMNMLEN != E_SYMNMLEN
+   -> Error, we need to cope with truncating or extending SYMNMLEN!;
+#else
+    memcpy(in->_n._n_name, ext->e.e_name, SYMNMLEN);
+#endif
+  }
+  in->n_value = bfd_h_get_32(abfd, (bfd_byte *) ext->e_value); 
+  in->n_scnum = bfd_h_get_16(abfd, (bfd_byte *) ext->e_scnum);
+  if (sizeof(ext->e_type) == 2){
+    in->n_type = bfd_h_get_16(abfd, (bfd_byte *) ext->e_type);
+  }
+  else {
+    in->n_type = bfd_h_get_32(abfd, (bfd_byte *) ext->e_type);
+  }
+  in->n_sclass = bfd_h_get_8(abfd, ext->e_sclass);
+  in->n_numaux = bfd_h_get_8(abfd, ext->e_numaux);
+
+  if (bfd_of_glue_owner != 0) /* we already have a toc, so go home */
+    return;
+
+  /* save the bfd for later allocation */
+  bfd_of_glue_owner = abfd;
+
+  s = bfd_get_section_by_name (bfd_of_glue_owner , 
+                              ARM2THUMB_GLUE_SECTION_NAME);
+
+  if (s == NULL) 
+    {
+      flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY ;
+      
+      s = bfd_make_section (bfd_of_glue_owner , ARM2THUMB_GLUE_SECTION_NAME);
+
+      if (s == NULL
+         || !bfd_set_section_flags (bfd_of_glue_owner, s, flags)
+         || !bfd_set_section_alignment (bfd_of_glue_owner, s, 2))
+       {
+         /* FIXME: set appropriate bfd error */
+         abort();
+       }
+    }
+
+  s = bfd_get_section_by_name ( bfd_of_glue_owner , THUMB2ARM_GLUE_SECTION_NAME);
+
+  if (s == NULL) 
+    {
+      flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY ;
+      
+      s = bfd_make_section (bfd_of_glue_owner , THUMB2ARM_GLUE_SECTION_NAME);
+      
+      if (s == NULL
+         || !bfd_set_section_flags (bfd_of_glue_owner, s, flags)
+         || !bfd_set_section_alignment (bfd_of_glue_owner, s, 2))
+       {
+         /* FIXME: set appropriate bfd error krk@cygnus.com */
+         abort();
+       }
+    }
+  
+  return;
+}
+
+static bfd_coff_backend_data arm_bfd_coff_std_swap_table =
+{
+  coff_swap_aux_in, arm_bfd_coff_swap_sym_in, coff_swap_lineno_in,
+  coff_swap_aux_out, coff_swap_sym_out,
+  coff_swap_lineno_out, coff_swap_reloc_out,
+  coff_swap_filehdr_out, coff_swap_aouthdr_out,
+  coff_swap_scnhdr_out,
+  FILHSZ, AOUTSZ, SCNHSZ, SYMESZ, AUXESZ, RELSZ, LINESZ,
+#ifdef COFF_LONG_FILENAMES
+  true,
+#else
+  false,
+#endif
+#ifdef COFF_LONG_SECTION_NAMES
+  true,
+#else
+  false,
+#endif
+  COFF_DEFAULT_SECTION_ALIGNMENT_POWER,
+  coff_swap_filehdr_in, coff_swap_aouthdr_in, coff_swap_scnhdr_in,
+  coff_swap_reloc_in, coff_bad_format_hook, coff_set_arch_mach_hook,
+  coff_mkobject_hook, styp_to_sec_flags, coff_set_alignment_hook,
+  coff_slurp_symbol_table, symname_in_debug_hook, coff_pointerize_aux_hook,
+  coff_print_aux, coff_reloc16_extra_cases, coff_reloc16_estimate,
+  coff_sym_is_global, coff_compute_section_file_positions,
+  coff_start_final_link, coff_relocate_section, coff_rtype_to_howto,
+  coff_adjust_symndx, coff_link_add_one_symbol
+};
+
 const bfd_target
 #ifdef TARGET_LITTLE_SYM
 TARGET_LITTLE_SYM =
@@ -683,7 +2715,7 @@ armcoff_little_vec =
      BFD_JUMP_TABLE_LINK (coff),
      BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
 
-  COFF_SWAP_TABLE,
+  & arm_bfd_coff_std_swap_table,
 };
 
 const bfd_target
@@ -746,5 +2778,5 @@ armcoff_big_vec =
      BFD_JUMP_TABLE_LINK (coff),
      BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
 
-  COFF_SWAP_TABLE,
+  & arm_bfd_coff_std_swap_table,
 };
This page took 0.060555 seconds and 4 git commands to generate.