* elf32-mips.c (mips_elf_calculate_relocation): R_MIPS_LITERAL
[deliverable/binutils-gdb.git] / bfd / elf32-arm.h
index 857fe38b7af266efd7648b419a41659c0690b419..c71c1d28c0cef6778a2c4321a5962c6bcafb17d3 100644 (file)
 typedef unsigned long int insn32;
 typedef unsigned short int insn16;
 
-static reloc_howto_type *elf32_arm_reloc_type_lookup
-  PARAMS ((bfd * abfd, bfd_reloc_code_real_type code));
-static void elf32_arm_info_to_howto
-  PARAMS ((bfd *, arelent *, Elf32_Internal_Rela *));
 static boolean elf32_arm_set_private_flags
   PARAMS ((bfd *, flagword));
 static boolean elf32_arm_copy_private_bfd_data
@@ -33,11 +29,14 @@ static boolean elf32_arm_merge_private_bfd_data
   PARAMS ((bfd *, bfd *));
 static boolean elf32_arm_print_private_bfd_data
   PARAMS ((bfd *, PTR));
-static int elf32_arm_get_symbol_type 
+static int elf32_arm_get_symbol_type
   PARAMS (( Elf_Internal_Sym *, int));
 static struct bfd_link_hash_table *elf32_arm_link_hash_table_create
   PARAMS ((bfd *));
-
+static bfd_reloc_status_type elf32_arm_final_link_relocate
+  PARAMS ((reloc_howto_type *, bfd *, bfd *, asection *, bfd_byte *,
+          Elf_Internal_Rela *, bfd_vma, struct bfd_link_info *, asection *,
+          const char *, unsigned char, struct elf_link_hash_entry *));
 
 static insn32 insert_thumb_branch
   PARAMS ((insn32, int));
@@ -49,6 +48,14 @@ static void record_arm_to_thumb_glue
   PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
 static void record_thumb_to_arm_glue
   PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
+static void elf32_arm_post_process_headers
+  PARAMS ((bfd *, struct bfd_link_info *));
+static int elf32_arm_to_thumb_stub
+  PARAMS ((struct bfd_link_info *, const char *, bfd *, bfd *, asection *,
+          bfd_byte *, asection *, bfd_vma, bfd_signed_vma, bfd_vma));
+static int elf32_thumb_to_arm_stub
+  PARAMS ((struct bfd_link_info *, const char *, bfd *, bfd *, asection *,
+          bfd_byte *, asection *, bfd_vma, bfd_signed_vma, bfd_vma));
 
 /* The linker script knows the section names for placement.
    The entry_names are used to do simple name mangling on the stubs.
@@ -64,6 +71,81 @@ static void record_thumb_to_arm_glue
 #define ARM2THUMB_GLUE_SECTION_NAME ".glue_7"
 #define ARM2THUMB_GLUE_ENTRY_NAME   "__%s_from_arm"
 
+/* The name of the dynamic interpreter.  This is put in the .interp
+   section.  */
+#define ELF_DYNAMIC_INTERPRETER     "/usr/lib/ld.so.1"
+
+/* The size in bytes of an entry in the procedure linkage table.  */
+
+#define PLT_ENTRY_SIZE 16
+
+/* The first entry in a procedure linkage table looks like
+   this.  It is set up so that any shared library function that is
+   called before the relocation has been set up calls the dynamic
+   linker first */
+
+static const bfd_byte elf32_arm_plt0_entry [PLT_ENTRY_SIZE] =
+{
+  0x04, 0xe0, 0x2d, 0xe5,      /* str   lr, [sp, #-4]!     */
+  0x10, 0xe0, 0x9f, 0xe5,      /* ldr   lr, [pc, #16]      */
+  0x0e, 0xe0, 0x8f, 0xe0,      /* adr   lr, pc, lr         */
+  0x08, 0xf0, 0xbe, 0xe5       /* ldr   pc, [lr, #8]!      */
+};
+
+/* Subsequent entries in a procedure linkage table look like
+   this.  */
+
+static const bfd_byte elf32_arm_plt_entry [PLT_ENTRY_SIZE] =
+{
+  0x04, 0xc0, 0x9f, 0xe5,      /* ldr   ip, [pc, #4]       */
+  0x0c, 0xc0, 0x8f, 0xe0,      /* add   ip, pc, ip         */
+  0x00, 0xf0, 0x9c, 0xe5,      /* ldr   pc, [ip]           */
+  0x00, 0x00, 0x00, 0x00        /* offset to symbol in got  */
+};
+
+
+/* The ARM linker needs to keep track of the number of relocs that it
+   decides to copy in check_relocs for each symbol.  This is so that
+   it can discard PC relative relocs if it doesn't need them when
+   linking with -Bsymbolic.  We store the information in a field
+   extending the regular ELF linker hash table.  */
+
+/* This structure keeps track of the number of PC relative relocs we
+   have copied for a given symbol.  */
+
+struct elf32_arm_pcrel_relocs_copied
+{
+  /* Next section.  */
+  struct elf32_arm_pcrel_relocs_copied * next;
+  /* A section in dynobj.  */
+  asection * section;
+  /* Number of relocs copied in this section.  */
+  bfd_size_type count;
+};
+
+/* Arm ELF linker hash entry.  */
+
+struct elf32_arm_link_hash_entry
+{
+  struct elf_link_hash_entry root;
+
+  /* Number of PC relative relocs copied for this symbol.  */
+  struct elf32_arm_pcrel_relocs_copied * pcrel_relocs_copied;
+};
+
+/* Declare this now that the above structures are defined.  */
+
+static boolean elf32_arm_discard_copies
+  PARAMS ((struct elf32_arm_link_hash_entry *, PTR));
+
+/* Traverse an arm ELF linker hash table.  */
+
+#define elf32_arm_link_hash_traverse(table, func, info)                        \
+  (elf_link_hash_traverse                                              \
+   (&(table)->root,                                                    \
+    (boolean (*) PARAMS ((struct elf_link_hash_entry *, PTR))) (func), \
+    (info)))
+
 /* Get the ARM elf linker hash table from a link_info structure.  */
 #define elf32_arm_hash_table(info) \
   ((struct elf32_arm_link_hash_table *) ((info)->hash))
@@ -81,11 +163,43 @@ struct elf32_arm_link_hash_table
     long int arm_glue_size;
 
     /* An arbitary input BFD chosen to hold the glue sections.  */
-    bfd *bfd_of_glue_owner;
+    bfd * bfd_of_glue_owner;
 
+    /* A boolean indicating whether knowledge of the ARM's pipeline
+       length should be applied by the linker.  */
+    int no_pipeline_knowledge;
   };
 
 
+/* Create an entry in an ARM ELF linker hash table.  */
+
+static struct bfd_hash_entry *
+elf32_arm_link_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry * entry;
+     struct bfd_hash_table * table;
+     const char * string;
+{
+  struct elf32_arm_link_hash_entry * ret =
+    (struct elf32_arm_link_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct elf32_arm_link_hash_entry *) NULL)
+    ret = ((struct elf32_arm_link_hash_entry *)
+          bfd_hash_allocate (table,
+                             sizeof (struct elf32_arm_link_hash_entry)));
+  if (ret == (struct elf32_arm_link_hash_entry *) NULL)
+    return (struct bfd_hash_entry *) ret;
+
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct elf32_arm_link_hash_entry *)
+        _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret,
+                                    table, string));
+  if (ret != (struct elf32_arm_link_hash_entry *) NULL)
+    ret->pcrel_relocs_copied = NULL;
+
+  return (struct bfd_hash_entry *) ret;
+}
 
 /* Create an ARM elf linker hash table */
 
@@ -101,7 +215,7 @@ elf32_arm_link_hash_table_create (abfd)
     return NULL;
 
   if (!_bfd_elf_link_hash_table_init (&ret->root, abfd,
-                                     _bfd_elf_link_hash_newfunc))
+                                     elf32_arm_link_hash_newfunc))
     {
       bfd_release (abfd, ret);
       return NULL;
@@ -110,6 +224,7 @@ elf32_arm_link_hash_table_create (abfd)
   ret->thumb_glue_size = 0;
   ret->arm_glue_size = 0;
   ret->bfd_of_glue_owner = NULL;
+  ret->no_pipeline_knowledge = 0;
 
   return &ret->root.root;
 }
@@ -212,7 +327,7 @@ static const insn32 a2t3_func_addr_insn = 0x00000001;
    ldmia r13! {r6, lr}
    bx    lr
    __func_addr:
-   .word        func 
+   .word        func
  */
 
 #define THUMB2ARM_GLUE_SIZE 8
@@ -229,11 +344,11 @@ static const insn32 t2a6_bx_insn = 0xe12fff1e;
 
 boolean
 bfd_elf32_arm_allocate_interworking_sections (info)
-     struct bfd_link_info *info;
+     struct bfd_link_info * info;
 {
-  asection *s;
-  bfd_byte *foo;
-  struct elf32_arm_link_hash_table *globals;
+  asection * s;
+  bfd_byte * foo;
+  struct elf32_arm_link_hash_table * globals;
 
   globals = elf32_arm_hash_table (info);
 
@@ -276,14 +391,14 @@ bfd_elf32_arm_allocate_interworking_sections (info)
 
 static void
 record_arm_to_thumb_glue (link_info, h)
-     struct bfd_link_info *link_info;
-     struct elf_link_hash_entry *h;
+     struct bfd_link_info * link_info;
+     struct elf_link_hash_entry * h;
 {
-  const char *name = h->root.root.string;
-  register asection *s;
-  char *tmp_name;
-  struct elf_link_hash_entry *myh;
-  struct elf32_arm_link_hash_table *globals;
+  const char * name = h->root.root.string;
+  register asection * s;
+  char * tmp_name;
+  struct elf_link_hash_entry * myh;
+  struct elf32_arm_link_hash_table * globals;
 
   globals = elf32_arm_hash_table (link_info);
 
@@ -430,6 +545,9 @@ bfd_elf32_arm_get_bfd_for_interworking (abfd, info)
 
   if (sec == NULL)
     {
+      /* Note: we do not include the flag SEC_LINKER_CREATED, as this
+        will prevent elf_link_input_bfd() from processing the contents
+        of this section.  */
       flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY;
 
       sec = bfd_make_section (abfd, ARM2THUMB_GLUE_SECTION_NAME);
@@ -438,6 +556,10 @@ bfd_elf32_arm_get_bfd_for_interworking (abfd, info)
          || !bfd_set_section_flags (abfd, sec, flags)
          || !bfd_set_section_alignment (abfd, sec, 2))
        return false;
+      
+      /* Set the gc mark to prevent the section from being removed by garbage
+        collection, despite the fact that no relocs refer to this section.  */
+      sec->gc_mark = 1;
     }
 
   sec = bfd_get_section_by_name (abfd, THUMB2ARM_GLUE_SECTION_NAME);
@@ -452,6 +574,8 @@ bfd_elf32_arm_get_bfd_for_interworking (abfd, info)
          || !bfd_set_section_flags (abfd, sec, flags)
          || !bfd_set_section_alignment (abfd, sec, 2))
        return false;
