2010-01-14 Doug Kwan <dougkwan@google.com>
authorDoug Kwan <dougkwan@google.com>
Fri, 15 Jan 2010 04:58:34 +0000 (04:58 +0000)
committerDoug Kwan <dougkwan@google.com>
Fri, 15 Jan 2010 04:58:34 +0000 (04:58 +0000)
* arm.cc (map, utility): Include headers.
(Target_arm::apply_cortex_a8_workaround): New method.
(Arm_relobj::do_relocate_sections): Apply any Cortex-A8 workaround.
(Target_arm::Scan::local): Handle R_ARM_THM_JUMP24, R_ARM_THM_JUMP19.
(Target_arm::Scan::global): R_ARM_THM_JUMP19.
(Target_arm::do_finalize_sections): Set fix_cortex_a8_ according to
the --[no-]fix-cortex-a8 command line options.
(Target_arm::Relocate::relocate): Handle R_ARM_JUMP19.
(Target_arm::relocate_stub): Use addend in instruction template.
* options.h (DEFINE_bool): Set the user-set flag.
(General_options): Add --[no-]-fix-cortex options.
* output.cc (Output_section::convert_input_sections_to_relaxed_sections)
: Update fast look-up map after conversion.

gold/ChangeLog
gold/arm.cc
gold/options.h
gold/output.cc

index 09c516b500080a8a948a0e8e2d38fd39eaaf58a2..e00687160dd9ca895e9158b46baad86f45f7ec80 100644 (file)
@@ -1,3 +1,19 @@
+2010-01-14  Doug Kwan  <dougkwan@google.com>
+
+       * arm.cc (map, utility): Include headers.
+       (Target_arm::apply_cortex_a8_workaround): New method.
+       (Arm_relobj::do_relocate_sections): Apply any Cortex-A8 workaround.
+       (Target_arm::Scan::local): Handle R_ARM_THM_JUMP24, R_ARM_THM_JUMP19.
+       (Target_arm::Scan::global): R_ARM_THM_JUMP19.
+       (Target_arm::do_finalize_sections): Set fix_cortex_a8_ according to
+       the --[no-]fix-cortex-a8 command line options.
+       (Target_arm::Relocate::relocate): Handle R_ARM_JUMP19.
+       (Target_arm::relocate_stub): Use addend in instruction template.
+       * options.h (DEFINE_bool): Set the user-set flag.
+       (General_options): Add --[no-]-fix-cortex options.
+       * output.cc (Output_section::convert_input_sections_to_relaxed_sections)
+       : Update fast look-up map after conversion. 
+
 2010-01-14  Sriraman Tallam  <tmsriram@google.com>
 
        * object.cc (Sized_relobj::do_layout): Change to call layout_gnu_stack
index 38f9d71a648ae2ececc5b1b91e452ca924a43f8f..cbac4a58682ef49ac08382b5e44385a11dc39a4e 100644 (file)
@@ -30,6 +30,8 @@
 #include <cstdio>
 #include <string>
 #include <algorithm>
+#include <map>
+#include <utility>
 
 #include "elfcpp.h"
 #include "parameters.h"
@@ -1686,6 +1688,11 @@ class Target_arm : public Sized_target<32, big_endian>
                                  section_size_type, section_size_type,
                                  const unsigned char*, Arm_address);
 
+  // Apply Cortex-A8 workaround to a branch.
+  void
+  apply_cortex_a8_workaround(const Cortex_a8_stub*, Arm_address,
+                            unsigned char*, Arm_address);
+
  protected:
   // Make an ELF object.
   Object*
