This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [ARM] VFP11 denorm erratum workaround in linker


Nick Clifton wrote:
Hi Julian,

Hi, sorry for the rather extreme delay in getting back to this, but here goes...


We've worked around this erratum in the linker

Hmm, I was going to argue against this, but when I reviewed my thoughts I realised that they came down to the fact that I would rather a large patch like this, with its potential for introducing new bugs, be put into gcc rather than the linker :-)


For the record though, let me state that I do not like patches like this, when they are liable to introduce new bugs, and they are being used work around somebody else's problem that they cannot/will not fix. I appreciate that working around hardware bugs is a necessary evil, I just do not like it very much.

The justification for the fix being in the linker is that it then works for static libraries as well as executables, in that they can be distributed without worrying about separate versions with the erratum workaround enabled. The ability to distribute such static libraries is important to ARM's customers, AIUI.


GCC at present only outputs scalar code for VFP units, so the default (pre-ARMv7 architectures) is to fix scalar code. The workaround can be disabled or selected for vector operations using the linker options:

Why would you want to only fix scalar code, but not fix vector code ? Just because gcc does not currently produce any, there is no guarantee that there will not be any in the final link. Surely the safe default should be to fix both ?

I've changed the default to not fix either, see below.


--vfp11-denorm-fix={none,scalar,vector}

Shouldn't that be:


--vfp11-denorm-fix={none,scalar,vector,both}

Or does "vector" mode imply "scalar" as well ?

Yes, vector-mode fixes are a superset of scalar-mode fixes. I've noted this in ld.texinfo.


I am also worried about the future. Suppose a new revision of the VFP unit comes out (in less than v7 architecture) which does not have this problem. Being able to disable this workaround on the command line is all very well, but what if the user forgets, or does not know about it ? I would not be at all surprised to receive bug reports in the future saying "why is your stupid linker creating all these branches to some .vfp11_veneer section?". To my mind hardware workarounds should be disabled by default and only enabled by users who know that they are working with broken hardware. (This also emphasises the fact that the software is working around broken hardware, so do not blame the software for any reduction in performance).

Yeah, I agree, so I've made the workaround fix optional: if a user knows he is using buggy hardware, the workaround must now be enabled explicitly.


The workaround works by scanning input code sections for potentially-troublesome code sequences, and replacing them by branches to a specially-constructed veneer (similar to the way ARM<->Thumb interworking stubs are created at link-time). The veneer contains the original instruction plus a branch back to the original subsequent instruction, which is sufficient to avoid the erratum in either scalar or vector mode.

In a really large program, could these branches go out of range ?

Maybe (we'd be talking about 32Mb binaries?) -- I've added error checking for that case, but I'm not sure if ld would already have blown up long before that point... I suspect so :-)


Tested with cross to arm-none-eabi, and also with the GCC testsuite (C/C++/libstdc++) with -mfpu=vfp/-mfloat-abi=softfp.

Did you also run the gas/ld/binutils testsuites ?

Yep.


+ /* Information about a VFP11 erratum veneer, or a branch to such a veneer.
+ The type can be:
+ 'a': branch to ARM veneer
+ 't': branch to Thumb veneer
+ 'A': ARM veneer
+ 'T': Thumb veneer. */

+ char type;

I do not see much point in 'type' being a char. An enum would be better (and self-documenting). I doubt that you would save any space by using the char as the structure is likely to be padded out to an int boundary anyway.

Fixed. (The previous ARM/Thumb/data mapping-symbol code still uses chars though).


It would be very nice if you could include a URL for the errata sheet here. Assuming that ARM make the sheet available for direct download.

I've had a look, and I don't think there's a public description. Richard?


This information really needs to be in the documentation. In fact you need to add documentation of the new switch to ld.texinfo and a mention of it to ld/NEWS as well.

I've added a section to ld.texinfo.


One other thing - I think that it would be a good idea to include a linker testsuite entry to check this new feature.

...and a couple of tests, though they're by no means exhaustive.


OK now?

Cheers,

Julian

ChangeLog

    bfd/
    * bfd-in2.h: Regenerate.
    * bfd-in.h (bfd_arm_vfp11_fix): New enum. Specify how VFP11
    instruction scanning should be done.
    (bfd_elf32_arm_init_maps, bfd_elf32_arm_vfp11_erratum_scan)
    (bfd_elf32_arm_vfp11_fix_veneer_locations): Add prototypes.
    (bfd_elf32_arm_set_target_relocs): Add vfp11 fix type argument to
    prototype.
    * elf-bfd.h (elf_backend_write_section): Add struct bfd_link_info
    argument.
    * elf32-arm.c (VFP11_ERRATUM_VENEER_SECTION_NAME)
    (VFP11_ERRATUM_VENEER_ENTRY_NAME): Define macros.
    (elf32_vfp11_erratum_type): New enum.
    (elf32_vfp11_erratum_list): New struct. List of veneers or jumps to
    veneers.
    (_arm_elf_section_data): Add mapsize, erratumcount, erratumlist.
    (elf32_arm_link_hash_table): Add vfp11_erratum_glue_size,
    vfp11_fix and num_vfp11_fixes fields.
    (elf32_arm_link_hash_table_create): Initialise vfp11_fix,
    vfp11_erratum_glue_size, num_vfp11_fixes fields.
    (VFP11_ERRATUM_VENEER_SIZE): Define. Size of an (ARM) veneer.
    (bfd_elf32_arm_allocate_interworking_sections): Initialise erratum
    glue section.
    (elf32_arm_section_map_add): Add an code/data mapping symbol entry
    to a section's map.
    (record_vfp11_erratum_veneer): Create a single veneer, and its
    associated symbols.
    (bfd_elf32_arm_add_glue_sections_to_bfd): Add vfp11 erratum glue.
    (bfd_elf32_arm_init_maps): Initialise mapping symbol table for input
    BFDs.
    (bfd_elf32_arm_set_vfp11_fix): Set the type of erratum workaround
    required.
    (bfd_arm_vfp11_pipe): Define VFP11 instruction pipes.
    (bfd_arm_vfp11_regno): Recode a register number from a VFP11 insn.
    (bfd_arm_vfp11_write_mask): Update write mask according to coded
    register number.
    (bfd_arm_vfp11_antidependency): New function.
    (bfd_arm_vfp11_insn_decode): Decode a VFP11 insn.
    (elf32_arm_compare_mapping): Declare.
    (bfd_elf32_arm_vfp11_erratum_scan): Scan the sections of an input
    BFD for potential erratum-triggering insns. Record results.
    (bfd_elf32_arm_vfp11_fix_veneer_locations): Find out where veneers
    and branches to veneers have been placed in virtual memory after
    layout.
    (bfd_elf32_arm_set_target_relocs): Set vfp11_fix field in global
    hash table.
    (elf32_arm_output_symbol_hook): Remove.
    (elf32_arm_write_section): Output veneers, and branches to veneers.
    Use maps from input sections, not output sections, for code
    byte-swapping.
    * elf32-ppc.c (ppc_elf_write_section): Add dummy link_info argument.
    * elf32-score.c (_bfd_score_elf_write_section): Likewise.
    * elfxx-mips.c (_bfd_mips_elf_write_section): Likewise.
    * elfxx-mips.h (_bfd_mips_elf_write_section): Likewise.

    ld/
    * NEWS: Mention --vfp11-denorm-fix option.
    * ld.texinfo: Document above.
    * emulparams/armelf_linux.sh (OTHER_TEXT_SECTIONS): Add
    .vfp11_veneer section.
    * emulparams/armelf.sh (OTHER_TEXT_SECTIONS): Likewise.
    * emultempl/armelf.em (vfp11_denorm_fix): New static variable.
    (arm_elf_before_allocation): Call bfd_elf32_arm_set_vfp11_fix,
    bfd_elf32_arm_init_maps and bfd_elf32_arm_vfp11_erratum_scan.
    (arm_elf_after_allocation): New function. Call
    bfd_elf32_arm_vfp11_fix_veneer_locations for all input statements.
    (arm_elf_create_output_section_statements): Pass vfp11 fix command
    line option to BFD.
    (OPTION_VFP11_DENORM_FIX): New option.
    (PARSE_AND_LIST_LONGOPTS): Handle new option.
    (PARSE_AND_LIST_OPTIONS): Likewise.
    (PARSE_AND_LIST_ARGS_CASES): Likewise.
    (LDEMUL_AFTER_ALLOCATION): Define.

    ld/testsuite/
    * ld-arm/arm-elf.exp: Add VFP11 tests.
    * ld-arm/vfp11-fix-none.s: New file.
    * ld-arm/vfp11-fix-none.d: Expected disassembly of above.
    * ld-arm/vfp11-fix-scalar.s: New file.
    * ld-arm/vfp11-fix-scalar.d: Expected disassembly of above.
    * ld-arm/vfp11-fix-vector.s: New file.
    * ld-arm/vfp11-fix-vector.d: Expected disassembly of above.
? ld/testsuite/ld-arm/vfp11-fix-none
? ld/testsuite/ld-arm/vfp11-fix-scalar
? ld/testsuite/ld-arm/vfp11-fix-vector
Index: bfd/bfd-in.h
===================================================================
RCS file: /cvs/src/src/bfd/bfd-in.h,v
retrieving revision 1.122
diff -c -p -r1.122 bfd-in.h
*** bfd/bfd-in.h	20 Nov 2006 02:09:56 -0000	1.122
--- bfd/bfd-in.h	22 Jan 2007 14:51:19 -0000
*************** extern bfd_boolean bfd_coff_set_symbol_c
*** 846,851 ****
--- 846,872 ----
  extern bfd_boolean bfd_m68k_coff_create_embedded_relocs
    (bfd *, struct bfd_link_info *, struct bfd_section *, struct bfd_section *, char **);
  
+ /* ARM VFP11 erratum workaround support.  */
+ typedef enum
+ {
+   BFD_ARM_VFP11_FIX_DEFAULT,
+   BFD_ARM_VFP11_FIX_NONE,
+   BFD_ARM_VFP11_FIX_SCALAR,
+   BFD_ARM_VFP11_FIX_VECTOR
+ } bfd_arm_vfp11_fix;
+ 
+ extern void bfd_elf32_arm_init_maps
+   (bfd *);
+ 
+ extern void bfd_elf32_arm_set_vfp11_fix
+   (bfd *, struct bfd_link_info *);
+ 
+ extern bfd_boolean bfd_elf32_arm_vfp11_erratum_scan
+   (bfd *, struct bfd_link_info *);
+ 
+ extern void bfd_elf32_arm_vfp11_fix_veneer_locations
+   (bfd *, struct bfd_link_info *);
+ 
  /* ARM Interworking support.  Called from linker.  */
  extern bfd_boolean bfd_arm_allocate_interworking_sections
    (struct bfd_link_info *);
*************** extern bfd_boolean bfd_elf32_arm_process
*** 874,880 ****
    (bfd *, struct bfd_link_info *);
  
  void bfd_elf32_arm_set_target_relocs
!   (struct bfd_link_info *, int, char *, int, int);
  
  extern bfd_boolean bfd_elf32_arm_get_bfd_for_interworking
    (bfd *, struct bfd_link_info *);
--- 895,901 ----
    (bfd *, struct bfd_link_info *);
  
  void bfd_elf32_arm_set_target_relocs
!   (struct bfd_link_info *, int, char *, int, int, bfd_arm_vfp11_fix);
  
  extern bfd_boolean bfd_elf32_arm_get_bfd_for_interworking
    (bfd *, struct bfd_link_info *);
Index: bfd/bfd-in2.h
===================================================================
RCS file: /cvs/src/src/bfd/bfd-in2.h,v
retrieving revision 1.412
diff -c -p -r1.412 bfd-in2.h
*** bfd/bfd-in2.h	8 Jan 2007 18:42:36 -0000	1.412
--- bfd/bfd-in2.h	22 Jan 2007 14:51:19 -0000
*************** extern bfd_boolean bfd_coff_set_symbol_c
*** 853,858 ****
--- 853,879 ----
  extern bfd_boolean bfd_m68k_coff_create_embedded_relocs
    (bfd *, struct bfd_link_info *, struct bfd_section *, struct bfd_section *, char **);
  
+ /* ARM VFP11 erratum workaround support.  */
+ typedef enum
+ {
+   BFD_ARM_VFP11_FIX_DEFAULT,
+   BFD_ARM_VFP11_FIX_NONE,
+   BFD_ARM_VFP11_FIX_SCALAR,
+   BFD_ARM_VFP11_FIX_VECTOR
+ } bfd_arm_vfp11_fix;
+ 
+ extern void bfd_elf32_arm_init_maps
+   (bfd *);
+ 
+ extern void bfd_elf32_arm_set_vfp11_fix
+   (bfd *, struct bfd_link_info *);
+ 
+ extern bfd_boolean bfd_elf32_arm_vfp11_erratum_scan
+   (bfd *, struct bfd_link_info *);
+ 
+ extern void bfd_elf32_arm_vfp11_fix_veneer_locations
+   (bfd *, struct bfd_link_info *);
+ 
  /* ARM Interworking support.  Called from linker.  */
  extern bfd_boolean bfd_arm_allocate_interworking_sections
    (struct bfd_link_info *);
*************** extern bfd_boolean bfd_elf32_arm_process
*** 881,887 ****
    (bfd *, struct bfd_link_info *);
  
  void bfd_elf32_arm_set_target_relocs
!   (struct bfd_link_info *, int, char *, int, int);
  
  extern bfd_boolean bfd_elf32_arm_get_bfd_for_interworking
    (bfd *, struct bfd_link_info *);
--- 902,908 ----
    (bfd *, struct bfd_link_info *);
  
  void bfd_elf32_arm_set_target_relocs
!   (struct bfd_link_info *, int, char *, int, int, bfd_arm_vfp11_fix);
  
  extern bfd_boolean bfd_elf32_arm_get_bfd_for_interworking
    (bfd *, struct bfd_link_info *);
Index: bfd/elf-bfd.h
===================================================================
RCS file: /cvs/src/src/bfd/elf-bfd.h,v
retrieving revision 1.221
diff -c -p -r1.221 elf-bfd.h
*** bfd/elf-bfd.h	23 Dec 2006 09:57:38 -0000	1.221
--- bfd/elf-bfd.h	22 Jan 2007 14:51:19 -0000
*************** struct elf_backend_data
*** 985,991 ****
    /* This function, if defined, may write out the given section.
       Returns TRUE if it did so and FALSE if the caller should.  */
    bfd_boolean (*elf_backend_write_section)
!     (bfd *, asection *, bfd_byte *);
  
    /* The level of IRIX compatibility we're striving for.
       MIPS ELF specific function.  */
--- 985,991 ----
    /* This function, if defined, may write out the given section.
       Returns TRUE if it did so and FALSE if the caller should.  */
    bfd_boolean (*elf_backend_write_section)
!     (bfd *, struct bfd_link_info *, asection *, bfd_byte *);
  
    /* The level of IRIX compatibility we're striving for.
       MIPS ELF specific function.  */
Index: bfd/elf32-arm.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-arm.c,v
retrieving revision 1.103
diff -c -p -r1.103 elf32-arm.c
*** bfd/elf32-arm.c	29 Nov 2006 17:40:29 -0000	1.103
--- bfd/elf32-arm.c	22 Jan 2007 14:51:19 -0000
*************** typedef unsigned short int insn16;
*** 1875,1880 ****
--- 1875,1883 ----
  #define ARM2THUMB_GLUE_SECTION_NAME ".glue_7"
  #define ARM2THUMB_GLUE_ENTRY_NAME   "__%s_from_arm"
  
+ #define VFP11_ERRATUM_VENEER_SECTION_NAME ".vfp11_veneer"
+ #define VFP11_ERRATUM_VENEER_ENTRY_NAME   "__vfp11_veneer_%x"
+ 
  /* The name of the dynamic interpreter.  This is put in the .interp
     section.  */
  #define ELF_DYNAMIC_INTERPRETER     "/usr/lib/ld.so.1"
*************** typedef struct elf32_elf_section_map
*** 1987,1997 ****
--- 1990,2035 ----
  }
  elf32_arm_section_map;
  
