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]

STT_SPARC_REGISTER and gold...


Ian, I just wanted to get a small discussion going again
about this.  I've done some work towards adding proper
STT_SPARC_REGISTER support to gold, but I can't get past
a certain area about how to design this.  Perhaps you
can help.

BTW, another document for referencing this area of the Sparc
ABI is:

	http://www.sparc.org/standards/SCD.4.2.ps.Z

I will add that URL to the elfcpp/sparc.h file when I get a chance.

I've included my work-in-progress patch below.  It's just for
reference and nothing to be considered seriously.

So the issue is what to do about whether to keep these things in the
gold symbol table.  I think we should but this means:

1) For #scratch register uses, we need to modify the name
   internally to gold.  All #scratch register symbols have
   st_info==0, and thus have the null string.  The only
   difference between them is the st_value field, which will
   indicate the register number.

   Non-#scratch STT_SPARC_REGISTER symbols have a normal symbol
   name, just like any other symbol, which we should retain.

   So my idea was to change the name string for new #scratch
   register symbols, to the textual register name, such as
   "%g2", "%g3", "%g6", "%g7".

   But this means at symbol output time we have to undo this
   change somehow, so that the st_info field will end up being
   the correct value, zero, for #scratch entries.

2) If we do #1, this means that a hook is need in the symbol
   resolver.  There is one there, but like the current
   ->make_symbol() it's not very useful in it's current form
   as it would require a target to duplicate all of the
   current resolver logic inside of the target :-)

   if (object->target()->has_resolve())
     {
       Sized_target<size, big_endian>* sized_target;
       sized_target = object->sized_target<size, big_endian>();
       sized_target->resolve(to, sym, object, version);
       return;
     }

   To be optimally useful, it seems as if this interface should
   return a trinary state.  One of: REJECT, ACCEPT_UNCONDITIONALLY,
   and ACCEPT_BUT_CONTINUE_RESOLVE_PROECSSING.

   This hook is where I would determine if a STT_SPARC_REGISTER
   symbol is compatible with a previously one seen for the same
   register.  With the #scratch renaming idea in #1 this would
   work out perfectly, I think, as the gold symtab entry would
   be passed into the target resolver hook.

Next, there is the issue of how to output these things.  Based
upon my understanding of how this stuff works, we roughly have
to:

1) Not output STT_SPARC_REGISTER symbols that came from
   dynamic objects, they should only be used for validation
   and comparison against other STT_SPARC_REGISTER symbols
   seen.  It is the dynamic linker's job to look at such
   entries and take appropriate action.

2) Output (STB_LOCAL && visibility != HIDDEN) STT_SPARC_REGISTER
   symbols to the dynamic symbol table.

3) Output DT_SPARC_REGISTER entries into the .dynamic section,
   which point to the entries output in #2.

4) Because if the #scratch renaming suggested above, we have
   to undo that, or take some action to make the st_info
   field get output as zero for such symbol entries.

There are probably some other obscure details I've forgotten.

Anyways, Ian, any suggestions of ideas, given the above?

Thanks!

Index: sparc.cc
===================================================================
RCS file: /cvs/src/src/gold/sparc.cc,v
retrieving revision 1.5
diff -u -p -r1.5 sparc.cc
--- sparc.cc	19 Apr 2008 18:30:58 -0000	1.5
+++ sparc.cc	24 Apr 2008 00:46:44 -0000
@@ -63,6 +63,11 @@ class Target_sparc : public Sized_target
   {
   }
 
+  // Make a new symbol table entry for the target.
+  Sized_symbol<size>*
+  make_symbol(Object*, const elfcpp::Sym<size, big_endian>&,
+	      const char*, const char*, unsigned int);
+
   // Scan the relocations to look for symbol adjustments.
   void
   scan_relocs(const General_options& options,
@@ -318,6 +323,246 @@ class Target_sparc : public Sized_target
     GOT_TYPE_TLS_PAIR = 2,      // GOT entry for TLS module/offset pair
   };
 