@@ -4554,36 +4561,65 @@ Arm_relobj<big_endian>::do_relocate_sections(
       Arm_input_section<big_endian>* arm_input_section =
        arm_target->find_arm_input_section(this, i);
 
-      if (arm_input_section == NULL
-         || !arm_input_section->is_stub_table_owner()
-         || arm_input_section->stub_table()->empty())
-       continue;
-
-      // We cannot discard a section if it owns a stub table.
-      Output_section* os = this->output_section(i);
-      gold_assert(os != NULL);
-
-      relinfo.reloc_shndx = elfcpp::SHN_UNDEF;
-      relinfo.reloc_shdr = NULL;
-      relinfo.data_shndx = i;
-      relinfo.data_shdr = pshdrs + i * elfcpp::Elf_sizes<32>::shdr_size;
-
-      gold_assert((*pviews)[i].view != NULL);
-
-      // We are passed the output section view.  Adjust it to cover the
-      // stub table only.
-      Stub_table<big_endian>* stub_table = arm_input_section->stub_table();
-      gold_assert((stub_table->address() >= (*pviews)[i].address)
-                 && ((stub_table->address() + stub_table->data_size())
-                     <= (*pviews)[i].address + (*pviews)[i].view_size));
-
-      off_t offset = stub_table->address() - (*pviews)[i].address;
-      unsigned char* view = (*pviews)[i].view + offset;
-      Arm_address address = stub_table->address();
-      section_size_type view_size = stub_table->data_size();
+      if (arm_input_section != NULL
+         && arm_input_section->is_stub_table_owner()
+         && !arm_input_section->stub_table()->empty())
+       {
+         // We cannot discard a section if it owns a stub table.
+         Output_section* os = this->output_section(i);
+         gold_assert(os != NULL);
+
+         relinfo.reloc_shndx = elfcpp::SHN_UNDEF;
+         relinfo.reloc_shdr = NULL;
+         relinfo.data_shndx = i;
+         relinfo.data_shdr = pshdrs + i * elfcpp::Elf_sizes<32>::shdr_size;
+
+         gold_assert((*pviews)[i].view != NULL);
+
+         // We are passed the output section view.  Adjust it to cover the
+         // stub table only.
+         Stub_table<big_endian>* stub_table = arm_input_section->stub_table();
+         gold_assert((stub_table->address() >= (*pviews)[i].address)
+                     && ((stub_table->address() + stub_table->data_size())
+                         <= (*pviews)[i].address + (*pviews)[i].view_size));
+
+         off_t offset = stub_table->address() - (*pviews)[i].address;
+         unsigned char* view = (*pviews)[i].view + offset;
+         Arm_address address = stub_table->address();
+         section_size_type view_size = stub_table->data_size();
  
-      stub_table->relocate_stubs(&relinfo, arm_target, os, view, address,
-                                view_size);
+         stub_table->relocate_stubs(&relinfo, arm_target, os, view, address,
+                                    view_size);
+       }
+
+      // Apply Cortex A8 workaround if applicable.
+      if (this->section_has_cortex_a8_workaround(i))
+       {
+         unsigned char* view = (*pviews)[i].view;
+         Arm_address view_address = (*pviews)[i].address;
+         section_size_type view_size = (*pviews)[i].view_size;
+         Stub_table<big_endian>* stub_table = this->stub_tables_[i];
+
+         // Adjust view to cover section.
+         Output_section* os = this->output_section(i);
+         gold_assert(os != NULL);
+         Arm_address section_address = os->output_address(this, i, 0);
+         uint64_t section_size = this->section_size(i);
+
+         gold_assert(section_address >= view_address
+                     && ((section_address + section_size)
+                         <= (view_address + view_size)));
+
+         unsigned char* section_view = view + (section_address - view_address);
+
+         // Apply the Cortex-A8 workaround to the output address range
+         // corresponding to this input section.
+         stub_table->apply_cortex_a8_workaround_to_address_range(
+             arm_target,
+             section_view,
+             section_address,
+             section_size);
+       }
     }
 }
 
@@ -5086,6 +5122,8 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
     case elfcpp::R_ARM_CALL:
     case elfcpp::R_ARM_PREL31:
     case elfcpp::R_ARM_JUMP24:
+    case elfcpp::R_ARM_THM_JUMP24:
+    case elfcpp::R_ARM_THM_JUMP19:
     case elfcpp::R_ARM_PLT32:
     case elfcpp::R_ARM_THM_ABS5:
     case elfcpp::R_ARM_ABS8:
@@ -5272,6 +5310,7 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
 
     case elfcpp::R_ARM_JUMP24:
     case elfcpp::R_ARM_THM_JUMP24:
+    case elfcpp::R_ARM_THM_JUMP19:
     case elfcpp::R_ARM_CALL:
     case elfcpp::R_ARM_THM_CALL:
 
@@ -5474,11 +5513,27 @@ Target_arm<big_endian>::do_finalize_sections(
     }
 
   // Check BLX use.
-  Object_attribute* attr =
+  const Object_attribute* cpu_arch_attr =
     this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
-  if (attr->int_value() > elfcpp::TAG_CPU_ARCH_V4)
+  if (cpu_arch_attr->int_value() > elfcpp::TAG_CPU_ARCH_V4)
     this->set_may_use_blx(true);
  