+      
+      sec->gc_mark = 1;
     }
 
   /* Save the bfd for later use.  */
@@ -461,9 +585,10 @@ bfd_elf32_arm_get_bfd_for_interworking (abfd, info)
 }
 
 boolean
-bfd_elf32_arm_process_before_allocation (abfd, link_info)
+bfd_elf32_arm_process_before_allocation (abfd, link_info, no_pipeline_knowledge)
      bfd *abfd;
      struct bfd_link_info *link_info;
+     int no_pipeline_knowledge;
 {
   Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Rela *free_relocs = NULL;
@@ -489,6 +614,8 @@ bfd_elf32_arm_process_before_allocation (abfd, link_info)
   BFD_ASSERT (globals != NULL);
   BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
 
+  globals->no_pipeline_knowledge = no_pipeline_knowledge;
+
   /* Rummage around all the relocs and map the glue vectors.  */
   sec = abfd->sections;
 
@@ -513,7 +640,6 @@ bfd_elf32_arm_process_before_allocation (abfd, link_info)
        {
          long r_type;
          unsigned long r_index;
-         unsigned char code;
 
          struct elf_link_hash_entry *h;
 
@@ -521,7 +647,7 @@ bfd_elf32_arm_process_before_allocation (abfd, link_info)
          r_index = ELF32_R_SYM (irel->r_info);
 
          /* These are the only relocation types we care about */
-         if (r_type != R_ARM_PC24
+         if (   r_type != R_ARM_PC24
              && r_type != R_ARM_THM_PC22)
            continue;
 
@@ -596,9 +722,9 @@ bfd_elf32_arm_process_before_allocation (abfd, link_info)
              break;
 
            case R_ARM_THM_PC22:
-             /* This one is a call from thumb code.  We look 
+             /* This one is a call from thumb code.  We look
                 up the target of the call. If it is not a thumb
-                 target, we insert glue. */ 
+                 target, we insert glue.  */
 
              if (ELF_ST_TYPE (h->type) != STT_ARM_TFUNC)
                record_thumb_to_arm_glue (link_info, h);
@@ -622,61 +748,18 @@ error_return:
 
 }
 
-struct elf32_arm_reloc_map
-  {
-    unsigned char bfd_reloc_val;
-    unsigned char elf_reloc_val;
-  };
-
-static const struct elf32_arm_reloc_map elf32_arm_reloc_map[] =
-{
-  {BFD_RELOC_NONE, R_ARM_NONE,},
-  {BFD_RELOC_ARM_PCREL_BRANCH, R_ARM_PC24,},
-  {BFD_RELOC_32, R_ARM_ABS32,},
-  {BFD_RELOC_32_PCREL, R_ARM_REL32,},
-  {BFD_RELOC_8, R_ARM_ABS8,},
-  {BFD_RELOC_16, R_ARM_ABS16,},
-  {BFD_RELOC_ARM_OFFSET_IMM, R_ARM_ABS12,},
-  {BFD_RELOC_ARM_THUMB_OFFSET, R_ARM_THM_ABS5,},
-  {BFD_RELOC_THUMB_PCREL_BRANCH23, R_ARM_THM_PC22,},
-  {BFD_RELOC_VTABLE_INHERIT, R_ARM_GNU_VTINHERIT },
-  {BFD_RELOC_VTABLE_ENTRY, R_ARM_GNU_VTENTRY },
-  {BFD_RELOC_NONE, R_ARM_SBREL32,},
-  {BFD_RELOC_NONE, R_ARM_AMP_VCALL9,},
-  {BFD_RELOC_THUMB_PCREL_BRANCH12, R_ARM_THM_PC11,},
-  {BFD_RELOC_THUMB_PCREL_BRANCH9, R_ARM_THM_PC9,}
-};
-
-static reloc_howto_type *
-elf32_arm_reloc_type_lookup (abfd, code)
-     bfd *abfd;
-     bfd_reloc_code_real_type code;
-{
-  unsigned int i;
-
-  for (i = 0;
-     i < sizeof (elf32_arm_reloc_map) / sizeof (struct elf32_arm_reloc_map);
-       i++)
-    {
-      if (elf32_arm_reloc_map[i].bfd_reloc_val == code)
-       return &elf32_arm_howto_table[elf32_arm_reloc_map[i].elf_reloc_val];
-    }
-
-  return NULL;
-}
-
 /* 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 
+   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. 
+   instruction.
 
-   It takes two thumb instructions to encode the target address. Each has 
+   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). 
+   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 
+   Combine together and shifted left by 1 (it's a half word address) and
    there you have it.
 
    Op: 1111 = F,
@@ -684,7 +767,7 @@ elf32_arm_reloc_type_lookup (abfd, code)
    Op: 1111 = F,
    H-1, lower address-0 = 800
 
-   They can be ordered either way, but the arm tools I've seen always put 
+   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)
@@ -724,26 +807,26 @@ insert_thumb_branch (br_insn, rel_off)
 }
 
 /* Thumb code calling an ARM function */
-int
+static int
 elf32_thumb_to_arm_stub (info, name, input_bfd, output_bfd, input_section,
                         hit_data, sym_sec, offset, addend, val)
-     struct bfd_link_info *info;
-     char *name;
-     bfd *input_bfd;
-     bfd *output_bfd;
-     asection *input_section;
-     bfd_byte *hit_data;
-     asection *sym_sec;
-     int offset;
-     int addend;
-     bfd_vma val;
+     struct bfd_link_info * info;
+     const char *           name;
+     bfd *                  input_bfd;
+     bfd *                  output_bfd;
+     asection *             input_section;
+     bfd_byte *             hit_data;
+     asection *             sym_sec;
+     bfd_vma                offset;
+     bfd_signed_vma         addend;
+     bfd_vma                val;
 {
-  asection *s = 0;
+  asection * s = 0;
   long int my_offset;
   unsigned long int tmp;
   long int ret_offset;
-  struct elf_link_hash_entry *myh;
-  struct elf32_arm_link_hash_table *globals;
+  struct elf_link_hash_entry * myh;
+  struct elf32_arm_link_hash_table * globals;
 
   myh = find_thumb_glue (info, name, input_bfd);
   if (myh == NULL)
@@ -824,27 +907,26 @@ elf32_thumb_to_arm_stub (info, name, input_bfd, output_bfd, input_section,
 }
 
 /* Arm code calling a Thumb function */
-int
+static int
 elf32_arm_to_thumb_stub (info, name, input_bfd, output_bfd, input_section,
                         hit_data, sym_sec, offset, addend, val)
-
-     struct bfd_link_info *info;
-     char *name;
-     bfd *input_bfd;
-     bfd *output_bfd;
-     asection *input_section;
-     bfd_byte *hit_data;
-     asection *sym_sec;
-     int offset;
-     int addend;
-     bfd_vma val;
+     struct bfd_link_info * info;
+     const char *           name;
+     bfd *                  input_bfd;
+     bfd *                  output_bfd;
+     asection *             input_section;
+     bfd_byte *             hit_data;
+     asection *             sym_sec;
+     bfd_vma                offset;
+     bfd_signed_vma         addend;
+     bfd_vma                val;
 {
   unsigned long int tmp;
   long int my_offset;
-  asection *s;
+  asection * s;
   long int ret_offset;
-  struct elf_link_hash_entry *myh;
-  struct elf32_arm_link_hash_table *globals;
+  struct elf_link_hash_entry * myh;
+  struct elf32_arm_link_hash_table * globals;
 
   myh = find_arm_glue (info, name, input_bfd);
   if (myh == NULL)
@@ -902,93 +984,295 @@ elf32_arm_to_thumb_stub (info, name, input_bfd, output_bfd, input_section,
        + input_section->output_section->vma
        + offset + addend)
     - 8;
-
+  
   tmp = tmp | ((ret_offset >> 2) & 0x00FFFFFF);
 
   bfd_put_32 (output_bfd, tmp, hit_data
              - input_section->vma);
 
-
   return true;
 }
 
 /* Perform a relocation as part of a final link.  */
 static bfd_reloc_status_type
 elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
-                              input_section, contents, offset, value,
-                              addend, info, sym_sec, sym_name, sym_flags)
-     reloc_howto_type *howto;
-     bfd *input_bfd;
-     bfd *output_bfd;
-     asection *input_section;
-     bfd_byte *contents;
-     bfd_vma offset;
-     bfd_vma value;
-     bfd_vma addend;
-     struct bfd_link_info *info;
-     asection *sym_sec;
-     const char *sym_name;
-     unsigned char sym_flags;
+                              input_section, contents, rel, value,
+                              info, sym_sec, sym_name, sym_flags, h)
+     reloc_howto_type *     howto;
+     bfd *                  input_bfd;
+     bfd *                  output_bfd;
+     asection *             input_section;
+     bfd_byte *             contents;
+     Elf_Internal_Rela *    rel;
+     bfd_vma                value;
+     struct bfd_link_info * info;
+     asection *             sym_sec;
+     const char *           sym_name;
+     unsigned char          sym_flags;
+     struct elf_link_hash_entry * h;
 {
-  unsigned long r_type = howto->type;
-  bfd_byte *hit_data = contents + offset;
+  unsigned long                 r_type = howto->type;
+  unsigned long                 r_symndx;
+  bfd_byte *                    hit_data = contents + rel->r_offset;
+  bfd *                         dynobj = NULL;
+  Elf_Internal_Shdr *           symtab_hdr;
+  struct elf_link_hash_entry ** sym_hashes;
+  bfd_vma *                     local_got_offsets;
+  asection *                    sgot = NULL;
+  asection *                    splt = NULL;
+  asection *                    sreloc = NULL;
+  bfd_vma                       addend;
+  bfd_signed_vma                signed_addend;
+  struct elf32_arm_link_hash_table * globals;
 
-  switch (r_type)
+  globals = elf32_arm_hash_table (info);
+
+  dynobj = elf_hash_table (info)->dynobj;
+  if (dynobj)
+    {
+      sgot = bfd_get_section_by_name (dynobj, ".got");
+      splt = bfd_get_section_by_name (dynobj, ".plt");
+    }
+  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+  sym_hashes = elf_sym_hashes (input_bfd);
+  local_got_offsets = elf_local_got_offsets (input_bfd);
+  r_symndx = ELF32_R_SYM (rel->r_info);
+
+#ifdef USE_REL
+  addend = bfd_get_32 (input_bfd, hit_data) & howto->src_mask;
+
+  if (addend & ((howto->src_mask + 1) >> 1))
     {
+      signed_addend = -1;
+      signed_addend &= ~ howto->src_mask;
+      signed_addend |= addend;
+    }
+  else
+    signed_addend = addend;
+#else
+  addend = signed_addend = rel->r_addend;
+#endif
 
+  switch (r_type)
+    {
     case R_ARM_NONE:
       return bfd_reloc_ok;
 
     case R_ARM_PC24:
-      /* Arm B/BL instruction */
-
-#ifdef USE_REL
-      addend = (bfd_get_32 (input_bfd, hit_data) & howto->src_mask);
-#endif
-      /* check for arm calling thumb function */
-      if (sym_flags == STT_ARM_TFUNC)
+    case R_ARM_ABS32:
+    case R_ARM_REL32:
+      /* When generating a shared object, these relocations are copied
+        into the output file to be resolved at run time. */
+
+      if (info->shared
+         && (r_type != R_ARM_PC24
+             || (h != NULL
+                 && h->dynindx != -1
+                 && (! info->symbolic
+                     || (h->elf_link_hash_flags
+                         & ELF_LINK_HASH_DEF_REGULAR) == 0))))
        {
-         elf32_arm_to_thumb_stub (info, sym_name, input_bfd, output_bfd,
-                  input_section, hit_data, sym_sec, offset, addend, value);
-         return bfd_reloc_ok;
+         Elf_Internal_Rel outrel;
+         boolean skip, relocate;
+
+         if (sreloc == NULL)
+           {
+             const char * name;
+
+             name = (bfd_elf_string_from_elf_section
+                     (input_bfd,
+                      elf_elfheader (input_bfd)->e_shstrndx,
+                      elf_section_data (input_section)->rel_hdr.sh_name));
+             if (name == NULL)
+               return bfd_reloc_notsupported;
+
+             BFD_ASSERT (strncmp (name, ".rel", 4) == 0
+                         && strcmp (bfd_get_section_name (input_bfd,
+                                                          input_section),
+                                    name + 4) == 0);
+
+             sreloc = bfd_get_section_by_name (dynobj, name);
+             BFD_ASSERT (sreloc != NULL);
+           }
+
+         skip = false;
+
+         if (elf_section_data (input_section)->stab_info == NULL)
+           outrel.r_offset = rel->r_offset;
+         else
+           {
+             bfd_vma off;
+
+             off = (_bfd_stab_section_offset
+                    (output_bfd, &elf_hash_table (info)->stab_info,
+                     input_section,
+                     & elf_section_data (input_section)->stab_info,
+                     rel->r_offset));
+             if (off == (bfd_vma) -1)
+               skip = true;
+             outrel.r_offset = off;
+           }
+
+         outrel.r_offset += (input_section->output_section->vma
+                             + input_section->output_offset);
+
+         if (skip)
+           {
+             memset (&outrel, 0, sizeof outrel);
+             relocate = false;
+           }
+         else if (r_type == R_ARM_PC24)
+           {
+             BFD_ASSERT (h != NULL && h->dynindx != -1);
+             if ((input_section->flags & SEC_ALLOC) != 0)
+               relocate = false;
+             else
+               relocate = true;
+             outrel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_PC24);
+           }
+         else
+           {
+             if (h == NULL
+                 || ((info->symbolic || h->dynindx == -1)
+                     && (h->elf_link_hash_flags
+                         & ELF_LINK_HASH_DEF_REGULAR) != 0))
+               {
+                 relocate = true;
+                 outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
+               }
+             else
+               {
+                 BFD_ASSERT (h->dynindx != -1);
+                 if ((input_section->flags & SEC_ALLOC) != 0)
+                   relocate = false;
+                 else
+                   relocate = true;
+                 outrel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_ABS32);
+               }
+           }
+
+         bfd_elf32_swap_reloc_out (output_bfd, &outrel,
+                                   (((Elf32_External_Rel *)
+                                     sreloc->contents)
+                                    + sreloc->reloc_count));
+         ++sreloc->reloc_count;
+         
+         /* If this reloc is against an external symbol, we do not want to
+            fiddle with the addend.  Otherwise, we need to include the symbol
+            value so that it becomes an addend for the dynamic reloc. */
+         if (! relocate)
+           return bfd_reloc_ok;
+
+         
+         return _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                          contents, rel->r_offset, value,
+                                          (bfd_vma) 0);
        }