+  // Class for handling 64-bit ABI STT_SPARC_REGISTER symbol table
+  // entries.
+  //
+  // Such entries are used to record the usage of global registers
+  // by an object file.  In assembler the register usage is denoted
+  // using the syntax:
+  //
+  //         .register REGISTER, USAGE
+  //
+  // Where REGISTER is one of %g2, %g3, %g6, %g7 and usage is
+  // one of #scratch, #ignore, or a global symbol name.  If the
+  // usage is #ignore, the assembler does not record any
+  // information about the global register usage into the object
+  // file.  If the usage is #scratch, the symbol name is an empty
+  // string, using st_index 0, and must have binding STB_GLOBAL.
+  //
+  // If the register represents a global symbol, and is initialized
+  // by the current object, st_shndx will be SHN_ABS and there will
+  // be an R_SPARC_REGISTER relocation.  Otherwise the st_shndx
+  // will be SHN_UNDEF.
+  //
+  // For the cases where usage information is recorded, the link
+  // editor is expected to collect all of the uses and make sure
+  // they are compatible.
+  class Global_register
+  {
+  public:
+    Global_register()
+    {
+      for (int i = 0; i < 4; i++)
+	this->reg_info_[i].name = NULL;
+    }
+
+    enum Global_register_map
+    {
+      GLOBAL_REG_G2 = 0,
+      GLOBAL_REG_G3 = 1,
+      GLOBAL_REG_G6 = 2,
+      GLOBAL_REG_G7 = 3,
+    };
+
+    int
+    global_reg_to_map(int reg)
+    {
+      switch (reg)
+	{
+	case 2:
+	  return GLOBAL_REG_G2;
+	case 3:
+	  return GLOBAL_REG_G3;
+	case 6:
+	  return GLOBAL_REG_G6;
+	case 7:
+	  return GLOBAL_REG_G7;
+	default:
+	  gold_unreachable();
+	}
+    }
+
+    int
+    map_to_global_reg(int map)
+    {
+      switch (map)
+	{
+	case GLOBAL_REG_G2:
+	  return 2;
+	case GLOBAL_REG_G3:
+	  return 3;
+	case GLOBAL_REG_G6:
+	  return 6;
+	case GLOBAL_REG_G7:
+	  return 7;
+	default:
+	  gold_unreachable();
+	}
+    }
+
+    // Check, for the given non-register symbol, that we don't have a
+    // matching STT_SPARC_REGISTER symbol.
+    void
+    check_nonregister_symbol(Object *object, enum elfcpp::STT st_type,
+			     const char* name)
+    {
+      for (int i = 0; i < 4; i++)
+	{
+	  struct Global_reg_info *gp = &this->reg_info_[i];
+
+	  if (gp->name == NULL || !*gp->name)
+	    continue;
+	  if (! strcmp (gp->name, name))
+	    {
+	      gold_error(_("%s: Symbol `%s' changed type to %d, "
+			   "previous STT_SPARC_REGISTER %%g%d in %s.\n"),
+			 object->name().c_str(), name,
+			 st_type, map_to_global_reg(i),
+			 gp->object->name().c_str());
+	    }
+	}
+    }
+
+    // Check, for a given STT_SPARC_REGISTER symbol, that we don't
+    // have a conflicting STT_SPARC_REGISTER entry.
+    bool
+    check_register_symbol(Object *object, int reg, const char* name,
+			  unsigned int shndx,
+			  const elfcpp::Sym<size, big_endian>& sym)
+    {
+      int map = global_reg_to_map(reg);
+      struct Global_reg_info *gp = &this->reg_info_[map];
+
+      // We don't have an existing entry, no conflict.
+      if (gp->name == NULL)
+	return true;
+
+      // Both entries are #scratch, no conflict.
+      if (!*gp->name && !*name)
+	return true;
+
+      
+    }
+
+    // Validate a STT_SPARC_REGISTER symbol.  Make sure it does not
+    // conflict with any existing entry.  Signal an error and return
+    // false on conflict.
+    bool
+    validate(Object *object, int reg, const char* name,
+	     unsigned int st_shndx,
+	     const elfcpp::Sym<size, big_endian>& sym)
+    {
+      if (size == 32)
+	{
+	  gold_error(_("%s: Unexpected STT_SPARC_REGISTER in "
+		       "32-bit object.\n"),
+		     object->name().c_str());
+	  return false;
+	}
+
+      switch (reg)
+	{
+	case 2:
+	case 3:
+	case 6:
+	case 7:
+	  break;
+
+	default:
+	  gold_error(_("%s: Unexpected STT_SPARC_REGISTER number %d.\n"),
+		     object->name().c_str(), reg);
+	  return false;
+	}
+
+      if (sym.get_st_size() != 0)
+	{
+	  gold_error(_("%s: Unexpected STT_SPARC_REGISTER st_size %lu.\n"),
+		     object->name().c_str(),
+		     static_cast<unsigned long>(sym.get_st_size()));
+	  return false;
+	}
+
+      if (sym.get_st_other() != 0)
+	{
+	  gold_error(_("%s: Unexpected STT_SPARC_REGISTER st_other %lu.\n"),
+		     object->name().c_str(),
+		     static_cast<unsigned long>(sym.get_st_other()));
+	  return false;
+	}
+
+      if (sym.get_st_name() == 0)
+	{
+	  if (sym.get_st_bind() != elfcpp::STB_GLOBAL)
+	    {
+	      gold_error(_("%s: STT_SPARC_REGISTER scratch uses incorrect "
+			   "binding %lu.\n"),
+			 object->name().c_str(),
+			 static_cast<unsigned long>(sym.get_st_bind()));
+	      return false;
+	    }
+	  if (st_shndx != elfcpp::SHN_UNDEF)
+	    {
+	      gold_error(_("%s: STT_SPARC_REGISTER scratch uses incorrect "
+			   "st_shndx %u.\n"),
+			 object->name().c_str(), st_shndx);
+	      return false;
+	    }
+	}
+      else
+	{
+	  if (st_shndx != elfcpp::SHN_UNDEF && st_shndx != elfcpp::SHN_ABS)
+	    {
+	      gold_error(_("%s: STT_SPARC_REGISTER global uses incorrect "
+			   "st_shndx %u.\n"),
+			 object->name().c_str(), st_shndx);
+	      return false;
+	    }
+	}
+
+      if (!check_register_symbol(object, reg, name, st_shndx, sym))
+	return false;
+
+      return true;
+    }
+
+    // Record information for a STT_SPARC_REGISTER symbol.
+    void
+    record(Object *object, int reg, const char* name,
+	   unsigned int shndx, enum elfcpp::STB bind)
+    {
+      int map = global_reg_to_map(reg);
+      struct Global_reg_info *gp = &this->reg_info_[map];
+
+      if (gp->name != NULL)
+	{
+	  if (strcmp (gp->name, name))
+	    {
+	      gold_error(_("%s: Register %%g%d used incompatibly: "
+			   "%s, previously was %s in object %s.\n"),
+			 object->name().c_str(), reg,
+			 (*name ? name : "#scratch"),
+			 (*gp->name ? gp->name : "#scratch"),
+			 gp->object->name().c_str());
+	    }
+	  return;
+	}
+      gp->object = object;
+      gp->name = name;
+      gp->shndx = shndx;
+      gp->bind = bind;
+    }
+
+  private:
+    struct Global_reg_info {
+      const char* name;
+      Object* object;
+      unsigned int shndx;
+      enum elfcpp::STB bind;
+    } reg_info_[4];
+  };
+
+  // STT_SPARC_REGISTER symbols seen.
+  class Global_register global_regs_;
   // The GOT section.
   Output_data_got<size, big_endian>* got_;
   // The PLT section.