+ /* Information about a VFP11 erratum veneer, or a branch to such a veneer.  */
+ 
+ typedef enum
+ {
+   VFP11_ERRATUM_BRANCH_TO_ARM_VENEER,
+   VFP11_ERRATUM_BRANCH_TO_THUMB_VENEER,
+   VFP11_ERRATUM_ARM_VENEER,
+   VFP11_ERRATUM_THUMB_VENEER
+ }
+ elf32_vfp11_erratum_type;
+ 
+ typedef struct elf32_vfp11_erratum_list
+ {
+   struct elf32_vfp11_erratum_list *next;
+   bfd_vma vma;
+   union
+   {
+     struct
+     {
+       struct elf32_vfp11_erratum_list *veneer;
+       unsigned int vfp_insn;
+     } b;
+     struct
+     {
+       struct elf32_vfp11_erratum_list *branch;
+       unsigned int id;
+     } v;
+   } u;
+   elf32_vfp11_erratum_type type;
+ }
+ elf32_vfp11_erratum_list;
+ 
  typedef struct _arm_elf_section_data
  {
    struct bfd_elf_section_data elf;
    unsigned int mapcount;
+   unsigned int mapsize;
    elf32_arm_section_map *map;
+   unsigned int erratumcount;
+   elf32_vfp11_erratum_list *erratumlist;
  }
  _arm_elf_section_data;
  
*************** struct elf32_arm_link_hash_table
*** 2120,2125 ****
--- 2158,2167 ----
      /* The size in bytes of the section containing the ARM-to-Thumb glue.  */
      bfd_size_type arm_glue_size;
  
+     /* The size in bytes of the section containing glue for VFP11 erratum
+        veneers.  */
+     bfd_size_type vfp11_erratum_glue_size;
+ 
      /* An arbitrary input BFD chosen to hold the glue sections.  */
      bfd * bfd_of_glue_owner;
  
*************** struct elf32_arm_link_hash_table
*** 2139,2144 ****
--- 2181,2193 ----
      /* Nonzero if the ARM/Thumb BLX instructions are available for use.  */
      int use_blx;
  
+     /* What sort of code sequences we should look for which may trigger the
+        VFP11 denorm erratum.  */
+     bfd_arm_vfp11_fix vfp11_fix;
+ 
+     /* Global counter for the number of fixes we have emitted.  */
+     int num_vfp11_fixes;
+ 
      /* The number of bytes in the initial entry in the PLT.  */
      bfd_size_type plt_header_size;
  
*************** elf32_arm_link_hash_table_create (bfd *a
*** 2401,2406 ****
--- 2450,2458 ----
    ret->srelplt2 = NULL;
    ret->thumb_glue_size = 0;
    ret->arm_glue_size = 0;
+   ret->vfp11_fix = BFD_ARM_VFP11_FIX_NONE;
+   ret->vfp11_erratum_glue_size = 0;
+   ret->num_vfp11_fixes = 0;
    ret->bfd_of_glue_owner = NULL;
    ret->byteswap_code = 0;
    ret->target1_is_rel = 0;
*************** static const insn16 t2a1_bx_pc_insn = 0x
*** 2540,2545 ****
--- 2592,2599 ----
  static const insn16 t2a2_noop_insn = 0x46c0;
  static const insn32 t2a3_b_insn = 0xea000000;
  
+ #define VFP11_ERRATUM_VENEER_SIZE 8
+ 
  #ifndef ELFARM_NABI_C_INCLUDED
  bfd_boolean
  bfd_elf32_arm_allocate_interworking_sections (struct bfd_link_info * info)
*************** bfd_elf32_arm_allocate_interworking_sect
*** 2581,2586 ****
--- 2635,2656 ----
        BFD_ASSERT (s->size == globals->thumb_glue_size);
        s->contents = foo;
      }
+   
+   if (globals->vfp11_erratum_glue_size != 0)
+     {
+       BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
+       
+       s = bfd_get_section_by_name
+         (globals->bfd_of_glue_owner, VFP11_ERRATUM_VENEER_SECTION_NAME);
+       
+       BFD_ASSERT (s != NULL);
+       
+       foo = bfd_alloc (globals->bfd_of_glue_owner,
+ 		       globals->vfp11_erratum_glue_size);
+       
+       BFD_ASSERT (s->size == globals->vfp11_erratum_glue_size);
+       s->contents = foo;
+     }
  
    return TRUE;
  }
*************** record_thumb_to_arm_glue (struct bfd_lin
*** 2729,2734 ****
--- 2799,2954 ----
    return;
  }
  