+      else switch (r_type)
+       {
+       case R_ARM_PC24:
+         /* Arm B/BL instruction */
 
-      value = value + addend;
-      value -= (input_section->output_section->vma
-               + input_section->output_offset + 8);
-      value -= offset;
-      value = value >> howto->rightshift;
+         /* Check for arm calling thumb function.  */
+         if (sym_flags == STT_ARM_TFUNC)
+           {
+             elf32_arm_to_thumb_stub (info, sym_name, input_bfd, output_bfd,
+                                      input_section, hit_data, sym_sec, rel->r_offset,
+                                      signed_addend, value);
+             return bfd_reloc_ok;
+           }
 
-      value &= 0xffffff;
-      value |= (bfd_get_32 (input_bfd, hit_data) & 0xff000000);
-      bfd_put_32 (input_bfd, value, hit_data);
-      return bfd_reloc_ok;
+         if (   strcmp (bfd_get_target (input_bfd), "elf32-littlearm-oabi") == 0
+             || strcmp (bfd_get_target (input_bfd), "elf32-bigarm-oabi") == 0)
+           {
+             /* The old way of doing things.  Trearing the addend as a
+                byte sized field and adding in the pipeline offset.  */
 
-    case R_ARM_ABS32:
-#ifdef USE_REL
-      addend = (bfd_get_32 (input_bfd, hit_data) & howto->src_mask);
-#endif
-      value += addend;
-      if (sym_flags == STT_ARM_TFUNC)
-       value |= 1;
-      bfd_put_32 (input_bfd, value, hit_data);
-      return bfd_reloc_ok;
+             value -= (input_section->output_section->vma
+                       + input_section->output_offset);
+             value -= rel->r_offset;
+             value += addend;
 
-    case R_ARM_REL32:
-#ifdef USE_REL
-      addend = (bfd_get_32 (input_bfd, hit_data) & howto->src_mask);
-#endif
-      value -= (input_section->output_section->vma
-               + input_section->output_offset);
-      value += addend;
+             if (! globals->no_pipeline_knowledge)
+               value -= 8;
+           }
+         else
+           {
+             /* The ARM ELF ABI says that this reloc is computed as: S - P + A
+                where:
+                 S is the address of the symbol in the relocation.
+                 P is address of the instruction being relocated.
+                 A is the addend (extracted from the instruction) in bytes.
+
+                S is held in 'value'.
+                P is the base address of the section containing the instruction
+                  plus the offset of the reloc into that section, ie:
+                    (input_section->output_section->vma +
+                     input_section->output_offset +
+                     rel->r_offset).
+                A is the addend, converted into bytes, ie:
+                    (signed_addend * 4)
+
+                Note: None of these operations have knowledge of the pipeline
+                size of the processor, thus it is up to the assembler to encode
+                this information into the addend.  */
+
+             value -= (input_section->output_section->vma
+                       + input_section->output_offset);
+             value -= rel->r_offset;
+             value += (signed_addend << howto->size);
+
+             /* Previous versions of this code also used to add in the pipeline
+                offset here.  This is wrong because the linker is not supposed
+                to know about such things, and one day it might change.  In order
+                to support old binaries that need the old behaviour however, so
+                we attempt to detect which ABI was used to create the reloc.  */
+             if (! globals->no_pipeline_knowledge)
+               {
+                 Elf_Internal_Ehdr * i_ehdrp; /* Elf file header, internal form */
+
+                 i_ehdrp = elf_elfheader (input_bfd);
+
+                 if (i_ehdrp->e_ident[EI_OSABI] == 0)
+                   value -= 8;
+               }
+           }
+
+         /* It is not an error for an undefined weak reference to be
+            out of range.  Any program that branches to such a symbol
+            is going to crash anyway, so there is no point worrying 
+            about getting the destination exactly right.  */        
+         if (! h || h->root.type != bfd_link_hash_undefweak)
+           {
+             /* Perform a signed range check.  */
+             signed_addend = value;
+             signed_addend >>= howto->rightshift;
+             if (signed_addend > ((bfd_signed_vma)(howto->dst_mask >> 1))
+                 || signed_addend < - ((bfd_signed_vma) ((howto->dst_mask + 1) >> 1)))
+               return bfd_reloc_overflow;
+           }
+             
+         value = (signed_addend & howto->dst_mask)
+           | (bfd_get_32 (input_bfd, hit_data) & (~ howto->dst_mask));
+         break;
+
+       case R_ARM_ABS32:
+         value += addend;
+         if (sym_flags == STT_ARM_TFUNC)
+           value |= 1;
+         break;
+
+       case R_ARM_REL32:
+         value -= (input_section->output_section->vma
+                   + input_section->output_offset);
+         value += addend;
+         break;
+       }
 
       bfd_put_32 (input_bfd, value, hit_data);
       return bfd_reloc_ok;
 
     case R_ARM_ABS8:
-#ifdef USE_REL
-     addend = (bfd_get_32 (input_bfd, hit_data) & howto->src_mask);
-#endif
       value += addend;
       if ((long) value > 0x7f || (long) value < -0x80)
        return bfd_reloc_overflow;
@@ -997,9 +1281,6 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
       return bfd_reloc_ok;
 
     case R_ARM_ABS16:
-#ifdef USE_REL
-      addend = (bfd_get_32 (input_bfd, hit_data) & howto->src_mask);
-#endif
       value += addend;
 
       if ((long) value > 0x7fff || (long) value < -0x8000)
@@ -1010,10 +1291,7 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
 
     case R_ARM_ABS12:
       /* Support ldr and str instruction for the arm */
-      /* Also thumb b (unconditional branch) */
-#ifdef USE_REL
-      addend = (bfd_get_32 (input_bfd, hit_data) & howto->src_mask);
-#endif
+      /* Also thumb b (unconditional branch).  ??? Really?  */
       value += addend;
 
       if ((long) value > 0x7ff || (long) value < -0x800)
@@ -1025,44 +1303,82 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
 
     case R_ARM_THM_ABS5:
       /* Support ldr and str instructions for the thumb. */
+#ifdef USE_REL
+      /* Need to refetch addend.  */
+      addend = bfd_get_16 (input_bfd, hit_data) & howto->src_mask;
+      /* ??? Need to determine shift amount from operand size.  */
+      addend >>= howto->rightshift;
+#endif
       value += addend;
 
+      /* ??? Isn't value unsigned?  */
       if ((long) value > 0x1f || (long) value < -0x10)
        return bfd_reloc_overflow;
 
-      value |= bfd_get_16 (input_bfd, hit_data) & 0xf82f;
+      /* ??? Value needs to be properly shifted into place first.  */
+      value |= bfd_get_16 (input_bfd, hit_data) & 0xf83f;
       bfd_put_16 (input_bfd, value, hit_data);
       return bfd_reloc_ok;
 
-
     case R_ARM_THM_PC22:
