[PATCH] gold: scanning ARM relocations.

Doug Kwan (關振德) dougkwan@google.com
Tue Jun 2 23:18:00 GMT 2009


This patch adds code to scan for global and local relocations in the
ARM (or more correctly Android) port of gold.  This is part of a
bigger patch and was broken out to make review easier.

-Doug

2009-06-02  Doug Kwan  <dougkwan@google.com>

        * gold/arm.cc (Target_arm::Scan::Scan):  Initialize
        issued_non_pic_error_.
        (class Target_arm::Scan): Declare new method check_non_pic.
        Define new method symbol_needs_plt_entry.
        Declare new data member issued_non_pic_error_.
        (class Target_arm::Relocate): Declare new method
        should_apply_static_reloc.
        (Target_arm::may_need_copy_reloc): Handle STT_ARM_TFUNC.
        (Target_arm::Scan::check_non_pic): Define new method.
        (Target_arm::Scan::local): Handle a small subset of reloc types used
        by Android.
        (Target_arm::Scan::local): Same.
        (Target_arm::Relocate::should_apply_statci_reloc): Define new method.
-------------- next part --------------
Index: gold/arm.cc
===================================================================
RCS file: /cvs/src/src/gold/arm.cc,v
retrieving revision 1.2
diff -u -u -p -r1.2 arm.cc
--- gold/arm.cc	30 May 2009 00:55:42 -0000	1.2
+++ gold/arm.cc	2 Jun 2009 23:08:21 -0000
@@ -75,8 +75,6 @@ class Output_data_plt_arm;
 // R_ARM_PREL31
 // 
 // Coming soon (pending patches):
-// - Local scanner
-// - Global scanner
 // - Relocation
 // - Defining section symbols __exidx_start and __exidx_stop.
 // - Support interworking.
@@ -209,6 +207,7 @@ class Target_arm : public Sized_target<3
   {
    public:
     Scan()
+      : issued_non_pic_error_(false)
     { }
 
     inline void
@@ -237,6 +236,29 @@ class Target_arm : public Sized_target<3
     static void
     unsupported_reloc_global(Sized_relobj<32, big_endian>*,
 			     unsigned int r_type, Symbol*);
+
+    void
+    check_non_pic(Relobj*, unsigned int r_type);
+
+    // Almost identical to Symbol::needs_plt_entry except that it also
+    // handles STT_ARM_TFUNC.
+    static bool
+    symbol_needs_plt_entry(const Symbol* sym)
+    {
+      // An undefined symbol from an executable does not need a PLT entry.
+      if (sym->is_undefined() && !parameters->options().shared())
+	return false;
+
+      return (!parameters->doing_static_link()
+	      && (sym->type() == elfcpp::STT_FUNC
+		  || sym->type() == elfcpp::STT_ARM_TFUNC)
+	      && (sym->is_from_dynobj()
+		  || sym->is_undefined()
+		  || sym->is_preemptible()));
+    }
+
+    // Whether we have issued an error about a non-PIC compilation.
+    bool issued_non_pic_error_;
   };
 
   // The class which implements relocation.
@@ -249,6 +271,13 @@ class Target_arm : public Sized_target<3
     ~Relocate()
     { }
 
+    // Return whether the static relocation needs to be applied.
+    inline bool
+    should_apply_static_reloc(const Sized_symbol<32>* gsym,
+			      int ref_flags,
+			      bool is_32bit,
+			      Output_section* output_section);
+
     // Do a relocation.  Return false if the caller should not issue
     // any warnings about this relocation.
     inline bool
@@ -306,7 +335,8 @@ class Target_arm : public Sized_target<3
   {
     return (!parameters->options().shared()
 	    && gsym->is_from_dynobj()
-	    && gsym->type() != elfcpp::STT_FUNC);
+	    && gsym->type() != elfcpp::STT_FUNC
+	    && gsym->type() != elfcpp::STT_ARM_TFUNC);
   }
 
   // Add a potential copy relocation.
@@ -669,18 +699,65 @@ Target_arm<big_endian>::Scan::unsupporte
 	     object->name().c_str(), r_type);
 }
 