+ 
+ /* Add an entry to the code/data map for section SEC.  */
+ 
+ static void
+ elf32_arm_section_map_add (asection *sec, char type, bfd_vma vma)
+ {
+   struct _arm_elf_section_data *sec_data = elf32_arm_section_data (sec);
+   unsigned int newidx;
+   
+   if (sec_data->map == NULL)
+     {
+       sec_data->map = bfd_malloc (sizeof (elf32_arm_section_map));
+       sec_data->mapcount = 0;
+       sec_data->mapsize = 1;
+     }
+   
+   newidx = sec_data->mapcount++;
+   
+   if (sec_data->mapcount > sec_data->mapsize)
+     {
+       sec_data->mapsize *= 2;
+       sec_data->map = bfd_realloc (sec_data->map, sec_data->mapsize
+ 				     * sizeof (elf32_arm_section_map));
+     }
+   
+   sec_data->map[newidx].vma = vma;
+   sec_data->map[newidx].type = type;
+ }
+ 
+ 
+ /* Record information about a VFP11 denorm-erratum veneer.  Only ARM-mode
+    veneers are handled for now.  */
+ 
+ static bfd_vma
+ record_vfp11_erratum_veneer (struct bfd_link_info *link_info,
+                              elf32_vfp11_erratum_list *branch,
+                              bfd *branch_bfd,
+                              asection *branch_sec,
+                              unsigned int offset)
+ {
+   asection *s;
+   struct elf32_arm_link_hash_table *hash_table;
+   char *tmp_name;
+   struct elf_link_hash_entry *myh;
+   struct bfd_link_hash_entry *bh;
+   bfd_vma val;
+   struct _arm_elf_section_data *sec_data;
+   int errcount;
+   elf32_vfp11_erratum_list *newerr;
+   
+   hash_table = elf32_arm_hash_table (link_info);
+   
+   BFD_ASSERT (hash_table != NULL);
+   BFD_ASSERT (hash_table->bfd_of_glue_owner != NULL);
+   
+   s = bfd_get_section_by_name
+     (hash_table->bfd_of_glue_owner, VFP11_ERRATUM_VENEER_SECTION_NAME);
+   
+   sec_data = elf32_arm_section_data (s);
+   
+   BFD_ASSERT (s != NULL);
+   
+   tmp_name = bfd_malloc ((bfd_size_type) strlen
+ 			 (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10);
+   
+   BFD_ASSERT (tmp_name);
+   
+   sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME,
+ 	   hash_table->num_vfp11_fixes);
+   
+   myh = elf_link_hash_lookup
+     (&(hash_table)->root, tmp_name, FALSE, FALSE, FALSE);
+   
+   BFD_ASSERT (myh == NULL);
+   
+   bh = NULL;
+   val = hash_table->vfp11_erratum_glue_size;
+   _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner,
+                                     tmp_name, BSF_FUNCTION | BSF_LOCAL, s, val,
+                                     NULL, TRUE, FALSE, &bh);
+ 
+   myh = (struct elf_link_hash_entry *) bh;
+   myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
+   myh->forced_local = 1;
+ 
+   /* Link veneer back to calling location.  */
+   errcount = ++(sec_data->erratumcount);
+   newerr = bfd_zmalloc (sizeof (elf32_vfp11_erratum_list));
+   
+   newerr->type = VFP11_ERRATUM_ARM_VENEER;
+   newerr->vma = -1;
+   newerr->u.v.branch = branch;
+   newerr->u.v.id = hash_table->num_vfp11_fixes;
+   branch->u.b.veneer = newerr;
+ 
+   newerr->next = sec_data->erratumlist;
+   sec_data->erratumlist = newerr;
+ 
+   /* A symbol for the return from the veneer.  */
+   sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME "_r",
+ 	   hash_table->num_vfp11_fixes);
+ 
+   myh = elf_link_hash_lookup
+     (&(hash_table)->root, tmp_name, FALSE, FALSE, FALSE);
+   
+   if (myh != NULL)
+     abort ();
+ 
+   bh = NULL;
+   val = offset + 4;
+   _bfd_generic_link_add_one_symbol (link_info, branch_bfd, tmp_name, BSF_LOCAL,
+ 				    branch_sec, val, NULL, TRUE, FALSE, &bh);
+   
+   myh = (struct elf_link_hash_entry *) bh;
+   myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
+   myh->forced_local = 1;
+ 
+   free (tmp_name);
+   
+   /* Generate a mapping symbol for the veneer section, and explicitly add an
+      entry for that symbol to the code/data map for the section.  */
+   if (hash_table->vfp11_erratum_glue_size == 0)
+     {
+       bh = NULL;
+       /* FIXME: Creates an ARM symbol.  Thumb mode will need attention if it
+          ever requires this erratum fix.  */
+       _bfd_generic_link_add_one_symbol (link_info,
+ 					hash_table->bfd_of_glue_owner, "$a",
+ 					BSF_LOCAL, s, 0, NULL,
+                                         TRUE, FALSE, &bh);
+ 
+       myh = (struct elf_link_hash_entry *) bh;
+       myh->type = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE);
+       myh->forced_local = 1;
+       
+       /* The elf32_arm_init_maps function only cares about symbols from input
+          BFDs.  We must make a note of this generated mapping symbol
+          ourselves so that code byteswapping works properly in
+          elf32_arm_write_section.  */
+       elf32_arm_section_map_add (s, 'a', 0);
+     }
+   
+   s->size += VFP11_ERRATUM_VENEER_SIZE;
+   hash_table->vfp11_erratum_glue_size += VFP11_ERRATUM_VENEER_SIZE;
+   hash_table->num_vfp11_fixes++;
+   
+   /* The offset of the veneer.  */
+   return val;
+ }
+ 
  /* Add the glue sections to ABFD.  This function is called from the
     linker scripts in ld/emultempl/{armelf}.em.  */
  
*************** bfd_elf32_arm_add_glue_sections_to_bfd (
*** 2785,2790 ****
--- 3005,3028 ----
        sec->gc_mark = 1;
      }
  
+   sec = bfd_get_section_by_name (abfd, VFP11_ERRATUM_VENEER_SECTION_NAME);
+ 
+   if (sec == NULL)
+     {
+       flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ 	       | SEC_CODE | SEC_READONLY);
+ 
+       sec = bfd_make_section_with_flags (abfd,
+ 					 VFP11_ERRATUM_VENEER_SECTION_NAME,
+                                          flags);
+ 
+       if (sec == NULL
+ 	  || !bfd_set_section_alignment (abfd, sec, 2))
+ 	return FALSE;
+ 
+       sec->gc_mark = 1;
+     }
+ 
    return TRUE;
  }
  
*************** error_return:
*** 2988,2993 ****
--- 3226,3860 ----
  #endif
  
  