@@ -340,7 +585,7 @@ Target::Target_info Target_sparc<32, tru
   32,			// size
   true,			// is_big_endian
   elfcpp::EM_SPARC,	// machine_code
-  false,		// has_make_symbol
+  true,			// has_make_symbol
   false,		// has_resolve
   false,		// has_code_fill
   true,			// is_default_stack_executable
@@ -357,7 +602,7 @@ Target::Target_info Target_sparc<64, tru
   64,			// size
   true,			// is_big_endian
   elfcpp::EM_SPARCV9,	// machine_code
-  false,		// has_make_symbol
+  true,			// has_make_symbol
   false,		// has_resolve
   false,		// has_code_fill
   true,			// is_default_stack_executable
@@ -368,6 +613,36 @@ Target::Target_info Target_sparc<64, tru
   8 * 1024		// common_pagesize (overridable by -z common-page-size)
 };
 
+template<int size, bool big_endian>
+Sized_symbol<size>*
+Target_sparc<size, big_endian>::make_symbol(Object *object,
+			const elfcpp::Sym<size, big_endian>& sym,
+			const char *name, const char *,
+			unsigned int st_shndx)
+{
+  if (sym.get_st_type() != elfcpp::STT_SPARC_REGISTER)
+    {
+      if (size == 64)
+	this->global_regs_.check_nonregister_symbol(object,
+						    sym.get_st_type(),
+						    name);
+
+      return new Sized_symbol<size>();
+    }
+
+  int reg = static_cast<int>(sym.get_st_value());
+  if (!this->global_regs_.validate(object, reg, name, st_shndx, sym))
+    return NULL;
+
+  // If this is coming from a dynamic object, do not record it.
+  // Such work is done by the dynamic linker at run time.
+  if (!object->is_dynamic())
+    this->global_regs_.record(object, reg, name,
+			      st_shndx, sym.get_st_bind());
+
+  return NULL;
+}
+
 // We have to take care here, even when operating in little-endian
 // mode, sparc instructions are still big endian.
 template<int size, bool big_endian>
