[PATCH][ARM] Fix output local symbol count after relaxation.

Doug Kwan (關振德) dougkwan@google.com
Fri Jan 29 00:41:00 GMT 2010


Hi,

    This patch adds code to fix output local symbol count fo an object
after relaxation.  This is required if one of its input sections is
discarded or shrunk by EXIDX entry merging.  To avoid unnecessary
updating, we mark an object if an EXIDX input section of it is either
dropped or relaxed.  At the end of relaxation, we update the output
local symbol counts of such marked objects.  This done only for ARM in
the relaxation hook and does not affect performance of other targets.

-Doug

2010-01-28  Doug Kwan  <dougkwan@google.com>

        * arm.cc (Arm_relobj::Arm_relobj): Initialize new data member
        output_local_symbol_count_needs_update_.
        (Arm_relobj::output_local_symbol_count_needs_update,
         Arm_relobj::set_output_local_symbol_count_needs_update,
         Arm_relobj::update_output_local_symbol_count): New methods.
        (Arm_relobj::output_local_symbol_count_needs_update_): New data
        member.
        (Arm_exidx_cantunwind::do_fixed_endian_write): Write address
        of pointed function as in a R_ARM_PREL31 relocation.
        (Arm_output_section<big_endian>::fix_exidx_coverage): Mark objects
        for output local symbol count updating.
        (Target_arm::do_relax): Update output local symbol counts in objects
        if necessary.
        * object.h (Sized_relobj::set_output_local_symbol_count): New method.
-------------- next part --------------
Index: gold/arm.cc
===================================================================
RCS file: /cvs/src/src/gold/arm.cc,v
retrieving revision 1.67
diff -u -u -p -r1.67 arm.cc
--- gold/arm.cc	27 Jan 2010 15:17:46 -0000	1.67
+++ gold/arm.cc	29 Jan 2010 00:31:48 -0000
@@ -1405,7 +1405,8 @@ class Arm_relobj : public Sized_relobj<3
     : Sized_relobj<32, big_endian>(name, input_file, offset, ehdr),
       stub_tables_(), local_symbol_is_thumb_function_(),
       attributes_section_data_(NULL), mapping_symbols_info_(),
-      section_has_cortex_a8_workaround_(NULL), exidx_section_map_()
+      section_has_cortex_a8_workaround_(NULL), exidx_section_map_(),
+      output_local_symbol_count_needs_update_(false)
   { }
 
   ~Arm_relobj()
@@ -1530,6 +1531,20 @@ class Arm_relobj : public Sized_relobj<3
 	    : NULL);
   }
 
+  // Whether output local symbol count needs updating.
+  bool
+  output_local_symbol_count_needs_update() const
+  { return this->output_local_symbol_count_needs_update_; }
+
+  // Set output_local_symbol_count_needs_update flag to be true.
+  void
+  set_output_local_symbol_count_needs_update()
+  { this->output_local_symbol_count_needs_update_ = true; }
+  
+  // Update output local symbol count at the end of relaxation.
+  void
+  update_output_local_symbol_count();
+
  protected:
   // Post constructor setup.
   void
@@ -1606,6 +1621,8 @@ class Arm_relobj : public Sized_relobj<3
   std::vector<bool>* section_has_cortex_a8_workaround_;
   // Map a text section to its associated .ARM.exidx section, if there is one.
   Exidx_section_map exidx_section_map_;
+  // Whether output local symbol count needs updating.
+  bool output_local_symbol_count_needs_update_;
 };
 
 // Arm_dynobj class.
@@ -4827,7 +4844,10 @@ Arm_exidx_cantunwind::do_fixed_endian_wr
   // Write out the entry.  The first word either points to the beginning
   // or after the end of a text section.  The second word is the special
   // EXIDX_CANTUNWIND value.
-  elfcpp::Swap<32, big_endian>::writeval(wv, output_address);
+  uint32_t prel31_offset = output_address - this->address();
+  if (utils::has_overflow<31>(offset))
+    gold_error(_("PREL31 overflow in EXIDX_CANTUNWIND entry"));
+  elfcpp::Swap<32, big_endian>::writeval(wv, prel31_offset & 0x7fffffffU);
   elfcpp::Swap<32, big_endian>::writeval(wv + 1, elfcpp::EXIDX_CANTUNWIND);
 
   of->write_output_view(this->offset(), oview_size, oview);
@@ -5430,6 +5450,10 @@ Arm_output_section<big_endian>::fix_exid
 	  // The whole EXIDX section got merged.  Remove it from output.
 	  gold_assert(section_offset_map == NULL);
 	  exidx_relobj->set_output_section(exidx_shndx, NULL);
+
+	  // All local symbols defined in this input section will be dropped.
+	  // We need to adjust output local symbol count.
+	  arm_relobj->set_output_local_symbol_count_needs_update();
 	}
       else if (deleted_bytes > 0)
 	{
@@ -5441,6 +5465,11 @@ Arm_output_section<big_endian>::fix_exid
 					 *section_offset_map, deleted_bytes);
 	  this->add_relaxed_input_section(merged_section);
 	  arm_relobj->convert_input_section_to_relaxed_section(exidx_shndx);
+
+	  // All local symbols defined in discarded portions of this input
+	  // section will be dropped.  We need to adjust output local symbol
+	  // count.
+	  arm_relobj->set_output_local_symbol_count_needs_update();
 	}
       else
 	{
@@ -6067,6 +6096,99 @@ Arm_relobj<big_endian>::do_gc_process_re
     }
 }
 