+ /* Initialise maps of ARM/Thumb/data for input BFDs.  */
+ 
+ void
+ bfd_elf32_arm_init_maps (bfd *abfd)
+ {
+   Elf_Internal_Sym *isymbuf;
+   Elf_Internal_Shdr *hdr;
+   unsigned int i, localsyms;
+ 
+   if ((abfd->flags & DYNAMIC) != 0)
+     return;
+ 
+   hdr = &elf_tdata (abfd)->symtab_hdr;
+   localsyms = hdr->sh_info;
+ 
+   /* Obtain a buffer full of symbols for this BFD. The hdr->sh_info field
+      should contain the number of local symbols, which should come before any
+      global symbols.  Mapping symbols are always local.  */
+   isymbuf = bfd_elf_get_elf_syms (abfd, hdr, localsyms, 0, NULL, NULL,
+ 				  NULL);
+ 
+   /* No internal symbols read?  Skip this BFD.  */
+   if (isymbuf == NULL)
+     return;
+ 
+   for (i = 0; i < localsyms; i++)
+     {
+       Elf_Internal_Sym *isym = &isymbuf[i];
+       asection *sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+       const char *name;
+       
+       if (sec != NULL
+           && ELF_ST_BIND (isym->st_info) == STB_LOCAL)
+         {
+           name = bfd_elf_string_from_elf_section (abfd,
+             hdr->sh_link, isym->st_name);
+           
+           if (bfd_is_arm_special_symbol_name (name,
+ 					      BFD_ARM_SPECIAL_SYM_TYPE_MAP))
+             elf32_arm_section_map_add (sec, name[1], isym->st_value);
+         }
+     }
+ }
+ 
+ 
+ void
+ bfd_elf32_arm_set_vfp11_fix (bfd *obfd, struct bfd_link_info *link_info)
+ {
+   struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info);
+   aeabi_attribute *out_attr = elf32_arm_tdata (obfd)->known_eabi_attributes;
+   
+   /* We assume that ARMv7+ does not need the VFP11 denorm erratum fix.  */
+   if (out_attr[Tag_CPU_arch].i >= TAG_CPU_ARCH_V7)
+     {
+       switch (globals->vfp11_fix)
+         {
+         case BFD_ARM_VFP11_FIX_DEFAULT:
+         case BFD_ARM_VFP11_FIX_NONE:
+           globals->vfp11_fix = BFD_ARM_VFP11_FIX_NONE;
+           break;
+         
+         default:
+           /* Give a warning, but do as the user requests anyway.  */
+           (*_bfd_error_handler) (_("%B: warning: selected VFP11 erratum "
+             "workaround is not necessary for target architecture"), obfd);
+         }
+     }
+   else if (globals->vfp11_fix == BFD_ARM_VFP11_FIX_DEFAULT)
+     /* For earlier architectures, we might need the workaround, but do not
+        enable it by default.  If users is running with broken hardware, they
+        must enable the erratum fix explicitly.  */
+     globals->vfp11_fix = BFD_ARM_VFP11_FIX_NONE;
+ }
+ 
+ 
+ enum bfd_arm_vfp11_pipe {
+   VFP11_FMAC,
+   VFP11_LS,
+   VFP11_DS,
+   VFP11_BAD
+ };
+ 
+ /* Return a VFP register number.  This is encoded as RX:X for single-precision
+    registers, or X:RX for double-precision registers, where RX is the group of
+    four bits in the instruction encoding and X is the single extension bit.
+    RX and X fields are specified using their lowest (starting) bit.  The return
+    value is:
+ 
+      0...31: single-precision registers s0...s31
+      32...63: double-precision registers d0...d31.
+   
+    Although X should be zero for VFP11 (encoding d0...d15 only), we might
+    encounter VFP3 instructions, so we allow the full range for DP registers.  */
+    
+ static unsigned int
+ bfd_arm_vfp11_regno (unsigned int insn, bfd_boolean is_double, unsigned int rx,
+                      unsigned int x)
+ {
+   if (is_double)
+     return (((insn >> rx) & 0xf) | (((insn >> x) & 1) << 4)) + 32;
+   else
+     return (((insn >> rx) & 0xf) << 1) | ((insn >> x) & 1);
+ }
+ 
+ /* Set bits in *WMASK according to a register number REG as encoded by
+    bfd_arm_vfp11_regno().  Ignore d16-d31.  */
+ 
+ static void
+ bfd_arm_vfp11_write_mask (unsigned int *wmask, unsigned int reg)
+ {
+   if (reg < 32)
+     *wmask |= 1 << reg;
+   else if (reg < 48)
+     *wmask |= 3 << ((reg - 32) * 2);
+ }
+ 
+ /* Return TRUE if WMASK overwrites anything in REGS.  */
+ 
+ static bfd_boolean
+ bfd_arm_vfp11_antidependency (unsigned int wmask, int *regs, int numregs)
+ {
+   int i;
+   
+   for (i = 0; i < numregs; i++)
+     {
+       unsigned int reg = regs[i];
+ 
+       if (reg < 32 && (wmask & (1 << reg)) != 0)
+         return TRUE;
+       
+       reg -= 32;
+ 
+       if (reg >= 16)
+         continue;
+       
+       if ((wmask & (3 << (reg * 2))) != 0)
+         return TRUE;
+     }
+   
+   return FALSE;
+ }
+ 
+ /* In this function, we're interested in two things: finding input registers
+    for VFP data-processing instructions, and finding the set of registers which
+    arbitrary VFP instructions may write to.  We use a 32-bit unsigned int to
+    hold the written set, so FLDM etc. are easy to deal with (we're only
+    interested in 32 SP registers or 16 dp registers, due to the VFP version
+    implemented by the chip in question).  DP registers are marked by setting
+    both SP registers in the write mask).  */
+ 
+ static enum bfd_arm_vfp11_pipe
+ bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
+                            int *numregs)
+ {
+   enum bfd_arm_vfp11_pipe pipe = VFP11_BAD;
+   bfd_boolean is_double = ((insn & 0xf00) == 0xb00) ? 1 : 0;
+ 
+   if ((insn & 0x0f000e10) == 0x0e000a00)  /* A data-processing insn.  */
+     {
+       unsigned int pqrs;
+       unsigned int fd = bfd_arm_vfp11_regno (insn, is_double, 12, 22);
+       unsigned int fm = bfd_arm_vfp11_regno (insn, is_double, 0, 5);
+ 
+       pqrs = ((insn & 0x00800000) >> 20)
+            | ((insn & 0x00300000) >> 19)
+            | ((insn & 0x00000040) >> 6);
+ 
+       switch (pqrs)
+         {
+         case 0: /* fmac[sd].  */
+         case 1: /* fnmac[sd].  */
+         case 2: /* fmsc[sd].  */
+         case 3: /* fnmsc[sd].  */
+           pipe = VFP11_FMAC;
+           bfd_arm_vfp11_write_mask (destmask, fd);
+           regs[0] = fd;
+           regs[1] = bfd_arm_vfp11_regno (insn, is_double, 16, 7);  /* Fn.  */
+           regs[2] = fm;
+           *numregs = 3;
+           break;
+ 
+         case 4: /* fmul[sd].  */
+         case 5: /* fnmul[sd].  */
+         case 6: /* fadd[sd].  */
+         case 7: /* fsub[sd].  */
+           pipe = VFP11_FMAC;
+           goto vfp_binop;
+ 
+         case 8: /* fdiv[sd].  */
+           pipe = VFP11_DS;
+           vfp_binop:
+           bfd_arm_vfp11_write_mask (destmask, fd);
+           regs[0] = bfd_arm_vfp11_regno (insn, is_double, 16, 7);   /* Fn.  */
+           regs[1] = fm;
+           *numregs = 2;
+           break;
+ 
+         case 15: /* extended opcode.  */
+           {
+             unsigned int extn = ((insn >> 15) & 0x1e)
+                               | ((insn >> 7) & 1);
+ 
+             switch (extn)
+               {
+               case 0: /* fcpy[sd].  */
+               case 1: /* fabs[sd].  */
+               case 2: /* fneg[sd].  */
+               case 8: /* fcmp[sd].  */
+               case 9: /* fcmpe[sd].  */
+               case 10: /* fcmpz[sd].  */
+               case 11: /* fcmpez[sd].  */
+               case 16: /* fuito[sd].  */
+               case 17: /* fsito[sd].  */
+               case 24: /* ftoui[sd].  */
+               case 25: /* ftouiz[sd].  */
+               case 26: /* ftosi[sd].  */
+               case 27: /* ftosiz[sd].  */
+                 /* These instructions will not bounce due to underflow.  */
+                 *numregs = 0;
+                 pipe = VFP11_FMAC;
+                 break;
+ 
+               case 3: /* fsqrt[sd].  */
+                 /* fsqrt cannot underflow, but it can (perhaps) overwrite
+                    registers to cause the erratum in previous instructions.  */
+                 bfd_arm_vfp11_write_mask (destmask, fd);
+                 pipe = VFP11_DS;
+                 break;
+ 
+               case 15: /* fcvt{ds,sd}.  */
+                 {
+                   int rnum = 0;
+ 
+                   bfd_arm_vfp11_write_mask (destmask, fd);
+ 
+ 		  /* Only FCVTSD can underflow.  */
+                   if ((insn & 0x100) != 0)
+                     regs[rnum++] = fm;
+ 
+                   *numregs = rnum;
+ 
+                   pipe = VFP11_FMAC;
+                 }
+                 break;
+ 
+               default:
+                 return VFP11_BAD;
+               }
+           }
+           break;
+ 
+         default:
+           return VFP11_BAD;
+         }
+     }
+   /* Two-register transfer.  */
+   else if ((insn & 0x0fe00ed0) == 0x0c400a10)
+     {
+       unsigned int fm = bfd_arm_vfp11_regno (insn, is_double, 0, 5);
+       
+       if ((insn & 0x100000) == 0)
+ 	{
+           if (is_double)
+             bfd_arm_vfp11_write_mask (destmask, fm);
+           else
+             {
+               bfd_arm_vfp11_write_mask (destmask, fm);
+               bfd_arm_vfp11_write_mask (destmask, fm + 1);
+             }
+ 	}
+ 
+       pipe = VFP11_LS;
+     }
+   else if ((insn & 0x0e100e00) == 0x0c100a00)  /* A load insn.  */
+     {
+       int fd = bfd_arm_vfp11_regno (insn, is_double, 12, 22);
+       unsigned int puw = ((insn >> 21) & 0x1) | (((insn >> 23) & 3) << 1);
+       
+       switch (puw)
+         {
+         case 0: /* Two-reg transfer.  We should catch these above.  */
+           abort ();
+         
+         case 2: /* fldm[sdx].  */
+         case 3:
+         case 5:
+           {
+             unsigned int i, offset = insn & 0xff;
+ 
+             if (is_double)
+               offset >>= 1;
+ 
+             for (i = fd; i < fd + offset; i++)
+               bfd_arm_vfp11_write_mask (destmask, i);
+           }
+           break;
+         
+         case 4: /* fld[sd].  */
+         case 6:
+           bfd_arm_vfp11_write_mask (destmask, fd);
+           break;
+         
+         default:
+           return VFP11_BAD;
+         }
+ 
+       pipe = VFP11_LS;
+     }
+   /* Single-register transfer. Note L==0.  */
+   else if ((insn & 0x0f100e10) == 0x0e000a10)
+     {
+       unsigned int opcode = (insn >> 21) & 7;
+       unsigned int fn = bfd_arm_vfp11_regno (insn, is_double, 16, 7);
+ 
+       switch (opcode)
+         {
+         case 0: /* fmsr/fmdlr.  */
+         case 1: /* fmdhr.  */
+           /* Mark fmdhr and fmdlr as writing to the whole of the DP
+              destination register.  I don't know if this is exactly right,
+              but it is the conservative choice.  */
+           bfd_arm_vfp11_write_mask (destmask, fn);
+           break;
+ 
+         case 7: /* fmxr.  */
+           break;
+         }
+ 
+       pipe = VFP11_LS;
+     }
+ 
+   return pipe;
+ }
+ 
+ 
+ static int elf32_arm_compare_mapping (const void * a, const void * b);
+ 
+ 
+ /* Look for potentially-troublesome code sequences which might trigger the
+    VFP11 denormal/antidependency erratum.  See, e.g., the ARM1136 errata sheet
+    (available from ARM) for details of the erratum.  A short version is
+    described in ld.texinfo.  */
+ 
+ bfd_boolean
+ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
+ {
+   asection *sec;
+   bfd_byte *contents = NULL;
+   int state = 0;
+   int regs[3], numregs = 0;
+   struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info);
+   int use_vector = (globals->vfp11_fix == BFD_ARM_VFP11_FIX_VECTOR);
+   
+   /* We use a simple FSM to match troublesome VFP11 instruction sequences.
+      The states transition as follows:
+      
+        0 -> 1 (vector) or 0 -> 2 (scalar)
+            A VFP FMAC-pipeline instruction has been seen. Fill
+            regs[0]..regs[numregs-1] with its input operands. Remember this
+            instruction in 'first_fmac'.
+ 
+        1 -> 2
+            Any instruction, except for a VFP instruction which overwrites
+            regs[*].
+        
+        1 -> 3 [ -> 0 ]  or
+        2 -> 3 [ -> 0 ]
+            A VFP instruction has been seen which overwrites any of regs[*].
+            We must make a veneer!  Reset state to 0 before examining next
+            instruction.
+        
+        2 -> 0
+            If we fail to match anything in state 2, reset to state 0 and reset
+            the instruction pointer to the instruction after 'first_fmac'.
+ 
+      If the VFP11 vector mode is in use, there must be at least two unrelated
+      instructions between anti-dependent VFP11 instructions to properly avoid
+      triggering the erratum, hence the use of the extra state 1.
+   */
+ 
+   /* If we are only performing a partial link do not bother
+      to construct any glue.  */
+   if (link_info->relocatable)
+     return TRUE;
+ 
+   /* We should have chosen a fix type by the time we get here.  */
+   BFD_ASSERT (globals->vfp11_fix != BFD_ARM_VFP11_FIX_DEFAULT);
+ 
+   if (globals->vfp11_fix == BFD_ARM_VFP11_FIX_NONE)
+     return TRUE;
+   
+   for (sec = abfd->sections; sec != NULL; sec = sec->next)
+     {
+       unsigned int i, span, first_fmac = 0, veneer_of_insn = 0;
+       struct _arm_elf_section_data *sec_data;
+ 
+       /* If we don't have executable progbits, we're not interested in this
+          section.  Also skip if section is to be excluded.  */
+       if (elf_section_type (sec) != SHT_PROGBITS
+           || (elf_section_flags (sec) & SHF_EXECINSTR) == 0
+           || (sec->flags & SEC_EXCLUDE) != 0
+           || strcmp (sec->name, VFP11_ERRATUM_VENEER_SECTION_NAME) == 0)
+         continue;
+ 
+       sec_data = elf32_arm_section_data (sec);
+       
+       if (sec_data->mapcount == 0)
+         continue;
+       
+       if (elf_section_data (sec)->this_hdr.contents != NULL)
+ 	contents = elf_section_data (sec)->this_hdr.contents;
+       else if (! bfd_malloc_and_get_section (abfd, sec, &contents))
+ 	goto error_return;
+ 
+       qsort (sec_data->map, sec_data->mapcount, sizeof (elf32_arm_section_map),
+ 	     elf32_arm_compare_mapping);
+ 
+       for (span = 0; span < sec_data->mapcount; span++)
+         {
+           unsigned int span_start = sec_data->map[span].vma;
+           unsigned int span_end = (span == sec_data->mapcount - 1)
+ 				  ? sec->size : sec_data->map[span + 1].vma;
+           char span_type = sec_data->map[span].type;
+           
+           /* FIXME: Only ARM mode is supported at present.  We may need to
+              support Thumb-2 mode also at some point.  */
+           if (span_type != 'a')
+             continue;
+ 
+           for (i = span_start; i < span_end;)
+             {
+               unsigned int next_i = i + 4;
+               unsigned int insn = bfd_big_endian (abfd)
+                 ? (contents[i] << 24)
+                   | (contents[i + 1] << 16)
+                   | (contents[i + 2] << 8)
+                   | contents[i + 3]
+                 : (contents[i + 3] << 24)
+                   | (contents[i + 2] << 16)
+                   | (contents[i + 1] << 8)
+                   | contents[i];
+               unsigned int writemask = 0;
+               enum bfd_arm_vfp11_pipe pipe;
+ 
+               switch (state)
+                 {
+                 case 0:
+                   pipe = bfd_arm_vfp11_insn_decode (insn, &writemask, regs,
+                                                     &numregs);
+                   /* I'm assuming the VFP11 erratum can trigger with denorm
+                      operands on either the FMAC or the DS pipeline. This might
+                      lead to slightly overenthusiastic veneer insertion.  */
+                   if (pipe == VFP11_FMAC || pipe == VFP11_DS)
+                     {
+                       state = use_vector ? 1 : 2;
+                       first_fmac = i;
+                       veneer_of_insn = insn;
+                     }
+                   break;
+ 
+                 case 1:
+                   {
+                     int other_regs[3], other_numregs;
+                     pipe = bfd_arm_vfp11_insn_decode (insn, &writemask,
+ 						      other_regs,
+                                                       &other_numregs);
+                     if (pipe != VFP11_BAD
+                         && bfd_arm_vfp11_antidependency (writemask, regs,
+ 							 numregs))
+                       state = 3;
+                     else
+                       state = 2;
+                   }
+                   break;
+ 
+                 case 2:
+                   {
+                     int other_regs[3], other_numregs;
+                     pipe = bfd_arm_vfp11_insn_decode (insn, &writemask,
+ 						      other_regs,
+                                                       &other_numregs);
+                     if (pipe != VFP11_BAD
+                         && bfd_arm_vfp11_antidependency (writemask, regs,
+ 							 numregs))
+                       state = 3;
+                     else
+                       {
+                         state = 0;
+                         next_i = first_fmac + 4;
+                       }
+                   }
+                   break;
+ 
+                 case 3:
+                   abort ();  /* Should be unreachable.  */
+                 }
+ 
+               if (state == 3)
+                 {
+                   elf32_vfp11_erratum_list *newerr
+                     = bfd_zmalloc (sizeof (elf32_vfp11_erratum_list));
+                   int errcount;
+ 
+                   errcount = ++(elf32_arm_section_data (sec)->erratumcount);
+ 
+                   newerr->u.b.vfp_insn = veneer_of_insn;
+ 
+                   switch (span_type)
+                     {
+                     case 'a':
+                       newerr->type = VFP11_ERRATUM_BRANCH_TO_ARM_VENEER;
+                       break;
+                     
+                     default:
+                       abort ();
+                     }
+ 
+                   record_vfp11_erratum_veneer (link_info, newerr, abfd, sec,
+ 					       first_fmac);
+ 
+                   newerr->vma = -1;
+ 
+                   newerr->next = sec_data->erratumlist;
+                   sec_data->erratumlist = newerr;
+ 
+                   state = 0;
+                 }
+ 
+               i = next_i;
+             }
+         }
+       
+       if (contents != NULL
+           && elf_section_data (sec)->this_hdr.contents != contents)
+         free (contents);
+       contents = NULL;
+     }
+ 
+   return TRUE;
+ 
+ error_return:
+   if (contents != NULL
+       && elf_section_data (sec)->this_hdr.contents != contents)
+     free (contents);
+   
+   return FALSE;
+ }
+ 
+ /* Find virtual-memory addresses for VFP11 erratum veneers and return locations
+    after sections have been laid out, using specially-named symbols.  */
+ 
+ void
+ bfd_elf32_arm_vfp11_fix_veneer_locations (bfd *abfd,
+ 					  struct bfd_link_info *link_info)
+ {
+   asection *sec;
+   struct elf32_arm_link_hash_table *globals;
+   char *tmp_name;
+   
+   if (link_info->relocatable)
+     return;
+   
+   globals = elf32_arm_hash_table (link_info);
+   
+   tmp_name = bfd_malloc ((bfd_size_type) strlen
+ 			   (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10);
+ 
+   for (sec = abfd->sections; sec != NULL; sec = sec->next)
+     {
+       struct _arm_elf_section_data *sec_data = elf32_arm_section_data (sec);
+       elf32_vfp11_erratum_list *errnode = sec_data->erratumlist;
+       
+       for (; errnode != NULL; errnode = errnode->next)
+         {
+           struct elf_link_hash_entry *myh;
+           bfd_vma vma;
+ 
+           switch (errnode->type)
+             {
+             case VFP11_ERRATUM_BRANCH_TO_ARM_VENEER:
+             case VFP11_ERRATUM_BRANCH_TO_THUMB_VENEER:
+               /* Find veneer symbol.  */
+               sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME,
+ 		       errnode->u.b.veneer->u.v.id);
+ 
+               myh = elf_link_hash_lookup
+                 (&(globals)->root, tmp_name, FALSE, FALSE, TRUE);
+ 
+               if (myh == NULL)
+                 (*_bfd_error_handler) (_("%B: unable to find VFP11 veneer "
+                 			 "`%s'"), abfd, tmp_name);
+ 
+               vma = myh->root.u.def.section->output_section->vma
+                     + myh->root.u.def.section->output_offset
+                     + myh->root.u.def.value;
+ 
+               errnode->u.b.veneer->vma = vma;
+               break;
+ 
+ 	    case VFP11_ERRATUM_ARM_VENEER:
+             case VFP11_ERRATUM_THUMB_VENEER:
+               /* Find return location.  */
+               sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME "_r",
+                        errnode->u.v.id);
+ 
+               myh = elf_link_hash_lookup
+                 (&(globals)->root, tmp_name, FALSE, FALSE, TRUE);
+ 
+               if (myh == NULL)
+                 (*_bfd_error_handler) (_("%B: unable to find VFP11 veneer "
+ 					 "`%s'"), abfd, tmp_name);
+ 
+               vma = myh->root.u.def.section->output_section->vma
+                     + myh->root.u.def.section->output_offset
+                     + myh->root.u.def.value;
+ 
+               errnode->u.v.branch->vma = vma;
+               break;
+             
+             default:
+               abort ();
+             }
+         }
+     }
+   
+   free (tmp_name);
+ }
+ 
+ 
  /* Set target relocation values needed during linking.  */
  
  void