Index: symtab.cc
===================================================================
RCS file: /cvs/src/src/gold/symtab.cc,v
retrieving revision 1.94
diff -u -p -r1.94 symtab.cc
--- symtab.cc	19 Apr 2008 18:30:58 -0000	1.94
+++ symtab.cc	24 Apr 2008 00:46:45 -0000
@@ -696,7 +696,7 @@ Symbol_table::add_from_object(Object* ob
 	    ret = new Sized_symbol<size>();
 	  else
 	    {
-	      ret = target->make_symbol();
+	      ret = target->make_symbol(object, sym, name, version, st_shndx);
 	      if (ret == NULL)
 		{
 		  // This means that we don't want a symbol table
@@ -1207,21 +1207,7 @@ Symbol_table::define_special_symbol(cons
 	}
     }
 
-  const Target& target = parameters->target();
-  if (!target.has_make_symbol())
-    sym = new Sized_symbol<size>();
-  else
-    {
-      gold_assert(target.get_size() == size);
-      gold_assert(target.is_big_endian() ? big_endian : !big_endian);
-      typedef Sized_target<size, big_endian> My_target;
-      const My_target* sized_target =
-          static_cast<const My_target*>(&target);
-      sym = sized_target->make_symbol();
-      if (sym == NULL)
-        return NULL;
-    }
-
+  sym = new Sized_symbol<size>();
   if (add_to_table)
     add_loc->second = sym;
   else
Index: target.h
===================================================================
RCS file: /cvs/src/src/gold/target.h,v
retrieving revision 1.29
diff -u -p -r1.29 target.h
--- target.h	9 Apr 2008 00:48:13 -0000	1.29
+++ target.h	24 Apr 2008 00:46:45 -0000
@@ -246,7 +246,8 @@ class Sized_target : public Target
   // symbol table.  This will only be called if has_make_symbol()
   // returns true.
   virtual Sized_symbol<size>*
-  make_symbol() const
+  make_symbol(Object *, const elfcpp::Sym<size, big_endian>&,
+	      const char*, const char*, unsigned int)
   { gold_unreachable(); }
 
   // Resolve a symbol for the target.  This should be overridden by a


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