-      /* thumb BL (branch long instruction). */
+      /* Thumb BL (branch long instruction). */
       {
-       bfd_vma relocation;
-       boolean overflow = false;
-       bfd_vma insn = bfd_get_32 (input_bfd, hit_data);
-       bfd_vma src_mask = 0x007FFFFE;
+       bfd_vma        relocation;
+       boolean        overflow = false;
+       bfd_vma        upper_insn = bfd_get_16 (input_bfd, hit_data);
+       bfd_vma        lower_insn = bfd_get_16 (input_bfd, hit_data + 2);
        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 reloc_signed_min = ~ reloc_signed_max;
+       bfd_vma        check;
        bfd_signed_vma signed_check;
-       bfd_vma add;
-       bfd_signed_vma signed_add;
 
-        /* If it's not a call to thumb, assume call to arm */
-       if (sym_flags != STT_ARM_TFUNC)
+#ifdef USE_REL
+       /* Need to refetch the addend and squish the two 11 bit pieces
+          together.  */
+       {
+         bfd_vma upper = upper_insn & 0x7ff;
+         bfd_vma lower = lower_insn & 0x7ff;
+         upper = (upper ^ 0x400) - 0x400; /* sign extend */
+         addend = (upper << 12) | (lower << 1);
+         signed_addend = addend;
+       }
+#endif
+
+        /* If it is not a call to thumb, assume call to arm.
+          If it is a call relative to a section name, then it is not a
+          function call at all, but rather a long jump.  */
+       if (sym_flags != STT_ARM_TFUNC && sym_flags != STT_SECTION)
          {
            if (elf32_thumb_to_arm_stub
                (info, sym_name, input_bfd, output_bfd, input_section,
-                hit_data, sym_sec, offset, addend, value))
+                hit_data, sym_sec, rel->r_offset, signed_addend, value))
              return bfd_reloc_ok;
            else
              return bfd_reloc_dangerous;
          }
 
-       relocation = value + addend;
-       relocation -= (input_section->output_section->vma + input_section->output_offset);
-       relocation -= offset;
+       relocation = value + signed_addend;
+
+       relocation -= (input_section->output_section->vma
+                      + input_section->output_offset
+                      + rel->r_offset);
+       
+       if (! globals->no_pipeline_knowledge)
+         {
+           Elf_Internal_Ehdr * i_ehdrp; /* Elf file header, internal form */
+           
+           i_ehdrp = elf_elfheader (input_bfd);
+
+           /* Previous versions of this code also used to add in the pipline
+              offset here.  This is wrong because the linker is not supposed
+              to know about such things, and one day it might change.  In order
+              to support old binaries that need the old behaviour however, so
+              we attempt to detect which ABI was used to create the reloc.  */
+           if (   strcmp (bfd_get_target (input_bfd), "elf32-littlearm-oabi") == 0
+               || strcmp (bfd_get_target (input_bfd), "elf32-bigarm-oabi") == 0
+               || i_ehdrp->e_ident[EI_OSABI] == 0)
+             relocation += 4;
+         }
 
        check = relocation >> howto->rightshift;
 
@@ -1071,51 +1387,19 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
        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 = (((insn) & 0x07ff0000) >> 4) | (((insn) & 0x7ff) << 1);
-       else
-         add = ((((insn) & 0x7ff) << 12) | (((insn) & 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;
+         signed_check = check | ~((bfd_vma) -1 >> howto->rightshift);
 
        /* Assumes two's complement.  */
-       if (signed_check > reloc_signed_max
-           || signed_check < reloc_signed_min)
+       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:  */
-       insn = ((insn & ~howto->dst_mask) | relocation);
+       /* Put RELOCATION back into the insn.  */
+       upper_insn = (upper_insn & ~(bfd_vma) 0x7ff) | ((relocation >> 12) & 0x7ff);
+       lower_insn = (lower_insn & ~(bfd_vma) 0x7ff) | ((relocation >> 1) & 0x7ff);
 
        /* Put the relocated value back in the object file:  */
-       bfd_put_32 (input_bfd, insn, hit_data);
+       bfd_put_16 (input_bfd, upper_insn, hit_data);
+       bfd_put_16 (input_bfd, lower_insn, hit_data + 2);
 
        return (overflow ? bfd_reloc_overflow : bfd_reloc_ok);
       }
@@ -1125,6 +1409,165 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
     case R_ARM_GNU_VTENTRY:
       return bfd_reloc_ok;
 
+    case R_ARM_COPY:
+      return bfd_reloc_notsupported;
+
+    case R_ARM_GLOB_DAT:
+      return bfd_reloc_notsupported;
+
+    case R_ARM_JUMP_SLOT:
+      return bfd_reloc_notsupported;
+
+    case R_ARM_RELATIVE:
+      return bfd_reloc_notsupported;
+
+    case R_ARM_GOTOFF:
+      /* Relocation is relative to the start of the
+         global offset table.  */
+
+      BFD_ASSERT (sgot != NULL);
+      if (sgot == NULL)
+        return bfd_reloc_notsupported;
+      
+      /* Note that sgot->output_offset is not involved in this
+         calculation.  We always want the start of .got.  If we
+         define _GLOBAL_OFFSET_TABLE in a different way, as is
+         permitted by the ABI, we might have to change this
+         calculation. */
+
+      value -= sgot->output_section->vma;
+      return _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                      contents, rel->r_offset, value,
+                                      (bfd_vma) 0);
+
+    case R_ARM_GOTPC:
+      /* Use global offset table as symbol value. */
+
+      BFD_ASSERT (sgot != NULL);
+
+      if (sgot == NULL)
+        return bfd_reloc_notsupported;
+
+      value = sgot->output_section->vma;
+      return _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                      contents, rel->r_offset, value,
+                                      (bfd_vma) 0);
+
+    case R_ARM_GOT32:
+      /* Relocation is to the entry for this symbol in the
+         global offset table. */
+      if (sgot == NULL)
+       return bfd_reloc_notsupported;
+
+      if (h != NULL)
+       {
+         bfd_vma off;
+
+         off = h->got.offset;
+         BFD_ASSERT (off != (bfd_vma) -1);
+
+         if (!elf_hash_table (info)->dynamic_sections_created ||
+             (info->shared && (info->symbolic || h->dynindx == -1)
+              && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
+           {
+             /* This is actually a static link, or it is a -Bsymbolic link
+                and the symbol is defined locally.  We must initialize this
+                entry in the global offset table.  Since the offset must
+                always be a multiple of 4, we use the least significant bit
+                to record whether we have initialized it already.
+
+                When doing a dynamic link, we create a .rel.got relocation
+                entry to initialize the value.  This is done in the
+                finish_dynamic_symbol routine. */
+
+             if ((off & 1) != 0)
+               off &= ~1;
+             else
+               {
+                 bfd_put_32 (output_bfd, value, sgot->contents + off);
+                 h->got.offset |= 1;
+               }
+           }
+
+         value = sgot->output_offset + off;
+       }
+      else
+       {
+         bfd_vma off;
+
+         BFD_ASSERT (local_got_offsets != NULL &&
+                     local_got_offsets[r_symndx] != (bfd_vma) -1);
+
+         off = local_got_offsets[r_symndx];
+
+         /* The offset must always be a multiple of 4.  We use the
+            least significant bit to record whether we have already
+            generated the necessary reloc. */
+         if ((off & 1) != 0)
+           off &= ~1;
+         else
+           {
+             bfd_put_32 (output_bfd, value, sgot->contents + off);
+
+             if (info->shared)
+               {
+                 asection * srelgot;
+                 Elf_Internal_Rel outrel;
+
+                 srelgot = bfd_get_section_by_name (dynobj, ".rel.got");
+                 BFD_ASSERT (srelgot != NULL);
+
+                 outrel.r_offset = (sgot->output_section->vma
+                                    + sgot->output_offset
+                                    + off);
+                 outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
+                 bfd_elf32_swap_reloc_out (output_bfd, &outrel,
+                                           (((Elf32_External_Rel *)
+                                             srelgot->contents)
+                                            + srelgot->reloc_count));
+                 ++srelgot->reloc_count;
+               }
+
+             local_got_offsets[r_symndx] |= 1;
+           }
+
+         value = sgot->output_offset + off;
+       }
+      
+      return _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                      contents, rel->r_offset, value,
+                                      (bfd_vma) 0);
+
+    case R_ARM_PLT32:
+      /* Relocation is to the entry for this symbol in the
+         procedure linkage table.  */
+
+      /* Resolve a PLT32 reloc against a local symbol directly,
+         without using the procedure linkage table. */
+      if (h == NULL)
+        return _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                        contents, rel->r_offset, value,
+                                        (bfd_vma) 0);
+
+      if (h->plt.offset == (bfd_vma) -1)
+        /* We didn't make a PLT entry for this symbol.  This
+           happens when statically linking PIC code, or when
+           using -Bsymbolic.  */
+       return _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                        contents, rel->r_offset, value,
+                                        (bfd_vma) 0);
+
+      BFD_ASSERT(splt != NULL);
+      if (splt == NULL)
+        return bfd_reloc_notsupported;
+
+      value = (splt->output_section->vma
+              + splt->output_offset
+              + h->plt.offset);
+      return _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                      contents, rel->r_offset, value,
+                                      (bfd_vma) 0);
+
     case R_ARM_SBREL32:
       return bfd_reloc_notsupported;
 
@@ -1154,49 +1597,101 @@ elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
     }
 }
 
+#ifdef USE_REL
+/* Add INCREMENT to the reloc (of type HOWTO) at ADDRESS.  */
+static void
+arm_add_to_rel (abfd, address, howto, increment)
+     bfd *              abfd;
+     bfd_byte *         address;
+     reloc_howto_type * howto;
+     bfd_signed_vma     increment;
+{
+  bfd_vma        contents;
+  bfd_signed_vma addend;
+
+  contents = bfd_get_32 (abfd, address);
+
+  /* Get the (signed) value from the instruction.  */
+  addend = contents & howto->src_mask;
+  if (addend & ((howto->src_mask + 1) >> 1))
+    {
+      bfd_signed_vma mask;
+      
+      mask = -1;
+      mask &= ~ howto->src_mask;
+      addend |= mask;
+    }
+
+  /* Add in the increment, (which is a byte value).  */
+  switch (howto->type)
+    {
+    case R_ARM_THM_PC22:
+    default:
+      addend += increment;
+      break;
+      
+    case R_ARM_PC24:
+      addend <<= howto->size;
+      addend +=  increment;
+      
+      /* Should we check for overflow here ?  */
+
+      /* Drop any undesired bits.  */
+      addend >>= howto->rightshift;
+      break;
+    }
+  
+  contents = (contents & ~ howto->dst_mask) | (addend & howto->dst_mask);
+  
+  bfd_put_32 (abfd, contents, address);
+}
+#endif /* USE_REL */
 
 /* Relocate an ARM ELF section.  */
 static boolean
 elf32_arm_relocate_section (output_bfd, info, input_bfd, input_section,
                            contents, relocs, local_syms, local_sections)