*************** bfd_elf32_arm_set_target_relocs (struct 
*** 2995,3001 ****
  				 int target1_is_rel,
  				 char * target2_type,
                                   int fix_v4bx,
! 				 int use_blx)
  {
    struct elf32_arm_link_hash_table *globals;
  
--- 3862,3869 ----
  				 int target1_is_rel,
  				 char * target2_type,
                                   int fix_v4bx,
! 				 int use_blx,
!                                  bfd_arm_vfp11_fix vfp11_fix)
  {
    struct elf32_arm_link_hash_table *globals;
  
*************** bfd_elf32_arm_set_target_relocs (struct 
*** 3015,3020 ****
--- 3883,3889 ----
      }
    globals->fix_v4bx = fix_v4bx;
    globals->use_blx |= use_blx;
+   globals->vfp11_fix = vfp11_fix;
  }
  
  /* The thumb form of a long branch is a bit finicky, because the offset
*************** elf32_arm_size_dynamic_sections (bfd * o
*** 8041,8050 ****
  
    /* Here we rummage through the found bfds to collect glue information.  */
    for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
!     if (!bfd_elf32_arm_process_before_allocation (ibfd, info))
!       /* xgettext:c-format */
!       _bfd_error_handler (_("Errors encountered processing file %s"),
! 			  ibfd->filename);
  
    /* The check_relocs and adjust_dynamic_symbol entry points have
       determined the sizes of the various dynamic sections.  Allocate
--- 8910,8925 ----
  
    /* Here we rummage through the found bfds to collect glue information.  */
    for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