+// We are about to emit a dynamic relocation of type R_TYPE.  If the
+// dynamic linker does not support it, issue an error.  The GNU linker
+// only issues a non-PIC error for an allocated read-only section.
+// Here we know the section is allocated, but we don't know that it is
+// read-only.  But we check for all the relocation types which the
+// glibc dynamic linker supports, so it seems appropriate to issue an
+// error even if the section is not read-only.
+
+template<bool big_endian>
+void
+Target_arm<big_endian>::Scan::check_non_pic(Relobj* object,
+					    unsigned int r_type)
+{
+  switch (r_type)
+    {
+    // These are the relocation types supported by glibc for ARM.
+    case elfcpp::R_ARM_RELATIVE:
+    case elfcpp::R_ARM_COPY:
+    case elfcpp::R_ARM_GLOB_DAT:
+    case elfcpp::R_ARM_JUMP_SLOT:
+    case elfcpp::R_ARM_ABS32:
+    case elfcpp::R_ARM_PC24:
+    // FIXME: The following 3 types are not supported by Android's dynamic
+    // linker.
+    case elfcpp::R_ARM_TLS_DTPMOD32:
+    case elfcpp::R_ARM_TLS_DTPOFF32:
+    case elfcpp::R_ARM_TLS_TPOFF32:
+      return;
+
+    default:
+      // This prevents us from issuing more than one error per reloc
+      // section.  But we can still wind up issuing more than one
+      // error per object file.
+      if (this->issued_non_pic_error_)
+	return;
+      object->error(_("requires unsupported dynamic reloc; "
+		      "recompile with -fPIC"));
+      this->issued_non_pic_error_ = true;
+      return;
+
+    case elfcpp::R_ARM_NONE:
+      gold_unreachable();
+    }
+}
+
 // Scan a relocation for a local symbol.