-     bfd *output_bfd;
-     struct bfd_link_info *info;
-     bfd *input_bfd;
-     asection *input_section;
-     bfd_byte *contents;
-     Elf_Internal_Rela *relocs;
-     Elf_Internal_Sym *local_syms;
-     asection **local_sections;
+     bfd *                  output_bfd;
+     struct bfd_link_info * info;
+     bfd *                  input_bfd;
+     asection *             input_section;
+     bfd_byte *             contents;
+     Elf_Internal_Rela *    relocs;
+     Elf_Internal_Sym *     local_syms;
+     asection **            local_sections;
 {
-  Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes;
-  Elf_Internal_Rela *rel, *relend;
-  const char *name;
+  Elf_Internal_Shdr *           symtab_hdr;
+  struct elf_link_hash_entry ** sym_hashes;
+  Elf_Internal_Rela *           rel;
+  Elf_Internal_Rela *           relend;
+  const char *                  name;
 
-  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (input_bfd);
 
   rel = relocs;
   relend = relocs + input_section->reloc_count;
   for (; rel < relend; rel++)
     {
-      int r_type;
-      reloc_howto_type *howto;
-      unsigned long r_symndx;
-      Elf_Internal_Sym *sym;
-      asection *sec;
-      struct elf_link_hash_entry *h;
-      bfd_vma relocation;
-      bfd_reloc_status_type r;
+      int                          r_type;
+      reloc_howto_type *           howto;
+      unsigned long                r_symndx;
+      Elf_Internal_Sym *           sym;
+      asection *                   sec;
+      struct elf_link_hash_entry * h;
+      bfd_vma                      relocation;
+      bfd_reloc_status_type        r;
+      arelent                      bfd_reloc;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
-      r_type = ELF32_R_TYPE (rel->r_info);
+      r_type   = ELF32_R_TYPE (rel->r_info);
 
-      if (r_type == R_ARM_GNU_VTENTRY
-          || r_type == R_ARM_GNU_VTINHERIT )
+      if (   r_type == R_ARM_GNU_VTENTRY
+          || r_type == R_ARM_GNU_VTINHERIT)
         continue;
 
-      howto = elf32_arm_howto_table + r_type;
+      elf32_arm_info_to_howto (input_bfd, & bfd_reloc, rel);
+      howto = bfd_reloc.howto;
 
       if (info->relocateable)
        {
@@ -1210,7 +1705,13 @@ elf32_arm_relocate_section (output_bfd, info, input_bfd, input_section,
              if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
                {
                  sec = local_sections[r_symndx];
-                 rel->r_addend += sec->output_offset + sym->st_value;
+#ifdef USE_REL
+                 arm_add_to_rel (input_bfd, contents + rel->r_offset,
+                                 howto, sec->output_offset + sym->st_value);
+#else
+                 rel->r_addend += (sec->output_offset + sym->st_value)
+                   >> howto->rightshift;
+#endif
                }
            }
 
@@ -1238,13 +1739,68 @@ elf32_arm_relocate_section (output_bfd, info, input_bfd, input_section,
          if (h->root.type == bfd_link_hash_defined
              || h->root.type == bfd_link_hash_defweak)
            {
+             int relocation_needed = 1;
+
              sec = h->root.u.def.section;
-             relocation = (h->root.u.def.value
-                           + sec->output_section->vma
-                           + sec->output_offset);
+
+             /* In these cases, we don't need the relocation value.
+                We check specially because in some obscure cases
+                sec->output_section will be NULL. */
+             switch (r_type)
+               {
+               case R_ARM_PC24:
+               case R_ARM_ABS32:
+                 if (info->shared
+                     && (
+                         (!info->symbolic && h->dynindx != -1)
+                         || (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
+                         )
+                     && ((input_section->flags & SEC_ALLOC) != 0)
+                     )
+                   relocation_needed = 0;
+                 break;
+
+               case R_ARM_GOTPC:
+                 relocation_needed = 0;
+                 break;
+
+               case R_ARM_GOT32:
+                 if (elf_hash_table(info)->dynamic_sections_created
+                     && (!info->shared
+                         || (!info->symbolic && h->dynindx != -1)
+                         || (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
+                         )
+                     )
+                   relocation_needed = 0;
+                 break;
+
+               case R_ARM_PLT32:
+                 if (h->plt.offset != (bfd_vma)-1)
+                   relocation_needed = 0;
+                 break;
+
+               default:
+                 if (sec->output_section == NULL)
+                   {
+                     (*_bfd_error_handler)
+                       (_("%s: warning: unresolvable relocation against symbol `%s' from %s section"),
+                        bfd_get_filename (input_bfd), h->root.root.string,
+                        bfd_get_section_name (input_bfd, input_section));
+                     relocation_needed = 0;
+                   }
+               }
+
+             if (relocation_needed)
+               relocation = h->root.u.def.value
+                 + sec->output_section->vma
+                 + sec->output_offset;
+             else
+               relocation = 0;
            }
          else if (h->root.type == bfd_link_hash_undefweak)
            relocation = 0;
+         else if (info->shared && !info->symbolic && !info->no_undefined)
+           relocation = 0;
          else
            {
              if (!((*info->callbacks->undefined_symbol)
@@ -1264,14 +1820,12 @@ elf32_arm_relocate_section (output_bfd, info, input_bfd, input_section,
          if (name == NULL || *name == '\0')
            name = bfd_section_name (input_bfd, sec);
        }
-      
+
       r = elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
-                                        input_section,
-                                        contents, rel->r_offset,
-                                        relocation, rel->r_addend,
-                                        info, sec, name,
+                                        input_section, contents, rel,
+                                        relocation, info, sec, name,
                                         (h ? ELF_ST_TYPE (h->type) :
-                                         ELF_ST_TYPE (sym->st_info)));
+                                         ELF_ST_TYPE (sym->st_info)), h);
 
       if (r != bfd_reloc_ok)
        {
@@ -1408,17 +1962,32 @@ elf32_arm_merge_private_bfd_data (ibfd, obfd)
   flagword out_flags;
   flagword in_flags;
 
-  if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
+  if (   bfd_get_flavour (ibfd) != bfd_target_elf_flavour
       || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
     return true;
 
+  /* Check if we have the same endianess */
+  if (   ibfd->xvec->byteorder != obfd->xvec->byteorder
+      && obfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN
+      && ibfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN)
+    {
+      (*_bfd_error_handler)
+       (_("%s: compiled for a %s endian system and target is %s endian"),
+        bfd_get_filename (ibfd),
+        bfd_big_endian (ibfd) ? "big" : "little",
+        bfd_big_endian (obfd) ? "big" : "little");
+
+      bfd_set_error (bfd_error_wrong_format);
+      return false;
+    }
+
   /* The input BFD must have had its flags initialised.  */
   /* The following seems bogus to me -- The flags are initialized in
      the assembler but I don't think an elf_flags_init field is
      written into the object */
   /* BFD_ASSERT (elf_flags_init (ibfd)); */
 
-  in_flags = elf_elfheader (ibfd)->e_flags;
+  in_flags  = elf_elfheader (ibfd)->e_flags;
   out_flags = elf_elfheader (obfd)->e_flags;
 
   if (!elf_flags_init (obfd))
@@ -1539,11 +2108,11 @@ elf32_arm_get_symbol_type (elf_sym, type)
   else
     return type;
 }
-    
+
 static asection *
 elf32_arm_gc_mark_hook (abfd, info, rel, h, sym)
        bfd *abfd;
-       struct bfd_link_info *info;
+       struct bfd_link_info *info ATTRIBUTE_UNUSED;
        Elf_Internal_Rela *rel;
        struct elf_link_hash_entry *h;
        Elf_Internal_Sym *sym;
@@ -1565,6 +2134,9 @@ elf32_arm_gc_mark_hook (abfd, info, rel, h, sym)
 
           case bfd_link_hash_common:
             return h->root.u.c.p->section;
+
+         default:
+           break;
           }
        }
      }
@@ -1581,81 +2153,300 @@ elf32_arm_gc_mark_hook (abfd, info, rel, h, sym)
   return NULL;
 }
 
+/* Update the got entry reference counts for the section being removed.  */
+
 static boolean
 elf32_arm_gc_sweep_hook (abfd, info, sec, relocs)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     asection *sec;
-     const Elf_Internal_Rela *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 armelf */
+  /* We don't support garbage collection of GOT and PLT relocs yet.  */
   return true;
 }
 
-/* Look through the relocs for a section during the first phase.
-   Since we don't do .gots or .plts, we just need to consider the
-   virtual table relocs for gc.  */
+/* Look through the relocs for a section during the first phase.  */
+
 static boolean
 elf32_arm_check_relocs (abfd, info, sec, relocs)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     asection *sec;
-     const Elf_Internal_Rela *relocs;
+     bfd *                      abfd;
+     struct bfd_link_info *     info;
+     asection *                 sec;
+     const Elf_Internal_Rela *  relocs;
 {
-  Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes, **sym_hashes_end;
-  const Elf_Internal_Rela *rel;
-  const Elf_Internal_Rela *rel_end;
+  Elf_Internal_Shdr *           symtab_hdr;
+  struct elf_link_hash_entry ** sym_hashes;
+  struct elf_link_hash_entry ** sym_hashes_end;
+  const Elf_Internal_Rela *     rel;
+  const Elf_Internal_Rela *     rel_end;
+  bfd *                         dynobj;
+  asection * sgot, *srelgot, *sreloc;
+  bfd_vma * local_got_offsets;
+  
   if (info->relocateable)
     return true;
+  
+  sgot = srelgot = sreloc = NULL;
+  
+  dynobj = elf_hash_table (info)->dynobj;
+  local_got_offsets = elf_local_got_offsets (abfd);
+
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
   sym_hashes_end = sym_hashes + symtab_hdr->sh_size/sizeof(Elf32_External_Sym);
   if (!elf_bad_symtab (abfd))
     sym_hashes_end -= symtab_hdr->sh_info;
+  
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
       struct elf_link_hash_entry *h;
       unsigned long r_symndx;
+      
       r_symndx = ELF32_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
         h = NULL;
       else
         h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+      
+      /* Some relocs require a global offset table.  */
+      if (dynobj == NULL)
+       {
+         switch (ELF32_R_TYPE (rel->r_info))
+           {
+           case R_ARM_GOT32:
+           case R_ARM_GOTOFF:
+           case R_ARM_GOTPC:
+             elf_hash_table (info)->dynobj = dynobj = abfd;
+             if (! _bfd_elf_create_got_section (dynobj, info))
+               return false;
+             break;
+
+           default:
+             break;
+           }
+       }
+
       switch (ELF32_R_TYPE (rel->r_info))
         {
+         case R_ARM_GOT32:
+           /* This symbol requires a global offset table entry.  */
+           if (sgot == NULL)
+             {
+               sgot = bfd_get_section_by_name (dynobj, ".got");
+               BFD_ASSERT (sgot != NULL);
+             }
+
+           /* Get the got relocation section if necessary.  */
+           if (srelgot == NULL
+               && (h != NULL || info->shared))
+             {
+               srelgot = bfd_get_section_by_name (dynobj, ".rel.got");
+               
+               /* If no got relocation section, make one and initialize.  */
+               if (srelgot == NULL)
+                 {
+                   srelgot = bfd_make_section (dynobj, ".rel.got");
+                   if (srelgot == NULL
+                       || ! bfd_set_section_flags (dynobj, srelgot,
+                                                   (SEC_ALLOC
+                                                    | SEC_LOAD
+                                                    | SEC_HAS_CONTENTS
+                                                    | SEC_IN_MEMORY
+                                                    | SEC_LINKER_CREATED
+                                                    | SEC_READONLY))
+                       || ! bfd_set_section_alignment (dynobj, srelgot, 2))
+                     return false;
+                 }
+             }
+
+           if (h != NULL)
+             {
+               if (h->got.offset != (bfd_vma) -1)
+                 /* We have already allocated space in the .got.  */
+                 break;
+
+               h->got.offset = sgot->_raw_size;
+
+               /* Make sure this symbol is output as a dynamic symbol.  */
+               if (h->dynindx == -1)
+                 if (! bfd_elf32_link_record_dynamic_symbol (info, h))
+                   return false;
+
+               srelgot->_raw_size += sizeof (Elf32_External_Rel);
+             }
+           else
+             {
+               /* This is a global offset table entry for a local
+                   symbol.  */
+               if (local_got_offsets == NULL)
+                 {
+                   size_t size;
+                   register unsigned int i;
+
+                   size = symtab_hdr->sh_info * sizeof (bfd_vma);
+                   local_got_offsets = (bfd_vma *) bfd_alloc (abfd, size);
+                   if (local_got_offsets == NULL)
+                     return false;
+                   elf_local_got_offsets (abfd) = local_got_offsets;
+                   for (i = 0; i < symtab_hdr->sh_info; i++)
+                     local_got_offsets[i] = (bfd_vma) -1;
+                 }
+
+               if (local_got_offsets[r_symndx] != (bfd_vma) -1)
+                 /* We have already allocated space in the .got.  */
+                 break;
+
+               local_got_offsets[r_symndx] = sgot->_raw_size;
+
+               if (info->shared)
+                 /* If we are generating a shared object, we need to
+                    output a R_ARM_RELATIVE reloc so that the dynamic
+                    linker can adjust this GOT entry.  */
+                 srelgot->_raw_size += sizeof (Elf32_External_Rel);
+             }
+
+           sgot->_raw_size += 4;
+           break;
+
+         case R_ARM_PLT32:
+           /* This symbol requires a procedure linkage table entry.  We
+               actually build the entry in adjust_dynamic_symbol,
+               because this might be a case of linking PIC code which is
+               never referenced by a dynamic object, in which case we
+               don't need to generate a procedure linkage table entry
+               after all.  */
+
+           /* If this is a local symbol, we resolve it directly without
+               creating a procedure linkage table entry.  */
+           if (h == NULL)
+             continue;
+
+           h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+           break;
+
+         case R_ARM_ABS32:
+         case R_ARM_REL32:
+         case R_ARM_PC24:
+           /* If we are creating a shared library, and this is a reloc
+               against a global symbol, or a non PC relative reloc
+               against a local symbol, then we need to copy the reloc
+               into the shared library.  However, if we are linking with
+               -Bsymbolic, we do not need to copy a reloc against a
+               global symbol which is defined in an object we are
+               including in the link (i.e., DEF_REGULAR is set).  At
+               this point we have not seen all the input files, so it is
+               possible that DEF_REGULAR is not set now but will be set
+               later (it is never cleared).  We account for that
+               possibility below by storing information in the
+               pcrel_relocs_copied field of the hash table entry.  */
+           if (info->shared
+             && (ELF32_R_TYPE (rel->r_info) != R_ARM_PC24
+               || (h != NULL
+                 && (! info->symbolic
+                   || (h->elf_link_hash_flags
+                     & ELF_LINK_HASH_DEF_REGULAR) == 0))))
+             {
+               /* When creating a shared object, we must copy these
+                   reloc types into the output file.  We create a reloc
+                   section in dynobj and make room for this reloc.  */
+               if (sreloc == NULL)
+                 {
+                   const char * name;
+
+                   name = (bfd_elf_string_from_elf_section
+                           (abfd,
+                            elf_elfheader (abfd)->e_shstrndx,
+                            elf_section_data (sec)->rel_hdr.sh_name));
+                   if (name == NULL)
+                     return false;
+
+                   BFD_ASSERT (strncmp (name, ".rel", 4) == 0
+                               && strcmp (bfd_get_section_name (abfd, sec),
+                                          name + 4) == 0);
+
+                   sreloc = bfd_get_section_by_name (dynobj, name);
+                   if (sreloc == NULL)
+                     {
+                       flagword flags;
+
+                       sreloc = bfd_make_section (dynobj, name);
+                       flags = (SEC_HAS_CONTENTS | SEC_READONLY
+                                | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+                       if ((sec->flags & SEC_ALLOC) != 0)
+                         flags |= SEC_ALLOC | SEC_LOAD;
+                       if (sreloc == NULL
+                           || ! bfd_set_section_flags (dynobj, sreloc, flags)
+                           || ! bfd_set_section_alignment (dynobj, sreloc, 2))
+                         return false;
+                     }
+                 }
+
+               sreloc->_raw_size += sizeof (Elf32_External_Rel);
+               /* If we are linking with -Bsymbolic, and this is a
+                   global symbol, we count the number of PC relative
+                   relocations we have entered for this symbol, so that
+                   we can discard them again if the symbol is later
+                   defined by a regular object.  Note that this function
+                   is only called if we are using an elf_i386 linker
+                   hash table, which means that h is really a pointer to
+                   an elf_i386_link_hash_entry.  */
+               if (h != NULL && info->symbolic
+                   && ELF32_R_TYPE (rel->r_info) == R_ARM_PC24)
+                 {
+                   struct elf32_arm_link_hash_entry * eh;
+                   struct elf32_arm_pcrel_relocs_copied * p;
+
+                   eh = (struct elf32_arm_link_hash_entry *) h;
+
+                   for (p = eh->pcrel_relocs_copied; p != NULL; p = p->next)
+                     if (p->section == sreloc)
+                       break;
+
+                   if (p == NULL)
+                     {
+                       p = ((struct elf32_arm_pcrel_relocs_copied *)
+                            bfd_alloc (dynobj, sizeof * p));
+
+                       if (p == NULL)
+                         return false;
+                       p->next = eh->pcrel_relocs_copied;
+                       eh->pcrel_relocs_copied = p;
+                       p->section = sreloc;
+                       p->count = 0;
+                     }
+
+                   ++p->count;
+                 }
+             }
+           break;
+
         /* This relocation describes the C++ object vtable hierarchy.
            Reconstruct it for later use during GC.  */
         case R_ARM_GNU_VTINHERIT:
           if (!_bfd_elf32_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
             return false;
           break;
+         
         /* This relocation describes which C++ vtable entries are actually
            used.  Record for later use during GC.  */
         case R_ARM_GNU_VTENTRY:
-          if (!_bfd_elf32_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+          if (!_bfd_elf32_gc_record_vtentry (abfd, sec, h, rel->r_offset))
             return false;
           break;
         }
     }
+
   return true;
 }
 
-       
+
 /* Find the nearest line to a particular section and offset, for error
    reporting.   This code is a duplicate of the code in elf.c, except
    that it also accepts STT_ARM_TFUNC as a symbol that names a function. */
 
-boolean
+static boolean
 elf32_arm_find_nearest_line
   (abfd, section, symbols, offset, filename_ptr, functionname_ptr, line_ptr)
      bfd *          abfd;
@@ -1673,8 +2464,8 @@ elf32_arm_find_nearest_line
   asymbol **   p;
 
   if (_bfd_dwarf2_find_nearest_line (abfd, section, symbols, offset,
-                                    filename_ptr, functionname_ptr, 
-                                    line_ptr))
+                                    filename_ptr, functionname_ptr,
+                                    line_ptr, 0))
     return true;
 
   if (! _bfd_stab_section_find_nearest_line (abfd, symbols, section, offset,
@@ -1682,7 +2473,7 @@ elf32_arm_find_nearest_line
                                             functionname_ptr, line_ptr,
                                             &elf_tdata (abfd)->line_info))
     return false;
-  
+
   if (found)
     return true;
 
@@ -1729,26 +2520,705 @@ elf32_arm_find_nearest_line
   *filename_ptr = filename;
   *functionname_ptr = bfd_asymbol_name (func);
   *line_ptr = 0;
-  
+
+  return true;
+}
+
+/* Adjust a symbol defined by a dynamic object and referenced by a
+   regular object.  The current definition is in some section of the
+   dynamic object, but we're not including those sections.  We have to
+   change the definition to something the rest of the link can
+   understand.  */
+
+static boolean
+elf32_arm_adjust_dynamic_symbol (info, h)
+     struct bfd_link_info * info;
+     struct elf_link_hash_entry * h;
+{
+  bfd * dynobj;
+  asection * s;
+  unsigned int power_of_two;
+
+  dynobj = elf_hash_table (info)->dynobj;
+
+  /* Make sure we know what is going on here.  */
+  BFD_ASSERT (dynobj != NULL
+             && ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT)
+                 || h->weakdef != NULL
+                 || ((h->elf_link_hash_flags
+                      & ELF_LINK_HASH_DEF_DYNAMIC) != 0
+                     && (h->elf_link_hash_flags
+                         & ELF_LINK_HASH_REF_REGULAR) != 0
+                     && (h->elf_link_hash_flags
+                         & ELF_LINK_HASH_DEF_REGULAR) == 0)));
+
+  /* If this is a function, put it in the procedure linkage table.  We
+     will fill in the contents of the procedure linkage table later,
+     when we know the address of the .got section.  */
+  if (h->type == STT_FUNC
+      || (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
+    {
+      if (! info->shared
+         && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0
+         && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) == 0)
+       {
+         /* This case can occur if we saw a PLT32 reloc in an input
+             file, but the symbol was never referred to by a dynamic
+             object.  In such a case, we don't actually need to build
+             a procedure linkage table, and we can just do a PC32
+             reloc instead.  */
+         BFD_ASSERT ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0);
+         return true;
+       }
+
+      /* Make sure this symbol is output as a dynamic symbol.  */
+      if (h->dynindx == -1)
+       {
+         if (! bfd_elf32_link_record_dynamic_symbol (info, h))
+           return false;
+       }
+
+      s = bfd_get_section_by_name (dynobj, ".plt");
+      BFD_ASSERT (s != NULL);
+
+      /* If this is the first .plt entry, make room for the special
+        first entry.  */
+      if (s->_raw_size == 0)
+       s->_raw_size += PLT_ENTRY_SIZE;
+
+      /* If this symbol is not defined in a regular file, and we are
+        not generating a shared library, then set the symbol to this
+        location in the .plt.  This is required to make function
+        pointers compare as equal between the normal executable and
+        the shared library.  */
+      if (! info->shared
+         && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+       {
+         h->root.u.def.section = s;
+         h->root.u.def.value = s->_raw_size;
+       }
+
+      h->plt.offset = s->_raw_size;
+
+      /* Make room for this entry.  */
+      s->_raw_size += PLT_ENTRY_SIZE;
+
+      /* We also need to make an entry in the .got.plt section, which
+        will be placed in the .got section by the linker script.  */
+
+      s = bfd_get_section_by_name (dynobj, ".got.plt");
+      BFD_ASSERT (s != NULL);
+      s->_raw_size += 4;
+
+      /* We also need to make an entry in the .rel.plt section.  */
+
+      s = bfd_get_section_by_name (dynobj, ".rel.plt");
+      BFD_ASSERT (s != NULL);
+      s->_raw_size += sizeof (Elf32_External_Rel);
+
+      return true;
+    }
+
+  /* If this is a weak symbol, and there is a real definition, the
+     processor independent code will have arranged for us to see the
+     real definition first, and we can just use the same value.  */
+  if (h->weakdef != NULL)
+    {
+      BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined
+                 || h->weakdef->root.type == bfd_link_hash_defweak);
+      h->root.u.def.section = h->weakdef->root.u.def.section;
+      h->root.u.def.value = h->weakdef->root.u.def.value;
+      return true;
+    }
+
+  /* This is a reference to a symbol defined by a dynamic object which
+     is not a function.  */
+
+  /* If we are creating a shared library, we must presume that the
+     only references to the symbol are via the global offset table.
+     For such cases we need not do anything here; the relocations will
+     be handled correctly by relocate_section.  */
+  if (info->shared)
+    return true;
+
+  /* We must allocate the symbol in our .dynbss section, which will
+     become part of the .bss section of the executable.  There will be
+     an entry for this symbol in the .dynsym section.  The dynamic
+     object will contain position independent code, so all references
+     from the dynamic object to this symbol will go through the global
+     offset table.  The dynamic linker will use the .dynsym entry to
+     determine the address it must put in the global offset table, so
+     both the dynamic object and the regular object will refer to the
+     same memory location for the variable.  */
+
+  s = bfd_get_section_by_name (dynobj, ".dynbss");
+  BFD_ASSERT (s != NULL);
+
+  /* We must generate a R_ARM_COPY reloc to tell the dynamic linker to
+     copy the initial value out of the dynamic object and into the
+     runtime process image.  We need to remember the offset into the
+     .rel.bss section we are going to use.  */
+  if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
+    {
+      asection *srel;
+
+      srel = bfd_get_section_by_name (dynobj, ".rel.bss");
+      BFD_ASSERT (srel != NULL);
+      srel->_raw_size += sizeof (Elf32_External_Rel);
+      h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_COPY;
+    }
+
+  /* We need to figure out the alignment required for this symbol.  I
+     have no idea how ELF linkers handle this.  */
+  power_of_two = bfd_log2 (h->size);
+  if (power_of_two > 3)
+    power_of_two = 3;
+
+  /* Apply the required alignment.  */
+  s->_raw_size = BFD_ALIGN (s->_raw_size,
+                           (bfd_size_type) (1 << power_of_two));
+  if (power_of_two > bfd_get_section_alignment (dynobj, s))
+    {
+      if (! bfd_set_section_alignment (dynobj, s, power_of_two))
+       return false;
+    }
+
+  /* Define the symbol as being at this point in the section.  */
+  h->root.u.def.section = s;
+  h->root.u.def.value = s->_raw_size;
+
+  /* Increment the section size to make room for the symbol.  */
+  s->_raw_size += h->size;
+
+  return true;
+}
+
+/* Set the sizes of the dynamic sections.  */
+
+static boolean
+elf32_arm_size_dynamic_sections (output_bfd, info)
+     bfd * output_bfd;
+     struct bfd_link_info * info;
+{
+  bfd * dynobj;
+  asection * s;
+  boolean plt;
+  boolean relocs;
+  boolean reltext;
+
+  dynobj = elf_hash_table (info)->dynobj;
+  BFD_ASSERT (dynobj != NULL);
+
+  if (elf_hash_table (info)->dynamic_sections_created)
+    {
+      /* Set the contents of the .interp section to the interpreter.  */
+      if (! info->shared)
+       {
+         s = bfd_get_section_by_name (dynobj, ".interp");
+         BFD_ASSERT (s != NULL);
+         s->_raw_size = sizeof ELF_DYNAMIC_INTERPRETER;
+         s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER;
+       }
+    }
+  else
+    {
+      /* We may have created entries in the .rel.got section.
+         However, if we are not creating the dynamic sections, we will
+         not actually use these entries.  Reset the size of .rel.got,
+         which will cause it to get stripped from the output file
+         below.  */
+      s = bfd_get_section_by_name (dynobj, ".rel.got");
+      if (s != NULL)
+       s->_raw_size = 0;
+    }
+
+  /* If this is a -Bsymbolic shared link, then we need to discard all
+     PC relative relocs against symbols defined in a regular object.
+     We allocated space for them in the check_relocs routine, but we
+     will not fill them in in the relocate_section routine.  */
+  if (info->shared && info->symbolic)
+    elf32_arm_link_hash_traverse (elf32_arm_hash_table (info),
+                                 elf32_arm_discard_copies,
+                                 (PTR) NULL);
+
+  /* The check_relocs and adjust_dynamic_symbol entry points have
+     determined the sizes of the various dynamic sections.  Allocate
+     memory for them.  */
+  plt = false;
+  relocs = false;
+  reltext = false;
+  for (s = dynobj->sections; s != NULL; s = s->next)
+    {
+      const char * name;
+      boolean strip;
+
+      if ((s->flags & SEC_LINKER_CREATED) == 0)
+       continue;
+
+      /* It's OK to base decisions on the section name, because none
+        of the dynobj section names depend upon the input files.  */
+      name = bfd_get_section_name (dynobj, s);
+
+      strip = false;
+
+      if (strcmp (name, ".plt") == 0)
+       {
+         if (s->_raw_size == 0)
+           {
+             /* Strip this section if we don't need it; see the
+                 comment below.  */
+             strip = true;
+           }
+         else
+           {
+             /* Remember whether there is a PLT.  */
+             plt = true;
+           }
+       }
+      else if (strncmp (name, ".rel", 4) == 0)
+       {
+         if (s->_raw_size == 0)
+           {
+             /* If we don't need this section, strip it from the
+                output file.  This is mostly to handle .rel.bss and
+                .rel.plt.  We must create both sections in
+                create_dynamic_sections, because they must be created
+                before the linker maps input sections to output
+                sections.  The linker does that before
+                adjust_dynamic_symbol is called, and it is that
+                function which decides whether anything needs to go
+                into these sections.  */
+             strip = true;
+           }
+         else
+           {
+             asection * target;
+
+             /* Remember whether there are any reloc sections other
+                 than .rel.plt.  */
+             if (strcmp (name, ".rel.plt") != 0)
+               {
+                 const char *outname;
+
+                 relocs = true;
+
+                 /* If this relocation section applies to a read only
+                    section, then we probably need a DT_TEXTREL
+                    entry.  The entries in the .rel.plt section
+                    really apply to the .got section, which we
+                    created ourselves and so know is not readonly.  */
+                 outname = bfd_get_section_name (output_bfd,
+                                                 s->output_section);
+                 target = bfd_get_section_by_name (output_bfd, outname + 4);
+                 if (target != NULL
+                     && (target->flags & SEC_READONLY) != 0
+                     && (target->flags & SEC_ALLOC) != 0)
+                   reltext = true;
+               }
+
+             /* We use the reloc_count field as a counter if we need
+                to copy relocs into the output file.  */
+             s->reloc_count = 0;
+           }
+       }
+      else if (strncmp (name, ".got", 4) != 0)
+       {
+         /* It's not one of our sections, so don't allocate space.  */
+         continue;
+       }
+
+      if (strip)
+       {
+         asection ** spp;
+
+         for (spp = &s->output_section->owner->sections;
+              *spp != s->output_section;
+              spp = &(*spp)->next)
+           ;
+         *spp = s->output_section->next;
+         --s->output_section->owner->section_count;
+
+         continue;
+       }
+
+      /* Allocate memory for the section contents.  */
+      s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->_raw_size);
+      if (s->contents == NULL && s->_raw_size != 0)
+       return false;
+    }
+
+  if (elf_hash_table (info)->dynamic_sections_created)
+    {
+      /* Add some entries to the .dynamic section.  We fill in the
+        values later, in elf32_arm_finish_dynamic_sections, but we
+        must add the entries now so that we get the correct size for
+        the .dynamic section.  The DT_DEBUG entry is filled in by the
+        dynamic linker and used by the debugger.  */
+      if (! info->shared)
+       {
+         if (! bfd_elf32_add_dynamic_entry (info, DT_DEBUG, 0))
+           return false;
+       }
+
+      if (plt)
+       {
+         if (! bfd_elf32_add_dynamic_entry (info, DT_PLTGOT, 0)
+             || ! bfd_elf32_add_dynamic_entry (info, DT_PLTRELSZ, 0)
+             || ! bfd_elf32_add_dynamic_entry (info, DT_PLTREL, DT_REL)
+             || ! bfd_elf32_add_dynamic_entry (info, DT_JMPREL, 0))
+           return false;
+       }
+
+      if (relocs)
+       {
+         if (! bfd_elf32_add_dynamic_entry (info, DT_REL, 0)
+             || ! bfd_elf32_add_dynamic_entry (info, DT_RELSZ, 0)
+             || ! bfd_elf32_add_dynamic_entry (info, DT_RELENT,
+                                               sizeof (Elf32_External_Rel)))
+           return false;
+       }
+
+      if (reltext)
+       {
+         if (! bfd_elf32_add_dynamic_entry (info, DT_TEXTREL, 0))
+           return false;
+       }
+    }
+
+  return true;
+}
+
+/* This function is called via elf32_arm_link_hash_traverse if we are
+   creating a shared object with -Bsymbolic.  It discards the space
+   allocated to copy PC relative relocs against symbols which are
+   defined in regular objects.  We allocated space for them in the
+   check_relocs routine, but we won't fill them in in the
+   relocate_section routine.  */
+
+static boolean
+elf32_arm_discard_copies (h, ignore)
+     struct elf32_arm_link_hash_entry * h;
+     PTR ignore ATTRIBUTE_UNUSED;
+{
+  struct elf32_arm_pcrel_relocs_copied * s;
+
+  /* We only discard relocs for symbols defined in a regular object.  */
+  if ((h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+    return true;
+
+  for (s = h->pcrel_relocs_copied; s != NULL; s = s->next)
+    s->section->_raw_size -= s->count * sizeof (Elf32_External_Rel);
+
   return true;
 }
 
+/* Finish up dynamic symbol handling.  We set the contents of various
+   dynamic sections here.  */
+
+static boolean
+elf32_arm_finish_dynamic_symbol (output_bfd, info, h, sym)
+     bfd * output_bfd;
+     struct bfd_link_info * info;
+     struct elf_link_hash_entry * h;
+     Elf_Internal_Sym * sym;
+{
+  bfd * dynobj;
+
+  dynobj = elf_hash_table (info)->dynobj;
+
+  if (h->plt.offset != (bfd_vma) -1)
+    {
+      asection * splt;
+      asection * sgot;
+      asection * srel;
+      bfd_vma plt_index;
+      bfd_vma got_offset;
+      Elf_Internal_Rel rel;
+
+      /* This symbol has an entry in the procedure linkage table.  Set
+        it up.  */
+
+      BFD_ASSERT (h->dynindx != -1);
+
+      splt = bfd_get_section_by_name (dynobj, ".plt");
+      sgot = bfd_get_section_by_name (dynobj, ".got.plt");
+      srel = bfd_get_section_by_name (dynobj, ".rel.plt");
+      BFD_ASSERT (splt != NULL && sgot != NULL && srel != NULL);
+
+      /* Get the index in the procedure linkage table which
+        corresponds to this symbol.  This is the index of this symbol
+        in all the symbols for which we are making plt entries.  The
+        first entry in the procedure linkage table is reserved.  */
+      plt_index = h->plt.offset / PLT_ENTRY_SIZE - 1;
+
+      /* Get the offset into the .got table of the entry that
+        corresponds to this function.  Each .got entry is 4 bytes.
+        The first three are reserved.  */
+      got_offset = (plt_index + 3) * 4;
+
+      /* Fill in the entry in the procedure linkage table.  */
+      memcpy (splt->contents + h->plt.offset,
+              elf32_arm_plt_entry,
+             PLT_ENTRY_SIZE);
+      bfd_put_32 (output_bfd,
+                     (sgot->output_section->vma
+                      + sgot->output_offset
+                      + got_offset
+                      - splt->output_section->vma
+                      - splt->output_offset
+                      - h->plt.offset - 12),
+                     splt->contents + h->plt.offset + 12);
+
+      /* Fill in the entry in the global offset table.  */
+      bfd_put_32 (output_bfd,
+                 (splt->output_section->vma
+                  + splt->output_offset),
+                 sgot->contents + got_offset);
+
+      /* Fill in the entry in the .rel.plt section.  */
+      rel.r_offset = (sgot->output_section->vma
+                     + sgot->output_offset
+                     + got_offset);
+      rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_JUMP_SLOT);
+      bfd_elf32_swap_reloc_out (output_bfd, &rel,
+                               ((Elf32_External_Rel *) srel->contents
+                                + plt_index));
+
+      if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+       {
+         /* Mark the symbol as undefined, rather than as defined in
+            the .plt section.  Leave the value alone.  */
+         sym->st_shndx = SHN_UNDEF;
+       }
+    }
+
+  if (h->got.offset != (bfd_vma) -1)
+    {
+      asection * sgot;
+      asection * srel;
+      Elf_Internal_Rel rel;
+
+      /* This symbol has an entry in the global offset table.  Set it
+        up.  */
+
+      sgot = bfd_get_section_by_name (dynobj, ".got");
+      srel = bfd_get_section_by_name (dynobj, ".rel.got");
+      BFD_ASSERT (sgot != NULL && srel != NULL);
+
+      rel.r_offset = (sgot->output_section->vma
+                     + sgot->output_offset
+                     + (h->got.offset &~ 1));
+
+      /* If this is a -Bsymbolic link, and the symbol is defined
+        locally, we just want to emit a RELATIVE reloc.  The entry in
+        the global offset table will already have been initialized in
+        the relocate_section function.  */
+      if (info->shared
+         && (info->symbolic || h->dynindx == -1)
+         && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))
+       rel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
+      else
+       {
+         bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + h->got.offset);
+         rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_GLOB_DAT);
+       }
+
+      bfd_elf32_swap_reloc_out (output_bfd, &rel,
+                               ((Elf32_External_Rel *) srel->contents
+                                + srel->reloc_count));
+      ++srel->reloc_count;
+    }
+
+  if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_COPY) != 0)
+    {
+      asection * s;
+      Elf_Internal_Rel rel;
+
+      /* This symbol needs a copy reloc.  Set it up.  */
+
+      BFD_ASSERT (h->dynindx != -1
+                 && (h->root.type == bfd_link_hash_defined
+                     || h->root.type == bfd_link_hash_defweak));
+
+      s = bfd_get_section_by_name (h->root.u.def.section->owner,
+                                  ".rel.bss");
+      BFD_ASSERT (s != NULL);
+
+      rel.r_offset = (h->root.u.def.value
+                     + h->root.u.def.section->output_section->vma
+                     + h->root.u.def.section->output_offset);
+      rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_COPY);
+      bfd_elf32_swap_reloc_out (output_bfd, &rel,
+                               ((Elf32_External_Rel *) s->contents
+                                + s->reloc_count));
+      ++s->reloc_count;
+    }
+
+  /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute.  */
+  if (strcmp (h->root.root.string, "_DYNAMIC") == 0
+      || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
+    sym->st_shndx = SHN_ABS;
+
+  return true;
+}
+
+/* Finish up the dynamic sections.  */
+
+static boolean
+elf32_arm_finish_dynamic_sections (output_bfd, info)
+     bfd * output_bfd;
+     struct bfd_link_info * info;
+{
+  bfd * dynobj;
+  asection * sgot;
+  asection * sdyn;
+
+  dynobj = elf_hash_table (info)->dynobj;
+
+  sgot = bfd_get_section_by_name (dynobj, ".got.plt");
+  BFD_ASSERT (sgot != NULL);
+  sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+
+  if (elf_hash_table (info)->dynamic_sections_created)
+    {
+      asection *splt;
+      Elf32_External_Dyn *dyncon, *dynconend;
+
+      splt = bfd_get_section_by_name (dynobj, ".plt");
+      BFD_ASSERT (splt != NULL && sdyn != NULL);
+
+      dyncon = (Elf32_External_Dyn *) sdyn->contents;
+      dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->_raw_size);
+      for (; dyncon < dynconend; dyncon++)
+       {
+         Elf_Internal_Dyn dyn;
+         const char * name;
+         asection * s;
+
+         bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
+
+         switch (dyn.d_tag)
+           {
+           default:
+             break;
+
+           case DT_PLTGOT:
+             name = ".got";
+             goto get_vma;
+           case DT_JMPREL:
+             name = ".rel.plt";
+           get_vma:
+             s = bfd_get_section_by_name (output_bfd, name);
+             BFD_ASSERT (s != NULL);
+             dyn.d_un.d_ptr = s->vma;
+             bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
+             break;
+
+           case DT_PLTRELSZ:
+             s = bfd_get_section_by_name (output_bfd, ".rel.plt");
+             BFD_ASSERT (s != NULL);
+             if (s->_cooked_size != 0)
+               dyn.d_un.d_val = s->_cooked_size;
+             else
+               dyn.d_un.d_val = s->_raw_size;
+             bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
+             break;
+
+           case DT_RELSZ:
+             /* My reading of the SVR4 ABI indicates that the
+                procedure linkage table relocs (DT_JMPREL) should be
+                included in the overall relocs (DT_REL).  This is
+                what Solaris does.  However, UnixWare can not handle
+                that case.  Therefore, we override the DT_RELSZ entry
+                here to make it not include the JMPREL relocs.  Since
+                the linker script arranges for .rel.plt to follow all
+                other relocation sections, we don't have to worry
+                about changing the DT_REL entry.  */
+             s = bfd_get_section_by_name (output_bfd, ".rel.plt");
+             if (s != NULL)
+               {
+                 if (s->_cooked_size != 0)
+                   dyn.d_un.d_val -= s->_cooked_size;
+                 else
+                   dyn.d_un.d_val -= s->_raw_size;
+               }
+             bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
+             break;
+           }
+       }
+
+      /* Fill in the first entry in the procedure linkage table.  */
+      if (splt->_raw_size > 0)
+       memcpy (splt->contents, elf32_arm_plt0_entry, PLT_ENTRY_SIZE);
+
+      /* UnixWare sets the entsize of .plt to 4, although that doesn't
+        really seem like the right value.  */
+      elf_section_data (splt->output_section)->this_hdr.sh_entsize = 4;
+    }
+
+  /* Fill in the first three entries in the global offset table.  */
+  if (sgot->_raw_size > 0)
+    {
+      if (sdyn == NULL)
+       bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents);
+      else
+       bfd_put_32 (output_bfd,
+                   sdyn->output_section->vma + sdyn->output_offset,
+                   sgot->contents);
+      bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 4);
+      bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 8);
+    }
+
+  elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4;
+
+  return true;
+}
+
+static void
+elf32_arm_post_process_headers (abfd, link_info)
+     bfd * abfd;
+     struct bfd_link_info * link_info ATTRIBUTE_UNUSED;
+{
+  Elf_Internal_Ehdr * i_ehdrp; /* Elf file header, internal form */
+
+  i_ehdrp = elf_elfheader (abfd);
+
+  i_ehdrp->e_ident[EI_OSABI]      = ARM_ELF_OS_ABI_VERSION;
+  i_ehdrp->e_ident[EI_ABIVERSION] = ARM_ELF_ABI_VERSION;
+}
+
+
 #define ELF_ARCH                       bfd_arch_arm
 #define ELF_MACHINE_CODE               EM_ARM