!     {
!       /* Initialise mapping tables for code/data.  */
!       bfd_elf32_arm_init_maps (ibfd);
!       
!       if (!bfd_elf32_arm_process_before_allocation (ibfd, info)
! 	  || !bfd_elf32_arm_vfp11_erratum_scan (ibfd, info))
!         /* xgettext:c-format */
!         _bfd_error_handler (_("Errors encountered processing file %s"),
! 			    ibfd->filename);
!     }
  
    /* The check_relocs and adjust_dynamic_symbol entry points have
       determined the sizes of the various dynamic sections.  Allocate
*************** unrecord_section_with_arm_elf_section_da
*** 9084,9146 ****
      }
  }
  
- /* Called for each symbol.  Builds a section map based on mapping symbols.
-    Does not alter any of the symbols.  */
- 
- static bfd_boolean
- elf32_arm_output_symbol_hook (struct bfd_link_info *info,
- 			      const char *name,
- 			      Elf_Internal_Sym *elfsym,
- 			      asection *input_sec,
- 			      struct elf_link_hash_entry *h)
- {
-   int mapcount;
-   elf32_arm_section_map *map;
-   elf32_arm_section_map *newmap;
-   _arm_elf_section_data *arm_data;
-   struct elf32_arm_link_hash_table *globals;
- 
-   globals = elf32_arm_hash_table (info);
-   if (globals->vxworks_p
-       && !elf_vxworks_link_output_symbol_hook (info, name, elfsym,
- 					       input_sec, h))
-     return FALSE;
- 
-   /* Only do this on final link.  */
-   if (info->relocatable)
-     return TRUE;
- 
-   /* Only build a map if we need to byteswap code.  */
-   if (!globals->byteswap_code)
-     return TRUE;
- 
-   /* We only want mapping symbols.  */
-   if (!bfd_is_arm_special_symbol_name (name, BFD_ARM_SPECIAL_SYM_TYPE_MAP))
-     return TRUE;
- 
-   /* If this section has not been allocated an _arm_elf_section_data
-      structure then we cannot record anything.  */
-   arm_data = get_arm_elf_section_data (input_sec);
-   if (arm_data == NULL)
-     return TRUE;
- 
-   mapcount = arm_data->mapcount + 1;
-   map = arm_data->map;
- 
-   /* TODO: This may be inefficient, but we probably don't usually have many
-      mapping symbols per section.  */
-   newmap = bfd_realloc (map, mapcount * sizeof (* map));
-   if (newmap != NULL)
-     {
-       arm_data->map = newmap;
-       arm_data->mapcount = mapcount;
- 
-       newmap[mapcount - 1].vma = elfsym->st_value;
-       newmap[mapcount - 1].type = name[1];
-     }
- 
-   return TRUE;
- }
  
  typedef struct
  {
--- 9959,9964 ----
*************** elf32_arm_compare_mapping (const void * 
*** 9346,9360 ****
     written out as normal.  */
  
  static bfd_boolean
! elf32_arm_write_section (bfd *output_bfd ATTRIBUTE_UNUSED, asection *sec,
  			 bfd_byte *contents)
  {
!   int mapcount;
    _arm_elf_section_data *arm_data;
    elf32_arm_section_map *map;
    bfd_vma ptr;
    bfd_vma end;
!   bfd_vma offset;
    bfd_byte tmp;
    int i;
  
--- 10164,10181 ----
     written out as normal.  */
  
  static bfd_boolean
! elf32_arm_write_section (bfd *output_bfd,
! 			 struct bfd_link_info *link_info, asection *sec,
  			 bfd_byte *contents)
  {
!   int mapcount, errcount;
    _arm_elf_section_data *arm_data;
+   struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info);
    elf32_arm_section_map *map;
+   elf32_vfp11_erratum_list *errnode;
    bfd_vma ptr;
    bfd_vma end;
!   bfd_vma offset = sec->output_section->vma + sec->output_offset;
    bfd_byte tmp;
    int i;
  
*************** elf32_arm_write_section (bfd *output_bfd
*** 9366,9422 ****
  
    mapcount = arm_data->mapcount;
    map = arm_data->map;
  
    if (mapcount == 0)
      return FALSE;
  
!   qsort (map, mapcount, sizeof (* map), elf32_arm_compare_mapping);
! 
!   offset = sec->output_section->vma + sec->output_offset;
!   ptr = map[0].vma - offset;
!   for (i = 0; i < mapcount; i++)
      {
!       if (i == mapcount - 1)
! 	end = sec->size;
!       else
! 	end = map[i + 1].vma - offset;
  
!       switch (map[i].type)
! 	{
! 	case 'a':
! 	  /* Byte swap code words.  */
! 	  while (ptr + 3 < end)
! 	    {
! 	      tmp = contents[ptr];
! 	      contents[ptr] = contents[ptr + 3];
! 	      contents[ptr + 3] = tmp;
! 	      tmp = contents[ptr + 1];
! 	      contents[ptr + 1] = contents[ptr + 2];
! 	      contents[ptr + 2] = tmp;
! 	      ptr += 4;
! 	    }
! 	  break;
  
! 	case 't':
! 	  /* Byte swap code halfwords.  */
! 	  while (ptr + 1 < end)
! 	    {
! 	      tmp = contents[ptr];
! 	      contents[ptr] = contents[ptr + 1];
! 	      contents[ptr + 1] = tmp;
! 	      ptr += 2;
! 	    }
! 	  break;
  
! 	case 'd':
! 	  /* Leave data alone.  */
! 	  break;
! 	}
!       ptr = end;
      }
  
    free (map);
    arm_data->mapcount = 0;
    arm_data->map = NULL;
    unrecord_section_with_arm_elf_section_data (sec);
  
--- 10187,10322 ----
  
    mapcount = arm_data->mapcount;
    map = arm_data->map;
+   errcount = arm_data->erratumcount;
+ 
+   if (errcount != 0)
+     {
+       unsigned int endianflip = bfd_big_endian (output_bfd) ? 3 : 0;
+ 
+       for (errnode = arm_data->erratumlist; errnode != 0;
+            errnode = errnode->next)
+         {
+           bfd_vma index = errnode->vma - offset;
+ 
+           switch (errnode->type)
+             {
+             case VFP11_ERRATUM_BRANCH_TO_ARM_VENEER:
+               {
+                 bfd_vma branch_to_veneer;
+                 /* Original condition code of instruction, plus bit mask for
+                    ARM B instruction.  */
+                 unsigned int insn = (errnode->u.b.vfp_insn & 0xf0000000)
+                                   | 0x0a000000;
+ 
+ 		/* The instruction is before the label.  */
+ 		index -= 4;
+ 
+ 		/* Above offset included in -4 below.  */
+ 		branch_to_veneer = errnode->u.b.veneer->vma
+                                    - errnode->vma - 4;
+ 
+ 		if ((signed) branch_to_veneer < -(1 << 25)
+ 		    || (signed) branch_to_veneer >= (1 << 25))
+ 		  (*_bfd_error_handler) (_("%B: error: VFP11 veneer out of "
+ 					   "range"), output_bfd);
+ 
+                 insn |= (branch_to_veneer >> 2) & 0xffffff;
+                 contents[endianflip ^ index] = insn & 0xff;
+                 contents[endianflip ^ (index + 1)] = (insn >> 8) & 0xff;
+                 contents[endianflip ^ (index + 2)] = (insn >> 16) & 0xff;
+                 contents[endianflip ^ (index + 3)] = (insn >> 24) & 0xff;
+               }
+               break;
+ 
+ 	    case VFP11_ERRATUM_ARM_VENEER:
+               {
+                 bfd_vma branch_from_veneer;
+                 unsigned int insn;
+ 
+                 /* Take size of veneer into account.  */
+                 branch_from_veneer = errnode->u.v.branch->vma
+                                      - errnode->vma - 12;
+ 
+ 		if ((signed) branch_from_veneer < -(1 << 25)
+ 		    || (signed) branch_from_veneer >= (1 << 25))
+ 		  (*_bfd_error_handler) (_("%B: error: VFP11 veneer out of "
+ 					   "range"), output_bfd);
+ 
+                 /* Original instruction.  */
+                 insn = errnode->u.v.branch->u.b.vfp_insn;
+                 contents[endianflip ^ index] = insn & 0xff;
+                 contents[endianflip ^ (index + 1)] = (insn >> 8) & 0xff;
+                 contents[endianflip ^ (index + 2)] = (insn >> 16) & 0xff;
+                 contents[endianflip ^ (index + 3)] = (insn >> 24) & 0xff;
+ 
+                 /* Branch back to insn after original insn.  */
+                 insn = 0xea000000 | ((branch_from_veneer >> 2) & 0xffffff);
+                 contents[endianflip ^ (index + 4)] = insn & 0xff;
+                 contents[endianflip ^ (index + 5)] = (insn >> 8) & 0xff;
+                 contents[endianflip ^ (index + 6)] = (insn >> 16) & 0xff;
+                 contents[endianflip ^ (index + 7)] = (insn >> 24) & 0xff;
+               }
+               break;
+ 
+             default:
+               abort ();
+             }
+         }
+     }
  
    if (mapcount == 0)
      return FALSE;
  
!   if (globals->byteswap_code)
      {
!       qsort (map, mapcount, sizeof (* map), elf32_arm_compare_mapping);
  
!       ptr = map[0].vma;
!       for (i = 0; i < mapcount; i++)
!         {
!           if (i == mapcount - 1)
! 	    end = sec->size;
!           else
!             end = map[i + 1].vma;
! 
!           switch (map[i].type)
! 	    {
! 	    case 'a':
! 	      /* Byte swap code words.  */
! 	      while (ptr + 3 < end)
! 	        {
! 	          tmp = contents[ptr];
! 	          contents[ptr] = contents[ptr + 3];
! 	          contents[ptr + 3] = tmp;
! 	          tmp = contents[ptr + 1];
! 	          contents[ptr + 1] = contents[ptr + 2];
! 	          contents[ptr + 2] = tmp;
! 	          ptr += 4;
! 	        }
! 	      break;
  
! 	    case 't':
! 	      /* Byte swap code halfwords.  */
! 	      while (ptr + 1 < end)
! 	        {
! 	          tmp = contents[ptr];
! 	          contents[ptr] = contents[ptr + 1];
! 	          contents[ptr + 1] = tmp;
! 	          ptr += 2;
! 	        }
! 	      break;
  
! 	    case 'd':
! 	      /* Leave data alone.  */
! 	      break;
! 	    }
!           ptr = end;
!         }
      }
  
    free (map);
    arm_data->mapcount = 0;
+   arm_data->mapsize = 0;
    arm_data->map = NULL;
    unrecord_section_with_arm_elf_section_data (sec);
  
*************** const struct elf_size_info elf32_arm_siz
*** 9640,9646 ****
  #define elf_backend_create_dynamic_sections     elf32_arm_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_link_output_symbol_hook	elf32_arm_output_symbol_hook
  #define elf_backend_size_dynamic_sections	elf32_arm_size_dynamic_sections
  #define elf_backend_init_index_section		_bfd_elf_init_2_index_sections
  #define elf_backend_post_process_headers	elf32_arm_post_process_headers
--- 10540,10545 ----
Index: bfd/elf32-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-ppc.c,v
retrieving revision 1.207
diff -c -p -r1.207 elf32-ppc.c
*** bfd/elf32-ppc.c	22 Nov 2006 00:12:30 -0000	1.207
--- bfd/elf32-ppc.c	22 Jan 2007 14:51:19 -0000
*************** ppc_elf_begin_write_processing (bfd *abf
*** 2151,2156 ****
--- 2151,2157 ----
  
  static bfd_boolean
  ppc_elf_write_section (bfd *abfd ATTRIBUTE_UNUSED,
+ 		       struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
  		       asection *asec,
  		       bfd_byte *contents ATTRIBUTE_UNUSED)
  {
Index: bfd/elf32-score.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-score.c,v
retrieving revision 1.6
diff -c -p -r1.6 elf32-score.c
*** bfd/elf32-score.c	25 Dec 2006 09:22:06 -0000	1.6
--- bfd/elf32-score.c	22 Jan 2007 14:51:19 -0000
*************** _bfd_score_elf_section_processing (bfd *
*** 3489,3495 ****
  }
  
  static bfd_boolean
! _bfd_score_elf_write_section (bfd *output_bfd, asection *sec, bfd_byte *contents)
  {
    bfd_byte *to, *from, *end;
    int i;
--- 3489,3497 ----
  }
  
  static bfd_boolean
! _bfd_score_elf_write_section (bfd *output_bfd,
! 			      struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
!                               asection *sec, bfd_byte *contents)
  {
    bfd_byte *to, *from, *end;
    int i;
Index: bfd/elflink.c
===================================================================
RCS file: /cvs/src/src/bfd/elflink.c,v
retrieving revision 1.243
diff -c -p -r1.243 elflink.c
*** bfd/elflink.c	29 Dec 2006 17:56:32 -0000	1.243
--- bfd/elflink.c	22 Jan 2007 14:51:19 -0000
*************** elf_link_input_bfd (struct elf_final_lin
*** 7942,7948 ****
  
        /* Write out the modified section contents.  */
        if (bed->elf_backend_write_section
! 	  && (*bed->elf_backend_write_section) (output_bfd, o, contents))
  	{
  	  /* Section written out.  */
  	}
--- 7942,7949 ----
  
        /* Write out the modified section contents.  */
        if (bed->elf_backend_write_section
! 	  && (*bed->elf_backend_write_section) (output_bfd, finfo->info, o,
! 						contents))
  	{
  	  /* Section written out.  */
  	}
Index: bfd/elfxx-mips.c
===================================================================
RCS file: /cvs/src/src/bfd/elfxx-mips.c,v
retrieving revision 1.192
diff -c -p -r1.192 elfxx-mips.c
*** bfd/elfxx-mips.c	3 Nov 2006 00:58:10 -0000	1.192
--- bfd/elfxx-mips.c	22 Jan 2007 14:51:20 -0000
*************** _bfd_mips_elf_ignore_discarded_relocs (a
*** 9758,9765 ****
  }
  
  bfd_boolean
! _bfd_mips_elf_write_section (bfd *output_bfd, asection *sec,
! 			     bfd_byte *contents)
  {
    bfd_byte *to, *from, *end;
    int i;
--- 9758,9766 ----
  }
  
  bfd_boolean
! _bfd_mips_elf_write_section (bfd *output_bfd,
! 			     struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
!                              asection *sec, bfd_byte *contents)
  {
    bfd_byte *to, *from, *end;
    int i;
Index: bfd/elfxx-mips.h
===================================================================
RCS file: /cvs/src/src/bfd/elfxx-mips.h,v
retrieving revision 1.35
diff -c -p -r1.35 elfxx-mips.h
*** bfd/elfxx-mips.h	18 Jul 2006 08:56:44 -0000	1.35
--- bfd/elfxx-mips.h	22 Jan 2007 14:51:20 -0000
*************** extern bfd_boolean _bfd_mips_elf_print_p
*** 109,115 ****
  extern bfd_boolean _bfd_mips_elf_discard_info
    (bfd *, struct elf_reloc_cookie *, struct bfd_link_info *);
  extern bfd_boolean _bfd_mips_elf_write_section
!   (bfd *, asection *, bfd_byte *);
  
  extern bfd_boolean _bfd_mips_elf_read_ecoff_info
    (bfd *, asection *, struct ecoff_debug_info *);
--- 109,115 ----
  extern bfd_boolean _bfd_mips_elf_discard_info
    (bfd *, struct elf_reloc_cookie *, struct bfd_link_info *);
  extern bfd_boolean _bfd_mips_elf_write_section
!   (bfd *, struct bfd_link_info *, asection *, bfd_byte *);
  
  extern bfd_boolean _bfd_mips_elf_read_ecoff_info
    (bfd *, asection *, struct ecoff_debug_info *);
Index: ld/NEWS
===================================================================
RCS file: /cvs/src/src/ld/NEWS,v
retrieving revision 1.70
diff -c -p -r1.70 NEWS
*** ld/NEWS	20 Sep 2006 11:35:09 -0000	1.70
--- ld/NEWS	22 Jan 2007 14:51:20 -0000
***************
*** 17,22 ****
--- 17,25 ----
  * New switch: --print-gc-sections to list any sections removed by garabge
    collection.
  
+ * ARM: Added --vfp11-denorm-fix option to work around an erratum in current
+ VFP11 coprocessors.
+ 
  Changes in 2.17:
  
  * Support for the Infineon XC16X has been added by KPIT Cummins Infosystems.
Index: ld/ld.texinfo
===================================================================
RCS file: /cvs/src/src/ld/ld.texinfo,v
retrieving revision 1.180
diff -c -p -r1.180 ld.texinfo
*** ld/ld.texinfo	20 Nov 2006 20:07:51 -0000	1.180
--- ld/ld.texinfo	22 Jan 2007 14:51:20 -0000
*************** each PLT entry. This should lead to such
*** 5457,5462 ****
--- 5457,5492 ----
  This option is enabled implicitly for SymbianOS, so there is no need to
  specify it if you are using that target.
  
+ @cindex VFP11_DENORM_FIX
+ @kindex --vfp11-denorm-fix
+ The @samp{--vfp11-denorm-fix} switch enables a link-time workaround for a
+ bug in certain VFP11 coprocessor hardware, which sometimes allows
+ instructions with denorm operands (which must be handled by support code)
+ to have those operands overwritten by subsequent instructions before
+ the support code can read the intended values.
+ 
+ The bug may be avoided in scalar mode if you allow at least one
+ intervening instruction between a VFP11 instruction which uses a register
+ and another instruction which writes to the same register, or at least two
+ intervening instructions if vector mode is in use. The bug only affects
+ full-compliance floating-point mode: you do not need this workaround if
+ you are using "runfast" mode. Please contact ARM for further details.
+ 
+ If you know you are using buggy VFP11 hardware, you can
+ enable this workaround by specifying the linker option
+ @samp{--vfp-denorm-fix=scalar} if you are using the VFP11 scalar
+ mode only, or @samp{--vfp-denorm-fix=vector} if you are using
+ vector mode (the latter also works for scalar code). The default is
+ @samp{--vfp-denorm-fix=none}.
+ 
+ If the workaround is enabled, instructions are scanned for
+ potentially-troublesome sequences, and a veneer is created for each
+ such sequence which may trigger the erratum. The veneer consists of the
+ first instruction of the sequence and a branch back to the subsequent
+ instruction. The original instruction is then replaced with a branch to
+ the veneer. The extra cycles required to call and return from the veneer
+ are sufficient to avoid the erratum in both the scalar and vector cases.
+ 
  @ifclear GENERIC
  @lowersections
  @end ifclear
Index: ld/emulparams/armelf.sh
===================================================================
RCS file: /cvs/src/src/ld/emulparams/armelf.sh,v
retrieving revision 1.18
diff -c -p -r1.18 armelf.sh
*** ld/emulparams/armelf.sh	9 Aug 2006 00:52:31 -0000	1.18
--- ld/emulparams/armelf.sh	22 Jan 2007 14:51:20 -0000
*************** LITTLE_OUTPUT_FORMAT="elf32-littlearm"
*** 6,12 ****
  TEXT_START_ADDR=0x8000
  TEMPLATE_NAME=elf32
  EXTRA_EM_FILE=armelf
! OTHER_TEXT_SECTIONS='*(.glue_7t) *(.glue_7)'
  OTHER_BSS_SYMBOLS='__bss_start__ = .;'
  OTHER_BSS_END_SYMBOLS='_bss_end__ = . ; __bss_end__ = . ;'
  OTHER_END_SYMBOLS='__end__ = . ;'
--- 6,12 ----
  TEXT_START_ADDR=0x8000
  TEMPLATE_NAME=elf32
  EXTRA_EM_FILE=armelf
! OTHER_TEXT_SECTIONS='*(.glue_7t) *(.glue_7) *(.vfp11_veneer)'
  OTHER_BSS_SYMBOLS='__bss_start__ = .;'
  OTHER_BSS_END_SYMBOLS='_bss_end__ = . ; __bss_end__ = . ;'
  OTHER_END_SYMBOLS='__end__ = . ;'
Index: ld/emulparams/armelf_linux.sh
===================================================================
RCS file: /cvs/src/src/ld/emulparams/armelf_linux.sh,v
retrieving revision 1.16
diff -c -p -r1.16 armelf_linux.sh
*** ld/emulparams/armelf_linux.sh	30 May 2006 16:45:32 -0000	1.16
--- ld/emulparams/armelf_linux.sh	22 Jan 2007 14:51:20 -0000
*************** GENERATE_SHLIB_SCRIPT=yes
*** 11,17 ****
  GENERATE_PIE_SCRIPT=yes
  
  DATA_START_SYMBOLS='__data_start = . ;';
! OTHER_TEXT_SECTIONS='*(.glue_7t) *(.glue_7)'
  OTHER_BSS_SYMBOLS='__bss_start__ = .;'
  OTHER_BSS_END_SYMBOLS='_bss_end__ = . ; __bss_end__ = . ;'
  OTHER_END_SYMBOLS='__end__ = . ;'
--- 11,17 ----
  GENERATE_PIE_SCRIPT=yes
  
  DATA_START_SYMBOLS='__data_start = . ;';
! OTHER_TEXT_SECTIONS='*(.glue_7t) *(.glue_7) *(.vfp11_veneer)'
  OTHER_BSS_SYMBOLS='__bss_start__ = .;'
  OTHER_BSS_END_SYMBOLS='_bss_end__ = . ; __bss_end__ = . ;'
  OTHER_END_SYMBOLS='__end__ = . ;'
Index: ld/emulparams/armnto.sh
===================================================================
RCS file: /cvs/src/src/ld/emulparams/armnto.sh,v
retrieving revision 1.5
diff -c -p -r1.5 armnto.sh
*** ld/emulparams/armnto.sh	30 May 2006 16:45:32 -0000	1.5
--- ld/emulparams/armnto.sh	22 Jan 2007 14:51:20 -0000
*************** LITTLE_OUTPUT_FORMAT="elf32-littlearm"
*** 6,12 ****
  TEXT_START_ADDR=0x00100000
  TEMPLATE_NAME=elf32
  EXTRA_EM_FILE=armelf
! OTHER_TEXT_SECTIONS='*(.glue_7t) *(.glue_7)'
  OTHER_BSS_SYMBOLS='__bss_start__ = .;'
  OTHER_BSS_END_SYMBOLS='_bss_end__ = . ; __bss_end__ = . ;'
  OTHER_END_SYMBOLS='__end__ = . ;'
--- 6,12 ----
  TEXT_START_ADDR=0x00100000
  TEMPLATE_NAME=elf32
  EXTRA_EM_FILE=armelf
! OTHER_TEXT_SECTIONS='*(.glue_7t) *(.glue_7) *(.vfp11_veneer)'
  OTHER_BSS_SYMBOLS='__bss_start__ = .;'
  OTHER_BSS_END_SYMBOLS='_bss_end__ = . ; __bss_end__ = . ;'
  OTHER_END_SYMBOLS='__end__ = . ;'
Index: ld/emultempl/armelf.em
===================================================================
RCS file: /cvs/src/src/ld/emultempl/armelf.em,v
retrieving revision 1.52
diff -c -p -r1.52 armelf.em
*** ld/emultempl/armelf.em	13 Nov 2006 20:39:21 -0000	1.52
--- ld/emultempl/armelf.em	22 Jan 2007 14:51:20 -0000
*************** static int target1_is_rel = 0${TARGET1_I
*** 35,40 ****
--- 35,41 ----
  static char *target2_type = "${TARGET2_TYPE}";
  static int fix_v4bx = 0;
  static int use_blx = 0;
+ static bfd_arm_vfp11_fix vfp11_denorm_fix = BFD_ARM_VFP11_FIX_DEFAULT;
  
  static void
  gld${EMULATION_NAME}_before_parse (void)
*************** arm_elf_before_allocation (void)
*** 124,129 ****
--- 125,134 ----
  
    bfd_elf32_arm_set_byteswap_code (&link_info, byteswap_code);
  
+   /* Choose type of VFP11 erratum fix, or warn if specified fix is unnecessary
+      due to architecture version.  */
+   bfd_elf32_arm_set_vfp11_fix (output_bfd, &link_info);
+ 
    /* We should be able to set the size of the interworking stub section.  We
       can't do it until later if we have dynamic sections, though.  */
    if (! elf_hash_table (&link_info)->dynamic_sections_created)
*************** arm_elf_before_allocation (void)
*** 131,138 ****
        /* Here we rummage through the found bfds to collect glue information.  */
        LANG_FOR_EACH_INPUT_STATEMENT (is)
  	{
  	  if (!bfd_elf32_arm_process_before_allocation (is->the_bfd,
! 							&link_info))
  	    /* xgettext:c-format */
  	    einfo (_("Errors encountered processing file %s"), is->filename);
  	}
--- 136,147 ----
        /* Here we rummage through the found bfds to collect glue information.  */
        LANG_FOR_EACH_INPUT_STATEMENT (is)
  	{
+           /* Initialise mapping tables for code/data.  */
+           bfd_elf32_arm_init_maps (is->the_bfd);
+ 
  	  if (!bfd_elf32_arm_process_before_allocation (is->the_bfd,
! 							&link_info)
! 	      || !bfd_elf32_arm_vfp11_erratum_scan (is->the_bfd, &link_info))
  	    /* xgettext:c-format */
  	    einfo (_("Errors encountered processing file %s"), is->filename);
  	}
*************** arm_elf_before_allocation (void)
*** 146,151 ****
--- 155,176 ----
  }
  
  static void
+ arm_elf_after_allocation (void)
+ {
+   /* Call the standard elf routine.  */
+   after_allocation_default ();
+ 
+   {
+     LANG_FOR_EACH_INPUT_STATEMENT (is)
+       {
+         /* Figure out where VFP11 erratum veneers (and the labels returning
+            from same) have been placed.  */
+         bfd_elf32_arm_vfp11_fix_veneer_locations (is->the_bfd, &link_info);
+       }
+   }
+ }
+ 
+ static void
  arm_elf_finish (void)
  {
    struct bfd_link_hash_entry * h;
*************** static void
*** 214,220 ****
  arm_elf_create_output_section_statements (void)
  {
    bfd_elf32_arm_set_target_relocs (&link_info, target1_is_rel, target2_type,
!                                    fix_v4bx, use_blx);
  }
  
  EOF
--- 239,245 ----
  arm_elf_create_output_section_statements (void)
  {
    bfd_elf32_arm_set_target_relocs (&link_info, target1_is_rel, target2_type,
!                                    fix_v4bx, use_blx, vfp11_denorm_fix);
  }
  
  EOF
*************** PARSE_AND_LIST_PROLOGUE='
*** 230,235 ****
--- 255,261 ----
  #define OPTION_TARGET2			305
  #define OPTION_FIX_V4BX			306
  #define OPTION_USE_BLX			307
+ #define OPTION_VFP11_DENORM_FIX		308
  '
  
  PARSE_AND_LIST_SHORTOPTS=p
*************** PARSE_AND_LIST_LONGOPTS='
*** 243,248 ****
--- 269,275 ----
    { "target2", required_argument, NULL, OPTION_TARGET2},
    { "fix-v4bx", no_argument, NULL, OPTION_FIX_V4BX},
    { "use-blx", no_argument, NULL, OPTION_USE_BLX},
+   { "vfp11-denorm-fix", required_argument, NULL, OPTION_VFP11_DENORM_FIX},
  '
  
  PARSE_AND_LIST_OPTIONS='
*************** PARSE_AND_LIST_OPTIONS='
*** 253,258 ****
--- 280,286 ----
    fprintf (file, _("     --target2=<type>         Specify definition of R_ARM_TARGET2\n"));
    fprintf (file, _("     --fix-v4bx               Rewrite BX rn as MOV pc, rn for ARMv4\n"));
    fprintf (file, _("     --use-blx                Enable use of BLX instructions\n"));
+   fprintf (file, _("     --vfp11-denorm-fix       Specify how to fix VFP11 denorm erratum\n"));
  '
  
  PARSE_AND_LIST_ARGS_CASES='
*************** PARSE_AND_LIST_ARGS_CASES='
*** 287,298 ****
--- 315,338 ----
      case OPTION_USE_BLX:
        use_blx = 1;
        break;
+     
+     case OPTION_VFP11_DENORM_FIX:
+       if (strcmp (optarg, "none") == 0)
+         vfp11_denorm_fix = BFD_ARM_VFP11_FIX_NONE;
+       else if (strcmp (optarg, "scalar") == 0)
+         vfp11_denorm_fix = BFD_ARM_VFP11_FIX_SCALAR;
+       else if (strcmp (optarg, "vector") == 0)
+         vfp11_denorm_fix = BFD_ARM_VFP11_FIX_VECTOR;
+       else
+         einfo (_("Unrecognized VFP11 fix type '\''%s'\''.\n"), optarg);
+       break;
  '
  
  # We have our own after_open and before_allocation functions, but they call
  # the standard routines, so give them a different name.
  LDEMUL_AFTER_OPEN=arm_elf_after_open
  LDEMUL_BEFORE_ALLOCATION=arm_elf_before_allocation
+ LDEMUL_AFTER_ALLOCATION=arm_elf_after_allocation
  LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS=arm_elf_create_output_section_statements
  
  # Replace the elf before_parse function with our own.
Index: ld/testsuite/ld-arm/arm-elf.exp
===================================================================
RCS file: /cvs/src/src/ld/testsuite/ld-arm/arm-elf.exp,v
retrieving revision 1.18
diff -c -p -r1.18 arm-elf.exp
*** ld/testsuite/ld-arm/arm-elf.exp	17 Oct 2006 15:46:21 -0000	1.18
--- ld/testsuite/ld-arm/arm-elf.exp	22 Jan 2007 14:51:20 -0000
*************** set armelftests {
*** 144,149 ****
--- 144,161 ----
      {"Using Thumb lib by another lib" "-shared tmpdir/mixed-lib.so" "" {use-thumb-lib.s}
       {{readelf -Ds use-thumb-lib.sym}}
       "use-thumb-lib.so"}
+     {"VFP11 denorm erratum fix, scalar operation"
+      "-EL --vfp11-denorm-fix=scalar" "-EL" {vfp11-fix-scalar.s}
+      {{objdump -dr vfp11-fix-scalar.d}}
+      "vfp11-fix-scalar"}
+     {"VFP11 denorm erratum fix, vector operation"
+      "-EB --vfp11-denorm-fix=vector" "-EB" {vfp11-fix-vector.s}
+      {{objdump -dr vfp11-fix-vector.d}}
+      "vfp11-fix-vector"}
+     {"VFP11 denorm erratum fix, embedded code-like data"
+      "-EL --vfp11-denorm-fix=scalar" "-EL" {vfp11-fix-none.s}
+      {{objdump -dr vfp11-fix-none.d}}
+      "vfp11-fix-none"}
  
  }
  
Index: ld/testsuite/ld-arm/vfp11-fix-none.d
===================================================================
RCS file: ld/testsuite/ld-arm/vfp11-fix-none.d
diff -N ld/testsuite/ld-arm/vfp11-fix-none.d
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- ld/testsuite/ld-arm/vfp11-fix-none.d	22 Jan 2007 14:51:20 -0000
***************
*** 0 ****
--- 1,9 ----
+ 
+ .*: .*file format elf32-(big|little)arm
+ 
+ Disassembly of section \.text:
+ 
+ 00008000 <_start>:
+     8000:	ee474a20 	\.word	0xee474a20
+     8004:	ed927a00 	\.word	0xed927a00
+     8008:	e12fff1e 	bx	lr
Index: ld/testsuite/ld-arm/vfp11-fix-none.s
===================================================================
RCS file: ld/testsuite/ld-arm/vfp11-fix-none.s
diff -N ld/testsuite/ld-arm/vfp11-fix-none.s
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- ld/testsuite/ld-arm/vfp11-fix-none.s	22 Jan 2007 14:51:20 -0000
***************
*** 0 ****
--- 1,7 ----
+ 	.arm
+ 	.text
+ 	.globl _start
+ _start:
+ 	.word 0xee474a20
+ 	.word 0xed927a00
+ 	bx lr
Index: ld/testsuite/ld-arm/vfp11-fix-scalar.d
===================================================================
RCS file: ld/testsuite/ld-arm/vfp11-fix-scalar.d
diff -N ld/testsuite/ld-arm/vfp11-fix-scalar.d
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- ld/testsuite/ld-arm/vfp11-fix-scalar.d	22 Jan 2007 14:51:20 -0000
***************
*** 0 ****
--- 1,15 ----
+ 
+ .*: .*file format elf32-(big|little)arm
+ 
+ Disassembly of section \.text:
+ 
+ 00008000 <_start>:
+     8000:	0a000001 	beq	800c <__vfp11_veneer_0>
+ 
+ 00008004 <__vfp11_veneer_0_r>:
+     8004:	ed927a00 	flds	s14, \[r2\]
+     8008:	e12fff1e 	bx	lr
+ 
+ 0000800c <__vfp11_veneer_0>:
+     800c:	0e474a20 	fmacseq	s9, s14, s1
+     8010:	eafffffb 	b	8004 <__vfp11_veneer_0_r>
Index: ld/testsuite/ld-arm/vfp11-fix-scalar.s
===================================================================
RCS file: ld/testsuite/ld-arm/vfp11-fix-scalar.s
diff -N ld/testsuite/ld-arm/vfp11-fix-scalar.s
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- ld/testsuite/ld-arm/vfp11-fix-scalar.s	22 Jan 2007 14:51:20 -0000
***************
*** 0 ****
--- 1,7 ----
+ 	.arm
+ 	.text
+ 	.globl _start
+ _start:
+ 	fmacseq s9, s14, s1
+ 	flds s14, [r2]
+ 	bx lr
Index: ld/testsuite/ld-arm/vfp11-fix-vector.d
===================================================================
RCS file: ld/testsuite/ld-arm/vfp11-fix-vector.d
diff -N ld/testsuite/ld-arm/vfp11-fix-vector.d
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- ld/testsuite/ld-arm/vfp11-fix-vector.d	22 Jan 2007 14:51:20 -0000
***************
*** 0 ****
--- 1,16 ----
+ 
+ .*: .*file format elf32-(big|little)arm
+ 
+ Disassembly of section \.text:
+ 
+ 00008000 <_start>:
+     8000:	0a000002 	beq	8010 <__vfp11_veneer_0>
+ 
+ 00008004 <__vfp11_veneer_0_r>:
+     8004:	e1a02003 	mov	r2, r3
+     8008:	ed927a00 	flds	s14, \[r2\]
+     800c:	e12fff1e 	bx	lr
+ 
+ 00008010 <__vfp11_veneer_0>:
+     8010:	0e474a20 	fmacseq	s9, s14, s1
+     8014:	eafffffa 	b	8004 <__vfp11_veneer_0_r>
Index: ld/testsuite/ld-arm/vfp11-fix-vector.s
===================================================================
RCS file: ld/testsuite/ld-arm/vfp11-fix-vector.s
diff -N ld/testsuite/ld-arm/vfp11-fix-vector.s
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- ld/testsuite/ld-arm/vfp11-fix-vector.s	22 Jan 2007 14:51:20 -0000
***************
*** 0 ****
--- 1,8 ----
+ 	.arm
+ 	.text
+ 	.globl _start
+ _start:
+ 	fmacseq s9, s14, s1
+ 	mov r2,r3
+ 	flds s14, [r2]
+ 	bx lr

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]