Go patch committed: Implement go:linkname compiler directive

Ian Lance Taylor iant@golang.org
Tue Aug 9 23:08:00 GMT 2016


This patch to the Go frontend implements the go:linkname compiler
directive.  This is a special comment that permits renaming a symbol
in Go code that imports the unsafe package.  This is used for various
linker tricks.  This is similar to the gccgo-specific "extern"
compiler directive.

This only implements go:linkname for functions (both function
definitions and function declarations).  As far as I know that is all
it is ever used for.

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian
-------------- next part --------------
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 239296)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@
-d3636ca659ed7eed6d2e1cedfa0adccc6d81c07d
+85a9c6992d9660e36972c279a5252fd9591bb765
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/go.cc
===================================================================
--- gcc/go/gofrontend/go.cc	(revision 239109)
+++ gcc/go/gofrontend/go.cc	(working copy)
@@ -52,6 +52,7 @@ go_parse_input_files(const char** filena
 {
   go_assert(filename_count > 0);
 
+  Lex::Linknames all_linknames;
   for (unsigned int i = 0; i < filename_count; ++i)
     {
       if (i > 0)
@@ -76,6 +77,21 @@ go_parse_input_files(const char** filena
 
       if (strcmp(filename, "-") != 0)
 	fclose(file);
+
+      Lex::Linknames* linknames = lexer.get_and_clear_linknames();
+      if (linknames != NULL)
+	{
+	  if (!::gogo->current_file_imported_unsafe())
+	    {
+	      for (Lex::Linknames::const_iterator p = linknames->begin();
+		   p != linknames->end();
+		   ++p)
+		error_at(p->second.loc,
+			 ("//go:linkname only allowed in Go files that "
+			  "import \"unsafe\""));
+	    }
+	  all_linknames.insert(linknames->begin(), linknames->end());
+	}
     }
 
   ::gogo->linemap()->stop();
@@ -86,6 +102,13 @@ go_parse_input_files(const char** filena
   // define them now.
   ::gogo->define_global_names();
 
+  // Apply any go:linkname directives.
+  for (Lex::Linknames::const_iterator p = all_linknames.begin();
+       p != all_linknames.end();
+       ++p)
+    ::gogo->add_linkname(p->first, p->second.is_exported, p->second.ext_name,
+			 p->second.loc);
+
   // Finalize method lists and build stub methods for named types.
   ::gogo->finalize_methods();
 
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc	(revision 239282)
+++ gcc/go/gofrontend/gogo.cc	(working copy)
@@ -32,6 +32,7 @@ Gogo::Gogo(Backend* backend, Linemap* li
     file_block_names_(),
     imports_(),
     imported_unsafe_(false),
+    current_file_imported_unsafe_(false),
     packages_(),
     init_functions_(),
     var_deps_(),
@@ -449,6 +450,7 @@ Gogo::import_package(const std::string&
   if (filename == "unsafe")
     {
       this->import_unsafe(local_name, is_local_name_exported, location);
+      this->current_file_imported_unsafe_ = true;
       return;
     }
 
@@ -2000,6 +2002,29 @@ Gogo::add_dot_import_object(Named_object
   this->current_bindings()->add_named_object(no);
 }
 
+// Add a linkname.  This implements the go:linkname compiler directive.
+// We only support this for functions and function declarations.
+
+void
+Gogo::add_linkname(const std::string& go_name, bool is_exported,
+		   const std::string& ext_name, Location loc)
+{
+  Named_object* no =
+    this->package_->bindings()->lookup(this->pack_hidden_name(go_name,
+							      is_exported));
+  if (no == NULL)
+    error_at(loc, "%s is not defined", go_name.c_str());
+  else if (no->is_function())
+    no->func_value()->set_asm_name(ext_name);
+  else if (no->is_function_declaration())
+    no->func_declaration_value()->set_asm_name(ext_name);
+  else
+    error_at(loc,
+	     ("%s is not a function; "
+	      "//go:linkname is only supported for functions"),
+	     go_name.c_str());
+}
+
 // Mark all local variables used.  This is used when some types of
 // parse error occur.
 
@@ -2183,6 +2208,8 @@ Gogo::clear_file_scope()
         }
       package->clear_used();
     }
+
+  this->current_file_imported_unsafe_ = false;
 }
 
 // Queue up a type specific function for later writing.  These are
@@ -5034,6 +5061,12 @@ Function::get_or_make_decl(Gogo* gogo, N
             }
         }
 
+      if (!this->asm_name_.empty())
+	{
+	  asm_name = this->asm_name_;
+	  is_visible = true;
+	}
+
       // If a function calls the predeclared recover function, we
       // can't inline it, because recover behaves differently in a
       // function passed directly to defer.  If this is a recover
Index: gcc/go/gofrontend/gogo.h
===================================================================
--- gcc/go/gofrontend/gogo.h	(revision 239282)
+++ gcc/go/gofrontend/gogo.h	(working copy)
@@ -428,6 +428,12 @@ class Gogo
   add_file_block_name(const std::string& name, Location location)
   { this->file_block_names_[name] = location; }
 
+  // Add a linkname, from the go:linkname compiler directive.  This
+  // changes the externally visible name of go_name to be ext_name.
+  void
+  add_linkname(const std::string& go_name, bool is_exported,
+	       const std::string& ext_name, Location location);
+
   // Mark all local variables in current bindings as used.  This is
   // used when there is a parse error to avoid useless errors.
   void
@@ -463,6 +469,11 @@ class Gogo
   set_need_init_fn()
   { this->need_init_fn_ = true; }
 
+  // Return whether the current file imported the unsafe package.
+  bool
+  current_file_imported_unsafe() const
+  { return this->current_file_imported_unsafe_; }
+
   // Clear out all names in file scope.  This is called when we start
   // parsing a new file.
   void
@@ -760,6 +771,8 @@ class Gogo
   Imports imports_;
   // Whether the magic unsafe package was imported.
   bool imported_unsafe_;
+  // Whether the magic unsafe package was imported by the current file.
+  bool current_file_imported_unsafe_;
   // Mapping from package names we have seen to packages.  This does
   // not include the package we are compiling.
   Packages packages_;
@@ -975,6 +988,11 @@ class Function
   results_are_named() const
   { return this->results_are_named_; }
 
+  // Set the assembler name.
+  void
+  set_asm_name(const std::string& asm_name)
+  { this->asm_name_ = asm_name; }
+
   // Set the pragmas for this function.
   void
   set_pragmas(unsigned int pragmas)
@@ -1229,6 +1247,9 @@ class Function
   Labels labels_;
   // The number of local types defined in this function.
   unsigned int local_type_count_;
+  // The assembler name: this is the name that will be put in the object file.
+  // Set by the go:linkname compiler directive.  This is normally empty.
+  std::string asm_name_;
   // The function descriptor, if any.
   Expression* descriptor_;
   // The function decl.
Index: gcc/go/gofrontend/lex.cc
===================================================================
--- gcc/go/gofrontend/lex.cc	(revision 239282)
+++ gcc/go/gofrontend/lex.cc	(working copy)
@@ -443,7 +443,7 @@ Lex::Lex(const char* input_file_name, FI
   : input_file_name_(input_file_name), input_file_(input_file),
     linemap_(linemap), linebuf_(NULL), linebufsize_(120), linesize_(0),
     lineoff_(0), lineno_(0), add_semi_at_eol_(false), pragmas_(0),
-    extern_()
+    extern_(), linknames_(NULL)
 {
   this->linebuf_ = new char[this->linebufsize_];
   this->linemap_->start_file(input_file_name, 0);
@@ -1676,6 +1676,7 @@ Lex::skip_cpp_comment()
   // //extern comment.
   this->extern_.clear();
 
+  Location loc = this->location();
   size_t lineoff = this->lineoff_;
 
   const char* p = this->linebuf_ + lineoff;
@@ -1777,7 +1778,8 @@ Lex::skip_cpp_comment()
     {
       // As in the gc compiler, set the external link name for a Go symbol.
       std::string go_name;
-      std::string c_name;
+      std::string ext_name;
+      bool is_exported = false;
       if (ps < pend)
 	{
 	  while (ps < pend && (*ps == ' ' || *ps == '\t'))
@@ -1785,6 +1787,12 @@ Lex::skip_cpp_comment()
 	  if (ps < pend)
 	    {
 	      const char* pg = ps;
+
+	      unsigned int c;
+	      bool issued_error;
+	      ps = this->advance_one_utf8_char(ps, &c, &issued_error);
+	      is_exported = Lex::is_unicode_uppercase(c);
+
 	      while (ps < pend && *ps != ' ' && *ps != '\t')
 		++ps;
 	      if (ps < pend)
@@ -1798,18 +1806,22 @@ Lex::skip_cpp_comment()
 	      while (ps < pend && *ps != ' ' && *ps != '\t')
 		++ps;
 	      if (ps <= pend)
-		c_name = std::string(pc, ps - pc);
+		ext_name = std::string(pc, ps - pc);
 	    }
 	  if (ps != pend)
 	    {
 	      go_name.clear();
-	      c_name.clear();
+	      ext_name.clear();
 	    }
 	}
-      if (go_name.empty() || c_name.empty())
-	error_at(this->location(), "usage: //go:linkname localname linkname");
+      if (go_name.empty() || ext_name.empty())
+	error_at(loc, "usage: //go:linkname localname linkname");
       else
-	this->linknames_[go_name] = c_name;
+	{
+	  if (this->linknames_ == NULL)
+	    this->linknames_ = new Linknames();
+	  (*this->linknames_)[go_name] = Linkname(ext_name, is_exported, loc);
+	}
     }
   else if (verb == "go:nointerface")
     {
Index: gcc/go/gofrontend/lex.h
===================================================================
--- gcc/go/gofrontend/lex.h	(revision 239282)
+++ gcc/go/gofrontend/lex.h	(working copy)
@@ -375,6 +375,33 @@ class Lex
     return ret;
   }
 
+  struct Linkname
+  {
+    std::string ext_name;	// External name.
+    bool is_exported;		// Whether the internal name is exported.
+    Location loc;		// Location of go:linkname directive.
+
+    Linkname()
+      : ext_name(), is_exported(false), loc()
+    { }
+
+    Linkname(const std::string& ext_name_a, bool is_exported_a, Location loc_a)
+      : ext_name(ext_name_a), is_exported(is_exported_a), loc(loc_a)
+    { }
+  };
+
+  typedef std::map<std::string, Linkname> Linknames;
+
+  // Return the linknames seen so far, or NULL if none, and clear the
+  // set.  These are from go:linkname compiler directives.
+  Linknames*
+  get_and_clear_linknames()
+  {
+    Linknames* ret = this->linknames_;
+    this->linknames_ = NULL;
+    return ret;
+  }
+
   // Return whether the identifier NAME should be exported.  NAME is a
   // mangled name which includes only ASCII characters.
   static bool
@@ -514,8 +541,8 @@ class Lex
   // The external name to use for a function declaration, from a magic
   // //extern comment.
   std::string extern_;
-  // The list of //go:linkname comments.
-  std::map<std::string, std::string> linknames_;
+  // The list of //go:linkname comments, if any.
+  Linknames* linknames_;
 };
 
 #endif // !defined(GO_LEX_H)


More information about the Gcc-patches mailing list