+  // Check if we need to use Cortex-A8 workaround.
+  if (parameters->options().user_set_fix_cortex_a8())
+    this->fix_cortex_a8_ = parameters->options().fix_cortex_a8();
+  else
+    {
+      // If neither --fix-cortex-a8 nor --no-fix-cortex-a8 is used, turn on
+      // Cortex-A8 erratum workaround for ARMv7-A or ARMv7 with unknown
+      // profile.  
+      const Object_attribute* cpu_arch_profile_attr =
+       this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch_profile);
+      this->fix_cortex_a8_ =
+       (cpu_arch_attr->int_value() == elfcpp::TAG_CPU_ARCH_V7
+         && (cpu_arch_profile_attr->int_value() == 'A'
+             || cpu_arch_profile_attr->int_value() == 0));
+    }
+  
   // Fill in some more dynamic tags.
   const Reloc_section* rel_plt = (this->plt_ == NULL
                                  ? NULL
@@ -5940,6 +5995,12 @@ Target_arm<big_endian>::Relocate::relocate(
                                           is_weakly_undefined_without_plt);
       break;
 
+    case elfcpp::R_ARM_THM_JUMP19:
+      reloc_status =
+       Arm_relocate_functions::thm_jump19(view, object, psymval, address,
+                                          thumb_bit);
+      break;
+
     case elfcpp::R_ARM_PREL31:
       reloc_status = Arm_relocate_functions::prel31(view, object, psymval,
                                                    address, thumb_bit);
@@ -7711,7 +7772,7 @@ Target_arm<big_endian>::relocate_stub(
       gold_assert(reloc_offset + reloc_size <= view_size);
 
       // This is the address of the stub destination.
-      Arm_address target = stub->reloc_target(i);
+      Arm_address target = stub->reloc_target(i) + insn->reloc_addend();
       Symbol_value<32> symval;
       symval.set_output_value(target);
 
@@ -7949,6 +8010,58 @@ Target_arm<big_endian>::scan_span_for_cortex_a8_erratum(
     }
 }
 
+// Apply the Cortex-A8 workaround.
+
+template<bool big_endian>
+void
+Target_arm<big_endian>::apply_cortex_a8_workaround(
+    const Cortex_a8_stub* stub,
+    Arm_address stub_address,
+    unsigned char* insn_view,
+    Arm_address insn_address)
+{
+  typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
+  Valtype* wv = reinterpret_cast<Valtype*>(insn_view);
+  Valtype upper_insn = elfcpp::Swap<16, big_endian>::readval(wv);
+  Valtype lower_insn = elfcpp::Swap<16, big_endian>::readval(wv + 1);
+  off_t branch_offset = stub_address - (insn_address + 4);
+
+  typedef struct Arm_relocate_functions<big_endian> RelocFuncs;
+  switch (stub->stub_template()->type())
+    {
+    case arm_stub_a8_veneer_b_cond:
+      gold_assert(!utils::has_overflow<21>(branch_offset));
+      upper_insn = RelocFuncs::thumb32_cond_branch_upper(upper_insn,
+                                                        branch_offset);
+      lower_insn = RelocFuncs::thumb32_cond_branch_lower(lower_insn,
+                                                        branch_offset);
+      break;
+
+    case arm_stub_a8_veneer_b:
+    case arm_stub_a8_veneer_bl:
+    case arm_stub_a8_veneer_blx:
+      if ((lower_insn & 0x5000U) == 0x4000U)
+       // For a BLX instruction, make sure that the relocation is
+       // rounded up to a word boundary.  This follows the semantics of
+       // the instruction which specifies that bit 1 of the target
+       // address will come from bit 1 of the base address.
+       branch_offset = (branch_offset + 2) & ~3;
+
+      // Put BRANCH_OFFSET back into the insn.
+      gold_assert(!utils::has_overflow<25>(branch_offset));
+      upper_insn = RelocFuncs::thumb32_branch_upper(upper_insn, branch_offset);
+      lower_insn = RelocFuncs::thumb32_branch_lower(lower_insn, branch_offset);
+      break;
+
+    default:
+      gold_unreachable();
+    }
+
+  // Put the relocated value back in the object file:
+  elfcpp::Swap<16, big_endian>::writeval(wv, upper_insn);
+  elfcpp::Swap<16, big_endian>::writeval(wv + 1, lower_insn);
+}
+
 template<bool big_endian>
 class Target_selector_arm : public Target_selector
 {
index ab4a9e6ccb0b94982e996dd206e6a873c01383ac..c13601c03ca379e3c8d04953ffea69742a9f73a6 100644 (file)
@@ -315,7 +315,10 @@ struct Struct_special : public Struct_var
     void                                                                 \
     parse_to_value(const char*, const char*,                             \
                    Command_line*, General_options* options)              \
-    { options->set_##varname__(false); }                                 \
+    {                                                                    \
+      options->set_##varname__(false);                                   \
+      options->set_user_set_##varname__();                               \
+    }                                                                    \
                                                                          \
     options::One_option option;                                          \
   };                                                                     \
@@ -719,6 +722,10 @@ class General_options
   DEFINE_string(fini, options::ONE_DASH, '\0', "_fini",
                 N_("Call SYMBOL at unload-time"), N_("SYMBOL"));
 
+  DEFINE_bool(fix_cortex_a8, options::TWO_DASHES, '\0', false,
+             N_("(ARM only) Fix binaries for Cortex-A8 erratum."),
+             N_("(ARM only) Do not fix binaries for Cortex-A8 erratum."));
+
   DEFINE_bool(g, options::EXACTLY_ONE_DASH, '\0', false,
              N_("Ignored"), NULL);
 
index bb61d866da2c6cd8dad9f049e1b3c1930526abf3..634249fa303137431ee0608a42e9196cdf871b29 100644 (file)
@@ -2272,6 +2272,15 @@ Output_section::convert_input_sections_to_relaxed_sections(
            relaxed_sections,
            map,
            &this->input_sections_);
+
+  // Update fast look-up map.
+  if (this->is_relaxed_input_section_map_valid_)
+    for (size_t i = 0; i < relaxed_sections.size(); ++i)
+      {
+       Output_relaxed_input_section* poris = relaxed_sections[i];
+       Input_section_specifier iss(poris->relobj(), poris->shndx());
+       this->relaxed_input_section_map_[iss] = poris;
+      }
 }
 
 // Update the output section flags based on input section flags.