+// FIXME: This only handles a subset of relocation types used by Android
+// on ARM v5te devices.
 
 template<bool big_endian>
 inline void
 Target_arm<big_endian>::Scan::local(const General_options&,
-				    Symbol_table* /* symtab */,
-				    Layout* /* layout */,
-				    Target_arm* /* target */,
+				    Symbol_table* symtab,
+				    Layout* layout,
+				    Target_arm* target,
 				    Sized_relobj<32, big_endian>* object,
-				    unsigned int /* data_shndx */,
-				    Output_section* /* output_section */,
-				    const elfcpp::Rel<32, big_endian>& /* reloc */,
+				    unsigned int data_shndx,
+				    Output_section* output_section,
+				    const elfcpp::Rel<32, big_endian>& reloc,
 				    unsigned int r_type,
 				    const elfcpp::Sym<32, big_endian>&)
 {
@@ -690,6 +767,77 @@ Target_arm<big_endian>::Scan::local(cons
     case elfcpp::R_ARM_NONE:
       break;
 
+    case elfcpp::R_ARM_ABS32:
+      // If building a shared library (or a position-independent
+      // executable), we need to create a dynamic relocation for
+      // this location. The relocation applied at link time will
+      // apply the link-time value, so we flag the location with
+      // an R_ARM_RELATIVE relocation so the dynamic loader can
+      // relocate it easily.
+      if (parameters->options().output_is_position_independent())
+	{
+	  Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+	  unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+   	  // If we are to add more other reloc types than R_ARM_ABS32,
+   	  // we need to add check_non_pic(object, r_type) here.
+	  rel_dyn->add_local_relative(object, r_sym, elfcpp::R_ARM_RELATIVE,
+				      output_section, data_shndx,
+				      reloc.get_r_offset());
+	}
+      break;
+
+    case elfcpp::R_ARM_REL32:
+    case elfcpp::R_ARM_THM_CALL:
+    case elfcpp::R_ARM_CALL:
+    case elfcpp::R_ARM_PREL31:
+    case elfcpp::R_ARM_JUMP24:
+    case elfcpp::R_ARM_PLT32:
+      break;
+
+    case elfcpp::R_ARM_GOTOFF32:
+      // We need a GOT section:
+      target->got_section(symtab, layout);
+      break;
+
+    case elfcpp::R_ARM_BASE_PREL:
+      // FIXME: What about this?
+      break;
+
+    case elfcpp::R_ARM_GOT_BREL:
+      {
+	// The symbol requires a GOT entry.
+	Output_data_got<32, big_endian>* got =
+	  target->got_section(symtab, layout);
+	unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+	if (got->add_local(object, r_sym, GOT_TYPE_STANDARD))
+	  {
+	    // If we are generating a shared object, we need to add a
+	    // dynamic RELATIVE relocation for this symbol's GOT entry.
+	    if (parameters->options().output_is_position_independent())
+	      {
+		Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+		unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+		rel_dyn->add_local_relative(
+		    object, r_sym, elfcpp::R_ARM_RELATIVE, got,
+		    object->local_got_offset(r_sym, GOT_TYPE_STANDARD));
+	      }
+	  }
+      }
+      break;
+
+    case elfcpp::R_ARM_TARGET1:
+      // This should have been mapped to another type already.
+      // Fall through.
+    case elfcpp::R_ARM_COPY:
+    case elfcpp::R_ARM_GLOB_DAT:
+    case elfcpp::R_ARM_JUMP_SLOT:
+    case elfcpp::R_ARM_RELATIVE:
+      // These are relocations which should only be seen by the
+      // dynamic linker, and should never be seen here.
+      gold_error(_("%s: unexpected reloc %u in object file"),
+		 object->name().c_str(), r_type);
+      break;
+
     default:
       unsupported_reloc_local(object, r_type);
       break;
@@ -710,17 +858,19 @@ Target_arm<big_endian>::Scan::unsupporte
 }
 
 // Scan a relocation for a global symbol.
+// FIXME: This only handles a subset of relocation types used by Android
+// on ARM v5te devices.
 
 template<bool big_endian>
 inline void
 Target_arm<big_endian>::Scan::global(const General_options&,
-				     Symbol_table* /* symtab */,
-				     Layout* /* layout */,
-				     Target_arm* /* target */,
+				     Symbol_table* symtab,
+				     Layout* layout,
+				     Target_arm* target,
 				     Sized_relobj<32, big_endian>* object,
-				     unsigned int /* data_shndx */,
-				     Output_section* /* output_section */,
-				     const elfcpp::Rel<32, big_endian>& /* reloc */,
+				     unsigned int data_shndx,
+				     Output_section* output_section,
+				     const elfcpp::Rel<32, big_endian>& reloc,
 				     unsigned int r_type,
 				     Symbol* gsym)
 {
@@ -730,6 +880,155 @@ Target_arm<big_endian>::Scan::global(con
     case elfcpp::R_ARM_NONE:
       break;
 
+    case elfcpp::R_ARM_ABS32:
+      {
+	// Make a dynamic relocation if necessary.
+	if (gsym->needs_dynamic_reloc(Symbol::ABSOLUTE_REF))
+	  {
+	    if (target->may_need_copy_reloc(gsym))
+	      {
+		target->copy_reloc(symtab, layout, object,
+				   data_shndx, output_section, gsym, reloc);
+	      }
+	    else if (gsym->can_use_relative_reloc(false))
+	      {
+   		// If we are to add more other reloc types than R_ARM_ABS32,
+   		// we need to add check_non_pic(object, r_type) here.
+		Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+		rel_dyn->add_global_relative(gsym, elfcpp::R_ARM_RELATIVE,
+					     output_section, object,
+					     data_shndx, reloc.get_r_offset());
+	      }
+	    else
+	      {
+   		// If we are to add more other reloc types than R_ARM_ABS32,
+   		// we need to add check_non_pic(object, r_type) here.
+		Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+		rel_dyn->add_global(gsym, r_type, output_section, object,
+				    data_shndx, reloc.get_r_offset());
+	      }
+	  }
+      }
+      break;
+
+    case elfcpp::R_ARM_REL32:
+    case elfcpp::R_ARM_PREL31:
+      {
+	// Make a dynamic relocation if necessary.
+	int flags = Symbol::NON_PIC_REF;
+	if (gsym->needs_dynamic_reloc(flags))
+	  {
+	    if (target->may_need_copy_reloc(gsym))
+	      {
+		target->copy_reloc(symtab, layout, object,
+				   data_shndx, output_section, gsym, reloc);
+	      }
+	    else
+	      {
+		check_non_pic(object, r_type);
+		Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+		rel_dyn->add_global(gsym, r_type, output_section, object,
+				    data_shndx, reloc.get_r_offset());
+	      }
+	  }
+      }
+      break;
+
+    case elfcpp::R_ARM_JUMP24:
+    case elfcpp::R_ARM_THM_CALL:
+    case elfcpp::R_ARM_CALL:
+      {
+	if (Target_arm<big_endian>::Scan::symbol_needs_plt_entry(gsym))
+	  target->make_plt_entry(symtab, layout, gsym);
+	// Make a dynamic relocation if necessary.
+	int flags = Symbol::NON_PIC_REF;
+	if (gsym->type() == elfcpp::STT_FUNC
+	    | gsym->type() == elfcpp::STT_ARM_TFUNC)
+	  flags |= Symbol::FUNCTION_CALL;
+	if (gsym->needs_dynamic_reloc(flags))
+	  {
+	    if (target->may_need_copy_reloc(gsym))
+	      {
+		target->copy_reloc(symtab, layout, object,
+				   data_shndx, output_section, gsym,
+				   reloc);
+	      }
+	    else
+	      {
+		check_non_pic(object, r_type);
+		Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+		rel_dyn->add_global(gsym, r_type, output_section, object,
+				    data_shndx, reloc.get_r_offset());
+	      }
+	  }
+      }
+      break;
+
+    case elfcpp::R_ARM_PLT32:
+      // If the symbol is fully resolved, this is just a relative
+      // local reloc.  Otherwise we need a PLT entry.
+      if (gsym->final_value_is_known())
+	break;
+      // If building a shared library, we can also skip the PLT entry
+      // if the symbol is defined in the output file and is protected
+      // or hidden.
+      if (gsym->is_defined()
+	  && !gsym->is_from_dynobj()
+	  && !gsym->is_preemptible())
+	break;
+      target->make_plt_entry(symtab, layout, gsym);
+      break;
+
+    case elfcpp::R_ARM_GOTOFF32:
+      // We need a GOT section.
+      target->got_section(symtab, layout);
+      break;
+
+    case elfcpp::R_ARM_BASE_PREL:
+      // FIXME: What about this?
+      break;
+      
+    case elfcpp::R_ARM_GOT_BREL:
+      {
+	// The symbol requires a GOT entry.
+	Output_data_got<32, big_endian>* got =
+	  target->got_section(symtab, layout);
+	if (gsym->final_value_is_known())
+	  got->add_global(gsym, GOT_TYPE_STANDARD);
+	else
+	  {
+	    // If this symbol is not fully resolved, we need to add a
+	    // GOT entry with a dynamic relocation.
+	    Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+	    if (gsym->is_from_dynobj()
+		|| gsym->is_undefined()
+		|| gsym->is_preemptible())
+	      got->add_global_with_rel(gsym, GOT_TYPE_STANDARD,
+				       rel_dyn, elfcpp::R_ARM_GLOB_DAT);
+	    else
+	      {
+		if (got->add_global(gsym, GOT_TYPE_STANDARD))
+		  rel_dyn->add_global_relative(
+		      gsym, elfcpp::R_ARM_RELATIVE, got,
+		      gsym->got_offset(GOT_TYPE_STANDARD));
+	      }
+	  }
+      }
+      break;
+
+    case elfcpp::R_ARM_TARGET1:
+      // This should have been mapped to another type already.
+      // Fall through.
+    case elfcpp::R_ARM_COPY:
+    case elfcpp::R_ARM_GLOB_DAT:
+    case elfcpp::R_ARM_JUMP_SLOT:
+    case elfcpp::R_ARM_RELATIVE:
+      // These are relocations which should only be seen by the
+      // dynamic linker, and should never be seen here.
+      gold_error(_("%s: unexpected reloc %u in object file"),
+		 object->name().c_str(), r_type);
+      break;
+
     default:
       unsupported_reloc_global(object, r_type, gsym);
       break;
@@ -855,6 +1154,46 @@ Target_arm<big_endian>::do_finalize_sect
     this->copy_relocs_.emit(this->rel_dyn_section(layout));
 }
 
+// Return whether a direct absolute static relocation needs to be applied.
+// In cases where Scan::local() or Scan::global() has created
+// a dynamic relocation other than R_ARM_RELATIVE, the addend
+// of the relocation is carried in the data, and we must not
+// apply the static relocation.
+
+template<bool big_endian>
+inline bool
+Target_arm<big_endian>::Relocate::should_apply_static_reloc(
+    const Sized_symbol<32>* gsym,
+    int ref_flags,
+    bool is_32bit,
+    Output_section* output_section)
+{
+  // If the output section is not allocated, then we didn't call
+  // scan_relocs, we didn't create a dynamic reloc, and we must apply
+  // the reloc here.
+  if ((output_section->flags() & elfcpp::SHF_ALLOC) == 0)
+      return true;
+
+  // For local symbols, we will have created a non-RELATIVE dynamic
+  // relocation only if (a) the output is position independent,
+  // (b) the relocation is absolute (not pc- or segment-relative), and
+  // (c) the relocation is not 32 bits wide.
+  if (gsym == NULL)
+    return !(parameters->options().output_is_position_independent()
+	     && (ref_flags & Symbol::ABSOLUTE_REF)
+	     && !is_32bit);
+
+  // For global symbols, we use the same helper routines used in the
+  // scan pass.  If we did not create a dynamic relocation, or if we
+  // created a RELATIVE dynamic relocation, we should apply the static
+  // relocation.
+  bool has_dyn = gsym->needs_dynamic_reloc(ref_flags);
+  bool is_rel = (ref_flags & Symbol::ABSOLUTE_REF)
+		 && gsym->can_use_relative_reloc(ref_flags
+						 & Symbol::FUNCTION_CALL);
+  return !has_dyn || is_rel;
+}
+
 // Perform a relocation.
 
 template<bool big_endian>


More information about the Binutils mailing list