+#define ELF_MAXPAGESIZE                        0x8000
+
 
-#define bfd_elf32_bfd_reloc_type_lookup        elf32_arm_reloc_type_lookup
-#define elf_backend_relocate_section           elf32_arm_relocate_section
 #define bfd_elf32_bfd_copy_private_bfd_data    elf32_arm_copy_private_bfd_data
 #define bfd_elf32_bfd_merge_private_bfd_data   elf32_arm_merge_private_bfd_data
 #define bfd_elf32_bfd_set_private_flags                elf32_arm_set_private_flags
 #define bfd_elf32_bfd_print_private_bfd_data   elf32_arm_print_private_bfd_data
 #define bfd_elf32_bfd_link_hash_table_create    elf32_arm_link_hash_table_create
+#define bfd_elf32_bfd_reloc_type_lookup        elf32_arm_reloc_type_lookup
 #define bfd_elf32_find_nearest_line            elf32_arm_find_nearest_line
+
 #define elf_backend_get_symbol_type             elf32_arm_get_symbol_type
 #define elf_backend_gc_mark_hook                elf32_arm_gc_mark_hook
 #define elf_backend_gc_sweep_hook               elf32_arm_gc_sweep_hook
 #define elf_backend_check_relocs                elf32_arm_check_relocs
+#define elf_backend_relocate_section           elf32_arm_relocate_section
+#define elf_backend_adjust_dynamic_symbol      elf32_arm_adjust_dynamic_symbol
+#define elf_backend_create_dynamic_sections    _bfd_elf_create_dynamic_sections
+#define elf_backend_finish_dynamic_symbol      elf32_arm_finish_dynamic_symbol
+#define elf_backend_finish_dynamic_sections    elf32_arm_finish_dynamic_sections
+#define elf_backend_size_dynamic_sections      elf32_arm_size_dynamic_sections
+#define elf_backend_post_process_headers       elf32_arm_post_process_headers
 
 #define elf_backend_can_gc_sections 1
+#define elf_backend_plt_readonly    1
+#define elf_backend_want_got_plt    1
+#define elf_backend_want_plt_sym    0
+
+#define elf_backend_got_header_size    12
+#define elf_backend_plt_header_size    PLT_ENTRY_SIZE
 
 #include "elf32-target.h"
This page took 0.049968 seconds and 4 git commands to generate.