+// Update output local symbol count.  Owing to EXIDX entry merging, some local
+// symbols  will be removed in output.  Adjust output local symbol count
+// accordingly.  We can only changed the static output local symbol count.  It
+// is too late to change the dynamic symbols.
+
+template<bool big_endian>
+void
+Arm_relobj<big_endian>::update_output_local_symbol_count()
+{
+  // Caller should check that this needs updating.  We want caller checking
+  // because output_local_symbol_count_needs_update() is most likely inlined.
+  gold_assert(this->output_local_symbol_count_needs_update_);
+
+  gold_assert(this->symtab_shndx() != -1U);
+  if (this->symtab_shndx() == 0)
+    {
+      // This object has no symbols.  Weird but legal.
+      return;
+    }
+
+  // Read the symbol table section header.
+  const unsigned int symtab_shndx = this->symtab_shndx();
+  elfcpp::Shdr<32, big_endian>
+    symtabshdr(this, this->elf_file()->section_header(symtab_shndx));
+  gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
+
+  // Read the local symbols.
+  const int sym_size = elfcpp::Elf_sizes<32>::sym_size;
+  const unsigned int loccount = this->local_symbol_count();
+  gold_assert(loccount == symtabshdr.get_sh_info());
+  off_t locsize = loccount * sym_size;
+  const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
+					      locsize, true, true);
+
+  // Loop over the local symbols.
+
+  typedef typename Sized_relobj<32, big_endian>::Output_sections
+     Output_sections;
+  const Output_sections& out_sections(this->output_sections());
+  unsigned int shnum = this->shnum();
+  unsigned int count = 0;
+  // Skip the first, dummy, symbol.
+  psyms += sym_size;
+  for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
+    {
+      elfcpp::Sym<32, big_endian> sym(psyms);
+
+      Symbol_value<32>& lv((*this->local_values())[i]);
+
+      // This local symbol was already discarded by do_count_local_symbols.
+      if (!lv.needs_output_symtab_entry())
+	continue;
+
+      bool is_ordinary;
+      unsigned int shndx = this->adjust_sym_shndx(i, sym.get_st_shndx(),
+						  &is_ordinary);
+
+      if (shndx < shnum)
+	{
+	  Output_section* os = out_sections[shndx];
+
+	  // This local symbol no longer has an output section.  Discard it.
+	  if (os == NULL)
+	    {
+	      lv.set_no_output_symtab_entry();
+	      continue;
+	    }
+
+	  // Currently we only discard parts of EXIDX input sections.
+	  // We explicitly check for a merged EXIDX input section to avoid
+	  // calling Output_section_data::output_offset unless necessary.
+	  if ((this->get_output_section_offset(shndx) == invalid_address)
+	      && (this->exidx_input_section_by_shndx(shndx) != NULL))
+	    {
+	      section_offset_type output_offset =
+		os->output_offset(this, shndx, lv.input_value());
+	      if (output_offset == -1)
+		{
+		  // This symbol is defined in a part of an EXIDX input section
+		  // that is discarded due to entry merging.
+		  lv.set_no_output_symtab_entry();
+		  continue;
+		}	
+	    }
+	}
+
+      ++count;
+    }
+
+  this->set_output_local_symbol_count(count);
+  this->output_local_symbol_count_needs_update_ = false;
+}
+
 // Arm_dynobj methods.
 
 // Read the symbol information.
@@ -6726,6 +6848,8 @@ Target_arm<big_endian>::Scan::global(Sym
       break;
 
     case elfcpp::R_ARM_REL32:
+      break;
+
     case elfcpp::R_ARM_PREL31:
       {
 	// Make a dynamic relocation if necessary.
@@ -9477,10 +9601,27 @@ Target_arm<big_endian>::do_relax(
 
   // Finalize the stubs in the last relaxation pass.
   if (!continue_relaxation)
-    for (Stub_table_iterator sp = this->stub_tables_.begin();
-	 (sp != this->stub_tables_.end()) && !any_stub_table_changed;
-	 ++sp)
-      (*sp)->finalize_stubs();
+    {
+      for (Stub_table_iterator sp = this->stub_tables_.begin();
+	   (sp != this->stub_tables_.end()) && !any_stub_table_changed;
+	    ++sp)
+	(*sp)->finalize_stubs();
+
+      // Update output local symbol counts of objects if necessary.
+      for (Input_objects::Relobj_iterator op = input_objects->relobj_begin();
+	   op != input_objects->relobj_end();
+	   ++op)
+	{
+	  Arm_relobj<big_endian>* arm_relobj =
+	    Arm_relobj<big_endian>::as_arm_relobj(*op);
+
+	  // Update output local symbol counts.  We need to discard local
+	  // symbols defined in parts of input sections that are discarded by
+	  // relaxation.
+	  if (arm_relobj->output_local_symbol_count_needs_update())
+	    arm_relobj->update_output_local_symbol_count();
+	}
+    }
 
   return continue_relaxation;
 }
Index: gold/object.h
===================================================================
RCS file: /cvs/src/src/gold/object.h,v
retrieving revision 1.90
diff -u -u -p -r1.90 object.h
--- gold/object.h	26 Jan 2010 21:29:10 -0000	1.90
+++ gold/object.h	29 Jan 2010 00:31:48 -0000
@@ -1689,6 +1689,11 @@ class Sized_relobj : public Relobj
   do_relocate_sections(const Symbol_table* symtab, const Layout* layout,
 		       const unsigned char* pshdrs, Views* pviews);
 
+  // Allow a child to set output local symbol count.
+  void
+  set_output_local_symbol_count(unsigned int value)
+  { this->output_local_symbol_count_ = value; }
+   
  private:
   // For convenience.
   typedef Sized_relobj<size, big_endian> This;


More information about the Binutils mailing list