compiler, libgo: change mangling scheme

Overhaul the mangling scheme to avoid ambiguities if the package path
contains a dot. Instead of using dot both to separate components and
to mangle characters, use dot only to separate components and use
underscore to mangle characters.

For golang/go#41862

Change-Id: Iddc04422673a4cdc6773d24d3d75fc8945266773
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/271726
Trust: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/go/ast-dump.cc b/go/ast-dump.cc
index a3cbda9..eca0bf1 100644
--- a/go/ast-dump.cc
+++ b/go/ast-dump.cc
@@ -226,7 +226,11 @@
     // FIXME: write a type pretty printer instead of
     // using mangled names.
     if (this->gogo_ != NULL)
-      this->ostream() << "(" << t->mangled_name(this->gogo_) <<  ")";
+      {
+	Backend_name bname;
+	t->backend_name(this->gogo_, &bname);
+	this->ostream() << "(" << bname.name() << ")";
+      }
 }
 
 // Dump a textual representation of a block to the
diff --git a/go/export.cc b/go/export.cc
index 90a5f6d..e99c680 100644
--- a/go/export.cc
+++ b/go/export.cc
@@ -461,7 +461,7 @@
     return false;
 
   // We don't export various special functions.
-  if (Gogo::is_special_name(no->name()))
+  if (Gogo::special_name_pos(no->name()) != std::string::npos)
     return false;
 
   // Methods are exported with the type, not here.
@@ -524,7 +524,11 @@
     if (t1->classification() != t2->classification())
       return t1->classification() < t2->classification();
     Gogo* gogo = go_get_gogo();
-    return gogo->type_descriptor_name(t1, NULL).compare(gogo->type_descriptor_name(t2, NULL)) < 0;
+    Backend_name b1;
+    gogo->type_descriptor_backend_name(t1, NULL, &b1);
+    Backend_name b2;
+    gogo->type_descriptor_backend_name(t2, NULL, &b2);
+    return b1.name() < b2.name();
   }
 };
 
diff --git a/go/expressions.cc b/go/expressions.cc
index e76bc69..6bc9348 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -1596,7 +1596,8 @@
     return context->backend()->var_expression(this->dvar_, loc);
 
   Gogo* gogo = context->gogo();
-  std::string var_name(gogo->function_descriptor_name(no));
+  Backend_name bname;
+  gogo->function_descriptor_backend_name(no, &bname);
   bool is_descriptor = false;
   if (no->is_function_declaration()
       && !no->func_declaration_value()->asm_name().empty()
@@ -1616,10 +1617,11 @@
   Btype* btype = this->type()->get_backend(gogo);
 
   Bvariable* bvar;
-  std::string asm_name(go_selectively_encode_id(var_name));
   if (no->package() != NULL || is_descriptor)
-    bvar = context->backend()->immutable_struct_reference(var_name, asm_name,
-                                                          btype, loc);
+    bvar =
+      context->backend()->immutable_struct_reference(bname.name(),
+						     bname.optional_asm_name(),
+						     btype, loc);
   else
     {
       Location bloc = Linemap::predeclared_location();
@@ -1644,7 +1646,8 @@
       if (no->is_function() && no->func_value()->is_referenced_by_inline())
 	is_hidden = false;
 
-      bvar = context->backend()->immutable_struct(var_name, asm_name,
+      bvar = context->backend()->immutable_struct(bname.name(),
+						  bname.optional_asm_name(),
                                                   is_hidden, false,
 						  btype, bloc);
       Expression_list* vals = new Expression_list();
@@ -1654,8 +1657,9 @@
       Translate_context bcontext(gogo, NULL, NULL, NULL);
       bcontext.set_is_const();
       Bexpression* binit = init->get_backend(&bcontext);
-      context->backend()->immutable_struct_set_init(bvar, var_name, is_hidden,
-						    false, btype, bloc, binit);
+      context->backend()->immutable_struct_set_init(bvar, bname.name(),
+						    is_hidden, false, btype,
+						    bloc, binit);
     }
 
   this->dvar_ = bvar;
@@ -5190,11 +5194,9 @@
 	      copy_to_heap = (context->function() != NULL
                               || context->is_const());
 	    }
-	  std::string asm_name(go_selectively_encode_id(var_name));
 	  Bvariable* implicit =
-              gogo->backend()->implicit_variable(var_name, asm_name,
-                                                 btype, true, copy_to_heap,
-                                                 false, 0);
+              gogo->backend()->implicit_variable(var_name, "", btype, true,
+						 copy_to_heap, false, 0);
 	  gogo->backend()->implicit_variable_set_init(implicit, var_name, btype,
 						      true, copy_to_heap, false,
 						      bexpr);
@@ -5219,10 +5221,9 @@
 	       && this->expr_->is_static_initializer())
         {
 	  std::string var_name(gogo->initializer_name());
-	  std::string asm_name(go_selectively_encode_id(var_name));
           Bvariable* decl =
-              gogo->backend()->immutable_struct(var_name, asm_name,
-                                                true, false, btype, loc);
+              gogo->backend()->immutable_struct(var_name, "", true, false,
+						btype, loc);
           gogo->backend()->immutable_struct_set_init(decl, var_name, true,
 						     false, btype, loc, bexpr);
           bexpr = gogo->backend()->var_expression(decl, loc);
@@ -5230,9 +5231,8 @@
       else if (this->expr_->is_constant())
         {
           std::string var_name(gogo->initializer_name());
-          std::string asm_name(go_selectively_encode_id(var_name));
           Bvariable* decl =
-              gogo->backend()->implicit_variable(var_name, asm_name, btype,
+              gogo->backend()->implicit_variable(var_name, "", btype,
                                                  true, true, false, 0);
           gogo->backend()->implicit_variable_set_init(decl, var_name, btype,
                                                       true, true, false,
@@ -18251,9 +18251,8 @@
     {
       // The interface conversion table is defined elsewhere.
       Btype* btype = this->type()->get_backend(gogo);
-      std::string asm_name(go_selectively_encode_id(mangled_name));
       this->bvar_ =
-          gogo->backend()->immutable_struct_reference(mangled_name, asm_name,
+          gogo->backend()->immutable_struct_reference(mangled_name, "",
                                                       btype, loc);
       return gogo->backend()->var_expression(this->bvar_, this->location());
     }
@@ -18323,8 +18322,7 @@
   Bexpression* ctor =
       gogo->backend()->constructor_expression(btype, ctor_bexprs, loc);
 
-  std::string asm_name(go_selectively_encode_id(mangled_name));
-  this->bvar_ = gogo->backend()->immutable_struct(mangled_name, asm_name, false,
+  this->bvar_ = gogo->backend()->immutable_struct(mangled_name, "", false,
 						  !is_public, btype, loc);
   gogo->backend()->immutable_struct_set_init(this->bvar_, mangled_name, false,
                                              !is_public, btype, loc, ctor);
diff --git a/go/go-encode-id.cc b/go/go-encode-id.cc
index 550da97..7ab65f5 100644
--- a/go/go-encode-id.cc
+++ b/go/go-encode-id.cc
@@ -12,8 +12,8 @@
 #include "go-encode-id.h"
 #include "lex.h"
 
-// Return whether the character c is OK to use in the assembler.  We
-// only permit ASCII alphanumeric characters, underscore, and dot.
+// Return whether the character c can appear in a name that we are
+// encoding.  We only permit ASCII alphanumeric characters.
 
 static bool
 char_needs_encoding(char c)
@@ -32,7 +32,6 @@
     case 'y': case 'z':
     case '0': case '1': case '2': case '3': case '4':
     case '5': case '6': case '7': case '8': case '9':
-    case '_': case '.':
       return false;
     default:
       return true;
@@ -53,6 +52,52 @@
   return false;
 }
 
+// Map from characters to the underscore encoding for them.
+
+class Special_char_code
+{
+ public:
+  Special_char_code();
+
+  // Return the simple underscore encoding for C, or 0 if none.
+  char
+  code_for(unsigned int c) const
+  {
+    if (c <= 127)
+      return this->codes_[c];
+    return 0;
+  }
+
+ private:
+  // Encodings for characters.
+  char codes_[128];
+};
+
+// Construct the underscore encoding map.
+
+Special_char_code::Special_char_code()
+{
+  memset(this->codes_, 0, sizeof this->codes_);
+  this->codes_['_'] = '_';
+  this->codes_['.'] = '0';
+  this->codes_['/'] = '1';
+  this->codes_['*'] = '2';
+  this->codes_[','] = '3';
+  this->codes_['{'] = '4';
+  this->codes_['}'] = '5';
+  this->codes_['['] = '6';
+  this->codes_[']'] = '7';
+  this->codes_['('] = '8';
+  this->codes_[')'] = '9';
+  this->codes_['"'] = 'a';
+  this->codes_[' '] = 'b';
+  this->codes_[';'] = 'c';
+}
+
+// The singleton Special_char_code.
+
+static const Special_char_code special_char_code;
+
 // Pull the next UTF-8 character out of P and store it in *PC.  Return
 // the number of bytes read.
 
@@ -82,10 +127,9 @@
   return len;
 }
 
-// Encode an identifier using assembler-friendly characters. The encoding is
-// described in detail near the end of the long comment at the start of
-// names.cc. Short version: translate all non-ASCII-alphanumeric characters into
-// ..uXXXX or ..UXXXXXXXX, translate ASCII non-alphanumerics into ".zXX".
+// Encode an identifier using assembler-friendly characters.  The
+// encoding is described in detail near the end of the long comment at
+// the start of names.cc.
 
 std::string
 go_encode_id(const std::string &id)
@@ -96,50 +140,57 @@
       return id;
     }
 
-  // The encoding is only unambiguous if the input string does not
-  // contain ..z, ..u or ..U.
-  go_assert(id.find("..z") == std::string::npos);
-  go_assert(id.find("..u") == std::string::npos);
-  go_assert(id.find("..U") == std::string::npos);
-
   std::string ret;
   const char* p = id.c_str();
   const char* pend = p + id.length();
 
-  // A leading ".0" is a space introduced before a mangled type name
-  // that starts with a 'u' or 'U', to avoid confusion with the
-  // mangling used here.  We don't need a leading ".0", and we don't
-  // want symbols that start with '.', so remove it.
-  if (p[0] == '.' && p[1] == '0')
-    p += 2;
+  // We encode a leading digit, to ensure that no identifier starts
+  // with a digit.
+  if (pend > p && p[0] >= '0' && p[0] <= '9')
+    {
+      char buf[8];
+      snprintf(buf, sizeof buf, "_x%02x", p[0]);
+      ret.append(buf);
+      ++p;
+    }
 
   while (p < pend)
     {
       unsigned int c;
       size_t len = fetch_utf8_char(p, &c);
-      if (len == 1 && !char_needs_encoding(c))
+      if (len == 1)
 	{
-	  ret += c;
+	  if (!char_needs_encoding(c))
+	    ret.push_back(c);
+	  else
+	    {
+	      char code = special_char_code.code_for(c);
+	      if (code != 0)
+		{
+		  ret.push_back('_');
+		  ret.push_back(code);
+		}
+	      else
+		{
+		  char buf[8];
+		  snprintf(buf, sizeof buf, "_x%02x", c);
+		  ret.append(buf);
+		}
+	    }
 	}
       else
 	{
 	  char buf[16];
-          if (len == 1)
-            snprintf(buf, sizeof buf, "..z%02x", c);
-	  else if (c < 0x10000)
-	    snprintf(buf, sizeof buf, "..u%04x", c);
+	  if (c < 0x10000)
+	    snprintf(buf, sizeof buf, "_u%04x", c);
 	  else
-	    snprintf(buf, sizeof buf, "..U%08x", c);
-
-	  // We don't want a symbol to start with '.', so add a prefix
-	  // if needed.
-	  if (ret.empty())
-	    ret += '_';
-
-	  ret += buf;
+	    snprintf(buf, sizeof buf, "_U%08x", c);
+	  ret.append(buf);
 	}
+
       p += len;
     }
+
   return ret;
 }
 
@@ -170,64 +221,116 @@
   const char* pend = p + encoded.length();
   const Location loc = Linemap::predeclared_location();
 
-  // Special case for initial "_", in case it was introduced
-  // as a way to prevent encoded symbol starting with ".".
-  if (*p == '_' && (strncmp(p+1, "..u", 3) == 0 || strncmp(p+1, "..U", 3) == 0))
-    p++;
-
   while (p < pend)
     {
-      if (strncmp(p, "..z", 3) == 0)
-        {
-          const char* digits = p+3;
-          if (strlen(digits) < 2)
-            return "";
-          unsigned rune = hex_digits_to_unicode_codepoint(digits, 2);
-          Lex::append_char(rune, true, &ret, loc);
-          p += 5;
-        }
-      else if (strncmp(p, "..u", 3) == 0)
-        {
-          const char* digits = p+3;
-          if (strlen(digits) < 4)
-            return "";
-          unsigned rune = hex_digits_to_unicode_codepoint(digits, 4);
-          Lex::append_char(rune, true, &ret, loc);
-          p += 7;
-        }
-      else if (strncmp(p, "..U", 3) == 0)
-        {
-          const char* digits = p+3;
-          if (strlen(digits) < 8)
-            return "";
-          unsigned rune = hex_digits_to_unicode_codepoint(digits, 8);
-          Lex::append_char(rune, true, &ret, loc);
-          p += 11;
-        }
-      else
-        {
-          ret += *p;
-          p += 1;
-        }
+      if (*p != '_' || p + 1 == pend)
+	{
+	  ret.push_back(*p);
+	  p++;
+	  continue;
+	}
+
+      switch (p[1])
+	{
+	case '_':
+	  ret.push_back('_');
+	  p += 2;
+	  break;
+	case '0':
+	  ret.push_back('.');
+	  p += 2;
+	  break;
+	case '1':
+	  ret.push_back('/');
+	  p += 2;
+	  break;
+	case '2':
+	  ret.push_back('*');
+	  p += 2;
+	  break;
+	case '3':
+	  ret.push_back(',');
+	  p += 2;
+	  break;
+	case '4':
+	  ret.push_back('{');
+	  p += 2;
+	  break;
+	case '5':
+	  ret.push_back('}');
+	  p += 2;
+	  break;
+	case '6':
+	  ret.push_back('[');
+	  p += 2;
+	  break;
+	case '7':
+	  ret.push_back(']');
+	  p += 2;
+	  break;
+	case '8':
+	  ret.push_back('(');
+	  p += 2;
+	  break;
+	case '9':
+	  ret.push_back(')');
+	  p += 2;
+	  break;
+	case 'a':
+	  ret.push_back('"');
+	  p += 2;
+	  break;
+	case 'b':
+	  ret.push_back(' ');
+	  p += 2;
+	  break;
+	case 'c':
+	  ret.push_back(';');
+	  p += 2;
+	  break;
+        case 'x':
+	  {
+	    const char* digits = p + 2;
+	    if (strlen(digits) < 2)
+	      return "";
+	    unsigned int rune = hex_digits_to_unicode_codepoint(digits, 2);
+	    Lex::append_char(rune, true, &ret, loc);
+	    p += 4;
+	  }
+	  break;
+	case 'u':
+	  {
+	    const char* digits = p + 2;
+	    if (strlen(digits) < 4)
+	      return "";
+	    unsigned int rune = hex_digits_to_unicode_codepoint(digits, 4);
+	    Lex::append_char(rune, true, &ret, loc);
+	    p += 6;
+	  }
+	  break;
+	case 'U':
+	  {
+	    const char* digits = p + 2;
+	    if (strlen(digits) < 8)
+	      return "";
+	    unsigned int rune = hex_digits_to_unicode_codepoint(digits, 8);
+	    Lex::append_char(rune, true, &ret, loc);
+	    p += 10;
+	  }
+	  break;
+	default:
+	  return "";
+	}
     }
 
   return ret;
 }
 
-std::string
-go_selectively_encode_id(const std::string &id)
-{
-  if (go_id_needs_encoding(id))
-    return go_encode_id(id);
-  return std::string();
-}
-
 // Encode a struct field tag.  This is only used when we need to
 // create a type descriptor for an anonymous struct type with field
-// tags.  This mangling is applied before go_encode_id.  We skip
-// alphanumerics and underscore, replace every other single byte
-// character with .xNN, and leave larger UTF-8 characters for
-// go_encode_id.
+// tags.  Underscore encoding will be applied to the returned string.
+// The tag will appear between curly braces, so that is all we have to
+// avoid.
 
 std::string
 go_mangle_struct_tag(const std::string& tag)
@@ -241,28 +344,14 @@
       size_t len = fetch_utf8_char(p, &c);
       if (len > 1)
 	ret.append(p, len);
-      else if (!char_needs_encoding(c) && c != '.')
-	ret += c;
+      else if (c != '{' && c != '}' && c != '\\')
+	ret.push_back(c);
       else
 	{
-	  char buf[16];
-	  snprintf(buf, sizeof buf, ".x%02x", c);
-	  ret += buf;
+	  ret.push_back('\\');
+	  ret.push_back(c);
 	}
       p += len;
     }
   return ret;
 }
-
-// Encode a package path.
-
-std::string
-go_mangle_pkgpath(const std::string& pkgpath)
-{
-  std::string s = pkgpath;
-  for (size_t i = s.find('.');
-       i != std::string::npos;
-       i = s.find('.', i + 1))
-    s.replace(i, 1, ".x2e"); // 0x2e is the ASCII encoding for '.'
-  return s;
-}
diff --git a/go/go-encode-id.h b/go/go-encode-id.h
index 2d09b0c..cbafd54 100644
--- a/go/go-encode-id.h
+++ b/go/go-encode-id.h
@@ -25,21 +25,8 @@
 extern std::string
 go_decode_id(const std::string &id);
 
-// Returns the empty string if the specified name needs encoding,
-// otherwise invokes go_encode_id on the name and returns the result.
-extern std::string
-go_selectively_encode_id(const std::string &id);
-
 // Encodes a struct tag that appears in a type literal encoding.
 extern std::string
 go_mangle_struct_tag(const std::string& tag);
 
-// Encode a package path.  A package path can contain any arbitrary
-// character, including '.'.  go_encode_id expects that any '.' will
-// be inserted by name mangling in a controlled manner.  So first
-// translate any '.' using the same .x encoding as used by
-// go_mangle_struct_tag.
-extern std::string
-go_mangle_pkgpath(const std::string& pkgpath);
-
 #endif // !defined(GO_ENCODE_ID_H)
diff --git a/go/gogo.cc b/go/gogo.cc
index f40f131..93a4a57 100644
--- a/go/gogo.cc
+++ b/go/gogo.cc
@@ -299,7 +299,7 @@
 Gogo::set_pkgpath(const std::string& arg)
 {
   go_assert(!this->pkgpath_set_);
-  this->pkgpath_ = go_mangle_pkgpath(arg);
+  this->pkgpath_ = arg;
   this->pkgpath_set_ = true;
   this->pkgpath_from_option_ = true;
 }
@@ -324,32 +324,6 @@
   this->prefix_from_option_ = true;
 }
 
-// Given a name which may or may not have been hidden, append the
-// appropriate version of the name to the result string. Take care
-// to avoid creating a sequence that will be rejected by go_encode_id
-// (avoid ..u, ..U, ..z).
-void
-Gogo::append_possibly_hidden_name(std::string *result, const std::string& name)
-{
-  // FIXME: This adds in pkgpath twice for hidden symbols, which is
-  // less than ideal.
-  if (!Gogo::is_hidden_name(name))
-    (*result) += name;
-  else
-    {
-      std::string n = ".";
-      std::string pkgpath = Gogo::hidden_name_pkgpath(name);
-      char lastR = result->at(result->length() - 1);
-      char firstP = pkgpath.at(0);
-      if (lastR == '.' && (firstP == 'u' || firstP == 'U' || firstP == 'z'))
-        n = "_.";
-      n.append(pkgpath);
-      n.append(1, '.');
-      n.append(Gogo::unpack_hidden_name(name));
-      (*result) += n;
-    }
-}
-
 // Munge name for use in an error message.
 
 std::string
@@ -397,8 +371,7 @@
 	{
 	  if (!this->prefix_from_option_)
 	    this->prefix_ = "go";
-	  this->pkgpath_ = (go_mangle_pkgpath(this->prefix_) + '.'
-			    + package_name);
+	  this->pkgpath_ = this->prefix_ + '.' + package_name;
 	  this->pkgpath_symbol_ = (Gogo::pkgpath_for_symbol(this->prefix_) + '.'
 				   + Gogo::pkgpath_for_symbol(package_name));
 	}
@@ -997,9 +970,9 @@
        it != this->imported_init_fns_.end();
        ++it)
     {
-      std::string pkgpath =
-        this->pkgpath_from_init_fn_name((*it)->init_name());
-      list_names.push_back(this->type_descriptor_list_symbol(pkgpath));
+      std::string pkgpath_symbol =
+        this->pkgpath_symbol_from_init_fn_name((*it)->init_name());
+      list_names.push_back(this->type_descriptor_list_symbol(pkgpath_symbol));
     }
   // Add the main package itself.
   list_names.push_back(this->type_descriptor_list_symbol("main"));
@@ -2746,8 +2719,7 @@
 // parse tree is lowered.
 
 void
-Gogo::queue_hash_function(Type* type, int64_t size,
-			  const std::string& hash_name,
+Gogo::queue_hash_function(Type* type, int64_t size, Backend_name* bname,
 			  Function_type* hash_fntype)
 {
   go_assert(!this->specific_type_functions_are_written_);
@@ -2755,7 +2727,7 @@
   Specific_type_function::Specific_type_function_kind kind =
     Specific_type_function::SPECIFIC_HASH;
   Specific_type_function* tsf = new Specific_type_function(type, NULL, size,
-							   kind, hash_name,
+							   kind, bname,
 							   hash_fntype);
   this->specific_type_functions_.push_back(tsf);
 }
@@ -2766,15 +2738,14 @@
 
 void
 Gogo::queue_equal_function(Type* type, Named_type* name, int64_t size,
-			   const std::string& equal_name,
-			   Function_type* equal_fntype)
+			   Backend_name* bname, Function_type* equal_fntype)
 {
   go_assert(!this->specific_type_functions_are_written_);
   go_assert(!this->in_global_scope());
   Specific_type_function::Specific_type_function_kind kind =
     Specific_type_function::SPECIFIC_EQUAL;
   Specific_type_function* tsf = new Specific_type_function(type, name, size,
-							   kind, equal_name,
+							   kind, bname,
 							   equal_fntype);
   this->specific_type_functions_.push_back(tsf);
 }
@@ -2872,11 +2843,11 @@
       Specific_type_function* tsf = this->specific_type_functions_.back();
       this->specific_type_functions_.pop_back();
       if (tsf->kind == Specific_type_function::SPECIFIC_HASH)
-	tsf->type->write_hash_function(this, tsf->size, tsf->fnname,
+	tsf->type->write_hash_function(this, tsf->size, &tsf->bname,
 				       tsf->fntype);
       else
 	tsf->type->write_equal_function(this, tsf->name, tsf->size,
-					tsf->fnname, tsf->fntype);
+					&tsf->bname, tsf->fntype);
       delete tsf;
     }
   this->specific_type_functions_are_written_ = true;
@@ -6218,6 +6189,56 @@
   return true;
 }
 
+// Get the backend name.
+
+void
+Function::backend_name(Gogo* gogo, Named_object* no, Backend_name *bname)
+{
+  if (!this->asm_name_.empty())
+    bname->set_asm_name(this->asm_name_);
+  else if (no->package() == NULL && no->name() == gogo->get_init_fn_name())
+    {
+      // These names appear in the export data and are used
+      // directly in the assembler code.  If we change this here
+      // we need to change Gogo::init_imports.
+      bname->set_asm_name(no->name());
+    }
+  else if (this->enclosing_ != NULL)
+    {
+      // Rewrite the nested name to use the enclosing function name.
+      // We don't do this earlier because we just store simple names
+      // in a Named_object, not Backend_names.
+
+      // The name was set by nested_function_name, which always
+      // appends ..funcNNN.  We want that to be our suffix.
+      size_t pos = no->name().find("..func");
+      go_assert(pos != std::string::npos);
+
+      Named_object* enclosing = this->enclosing_;
+      while (true)
+	{
+	  Named_object* parent = enclosing->func_value()->enclosing();
+	  if (parent == NULL)
+	    break;
+	  enclosing = parent;
+	}
+
+      Type* rtype = NULL;
+      if (enclosing->func_value()->type()->is_method())
+	rtype = enclosing->func_value()->type()->receiver()->type();
+      gogo->function_backend_name(enclosing->name(), enclosing->package(),
+				  rtype, bname);
+      bname->append_suffix(no->name().substr(pos));
+    }
+  else
+    {
+      Type* rtype = NULL;
+      if (this->type_->is_method())
+	rtype = this->type_->receiver()->type();
+      gogo->function_backend_name(no->name(), no->package(), rtype, bname);
+    }
+}
+
 // Get the backend representation.
 
 Bfunction*
@@ -6226,7 +6247,6 @@
   if (this->fndecl_ == NULL)
     {
       unsigned int flags = 0;
-      bool is_init_fn = false;
       if (no->package() != NULL)
 	{
 	  // Functions defined in other packages must be visible.
@@ -6238,10 +6258,7 @@
                && !this->type_->is_method())
 	;
       else if (no->name() == gogo->get_init_fn_name())
-	{
-	  flags |= Backend::function_is_visible;
-	  is_init_fn = true;
-	}
+	flags |= Backend::function_is_visible;
       else if (Gogo::unpack_hidden_name(no->name()) == "main"
                && gogo->is_main_package())
 	flags |= Backend::function_is_visible;
@@ -6255,29 +6272,13 @@
 	    flags |= Backend::function_is_visible;
         }
 
-      Type* rtype = NULL;
-      if (this->type_->is_method())
-	rtype = this->type_->receiver()->type();
-
-      std::string asm_name;
       if (!this->asm_name_.empty())
 	{
-	  asm_name = this->asm_name_;
-
 	  // If an assembler name is explicitly specified, there must
 	  // be some reason to refer to the symbol from a different
 	  // object file.
 	  flags |= Backend::function_is_visible;
 	}
-      else if (is_init_fn)
-	{
-	  // These names appear in the export data and are used
-	  // directly in the assembler code.  If we change this here
-	  // we need to change Gogo::init_imports.
-	  asm_name = no->name();
-	}
-      else
-	asm_name = gogo->function_asm_name(no->name(), no->package(), rtype);
 
       // If an inline body refers to this function, then it
       // needs to be visible in the symbol table.
@@ -6337,13 +6338,36 @@
 	flags |= Backend::function_only_inline;
 
       Btype* functype = this->type_->get_backend_fntype(gogo);
-      this->fndecl_ =
-          gogo->backend()->function(functype, no->get_id(gogo), asm_name,
-				    flags, this->location());
+
+      Backend_name bname;
+      this->backend_name(gogo, no, &bname);
+
+      this->fndecl_ = gogo->backend()->function(functype,
+						bname.name(),
+						bname.optional_asm_name(),
+						flags,
+						this->location());
     }
   return this->fndecl_;
 }
 
+// Get the backend name.
+
+void
+Function_declaration::backend_name(Gogo* gogo, Named_object* no,
+				   Backend_name* bname)
+{
+  if (!this->asm_name_.empty())
+    bname->set_asm_name(this->asm_name_);
+  else
+    {
+      Type* rtype = NULL;
+      if (this->fntype_->is_method())
+	rtype = this->fntype_->receiver()->type();
+      gogo->function_backend_name(no->name(), no->package(), rtype, bname);
+    }
+}
+
 // Get the backend representation.
 
 Bfunction*
@@ -6375,21 +6399,16 @@
 	    flags |= Backend::function_does_not_return;
 	}
 
-      std::string asm_name;
-      if (this->asm_name_.empty())
-	{
-	  Type* rtype = NULL;
-	  if (this->fntype_->is_method())
-	    rtype = this->fntype_->receiver()->type();
-	  asm_name = gogo->function_asm_name(no->name(), no->package(), rtype);
-	}
-      else if (go_id_needs_encoding(no->get_id(gogo)))
-        asm_name = go_encode_id(no->get_id(gogo));
-
       Btype* functype = this->fntype_->get_backend_fntype(gogo);
-      this->fndecl_ =
-          gogo->backend()->function(functype, no->get_id(gogo), asm_name,
-				    flags, this->location());
+
+      Backend_name bname;
+      this->backend_name(gogo, no, &bname);
+
+      this->fndecl_ = gogo->backend()->function(functype,
+						bname.name(),
+						bname.optional_asm_name(),
+						flags,
+						this->location());
     }
 
   return this->fndecl_;
@@ -7993,7 +8012,6 @@
 	      type = Type::make_pointer_type(type);
 	    }
 
-	  const std::string n = Gogo::unpack_hidden_name(name);
 	  Btype* btype = type->get_backend(gogo);
 
 	  Bvariable* bvar;
@@ -8001,19 +8019,14 @@
 	    bvar = Map_type::backend_zero_value(gogo);
 	  else if (this->is_global_)
 	    {
-	      std::string var_name(package != NULL
-				   ? package->package_name()
-				   : gogo->package_name());
-	      var_name.push_back('.');
-	      var_name.append(n);
-
-              std::string asm_name(gogo->global_var_asm_name(name, package));
+	      Backend_name bname;
+	      gogo->global_var_backend_name(name, package, &bname);
 
 	      bool is_hidden = Gogo::is_hidden_name(name);
 	      // Hack to export runtime.writeBarrier.  FIXME.
 	      // This is because go:linkname doesn't work on variables.
 	      if (gogo->compiling_runtime()
-		  && var_name == "runtime.writeBarrier")
+		  && bname.name() == "runtime.writeBarrier")
 		is_hidden = false;
 
 	      // If an inline body refers to this variable, then it
@@ -8028,8 +8041,12 @@
 	      if (package != NULL)
 		is_hidden = false;
 
-	      bvar = backend->global_variable(var_name,
-					      asm_name,
+	      // For some reason asm_name can't be the empty string
+	      // for global_variable, so we call asm_name rather than
+	      // optional_asm_name here.  FIXME.
+
+	      bvar = backend->global_variable(bname.name(),
+					      bname.asm_name(),
 					      btype,
 					      package != NULL,
 					      is_hidden,
@@ -8043,6 +8060,7 @@
 	    }
 	  else
 	    {
+	      const std::string n = Gogo::unpack_hidden_name(name);
 	      Bfunction* bfunction = function->func_value()->get_decl();
 	      bool is_address_taken = (this->is_non_escaping_address_taken_
 				       && !this->is_in_heap());
@@ -8216,7 +8234,13 @@
       if (type != NULL && type->is_numeric_type())
 	{
 	  Btype* btype = type->get_backend(gogo);
-	  std::string name = const_no->get_id(gogo);
+	  std::string name;
+	  if (const_no->package() == NULL)
+	    name = gogo->pkgpath();
+	  else
+	    name = const_no->package()->pkgpath();
+	  name.push_back('.');
+	  name.append(Gogo::unpack_hidden_name(const_no->name()));
 	  const_decl =
 	    gogo->backend()->named_constant_expression(btype, name,
 						       const_decl, loc);
@@ -8637,54 +8661,6 @@
     go_unreachable();
 }
 
-// Return the external identifier for this object.
-
-std::string
-Named_object::get_id(Gogo* gogo)
-{
-  go_assert(!this->is_variable()
-	    && !this->is_result_variable()
-	    && !this->is_type());
-  std::string decl_name;
-  if (this->is_function_declaration()
-      && !this->func_declaration_value()->asm_name().empty())
-    decl_name = this->func_declaration_value()->asm_name();
-  else
-    {
-      std::string package_name;
-      if (this->package_ == NULL)
-	package_name = gogo->package_name();
-      else
-	package_name = this->package_->package_name();
-
-      // Note that this will be misleading if this is an unexported
-      // method generated for an embedded imported type.  In that case
-      // the unexported method should have the package name of the
-      // package from which it is imported, but we are going to give
-      // it our package name.  Fixing this would require knowing the
-      // package name, but we only know the package path.  It might be
-      // better to use package paths here anyhow.  This doesn't affect
-      // the assembler code, because we always set that name in
-      // Function::get_or_make_decl anyhow.  FIXME.
-
-      decl_name = package_name + '.' + Gogo::unpack_hidden_name(this->name_);
-
-      Function_type* fntype;
-      if (this->is_function())
-	fntype = this->func_value()->type();
-      else if (this->is_function_declaration())
-	fntype = this->func_declaration_value()->type();
-      else
-	fntype = NULL;
-      if (fntype != NULL && fntype->is_method())
-	{
-	  decl_name.push_back('.');
-	  decl_name.append(fntype->receiver()->type()->mangled_name(gogo));
-	}
-    }
-  return decl_name;
-}
-
 void
 debug_go_named_object(Named_object* no)
 {
diff --git a/go/gogo.h b/go/gogo.h
index 45be173..f22d476 100644
--- a/go/gogo.h
+++ b/go/gogo.h
@@ -57,6 +57,101 @@
 // This file declares the basic classes used to hold the internal
 // representation of Go which is built by the parser.
 
+// The name of some backend object.  Backend objects have a
+// user-visible name and an assembler name.  The user visible name
+// might include arbitrary Unicode characters.  The assembler name
+// will not.
+
+class Backend_name
+{
+ public:
+  Backend_name()
+    : prefix_(NULL), components_(), count_(0), suffix_(),
+      is_asm_name_(false)
+  {}
+
+  // Set the prefix.  Prefixes are always constant strings.
+  void
+  set_prefix(const char* p)
+  {
+    go_assert(this->prefix_ == NULL && !this->is_asm_name_);
+    this->prefix_ = p;
+  }
+
+  // Set the suffix.
+  void
+  set_suffix(const std::string& s)
+  {
+    go_assert(this->suffix_.empty() && !this->is_asm_name_);
+    this->suffix_ = s;
+  }
+
+  // Append to the suffix.
+  void
+  append_suffix(const std::string& s)
+  {
+    if (this->is_asm_name_)
+      this->components_[0].append(s);
+    else
+      this->suffix_.append(s);
+  }
+
+  // Add a component.
+  void
+  add(const std::string& c)
+  {
+    go_assert(this->count_ < Backend_name::max_components
+	      && !this->is_asm_name_);
+    this->components_[this->count_] = c;
+    ++this->count_;
+  }
+
+  // Set an assembler name specified by the user.  This overrides both
+  // the user-visible name and the assembler name.  No further
+  // encoding is applied.
+  void
+  set_asm_name(const std::string& n)
+  {
+    go_assert(this->prefix_ == NULL
+	      && this->count_ == 0
+	      && this->suffix_.empty()
+	      && !this->is_asm_name_);
+    this->components_[0] = n;
+    this->is_asm_name_ = true;
+  }
+
+  // Get the user visible name.
+  std::string
+  name() const;
+
+  // Get the assembler name.  This may be the same as the user visible
+  // name.
+  std::string
+  asm_name() const;
+
+  // Get an optional assembler name: if it would be the same as the
+  // user visible name, this returns the empty string.
+  std::string
+  optional_asm_name() const;
+
+ private:
+  // The maximum number of components.
+  static const int max_components = 4;
+
+  // An optional prefix that does not require encoding.
+  const char *prefix_;
+  // Up to four components.  The name will include these components
+  // separated by dots.  Each component will be underscore-encoded
+  // (see the long comment near the top of names.cc).
+  std::string components_[Backend_name::max_components];
+  // Number of components.
+  int count_;
+  // An optional suffix that does not require encoding.
+  std::string suffix_;
+  // True if components_[0] is an assembler name specified by the user.
+  bool is_asm_name_;
+};
+
 // An initialization function for an imported package.  This is a
 // magic function which initializes variables and runs the "init"
 // function.
@@ -613,7 +708,7 @@
   // is used when a type-specific hash function is needed when not at
   // top level.
   void
-  queue_hash_function(Type* type, int64_t size, const std::string& hash_name,
+  queue_hash_function(Type* type, int64_t size, Backend_name*,
 		      Function_type* hash_fntype);
 
   // Queue up a type-specific equal function to be written out.  This
@@ -621,8 +716,7 @@
   // top level.
   void
   queue_equal_function(Type* type, Named_type* name, int64_t size,
-		       const std::string& equal_name,
-		       Function_type* equal_fntype);
+		       Backend_name*, Function_type* equal_fntype);
 
   // Write out queued specific type functions.
   void
@@ -869,31 +963,32 @@
   Expression*
   allocate_memory(Type *type, Location);
 
-  // Return the assembler name to use for an exported function, a
-  // method, or a function/method declaration.
-  std::string
-  function_asm_name(const std::string& go_name, const Package*,
-		    const Type* receiver);
+  // Get the backend name to use for an exported function, a method,
+  // or a function/method declaration.
+  void
+  function_backend_name(const std::string& go_name, const Package*,
+			const Type* receiver, Backend_name*);
 
   // Return the name to use for a function descriptor.
-  std::string
-  function_descriptor_name(Named_object*);
+  void
+  function_descriptor_backend_name(Named_object*, Backend_name*);
 
   // Return the name to use for a generated stub method.
   std::string
   stub_method_name(const Package*, const std::string& method_name);
 
-  // Return the name of the hash function for TYPE.
-  std::string
-  hash_function_name(const Type*);
+  // Get the backend name of the hash function for TYPE.
+  void
+  hash_function_name(const Type*, Backend_name*);
 
-  // Return the name of the equal function for TYPE.
-  std::string
-  equal_function_name(const Type*, const Named_type*);
+  // Get the backend name of the equal function for TYPE.
+  void
+  equal_function_name(const Type*, const Named_type*, Backend_name*);
 
-  // Return the assembler name to use for a global variable.
-  std::string
-  global_var_asm_name(const std::string& go_name, const Package*);
+  // Get the backend name to use for a global variable.
+  void
+  global_var_backend_name(const std::string& go_name, const Package*,
+			  Backend_name*);
 
   // Return a name to use for an error case.  This should only be used
   // after reporting an error, and is used to avoid useless knockon
@@ -961,13 +1056,14 @@
   // Return the package path symbol from an init function name, which
   // can be a real init function or a dummy one.
   std::string
-  pkgpath_from_init_fn_name(std::string);
+  pkgpath_symbol_from_init_fn_name(std::string);
 
-  // Return the name for a type descriptor symbol.
-  std::string
-  type_descriptor_name(const Type*, Named_type*);
+  // Get the backend name for a type descriptor symbol.
+  void
+  type_descriptor_backend_name(const Type*, Named_type*, Backend_name*);
 
   // Return the name of the type descriptor list symbol of a package.
+  // The argument is an encoded pkgpath, as with pkgpath_symbol.
   std::string
   type_descriptor_list_symbol(const std::string&);
 
@@ -987,11 +1083,11 @@
   std::string
   interface_method_table_name(Interface_type*, Type*, bool is_pointer);
 
-  // Return whether NAME is a special name that can not be passed to
-  // unpack_hidden_name.  This is needed because various special names
-  // use "..SUFFIX", but unpack_hidden_name just looks for '.'.
-  static bool
-  is_special_name(const std::string& name);
+  // If NAME is a special name used as a Go identifier, return the
+  // position within the string where the special part of the name
+  // occurs.
+  static size_t
+  special_name_pos(const std::string& name);
 
  private:
   // During parsing, we keep a stack of functions.  Each function on
@@ -1056,6 +1152,9 @@
   Named_object*
   write_barrier_variable();
 
+  static bool
+  is_digits(const std::string&);
+
   // Type used to map import names to packages.
   typedef std::map<std::string, Package*> Imports;
 
@@ -1079,15 +1178,15 @@
     Named_type* name;
     int64_t size;
     Specific_type_function_kind kind;
-    std::string fnname;
+    Backend_name bname;
     Function_type* fntype;
 
     Specific_type_function(Type* atype, Named_type* aname, int64_t asize,
 			   Specific_type_function_kind akind,
-			   const std::string& afnname,
+			   Backend_name* abname,
 			   Function_type* afntype)
       : type(atype), name(aname), size(asize), kind(akind),
-	fnname(afnname), fntype(afntype)
+	bname(*abname), fntype(afntype)
     { }
   };
 
@@ -1631,6 +1730,10 @@
   Bstatement*
   return_value(Gogo*, Named_object*, Location) const;
 
+  // Get the backend name of this function.
+  void
+  backend_name(Gogo*, Named_object*, Backend_name*);
+
   // Get an expression for the variable holding the defer stack.
   Expression*
   defer_stack(Location);
@@ -1872,6 +1975,10 @@
   void
   build_backend_descriptor(Gogo*);
 
+  // Get the backend name of this function declaration.
+  void
+  backend_name(Gogo*, Named_object*, Backend_name*);
+
   // Export a function declaration.
   void
   export_func(Export* exp, const Named_object* no) const
@@ -2863,10 +2970,6 @@
   Bvariable*
   get_backend_variable(Gogo*, Named_object* function);
 
-  // Return the external identifier for this object.
-  std::string
-  get_id(Gogo*);
-
   // Get the backend representation of this object.
   void
   get_backend(Gogo*, std::vector<Bexpression*>&, std::vector<Btype*>&,
diff --git a/go/names.cc b/go/names.cc
index 1f0a545..0097417 100644
--- a/go/names.cc
+++ b/go/names.cc
@@ -15,53 +15,68 @@
 // assembly code.  This is not used for names that appear only in the
 // debug info.
 
-// Our external names contain only ASCII alphanumeric characters,
+// Our external names may contain only ASCII alphanumeric characters,
 // underscore, and dot.  (According to the GCC sources, dot is not
 // permitted in assembler symbols on VxWorks and MMIX.  We will not
-// support those systems.)  Go names can not contain dot, so we rely
-// on using dot to encode Unicode characters, and to separate Go
-// symbols by package, and so forth.  We assume that none of the
-// non-Go symbols in the final link will contain a dot, so we don't
-// worry about conflicts.
+// support those systems.)  Go identifiers cannot contain dot, but Go
+// package paths can.  Both Go identifiers and package paths can, of
+// course, contain all sorts of Unicode characters.
+//
+// The gc compiler uses names like "pkg.F", and it seems convenient to
+// emulate that.  Therefore, we will use dot to separate different
+// components of names.
+//
+// Since package paths can contain dot, to avoid ambiguity we must
+// encode package paths such that they do not contain any dot.  The
+// natural way to do this is to encode forbidden characters, including
+// dot, using a notation based on underscore.  We will, of course,
+// have to encode underscore itself.
+//
+// Since we will be using an underscore encoding for the package path,
+// it seems reasonable to use the same encoding for Go identifiers.
+// This has the disadvantage that encoded Go identifiers will appear
+// to be valid Go identifiers with funny spellings, but it seems like
+// the best available approach.
+//
+// Therefore, in the following discussion we may assume that none of
+// the names under discussion contain a dot.  All of the names we
+// generate for Go identifiers (that don't use //export or
+// //go:linkname) will contain at least one dot, as discussed below.
+// We assume that none of the non-Go symbols in the final link will
+// contain a dot, so we don't worry about conflicts.
 //
 // We first describe the basic symbol names, used to represent Go
-// functions and variables.  These never start with a dot, never end
-// with a dot, never contain two consecutive dots, and never contain a
-// dot followed by a digit.
+// functions and variables.
 //
 // The external name for a normal Go symbol NAME, a function or
 // variable, is simply "PKGPATH.NAME".  Note that NAME is not the
 // packed form used for the "hidden" name internally in the compiler;
-// it is the name that appears in the source code.  PKGPATH is the
-// -fgo-pkgpath option as adjusted by Gogo::pkgpath_for_symbol. Note
-// that PKGPATH can not contain a dot and neither can NAME.  Also,
-// NAME may not begin with a digit.  NAME may require further encoding
-// for non-ASCII characters as described below, but until that
-// encoding these symbols contain exactly one dot, and they do not
-// start with a dot.
+// it is the name that appears in the source code.  Both PKGPATH and
+// NAME will be encoded as described below.  The encoding process
+// ensures that neither encoded string can contain a dot, and neither
+// will start with a digit (NAME is a Go identifier that can't contain
+// a dot or start with a digit anyhow).  The encoding process means
+// that these external names contain exactly one dot and do not start
+// with a dot.
 //
 // The external name for a method NAME for a named type TYPE is
-// "PKGPATH.TYPE.NAME".  Unlike the gc compiler, the external name
-// does not indicate whether this is a pointer method or a value
-// method; a named type can not have both a pointer and value method
-// with the same name, so there is no ambiguity.  PKGPATH is the
-// package path of the package in which TYPE is defined.  Here none of
-// PKGPATH, TYPE, or NAME can be empty or contain a dot, and neither
-// TYPE nor NAME may begin with a digit.  Before encoding these names
-// contain exactly two dots, not consecutive, and they do not start
-// with a dot.
+// "PKGPATH.TYPE.NAME".  Both NAME and TYPE are simple Go identifiers.
+// Unlike the gc compiler, the external name does not indicate whether
+// this is a pointer method or a value method; a named type can not
+// have both a pointer and value method with the same name, so there
+// is no ambiguity.  PKGPATH is the package path of the package in
+// which TYPE is defined.  PKGPATH, TYPE, and NAME are encoded, and
+// cannot be empty or contain a dot or start with a digit.  These
+// external names contain exactly two dots, not consecutive, and they
+// do not start with a dot.
 //
 // It's uncommon, but the use of type literals with embedded fields
 // can cause us to have methods on unnamed types.  The external names
-// for these are also PKGPATH.TYPE.NAME, where TYPE is an
+// for these are also PKGPATH.TYPELIT.NAME, where TYPELIT is an
 // approximately readable version of the type literal, described
-// below.  As the type literal encoding always contains multiple dots,
-// these names always contain more than two dots.  Although the type
-// literal encoding contains dots, neither PKGPATH nor NAME can
-// contain a dot, and neither TYPE nor NAME can begin with a digit.
-// The effect is that PKGPATH is always the portion of the name before
-// the first dot and NAME is always the portion after the last dot.
-// There is no ambiguity as long as encoded type literals are
+// below.  A TYPELIT will always contain characters that cannot appear
+// in a Go identifier, so TYPELIT can never be confused with a TYPE
+// name.  There is no ambiguity as long as encoded type literals are
 // unambiguous.
 //
 // Also uncommon is an external name that must refer to a named type
@@ -91,46 +106,51 @@
 // the function with an added suffix "..f".
 //
 // A thunk for a go or defer statement is treated as a function whose
-// name is ".thunkNN" where NN is a sequence of digits (these
-// functions are never globally visible).  Thus the final name of a
-// thunk will be PKGPATH..thunkNN.
+// name is ".thunkNN", unencoded, where NN is a sequence of digits
+// (these functions are never globally visible).  Thus the final name
+// of a thunk will be PKGPATH..thunkNN (PKGPATH is encoded).
 //
-// An init function is treated as a function whose name is ".initNN"
-// where NN is a sequence of digits (these functions are never
-// globally visible).  Thus the final name of an init function will be
-// PKGPATH..initNN.
+// An init function is treated as a function whose name is ".initNN",
+// unencoded, where NN is a sequence of digits (these functions are
+// never globally visible).  Thus the final name of an init function
+// will be PKGPATH..initNN (PKGPATH is encoded).
 //
 // A nested function is given the name of outermost enclosing function
-// or method with an added suffix "..funcNN" where NN is a sequence of
-// digits.  Note that the function descriptor of a nested function, if
-// needed, will end with "..funcNN..f".
+// or method with an added suffix "..funcNN", unencoded, where NN is a
+// sequence of digits.  Note that the function descriptor of a nested
+// function, if needed, will end with "..funcNN..f".
 //
 // A recover thunk is the same as the name of the function with an
 // added suffix "..r".
 //
-// The name of a type descriptor for a named type is PKGPATH.TYPE..d.
+// The name of a type descriptor for a named type is
+// PKGPATH.TYPENAME..d (PKGPATH and TYPENAME are encoded).
 //
-// The name of a type descriptor for an unnamed type is type..TYPE.
-// That is, the string "type.." followed by the type literal encoding.
+// The name of a type descriptor for a pointer to a named type is
+// PKGPATH.TYPENAME..p (PKGPATH and TYPENAME are encoded).
+//
+// The name of a type descriptor for an unnamed type is type..TYPELIT.
+// That is, the string "type.." followed by the encoded type literal.
 // These names are common symbols, in the linker's sense of the word
 // common: in the final executable there is only one instance of the
-// type descriptor for a given unnamed type.  The type literal
-// encoding can never start with a digit or with 'u' or 'U'.
+// type descriptor for a given unnamed type.
 //
-// The name of the GC symbol for a named type is PKGPATH.TYPE..g.
+// The name of the GC symbol for a named type is PKGPATH.TYPE..g
+// (PKGPATH and TYPE are encoded).
 //
-// The name of the GC symbol for an unnamed type is typeg..TYPE.
+// The name of the GC symbol for an unnamed type is type..TYPELIT..g.
 // These are common symbols.
 //
 // The name of a ptrmask symbol is gcbits..B32 where B32 is an
-// encoding of the ptrmask bits using only ASCII letters without 'u'
-// or 'U'.  These are common symbols.
+// encoding of the ptrmask bits using only ASCII letters.  These are
+// common symbols.
 //
 // An interface method table for assigning the non-interface type TYPE
 // to the interface type ITYPE is named imt..ITYPE..TYPE.  If ITYPE or
-// TYPE is a named type, they are written as PKGPATH.TYPE.  Otherwise
-// they are written as a type literal.  An interface method table for
-// a pointer method set uses pimt instead of imt.
+// TYPE is a named type, they are written as PKGPATH.TYPE (where both
+// PKGPATH and TYPE are encoded).  Otherwise they are written as a
+// type literal.  An interface method table for a pointer method set
+// uses pimt instead of imt.
 //
 // The names of composite literal initializers, including the GC root
 // variable, are not referenced.  They must not conflict with any C
@@ -147,7 +167,7 @@
 // PKGPATH..import.  If a package doesn't need an init function, it
 // will have a dummy one, named ~PKGPATH.
 //
-// In each pacakge there is a list of all the type descriptors defined
+// In each package there is a list of all the type descriptors defined
 // in this package.  The name of the list is PKGPATH..types.
 //
 // In the main package it gathers all the type descriptor lists in a
@@ -161,109 +181,109 @@
 // The type literal encoding is not quite valid Go, as some aspects of
 // compiler generated types can not be represented.  For example,
 // incomparable struct types have an extra field "{x}".  Struct tags
-// are quoted inside curly braces, rather than introduce an encoding
-// for quotes.  Struct tags can contain any character, so any single
-// byte Unicode character that is not alphanumeric or underscore is
-// replaced with .xNN where NN is the hex encoding.
+// can contain any character, which will be underscore encoded as
+// usual.  In the unusual case of a curly brace or a backslash in a
+// struct tag, the brace or backslash will be backslash quoted, before
+// underscore encoding.
 //
-// There is a simple encoding for glue characters in type literals:
-//   .0 - ' '
-//   .1 - '*'
-//   .2 - ';'
-//   .3 - ','
-//   .4 - '{'
-//   .5 - '}'
-//   .6 - '['
-//   .7 - ']'
-//   .8 - '('
-//   .9 - ')'
-// This is unambiguous as, although the type literal can contain a dot
-// as shown above, those dots are always followed by a name and names
-// can not begin with a digit.  A dot is always followed by a name or
-// a digit, and a type literal can neither start nor end with a dot,
-// so this never introduces consecutive dots.
+// The underscore encoding is, naturally, an underscore followed by
+// other characters.  As there are various characters that commonly
+// appear in type literals and in package paths, we have a set of
+// short encodings.  Then we have general encodings for other
+// characters.
 //
-// Struct tags can contain any character, so they need special
-// treatment.  Alphanumerics, underscores, and Unicode characters that
-// require more than a single byte are left alone (Unicode characters
-// will be encoded later, as described below).  Other single bytes
-// characters are replace with .xNN where NN is the hex encoding.
+//   __ - '_'
+//   _0 - '.'
+//   _1 - '/'
+//   _2 - '*'
+//   _3 - ','
+//   _4 - '{'
+//   _5 - '}'
+//   _6 - '['
+//   _7 - ']'
+//   _8 - '('
+//   _9 - ')'
+//   _a - '"'
+//   _b - ' '
+//   _c - ';'
 //
-// Since Go identifiers can contain Unicode characters, we must encode
-// them into ASCII.  We do this last, after the name is generated as
-// described above and after type literals are encoded.  To make the
-// encoding unambiguous, we introduce it with two consecutive dots.
-// This is followed by the letter u and four hex digits or the letter
-// U and eight digits, just as in the language only using ..u and ..U
-// instead of \u and \U.  The compiler also produces identifiers that
-// are qualified by package path, which means that there may also be ASCII
-// characters that are not assembler-friendly (ex: '=', '/'). The encoding
-// scheme translates such characters into the "..zNN" where NN is the
-// hex value for the character. Since before this encoding names can never
-// contain consecutive dots followed by 'z', 'u' or 'U', and after this
-// encoding "..z", "..u" and "..U" are followed by a known number of
-// characters, this is unambiguous.
+// Other non-alphanumeric ASCII characters are encoded as _xNN, where
+// NN is the hex value for the character.  If an encoded name would
+// otherwise start with a digit, this encoding is also used for the
+// leading digit.
+//
+// Non-ASCII Unicode characters are encoded as _u and four hex digits
+// or _U and eight digits, just as in the language only using _u and
+// _U instead of \u and \U.
 //
 // Demangling these names is straightforward:
-//  - replace ..zXX with an ASCII character
-//  - replace ..uXXXX with a unicode character
-//  - replace ..UXXXXXXXX with a unicode character
-//  - replace .D, where D is a digit, with the character from the above
+//  - replace _xXX with an ASCII character
+//  - replace _uXXXX with a unicode character
+//  - replace _UXXXXXXXX with a unicode character
+//  - replace _C per the table above
 // That will get you as close as possible to a readable name.
 
-// Return the assembler name to use for an exported function, a
-// method, or a function/method declaration.  This is not called if
-// the function has been given an explicit name via a magic //extern
-// or //go:linkname comment.  GO_NAME is the name that appears in the
-// Go code.  PACKAGE is the package where the function is defined, and
-// is NULL for the package being compiled.  For a method, RTYPE is
+// Set BNAME to the name to use for an exported function, a method, or
+// a function/method declaration.  GO_NAME is the name that appears in
+// the Go code.  PACKAGE is the package where the function is defined,
+// and is NULL for the package being compiled.  For a method, RTYPE is
 // the method's receiver type; for a function, RTYPE is NULL.
 
-std::string
-Gogo::function_asm_name(const std::string& go_name, const Package* package,
-			const Type* rtype)
+void
+Gogo::function_backend_name(const std::string& go_name,
+			    const Package* package, const Type* rtype,
+			    Backend_name* bname)
 {
-  std::string ret;
   if (rtype != NULL)
-    ret = rtype->deref()->mangled_name(this);
+    rtype->deref()->backend_name(this, bname);
   else if (package == NULL)
-    ret = this->pkgpath();
+    bname->add(this->pkgpath());
   else
-    ret = package->pkgpath();
-  ret.push_back('.');
-  // Check for special names that will break if we use
-  // Gogo::unpack_hidden_name.
-  if (Gogo::is_special_name(go_name))
-    ret.append(go_name);
+    bname->add(package->pkgpath());
+
+  size_t pos = Gogo::special_name_pos(go_name);
+  if (pos == std::string::npos)
+    bname->add(Gogo::unpack_hidden_name(go_name));
   else
-    ret.append(Gogo::unpack_hidden_name(go_name));
-  return go_encode_id(ret);
+    {
+      if (pos > 0)
+	bname->add(go_name.substr(0, pos));
+      bname->set_suffix(go_name.substr(pos));
+    }
 }
 
-// Return the name to use for a function descriptor.  These symbols
-// are globally visible.
+// Set BNAME to the name to use for a function descriptor.  These
+// symbols are globally visible.
 
-std::string
-Gogo::function_descriptor_name(Named_object* no)
+void
+Gogo::function_descriptor_backend_name(Named_object* no,
+				       Backend_name* bname)
 {
-  if (no->is_function() && !no->func_value()->asm_name().empty())
-    return no->func_value()->asm_name() + "..f";
-  else if (no->is_function_declaration()
-	   && !no->func_declaration_value()->asm_name().empty())
-    return no->func_declaration_value()->asm_name() + "..f";
-  std::string ret = this->function_asm_name(no->name(), no->package(), NULL);
-  ret.append("..f");
-  return ret;
+  if (no->is_function())
+    no->func_value()->backend_name(this, no, bname);
+  else if (no->is_function_declaration())
+    no->func_declaration_value()->backend_name(this, no, bname);
+  else
+    go_unreachable();
+  bname->append_suffix("..f");
 }
 
-// Return the name to use for a generated stub method.  MNAME is the
-// method name.  PACKAGE is the package where the type that needs this
-// stub method is defined.  These functions are globally visible.
-// Note that this is the function name that corresponds to the name
-// used for the method in Go source code, if this stub method were
-// written in Go.  The assembler name will be generated by
-// Gogo::function_asm_name, and because this is a method that name
-// will include the receiver type.
+// Return the name to use for a generated stub method.  A stub method
+// is used as the method table entry for a promoted method of an
+// embedded type.  MNAME is the method name.  PACKAGE is the package
+// where the type that needs this stub method is defined.  These
+// functions are globally visible.
+//
+// This returns a name that acts like a Go identifier, as though the
+// stub method were written in Go as an explicitly defined method that
+// simply calls the promoted method.  The name we return here will
+// eventually be passed to function_backend_name, which will return a
+// name that includes the receiver type.
+//
+// We construct a unique method name and append "..stub".
+// function_backend_name will look for the "..stub" and turn that into
+// an unencoded suffix.  The rest of the name will be encoded as
+// usual.
 
 std::string
 Gogo::stub_method_name(const Package* package, const std::string& mname)
@@ -279,56 +299,70 @@
     return Gogo::unpack_hidden_name(mname) + "..stub";
 
   // We are creating a stub method for an unexported method of an
-  // imported embedded type.  We need to disambiguate the method name.
-  std::string ret = mpkgpath;
+  // imported embedded type.  A single type can have multiple promoted
+  // methods with the same unexported name, if it embeds types from
+  // different packages.  We need to disambiguate the method name.
+  // This produces an unambiguous name because even though MPKGPATH
+  // can be anything, we know that MNAME does not contain a dot.  The
+  // dot we return here, between MPKGPATH and MNAME, will wind up
+  // being underscore encoded.
+  std::string ret(mpkgpath);
   ret.push_back('.');
   ret.append(Gogo::unpack_hidden_name(mname));
   ret.append("..stub");
   return ret;
 }
 
-// Return the name of the hash function for TYPE.
+// Set BNAME to the name of the hash function for TYPE.
 
-std::string
-Gogo::hash_function_name(const Type* type)
+void
+Gogo::hash_function_name(const Type* type, Backend_name* bname)
 {
-  std::string tname = type->mangled_name(this);
-  return tname + "..hash";
-}
-
-// Return the name of the equal function for TYPE.  If NAME is not
-// NULL it is the name of the type.
-
-std::string
-Gogo::equal_function_name(const Type* type, const Named_type* name)
-{
-  const Type* rtype = type;
-  if (name != NULL)
-    rtype = name;
-  std::string tname = rtype->mangled_name(this);
-  return tname + "..eq";
-}
-
-// Return the assembler name to use for a global variable.  GO_NAME is
-// the name that appears in the Go code.  PACKAGE is the package where
-// the variable is defined, and is NULL for the package being
-// compiled.
-
-std::string
-Gogo::global_var_asm_name(const std::string& go_name, const Package* package)
-{
-  std::string ret;
-  if (package == NULL)
-    ret = this->pkgpath();
+  if (type->named_type() != NULL)
+    type->backend_name(this, bname);
   else
-    ret = package->pkgpath();
-  ret.append(1, '.');
-  ret.append(Gogo::unpack_hidden_name(go_name));
-  return go_encode_id(ret);
+    {
+      bname->add(this->pkgpath());
+      type->backend_name(this, bname);
+    }
+  bname->set_suffix("..hash");
+}
+
+// Set BNAME to the name of the equal function for TYPE.  If NAME is
+// not NULL it is the name of the type.
+
+void
+Gogo::equal_function_name(const Type* type, const Named_type* name,
+			  Backend_name* bname)
+{
+  if (name != NULL)
+    name->backend_name(this, bname);
+  else
+    {
+      bname->add(this->pkgpath());
+      type->backend_name(this, bname);
+    }
+  bname->set_suffix("..eq");
+}
+
+// Set BNAME to the name to use for a global variable.  GO_NAME is the
+// name that appears in the Go code.  PACKAGE is the package where the
+// variable is defined, and is NULL for the package being compiled.
+
+void
+Gogo::global_var_backend_name(const std::string& go_name,
+			      const Package* package,
+			      Backend_name* bname)
+{
+  if (package == NULL)
+    bname->add(this->pkgpath());
+  else
+    bname->add(package->pkgpath());
+  bname->add(Gogo::unpack_hidden_name(go_name));
 }
 
 // Return an erroneous name that indicates that an error has already
-// been reported.
+// been reported.  This name will act like a Go identifier.
 
 std::string
 Gogo::erroneous_name()
@@ -349,7 +383,10 @@
   return name.compare(0, 10, ".erroneous") == 0;
 }
 
-// Return a name for a thunk object.
+// Return a name for a thunk object.  This name will act like a Go
+// identifier.  The name returned here will eventually be passed to
+// function_backend_name, which will pull off the ..thunk as an
+// unencoded suffix.
 
 std::string
 Gogo::thunk_name()
@@ -358,7 +395,12 @@
   char thunk_name[50];
   snprintf(thunk_name, sizeof thunk_name, "..thunk%d", thunk_count);
   ++thunk_count;
-  std::string ret = this->pkgpath();
+  // We don't want to return a name that starts with a dot, as that
+  // will confuse Gogo::is_hidden_name.  And we don't want to change
+  // ..thunk, which fits our general theme and is used by code like
+  // runtime.Callers.  But the prefix doesn't matter, as the actual
+  // name will include the package path.
+  std::string ret = "go";
   return ret + thunk_name;
 }
 
@@ -368,13 +410,10 @@
 Gogo::is_thunk(const Named_object* no)
 {
   const std::string& name(no->name());
-  size_t i = name.find("..thunk");
+  size_t i = name.rfind("..thunk");
   if (i == std::string::npos)
     return false;
-  for (i += 7; i < name.size(); ++i)
-    if (name[i] < '0' || name[i] > '9')
-      return false;
-  return true;
+  return Gogo::is_digits(name.substr(i + 7));
 }
 
 // Return the name to use for an init function.  There can be multiple
@@ -387,11 +426,12 @@
   char buf[30];
   snprintf(buf, sizeof buf, "..init%d", init_count);
   ++init_count;
-  std::string ret = this->pkgpath();
-  return ret + buf;
+  return this->pkgpath() + buf;
 }
 
-// Return the name to use for a nested function.
+// Return the name to use for a nested function.  This name acts like
+// a Go identifier.  This name will be rewritten by
+// Function::backend_name.
 
 std::string
 Gogo::nested_function_name(Named_object* enclosing)
@@ -420,7 +460,9 @@
 	enclosing->func_value()->type()->receiver();
       if (rcvr != NULL)
 	{
-	  prefix = rcvr->type()->mangled_name(this);
+	  Backend_name bname;
+	  rcvr->type()->backend_name(this, &bname);
+	  prefix = bname.name();
 	  prefix.push_back('.');
 	}
       prefix.append(Gogo::unpack_hidden_name(enclosing->name()));
@@ -460,7 +502,8 @@
 }
 
 // Return the name to use for a recover thunk for the function NAME.
-// If the function is a method, RTYPE is the receiver type.
+// If the function is a method, RTYPE is the receiver type.  This is a
+// name that acts like a Go identifier.
 
 std::string
 Gogo::recover_thunk_name(const std::string& name, const Type* rtype)
@@ -468,10 +511,12 @@
   std::string ret;
   if (rtype != NULL)
     {
-      ret = rtype->mangled_name(this);
+      Backend_name bname;
+      rtype->backend_name(this, &bname);
+      ret = bname.name();
       ret.append(1, '.');
     }
-  if (Gogo::is_special_name(name))
+  if (Gogo::special_name_pos(name) != std::string::npos)
     ret.append(name);
   else
     ret.append(Gogo::unpack_hidden_name(name));
@@ -504,8 +549,8 @@
   return buf;
 }
 
-// Return the name of the variable used to represent the zero value of
-// a map.  This is a globally visible common symbol.
+// Return the assembler name of the variable used to represent the
+// zero value of a map.  This is a globally visible common symbol.
 
 std::string
 Gogo::map_zero_value_name()
@@ -513,7 +558,9 @@
   return "go..zerovalue";
 }
 
-// Return the name to use for the import control function.
+// Return the name to use for the import control function.  This name
+// is handled specially by Function::backend_name.  It is not encoded
+// further.
 
 const std::string&
 Gogo::get_init_fn_name()
@@ -550,7 +597,7 @@
 // can be a real init function or a dummy one.
 
 std::string
-Gogo::pkgpath_from_init_fn_name(std::string name)
+Gogo::pkgpath_symbol_from_init_fn_name(std::string name)
 {
   go_assert(!name.empty());
   if (name[0] == '~')
@@ -561,60 +608,38 @@
   go_unreachable();
 }
 
-// Return a mangled name for a type.  These names appear in symbol
-// names in the assembler file for things like type descriptors and
-// methods.
+// Set BNAME to a name for a type to use in a symbol.  Return a name
+// for a type to use in a symbol.  These names appear in symbol names
+// in the assembler file for things like type descriptors and methods.
 
-std::string
-Type::mangled_name(Gogo* gogo) const
+void
+Type::backend_name(Gogo* gogo, Backend_name* bname) const
 {
-  std::string ret;
-
-  // The do_mangled_name virtual function will set RET to the mangled
-  // name before glue character mapping.
-  this->do_mangled_name(gogo, &ret);
-
-  // Type descriptor names and interface method table names use a ".."
-  // before the mangled name of a type, so to avoid ambiguity the
-  // mangled name must not start with 'u' or 'U' or a digit.
-  go_assert((ret[0] < '0' || ret[0] > '9') && ret[0] != ' ');
-  if (ret[0] == 'u' || ret[0] == 'U')
-    ret = " " + ret;
-
-  // Map glue characters as described above.
-
-  // The mapping is only unambiguous if there is no .DIGIT in the
-  // string, so check that.
-  for (size_t i = ret.find('.');
-       i != std::string::npos;
-       i = ret.find('.', i + 1))
+  // Special case top level named types to get nicer name encodings
+  // for this common case.
+  const Named_type* nt = this->unalias()->named_type();
+  if (nt != NULL && !nt->is_builtin())
     {
-      if (i + 1 < ret.size())
+      unsigned int index;
+      if (nt->in_function(&index) == NULL)
 	{
-	  char c = ret[i + 1];
-	  go_assert(c < '0' || c > '9');
+	  const Named_object* no = nt->named_object();
+	  if (no->package() == NULL)
+	    bname->add(gogo->pkgpath());
+	  else
+	    bname->add(no->package()->pkgpath());
+	  bname->add(Gogo::unpack_hidden_name(no->name()));
+	  return;
 	}
     }
 
-  // The order of these characters is the replacement code.
-  const char * const replace = " *;,{}[]()";
+  std::string name;
 
-  const size_t rlen = strlen(replace);
-  char buf[2];
-  buf[0] = '.';
-  for (size_t ri = 0; ri < rlen; ++ri)
-    {
-      buf[1] = '0' + ri;
-      while (true)
-	{
-	  size_t i = ret.find(replace[ri]);
-	  if (i == std::string::npos)
-	    break;
-	  ret.replace(i, 1, buf, 2);
-	}
-    }
+  // The do_symbol_name virtual function will set RET to the mangled
+  // name before encoding.
+  this->do_mangled_name(gogo, &name);
 
-  return ret;
+  bname->add(name);
 }
 
 // The mangled name is implemented as a method on each instance of
@@ -701,12 +726,7 @@
 	  else
 	    ret->push_back(',');
 	  if (this->is_varargs_ && p + 1 == params->end())
-	    {
-	      // We can't use "..." here because the mangled name
-	      // might start with 'u' or 'U', which would be ambiguous
-	      // with the encoding of Unicode characters.
-	      ret->append(",,,");
-	    }
+	    ret->append("...");
 	  this->append_mangled_name(p->type(), gogo, ret);
 	}
     }
@@ -776,15 +796,15 @@
 	  if (p->is_anonymous()
 	      && p->type()->named_type() != NULL
 	      && p->type()->named_type()->is_alias())
-	    p->type()->named_type()->append_mangled_type_name(gogo, true, ret);
+	    p->type()->named_type()->append_symbol_type_name(gogo, true, ret);
 	  else
 	    this->append_mangled_name(p->type(), gogo, ret);
 
 	  if (p->has_tag())
 	    {
 	      // Use curly braces around a struct tag, since they are
-	      // unambiguous here and we have no encoding for
-	      // quotation marks.
+	      // unambiguous here and struct tags rarely contain curly
+	      // braces.
 	      ret->push_back('{');
 	      ret->append(go_mangle_struct_tag(p->tag()));
 	      ret->push_back('}');
@@ -837,10 +857,10 @@
 Channel_type::do_mangled_name(Gogo* gogo, std::string* ret) const
 {
   if (!this->may_send_)
-    ret->append("{}");
+    ret->append("<-");
   ret->append("chan");
   if (!this->may_receive_)
-    ret->append("{}");
+    ret->append("<-");
   ret->push_back(' ');
   this->append_mangled_name(this->element_type_, gogo, ret);
 }
@@ -883,7 +903,7 @@
 void
 Named_type::do_mangled_name(Gogo* gogo, std::string* ret) const
 {
-  this->append_mangled_type_name(gogo, false, ret);
+  this->append_symbol_type_name(gogo, false, ret);
 }
 
 void
@@ -903,13 +923,13 @@
     }
 }
 
-// Append the mangled name for a named type to RET.  For an alias we
+// Append the symbol name for a named type to RET.  For an alias we
 // normally use the real name, but if USE_ALIAS is true we use the
 // alias name itself.
 
 void
-Named_type::append_mangled_type_name(Gogo* gogo, bool use_alias,
-				     std::string* ret) const
+Named_type::append_symbol_type_name(Gogo* gogo, bool use_alias,
+				    std::string* ret) const
 {
   if (this->is_error_)
     return;
@@ -933,7 +953,11 @@
 	  const Typed_identifier* rcvr =
 	    this->in_function_->func_value()->type()->receiver();
 	  if (rcvr != NULL)
-	    ret->append(rcvr->type()->deref()->mangled_name(gogo));
+	    {
+	      Backend_name bname;
+	      rcvr->type()->deref()->backend_name(gogo, &bname);
+	      ret->append(bname.name());
+	    }
 	  else if (this->in_function_->package() == NULL)
 	    ret->append(gogo->pkgpath());
 	  else
@@ -956,23 +980,46 @@
   if (this->in_function_ != NULL && this->in_function_index_ > 0)
     {
       char buf[30];
-      snprintf(buf, sizeof buf, "..i%u", this->in_function_index_);
+      snprintf(buf, sizeof buf, ".i%u", this->in_function_index_);
       ret->append(buf);
     }
 }
 
-// Return the name for the type descriptor symbol for TYPE.  This can
-// be a global, common, or local symbol, depending.  NT is not NULL if
-// it is the name to use.
+// Given a name which may or may not have been hidden, append the
+// appropriate version of the name to the result string.
 
-std::string
-Gogo::type_descriptor_name(const Type* type, Named_type* nt)
+void
+Gogo::append_possibly_hidden_name(std::string *result, const std::string& name)
+{
+  if (!Gogo::is_hidden_name(name))
+    *result += name;
+  else
+    *result += name.substr(1);
+}
+
+// Set BNAME to the name for the type descriptor symbol for TYPE.
+// This can be a global, common, or local symbol, depending.  NT is
+// not NULL if it is the name to use.
+
+void
+Gogo::type_descriptor_backend_name(const Type* type, Named_type* nt,
+				   Backend_name* bname)
 {
   // The type descriptor symbol for the unsafe.Pointer type is defined
   // in libgo/runtime/go-unsafe-pointer.c, so just use a reference to
   // that symbol for all unsafe pointer types.
   if (type->is_unsafe_pointer_type())
-    return "unsafe.Pointer..d";
+    {
+      bname->set_asm_name("unsafe.Pointer..d");
+      return;
+    }
+
+  bool is_pointer = false;
+  if (nt == NULL && type->points_to() != NULL)
+    {
+      nt = type->points_to()->named_type();
+      is_pointer = true;
+    }
 
   if (nt == NULL)
     {
@@ -981,63 +1028,28 @@
       // using a named type, like "int".
       go_assert(!type->is_basic_type());
 
-      return "type.." + type->mangled_name(this);
+      type->backend_name(this, bname);
+      bname->set_prefix("type..");
     }
-
-  std::string ret;
-  Named_object* no = nt->named_object();
-  unsigned int index;
-  const Named_object* in_function = nt->in_function(&index);
-  if (nt->is_builtin())
-    go_assert(in_function == NULL);
   else
     {
-      if (in_function != NULL)
-	{
-	  const Typed_identifier* rcvr =
-	    in_function->func_value()->type()->receiver();
-	  if (rcvr != NULL)
-	    ret.append(rcvr->type()->deref()->mangled_name(this));
-	  else if (in_function->package() == NULL)
-	    ret.append(this->pkgpath());
-	  else
-	    ret.append(in_function->package()->pkgpath());
-	  ret.push_back('.');
-	  ret.append(Gogo::unpack_hidden_name(in_function->name()));
-	  ret.push_back('.');
-	}
-
-      if (no->package() == NULL)
-	ret.append(this->pkgpath());
-      else
-	ret.append(no->package()->pkgpath());
-      ret.push_back('.');
+      nt->backend_name(this, bname);
+      bname->set_suffix(is_pointer ? "..p" : "..d");
     }
-
-  Gogo::append_possibly_hidden_name(&ret, no->name());
-
-  if (in_function != NULL && index > 0)
-    {
-      char buf[30];
-      snprintf(buf, sizeof buf, "..i%u", index);
-      ret.append(buf);
-    }
-
-  ret.append("..d");
-
-  return ret;
 }
 
 // Return the name of the type descriptor list symbol of a package.
+// This is passed directly to the backend without further encoding.
 
 std::string
-Gogo::type_descriptor_list_symbol(const std::string& pkgpath)
+Gogo::type_descriptor_list_symbol(const std::string& pkgpath_symbol)
 {
-  return pkgpath + "..types";
+  return pkgpath_symbol + "..types";
 }
 
 // Return the name of the list of all type descriptor lists.  This is
-// only used in the main package.
+// only used in the main package.  This is passed directly to the
+// backend without further encoding.
 
 std::string
 Gogo::typelists_symbol()
@@ -1045,24 +1057,30 @@
   return "go..typelists";
 }
 
-// Return the name for the GC symbol for a type.  This is used to
-// initialize the gcdata field of a type descriptor.  This is a local
-// name never referenced outside of this assembly file.  (Note that
-// some type descriptors will initialize the gcdata field with a name
-// generated by ptrmask_symbol_name rather than this method.)
+// Return the assembler name for the GC symbol for a type.  This is
+// used to initialize the gcdata field of a type descriptor.  This is
+// a local name never referenced outside of this assembly file.  (Note
+// that some type descriptors will initialize the gcdata field with a
+// name generated by ptrmask_symbol_name rather than this method.)
+// This is passed directly to the backend without further encoding.
 
 std::string
 Gogo::gc_symbol_name(Type* type)
 {
-  return this->type_descriptor_name(type, type->named_type()) + "..g";
+  Backend_name bname;
+  this->type_descriptor_backend_name(type, type->named_type(), &bname);
+  bname.append_suffix("..g");
+  return bname.asm_name();
 }
 
-// Return the name for a ptrmask variable.  PTRMASK_SYM_NAME is a
-// base32 string encoding the ptrmask (as returned by Ptrmask::symname
-// in types.cc).  This name is used to intialize the gcdata field of a
-// type descriptor.  These names are globally visible.  (Note that
-// some type descriptors will initialize the gcdata field with a name
-// generated by gc_symbol_name rather than this method.)
+// Return the assembler name for a ptrmask variable.  PTRMASK_SYM_NAME
+// is a base32 string encoding the ptrmask (as returned by
+// Ptrmask::symname in types.cc).  This name is used to intialize the
+// gcdata field of a type descriptor.  These names are globally
+// visible.  (Note that some type descriptors will initialize the
+// gcdata field with a name generated by gc_symbol_name rather than
+// this method.)  This is passed directly to the backend without
+// further encoding.
 
 std::string
 Gogo::ptrmask_symbol_name(const std::string& ptrmask_sym_name)
@@ -1070,34 +1088,123 @@
   return "gcbits.." + ptrmask_sym_name;
 }
 
-// Return the name to use for an interface method table used for the
-// ordinary type TYPE converted to the interface type ITYPE.
+// Return the assembler name to use for an interface method table used
+// for the ordinary type TYPE converted to the interface type ITYPE.
 // IS_POINTER is true if this is for the method set for a pointer
-// receiver.
+// receiver.  This is passed directly to the backend without further
+// encoding.
 
 std::string
 Gogo::interface_method_table_name(Interface_type* itype, Type* type,
 				  bool is_pointer)
 {
+  Backend_name iname;
+  itype->backend_name(this, &iname);
+  Backend_name tname;
+  type->backend_name(this, &tname);
   return ((is_pointer ? "pimt.." : "imt..")
-	  + itype->mangled_name(this)
+	  + iname.asm_name()
 	  + ".."
-	  + type->mangled_name(this));
+	  + tname.asm_name());
 }
 
-// Return whether NAME is a special name that can not be passed to
-// unpack_hidden_name.  This is needed because various special names
-// use "..SUFFIX", but unpack_hidden_name just looks for '.'.
+// If NAME is a special name with a ".." suffix, return the position
+// of that suffix.  This is needed because various special names use
+// "..SUFFIX", but unpack_hidden_name just looks for '.', and because
+// we don't want to encode the suffix.
+
+size_t
+Gogo::special_name_pos(const std::string& name)
+{
+  size_t pos = name.rfind("..");
+  if (pos == std::string::npos)
+    return pos;
+  std::string suffix(name.substr(pos));
+  if (suffix == "..hash"
+      || suffix == "..eq"
+      || suffix == "..stub"
+      || suffix == "..d"
+      || suffix == "..f"
+      || suffix == "..r"
+      || suffix == "..import")
+    return pos;
+  if ((suffix.compare(2, 4, "func") == 0
+       || suffix.compare(2, 4, "init") == 0)
+      && Gogo::is_digits(suffix.substr(6)))
+    return pos;
+  if (suffix.compare(2, 5, "thunk") == 0
+      && Gogo::is_digits(suffix.substr(7)))
+    return pos;
+  return std::string::npos;
+}
+
+// Return whether the string is non-empty and contains only digits.
 
 bool
-Gogo::is_special_name(const std::string& name)
+Gogo::is_digits(const std::string& s)
 {
-  return (name.find("..hash") != std::string::npos
-	  || name.find("..eq") != std::string::npos
-	  || name.find("..stub") != std::string::npos
-	  || name.find("..func") != std::string::npos
-	  || name.find("..r") != std::string::npos
-	  || name.find("..init") != std::string::npos
-	  || name.find("..thunk") != std::string::npos
-	  || name.find("..import") != std::string::npos);
+  if (s.empty())
+    return false;
+  for (size_t i = 0; i < s.size(); ++i)
+    if (s[i] < '0' || s[i] > '9')
+      return false;
+  return true;
+}
+
+// Class Backend_name.
+
+// Get the user visible name.
+
+std::string
+Backend_name::name() const
+{
+  if (this->is_asm_name_)
+    return this->components_[0];
+  std::string ret;
+  if (this->prefix_ != NULL)
+    ret.append(this->prefix_);
+  for (int i = 0; i < this->count_; i++)
+    {
+      if (i > 0)
+	ret.push_back('.');
+      ret.append(this->components_[i]);
+    }
+  if (!this->suffix_.empty())
+    ret.append(this->suffix_);
+  return ret;
+}
+
+// Get the assembler name.
+
+std::string
+Backend_name::asm_name() const
+{
+  if (this->is_asm_name_)
+    return this->components_[0];
+  std::string ret;
+  if (this->prefix_ != NULL)
+    ret.append(this->prefix_);
+  for (int i = 0; i < this->count_; i++)
+    {
+      if (i > 0)
+	ret.push_back('.');
+      ret.append(go_encode_id(this->components_[i]));
+    }
+  if (!this->suffix_.empty())
+    ret.append(this->suffix_);
+  return ret;
+}
+
+// Get the assembler name, or the empty string if it is the same as
+// the user visible name.
+
+std::string
+Backend_name::optional_asm_name() const
+{
+  if (this->is_asm_name_)
+    return "";
+  for (int i = 0; i < this->count_; i++)
+    if (go_id_needs_encoding(this->components_[i]))
+      return this->asm_name();
+  return "";
 }
diff --git a/go/runtime.def b/go/runtime.def
index 0796cba..b608766 100644
--- a/go/runtime.def
+++ b/go/runtime.def
@@ -83,7 +83,7 @@
 		R1(MAP))
 
 // Make a map with no hint, or a small constant hint.
-DEF_GO_RUNTIME(MAKEMAP_SMALL, "runtime.makemap_small", P0(), R1(MAP))
+DEF_GO_RUNTIME(MAKEMAP_SMALL, "runtime.makemap__small", P0(), R1(MAP))
 
 // Build a map from a composite literal.
 DEF_GO_RUNTIME(CONSTRUCT_MAP, "__go_construct_map",
@@ -95,19 +95,19 @@
 	       R1(POINTER))
 
 // Look up a uint32 key in a map.
-DEF_GO_RUNTIME(MAPACCESS1_FAST32, "runtime.mapaccess1_fast32",
+DEF_GO_RUNTIME(MAPACCESS1_FAST32, "runtime.mapaccess1__fast32",
                P3(TYPE, MAP, UINT32), R1(POINTER))
 
 // Look up a uint64 key in a map.
-DEF_GO_RUNTIME(MAPACCESS1_FAST64, "runtime.mapaccess1_fast64",
+DEF_GO_RUNTIME(MAPACCESS1_FAST64, "runtime.mapaccess1__fast64",
                P3(TYPE, MAP, UINT64), R1(POINTER))
 
 // Look up a string key in a map.
-DEF_GO_RUNTIME(MAPACCESS1_FASTSTR, "runtime.mapaccess1_faststr",
+DEF_GO_RUNTIME(MAPACCESS1_FASTSTR, "runtime.mapaccess1__faststr",
                P3(TYPE, MAP, STRING), R1(POINTER))
 
 // Look up a key in a map when the value is large.
-DEF_GO_RUNTIME(MAPACCESS1_FAT, "runtime.mapaccess1_fat",
+DEF_GO_RUNTIME(MAPACCESS1_FAT, "runtime.mapaccess1__fat",
 	       P4(TYPE, MAP, POINTER, POINTER), R1(POINTER))
 
 // Look up a key in a map returning the value and whether it is
@@ -117,22 +117,22 @@
 
 // Look up a uint32 key in a map returning the value and whether
 // it is present.
-DEF_GO_RUNTIME(MAPACCESS2_FAST32, "runtime.mapaccess2_fast32",
+DEF_GO_RUNTIME(MAPACCESS2_FAST32, "runtime.mapaccess2__fast32",
                P3(TYPE, MAP, UINT32), R2(POINTER, BOOL))
 
 // Look up a uint64 key in a map returning the value and whether
 // it is present.
-DEF_GO_RUNTIME(MAPACCESS2_FAST64, "runtime.mapaccess2_fast64",
+DEF_GO_RUNTIME(MAPACCESS2_FAST64, "runtime.mapaccess2__fast64",
                P3(TYPE, MAP, UINT64), R2(POINTER, BOOL))
 
 // Look up a string key in a map returning the value and whether
 // it is present.
-DEF_GO_RUNTIME(MAPACCESS2_FASTSTR, "runtime.mapaccess2_faststr",
+DEF_GO_RUNTIME(MAPACCESS2_FASTSTR, "runtime.mapaccess2__faststr",
                P3(TYPE, MAP, STRING), R2(POINTER, BOOL))
 
 // Look up a key in a map, returning the value and whether it is
 // present, when the value is large.
-DEF_GO_RUNTIME(MAPACCESS2_FAT, "runtime.mapaccess2_fat",
+DEF_GO_RUNTIME(MAPACCESS2_FAT, "runtime.mapaccess2__fat",
 	       P4(TYPE, MAP, POINTER, POINTER), R2(POINTER, BOOL))
 
 // Assignment to a key in a map.
@@ -140,38 +140,38 @@
 	       R1(POINTER))
 
 // Assignment to a uint32 key in a map.
-DEF_GO_RUNTIME(MAPASSIGN_FAST32, "runtime.mapassign_fast32",
+DEF_GO_RUNTIME(MAPASSIGN_FAST32, "runtime.mapassign__fast32",
                P3(TYPE, MAP, UINT32), R1(POINTER))
 
 // Assignment to a uint64 key in a map.
-DEF_GO_RUNTIME(MAPASSIGN_FAST64, "runtime.mapassign_fast64",
+DEF_GO_RUNTIME(MAPASSIGN_FAST64, "runtime.mapassign__fast64",
                P3(TYPE, MAP, UINT64), R1(POINTER))
 
 // Assignment to a 32-bit pointer key in a map.
-DEF_GO_RUNTIME(MAPASSIGN_FAST32PTR, "runtime.mapassign_fast32ptr",
+DEF_GO_RUNTIME(MAPASSIGN_FAST32PTR, "runtime.mapassign__fast32ptr",
                P3(TYPE, MAP, POINTER), R1(POINTER))
 
 // Assignment to a 64-bit pointer key in a map.
-DEF_GO_RUNTIME(MAPASSIGN_FAST64PTR, "runtime.mapassign_fast64ptr",
+DEF_GO_RUNTIME(MAPASSIGN_FAST64PTR, "runtime.mapassign__fast64ptr",
                P3(TYPE, MAP, POINTER), R1(POINTER))
 
 // Assignment to a string key in a map.
-DEF_GO_RUNTIME(MAPASSIGN_FASTSTR, "runtime.mapassign_faststr",
+DEF_GO_RUNTIME(MAPASSIGN_FASTSTR, "runtime.mapassign__faststr",
                P3(TYPE, MAP, STRING), R1(POINTER))
 
 // Delete a key from a map.
 DEF_GO_RUNTIME(MAPDELETE, "runtime.mapdelete", P3(TYPE, MAP, POINTER), R0())
 
 // Delete a uint32 key from a map.
-DEF_GO_RUNTIME(MAPDELETE_FAST32, "runtime.mapdelete_fast32",
+DEF_GO_RUNTIME(MAPDELETE_FAST32, "runtime.mapdelete__fast32",
                P3(TYPE, MAP, UINT32), R0())
 
 // Delete a uint64 key from a map.
-DEF_GO_RUNTIME(MAPDELETE_FAST64, "runtime.mapdelete_fast64",
+DEF_GO_RUNTIME(MAPDELETE_FAST64, "runtime.mapdelete__fast64",
                P3(TYPE, MAP, UINT64), R0())
 
 // Delete a string key from a map.
-DEF_GO_RUNTIME(MAPDELETE_FASTSTR, "runtime.mapdelete_faststr",
+DEF_GO_RUNTIME(MAPDELETE_FASTSTR, "runtime.mapdelete__faststr",
                P3(TYPE, MAP, STRING), R0())
 
 // Begin a range over a map.
diff --git a/go/types.cc b/go/types.cc
index f3bcf2e..c4570b4 100644
--- a/go/types.cc
+++ b/go/types.cc
@@ -1342,19 +1342,21 @@
 
       Type* td_type = Type::make_type_descriptor_type();
       Btype* td_btype = td_type->get_backend(gogo);
-      std::string name = gogo->type_descriptor_name(this, nt);
-      std::string asm_name(go_selectively_encode_id(name));
+      Backend_name bname;
+      gogo->type_descriptor_backend_name(this, nt, &bname);
       this->type_descriptor_var_ =
-	  gogo->backend()->immutable_struct_reference(name, asm_name,
-						      td_btype,
-						      bloc);
+	gogo->backend()->immutable_struct_reference(bname.name(),
+						    bname.optional_asm_name(),
+						    td_btype,
+						    bloc);
 
       if (phash != NULL)
 	*phash = this->type_descriptor_var_;
       return;
     }
 
-  std::string var_name = gogo->type_descriptor_name(this, nt);
+  Backend_name bname;
+  gogo->type_descriptor_backend_name(this, nt, &bname);
 
   // Build the contents of the type descriptor.
   Expression* initializer = this->do_type_descriptor(gogo, NULL);
@@ -1366,11 +1368,11 @@
   const Package* dummy;
   if (this->type_descriptor_defined_elsewhere(nt, &dummy))
     {
-      std::string asm_name(go_selectively_encode_id(var_name));
       this->type_descriptor_var_ =
-	  gogo->backend()->immutable_struct_reference(var_name, asm_name,
-						      initializer_btype,
-						      loc);
+	gogo->backend()->immutable_struct_reference(bname.name(),
+						    bname.optional_asm_name(),
+						    initializer_btype,
+						    loc);
       if (phash != NULL)
 	*phash = this->type_descriptor_var_;
       return;
@@ -1399,10 +1401,10 @@
   // ensure that type_descriptor_pointer will work if called while
   // converting INITIALIZER.
 
-  std::string asm_name(go_selectively_encode_id(var_name));
   this->type_descriptor_var_ =
-      gogo->backend()->immutable_struct(var_name, asm_name, false, is_common,
-				      initializer_btype, loc);
+    gogo->backend()->immutable_struct(bname.name(), bname.optional_asm_name(),
+				      false, is_common, initializer_btype,
+				      loc);
   if (phash != NULL)
     *phash = this->type_descriptor_var_;
 
@@ -1411,7 +1413,7 @@
   Bexpression* binitializer = initializer->get_backend(&context);
 
   gogo->backend()->immutable_struct_set_init(this->type_descriptor_var_,
-					     var_name, false, is_common,
+					     bname.name(), false, is_common,
 					     initializer_btype, loc,
 					     binitializer);
 
@@ -1924,19 +1926,20 @@
       return ins.first->second;
     }
 
-  std::string hash_name = gogo->hash_function_name(type);
+  Backend_name bname;
+  gogo->hash_function_name(type, &bname);
 
   Location bloc = Linemap::predeclared_location();
 
-  Named_object* hash_fn = gogo->declare_package_function(hash_name,
+  Named_object* hash_fn = gogo->declare_package_function(bname.name(),
 							 hash_fntype, bloc);
 
   ins.first->second = hash_fn;
 
   if (gogo->in_global_scope())
-    type->write_hash_function(gogo, size, hash_name, hash_fntype);
+    type->write_hash_function(gogo, size, &bname, hash_fntype);
   else
-    gogo->queue_hash_function(type, size, hash_name, hash_fntype);
+    gogo->queue_hash_function(type, size, &bname, hash_fntype);
 
   return hash_fn;
 }
@@ -1944,8 +1947,7 @@
 // Write the hash function for a type that needs it written specially.
 
 void
-Type::write_hash_function(Gogo* gogo, int64_t size,
-			  const std::string& hash_name,
+Type::write_hash_function(Gogo* gogo, int64_t size, const Backend_name* bname,
 			  Function_type* hash_fntype)
 {
   Location bloc = Linemap::predeclared_location();
@@ -1958,8 +1960,9 @@
 
   go_assert(this->is_comparable());
 
-  Named_object* hash_fn = gogo->start_function(hash_name, hash_fntype, false,
-					       bloc);
+  Named_object* hash_fn = gogo->start_function(bname->name(), hash_fntype,
+					       false, bloc);
+  hash_fn->func_value()->set_asm_name(bname->asm_name());
   hash_fn->func_value()->set_is_type_specific_function();
   gogo->start_block(bloc);
 
@@ -2245,7 +2248,8 @@
       return ins.first->second;
     }
 
-  std::string equal_name = gogo->equal_function_name(this, name);
+  Backend_name bname;
+  gogo->equal_function_name(this, name, &bname);
 
   Location bloc = Linemap::predeclared_location();
 
@@ -2255,19 +2259,21 @@
 
   Named_object* equal_fn;
   if (is_defined_elsewhere)
-    equal_fn = Named_object::make_function_declaration(equal_name, package,
+    equal_fn = Named_object::make_function_declaration(bname.name(), package,
 						       equal_fntype, bloc);
   else
-    equal_fn = gogo->declare_package_function(equal_name, equal_fntype, bloc);
+    equal_fn = gogo->declare_package_function(bname.name(), equal_fntype, bloc);
 
   ins.first->second = equal_fn;
 
-  if (!is_defined_elsewhere)
+  if (is_defined_elsewhere)
+    equal_fn->func_declaration_value()->set_asm_name(bname.asm_name());
+  else
     {
       if (gogo->in_global_scope())
-	this->write_equal_function(gogo, name, size, equal_name, equal_fntype);
+	this->write_equal_function(gogo, name, size, &bname, equal_fntype);
       else
-	gogo->queue_equal_function(this, name, size, equal_name, equal_fntype);
+	gogo->queue_equal_function(this, name, size, &bname, equal_fntype);
     }
 
   return equal_fn;
@@ -2278,7 +2284,7 @@
 
 void
 Type::write_equal_function(Gogo* gogo, Named_type* name, int64_t size,
-			   const std::string& equal_name,
+			   const Backend_name* bname,
 			   Function_type* equal_fntype)
 {
   Location bloc = Linemap::predeclared_location();
@@ -2291,8 +2297,9 @@
 
   go_assert(this->is_comparable());
 
-  Named_object* equal_fn = gogo->start_function(equal_name, equal_fntype,
+  Named_object* equal_fn = gogo->start_function(bname->name(), equal_fntype,
 						false, bloc);
+  equal_fn->func_value()->set_asm_name(bname->asm_name());
   equal_fn->func_value()->set_is_type_specific_function();
   gogo->start_block(bloc);
 
@@ -2671,9 +2678,8 @@
   const Package* dummy;
   if (this->type_descriptor_defined_elsewhere(nt, &dummy))
     {
-      std::string asm_name(go_selectively_encode_id(sym_name));
       this->gc_symbol_var_ =
-          gogo->backend()->implicit_variable_reference(sym_name, asm_name,
+          gogo->backend()->implicit_variable_reference(sym_name, "",
                                                        sym_btype);
       if (phash != NULL)
 	*phash = this->gc_symbol_var_;
@@ -2699,10 +2705,9 @@
   // Since we are building the GC symbol in this package, we must create the
   // variable before converting the initializer to its backend representation
   // because the initializer may refer to the GC symbol for this type.
-  std::string asm_name(go_selectively_encode_id(sym_name));
   this->gc_symbol_var_ =
-      gogo->backend()->implicit_variable(sym_name, asm_name,
-					 sym_btype, false, true, is_common, 0);
+      gogo->backend()->implicit_variable(sym_name, "", sym_btype, false, true,
+					 is_common, 0);
   if (phash != NULL)
     *phash = this->gc_symbol_var_;
 
@@ -2876,14 +2881,17 @@
     }
 }
 
-// Return a symbol name for this ptrmask. This is used to coalesce identical
-// ptrmasks, which are common. The symbol name must use only characters that are
-// valid in symbols. It's nice if it's short. For smaller ptrmasks, we convert
-// it to a string that uses only 32 characters, avoiding digits and u and U. For
-// longer pointer masks, apply the same process to the SHA1 digest of the bits,
-// so as to avoid pathologically long symbol names (see related Go issues #32083
-// and #11583 for more on this). To avoid collisions between the two encoding
-// schemes, use a prefix ("X") for the SHA form to disambiguate.
+// Return a symbol name for this ptrmask. This is used to coalesce
+// identical ptrmasks, which are common.  The symbol name must use
+// only characters that are valid in symbols.  It's nice if it's
+// short.  For smaller ptrmasks, we convert it to a string that uses
+// only 32 characters.  For longer pointer masks, apply the same
+// process to the SHA1 digest of the bits, so as to avoid
+// pathologically long symbol names (see related Go issues #32083 and
+// #11583 for more on this).  To avoid collisions between the two
+// encoding schemes, use a prefix ("X") for the SHA form to
+// disambiguate.
+
 std::string
 Ptrmask::symname() const
 {
@@ -2911,7 +2919,7 @@
       bits = &shabits;
     }
 
-  const char chars[33] = "abcdefghijklmnopqrstvwxyzABCDEFG";
+  const char chars[33] = "abcdefghijklmnopqrstuvwxyzABCDEF";
   go_assert(chars[32] == '\0');
   std::string ret(prefix);
   unsigned int b = 0;
@@ -2995,9 +3003,8 @@
   context.set_is_const();
   Bexpression* bval = val->get_backend(&context);
 
-  std::string asm_name(go_selectively_encode_id(sym_name));
   Btype *btype = val->type()->get_backend(gogo);
-  Bvariable* ret = gogo->backend()->implicit_variable(sym_name, asm_name,
+  Bvariable* ret = gogo->backend()->implicit_variable(sym_name, "",
 						      btype, false, true,
 						      true, 0);
   gogo->backend()->implicit_variable_set_init(ret, sym_name, btype, false,
@@ -8092,11 +8099,9 @@
   Btype* barray_type = gogo->backend()->array_type(buint8_type, blength);
 
   std::string zname = Map_type::zero_value->name();
-  std::string asm_name(go_selectively_encode_id(zname));
   Bvariable* zvar =
-      gogo->backend()->implicit_variable(zname, asm_name,
-                                         barray_type, false, false, true,
-					 Map_type::zero_value_align);
+      gogo->backend()->implicit_variable(zname, "", barray_type, false, false,
+					 true, Map_type::zero_value_align);
   gogo->backend()->implicit_variable_set_init(zvar, zname, barray_type,
 					      false, false, true, NULL);
   return zvar;
diff --git a/go/types.h b/go/types.h
index 9ac8516..5965d5a 100644
--- a/go/types.h
+++ b/go/types.h
@@ -49,6 +49,7 @@
 class Translate_context;
 class Export;
 class Import;
+class Backend_name;
 class Btype;
 class Bexpression;
 class Bvariable;
@@ -1008,11 +1009,11 @@
   std::string
   reflection(Gogo*) const;
 
-  // Return a mangled name for the type.  This is a name which can be
-  // used in assembler code.  Identical types should have the same
-  // manged name.
-  std::string
-  mangled_name(Gogo*) const;
+  // Add the backend name for the type to BNAME.  This will add one or
+  // two name components.  Identical types should have the same
+  // backend name.
+  void
+  backend_name(Gogo*, Backend_name* bname) const;
 
   // If the size of the type can be determined, set *PSIZE to the size
   // in bytes and return true.  Otherwise, return false.  This queries
@@ -1066,12 +1067,11 @@
   // Write the equal function for a type.
   void
   write_equal_function(Gogo*, Named_type*, int64_t size,
-		       const std::string& equal_name,
-		       Function_type* equal_fntype);
+		       const Backend_name*, Function_type* equal_fntype);
 
   // Write the hash function for a type.
   void
-  write_hash_function(Gogo*, int64_t size, const std::string& hash_name,
+  write_hash_function(Gogo*, int64_t size, const Backend_name*,
 		      Function_type* hash_fntype);
 
   // Return the alignment required by the memequalN function.
@@ -3557,10 +3557,10 @@
   void
   append_reflection_type_name(Gogo*, bool use_alias, std::string*) const;
 
-  // Append the mangled type name as for Type::append_mangled_name,
+  // Append the symbol type name as for Type::append_mangled_name,
   // but if USE_ALIAS use the alias name rather than the alias target.
   void
-  append_mangled_type_name(Gogo*, bool use_alias, std::string*) const;
+  append_symbol_type_name(Gogo*, bool use_alias, std::string*) const;
 
   // Import a named type.
   static void
diff --git a/libgo/configure b/libgo/configure
index e7379f8..1d7d007 100755
--- a/libgo/configure
+++ b/libgo/configure
@@ -2551,7 +2551,7 @@
 ac_config_headers="$ac_config_headers config.h"
 
 
-libtool_VERSION=17:0:0
+libtool_VERSION=18:0:0
 
 
 # Default to --enable-multilib
diff --git a/libgo/configure.ac b/libgo/configure.ac
index 80537f5..5013f41 100644
--- a/libgo/configure.ac
+++ b/libgo/configure.ac
@@ -10,7 +10,7 @@
 AC_CONFIG_SRCDIR(Makefile.am)
 AC_CONFIG_HEADER(config.h)
 
-libtool_VERSION=17:0:0
+libtool_VERSION=18:0:0
 AC_SUBST(libtool_VERSION)
 
 AM_ENABLE_MULTILIB(, ..)
diff --git a/libgo/go/cmd/cgo/out.go b/libgo/go/cmd/cgo/out.go
index 2ab8425..0544139 100644
--- a/libgo/go/cmd/cgo/out.go
+++ b/libgo/go/cmd/cgo/out.go
@@ -191,7 +191,7 @@
 			panic(fmt.Errorf("invalid var kind %q", n.Kind))
 		}
 		if *gccgo {
-			fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle)
+			fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, gccgoToSymbol(n.Mangle))
 			fmt.Fprintf(&gccgoInit, "\t%s = &%s;\n", n.Mangle, n.C)
 			fmt.Fprintf(fc, "\n")
 		}
@@ -1168,7 +1168,7 @@
 		// will not be able to link against it from the C
 		// code.
 		goName := "Cgoexp_" + exp.ExpName
-		fmt.Fprintf(fgcc, `extern %s %s %s __asm__("%s.%s");`, cRet, goName, cParams, gccgoSymbolPrefix, goName)
+		fmt.Fprintf(fgcc, `extern %s %s %s __asm__("%s.%s");`, cRet, goName, cParams, gccgoSymbolPrefix, gccgoToSymbol(goName))
 		fmt.Fprint(fgcc, "\n")
 
 		fmt.Fprint(fgcc, "\nCGO_NO_SANITIZE_THREAD\n")
@@ -1202,7 +1202,7 @@
 		fmt.Fprint(fgcc, "}\n")
 
 		// Dummy declaration for _cgo_main.c
-		fmt.Fprintf(fm, `char %s[1] __asm__("%s.%s");`, goName, gccgoSymbolPrefix, goName)
+		fmt.Fprintf(fm, `char %s[1] __asm__("%s.%s");`, goName, gccgoSymbolPrefix, gccgoToSymbol(goName))
 		fmt.Fprint(fm, "\n")
 
 		// For gccgo we use a wrapper function in Go, in order
@@ -1286,9 +1286,8 @@
 	fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog())
 }
 
-// gccgoPkgpathToSymbol converts a package path to a mangled packagepath
-// symbol.
-func gccgoPkgpathToSymbol(ppath string) string {
+// gccgoToSymbol converts a name to a mangled symbol for gccgo.
+func gccgoToSymbol(ppath string) string {
 	if gccgoMangler == nil {
 		var err error
 		cmd := os.Getenv("GCCGO")
@@ -1313,12 +1312,12 @@
 	}
 
 	if *gccgopkgpath != "" {
-		return gccgoPkgpathToSymbol(*gccgopkgpath)
+		return gccgoToSymbol(*gccgopkgpath)
 	}
 	if *gccgoprefix == "" && p.PackageName == "main" {
 		return "main"
 	}
-	prefix := gccgoPkgpathToSymbol(*gccgoprefix)
+	prefix := gccgoToSymbol(*gccgoprefix)
 	if prefix == "" {
 		prefix = "go"
 	}
@@ -1710,8 +1709,12 @@
 `
 
 func (p *Package) cPrologGccgo() string {
-	return strings.Replace(strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1),
-		"GCCGOSYMBOLPREF", p.gccgoSymbolPrefix(), -1)
+	r := strings.NewReplacer(
+		"PREFIX", cPrefix,
+		"GCCGOSYMBOLPREF", p.gccgoSymbolPrefix(),
+		"_cgoCheckPointer", gccgoToSymbol("_cgoCheckPointer"),
+		"_cgoCheckResult", gccgoToSymbol("_cgoCheckResult"))
+	return r.Replace(cPrologGccgo)
 }
 
 const cPrologGccgo = `
diff --git a/libgo/go/cmd/internal/pkgpath/pkgpath.go b/libgo/go/cmd/internal/pkgpath/pkgpath.go
index 0b24468..40a040a 100644
--- a/libgo/go/cmd/internal/pkgpath/pkgpath.go
+++ b/libgo/go/cmd/internal/pkgpath/pkgpath.go
@@ -50,9 +50,12 @@
 		return nil, err
 	}
 
-	// New mangling: expect go.l..u00e4ufer.Run
-	// Old mangling: expect go.l__ufer.Run
-	if bytes.Contains(buf, []byte("go.l..u00e4ufer.Run")) {
+	// Original mangling: go.l__ufer.Run
+	// Mangling v2: go.l..u00e4ufer.Run
+	// Mangling v3: go_0l_u00e4ufer.Run
+	if bytes.Contains(buf, []byte("go_0l_u00e4ufer.Run")) {
+		return toSymbolV3, nil
+	} else if bytes.Contains(buf, []byte("go.l..u00e4ufer.Run")) {
 		return toSymbolV2, nil
 	} else if bytes.Contains(buf, []byte("go.l__ufer.Run")) {
 		return toSymbolV1, nil
@@ -82,7 +85,7 @@
 	return strings.Map(clean, ppath)
 }
 
-// toSymbolV2 converts a package path using the newer mangling scheme.
+// toSymbolV2 converts a package path using the second mangling scheme.
 func toSymbolV2(ppath string) string {
 	// This has to build at boostrap time, so it has to build
 	// with Go 1.4, so we don't use strings.Builder.
@@ -112,3 +115,60 @@
 	}
 	return string(bsl)
 }
+
+// v3UnderscoreCodes maps from a character that supports an underscore
+// encoding to the underscore encoding character.
+var v3UnderscoreCodes = map[byte]byte{
+	'_': '_',
+	'.': '0',
+	'/': '1',
+	'*': '2',
+	',': '3',
+	'{': '4',
+	'}': '5',
+	'[': '6',
+	']': '7',
+	'(': '8',
+	')': '9',
+	'"': 'a',
+	' ': 'b',
+	';': 'c',
+}
+
+// toSymbolV3 converts a package path using the third mangling scheme.
+func toSymbolV3(ppath string) string {
+	// This has to build at boostrap time, so it has to build
+	// with Go 1.4, so we don't use strings.Builder.
+	bsl := make([]byte, 0, len(ppath))
+	changed := false
+	for _, c := range ppath {
+		if ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') {
+			bsl = append(bsl, byte(c))
+			continue
+		}
+
+		if c < 0x80 {
+			if u, ok := v3UnderscoreCodes[byte(c)]; ok {
+				bsl = append(bsl, '_', u)
+				changed = true
+				continue
+			}
+		}
+
+		var enc string
+		switch {
+		case c < 0x80:
+			enc = fmt.Sprintf("_x%02x", c)
+		case c < 0x10000:
+			enc = fmt.Sprintf("_u%04x", c)
+		default:
+			enc = fmt.Sprintf("_U%08x", c)
+		}
+		bsl = append(bsl, enc...)
+		changed = true
+	}
+	if !changed {
+		return ppath
+	}
+	return string(bsl)
+}
diff --git a/libgo/go/cmd/internal/pkgpath/pkgpath_test.go b/libgo/go/cmd/internal/pkgpath/pkgpath_test.go
index 7355f81..232e803 100644
--- a/libgo/go/cmd/internal/pkgpath/pkgpath_test.go
+++ b/libgo/go/cmd/internal/pkgpath/pkgpath_test.go
@@ -24,6 +24,9 @@
 	case "v2":
 		os.Stdout.WriteString(`.string	"go.l..u00e4ufer.Run"`)
 		os.Exit(0)
+	case "v3":
+		os.Stdout.WriteString(`.string	"go_0l_u00e4ufer.Run"`)
+		os.Exit(0)
 	case "error":
 		os.Stdout.WriteString(`unknown string`)
 		os.Exit(0)
@@ -46,6 +49,10 @@
 			mangled: "p..u00e4..u4e16..U0001f703",
 		},
 		{
+			env:     "v3",
+			mangled: "p_u00e4_u4e16_U0001f703",
+		},
+		{
 			env:  "error",
 			fail: true,
 		},
@@ -75,32 +82,37 @@
 }
 
 var symbolTests = []struct {
-	input, v1, v2 string
+	input, v1, v2, v3 string
 }{
 	{
 		"",
 		"",
 		"",
+		"",
 	},
 	{
 		"bytes",
 		"bytes",
 		"bytes",
+		"bytes",
 	},
 	{
 		"net/http",
 		"net_http",
 		"net..z2fhttp",
+		"net_1http",
 	},
 	{
 		"golang.org/x/net/http",
 		"golang_org_x_net_http",
 		"golang.x2eorg..z2fx..z2fnet..z2fhttp",
+		"golang_0org_1x_1net_1http",
 	},
 	{
 		"päδΈ–.πŸœƒ",
 		"p____",
 		"p..u00e4..u4e16.x2e..U0001f703",
+		"p_u00e4_u4e16_0_U0001f703",
 	},
 }
 
@@ -119,3 +131,11 @@
 		}
 	}
 }
+
+func TestV3(t *testing.T) {
+	for _, test := range symbolTests {
+		if got, want := toSymbolV3(test.input), test.v3; got != want {
+			t.Errorf("toSymbolV3(%q) = %q, want %q", test.input, got, want)
+		}
+	}
+}
diff --git a/libgo/go/go/internal/srcimporter/srcimporter.go b/libgo/go/go/internal/srcimporter/srcimporter.go
index 90bb3a9..3a2ca9a 100644
--- a/libgo/go/go/internal/srcimporter/srcimporter.go
+++ b/libgo/go/go/internal/srcimporter/srcimporter.go
@@ -262,5 +262,5 @@
 	return filepath.Join(elem...)
 }
 
-//go:linkname setUsesCgo go/types.srcimporter_setUsesCgo
+//go:linkname setUsesCgo go_1types.srcimporter__setUsesCgo
 func setUsesCgo(conf *types.Config)
diff --git a/libgo/go/internal/bytealg/bytealg.c b/libgo/go/internal/bytealg/bytealg.c
index 969732d..6c08340 100644
--- a/libgo/go/internal/bytealg/bytealg.c
+++ b/libgo/go/internal/bytealg/bytealg.c
@@ -38,7 +38,7 @@
 #endif
 
 intgo Compare(struct __go_open_array, struct __go_open_array)
-  __asm__(GOSYM_PREFIX "internal..z2fbytealg.Compare")
+  __asm__(GOSYM_PREFIX "internal_1bytealg.Compare")
   __attribute__((no_split_stack));
 
 intgo Compare(struct __go_open_array a, struct __go_open_array b)
@@ -69,7 +69,7 @@
 }
 
 intgo IndexByte(struct __go_open_array, byte)
-  __asm__(GOSYM_PREFIX "internal..z2fbytealg.IndexByte")
+  __asm__(GOSYM_PREFIX "internal_1bytealg.IndexByte")
   __attribute__((no_split_stack));
 
 intgo IndexByte(struct __go_open_array b, byte c)
@@ -85,7 +85,7 @@
 
 
 intgo IndexByteString(String, byte)
-  __asm__(GOSYM_PREFIX "internal..z2fbytealg.IndexByteString")
+  __asm__(GOSYM_PREFIX "internal_1bytealg.IndexByteString")
   __attribute__((no_split_stack));
 
 intgo IndexByteString(String s, byte c)
@@ -100,7 +100,7 @@
 }
 
 intgo Index(struct __go_open_array, struct __go_open_array)
-  __asm__(GOSYM_PREFIX "internal..z2fbytealg.Index")
+  __asm__(GOSYM_PREFIX "internal_1bytealg.Index")
   __attribute__((no_split_stack));
 
 intgo Index(struct __go_open_array a, struct __go_open_array b)
@@ -115,7 +115,7 @@
 }
 
 intgo IndexString(String, String)
-  __asm__(GOSYM_PREFIX "internal..z2fbytealg.IndexString")
+  __asm__(GOSYM_PREFIX "internal_1bytealg.IndexString")
   __attribute__((no_split_stack));
 
 intgo IndexString(String a, String b)
diff --git a/libgo/go/internal/cpu/cpu_gccgo.c b/libgo/go/internal/cpu/cpu_gccgo.c
index 6b40f01..da26f4d 100644
--- a/libgo/go/internal/cpu/cpu_gccgo.c
+++ b/libgo/go/internal/cpu/cpu_gccgo.c
@@ -21,7 +21,7 @@
 };
 
 struct cpuid_ret cpuid(uint32_t, uint32_t)
-  __asm__(GOSYM_PREFIX "internal..z2fcpu.cpuid")
+  __asm__(GOSYM_PREFIX "internal_1cpu.cpuid")
   __attribute__((no_split_stack));
 
 struct cpuid_ret cpuid(uint32_t eaxArg, uint32_t ecxArg) {
@@ -45,7 +45,7 @@
 };
 
 struct xgetbv_ret xgetbv(void)
-  __asm__(GOSYM_PREFIX "internal..z2fcpu.xgetbv")
+  __asm__(GOSYM_PREFIX "internal_1cpu.xgetbv")
   __attribute__((no_split_stack));
 
 #pragma GCC push_options
@@ -82,7 +82,7 @@
 };
 
 struct facilityList stfle(void)
-  __asm__(GOSYM_PREFIX "internal..z2fcpu.stfle")
+  __asm__(GOSYM_PREFIX "internal_1cpu.stfle")
   __attribute__((no_split_stack));
 
 struct facilityList stfle(void) {
@@ -96,7 +96,7 @@
 }
 
 struct queryResult kmQuery(void)
-  __asm__(GOSYM_PREFIX "internal..z2fcpu.kmQuery")
+  __asm__(GOSYM_PREFIX "internal_1cpu.kmQuery")
   __attribute__((no_split_stack));
 
 struct queryResult kmQuery() {
@@ -110,7 +110,7 @@
 }
 
 struct queryResult kmcQuery(void)
-  __asm__(GOSYM_PREFIX "internal..z2fcpu.kmcQuery")
+  __asm__(GOSYM_PREFIX "internal_1cpu.kmcQuery")
   __attribute__((no_split_stack));
 
 struct queryResult kmcQuery() {
@@ -125,7 +125,7 @@
 }
 
 struct queryResult kmctrQuery(void)
-  __asm__(GOSYM_PREFIX "internal..z2fcpu.kmctrQuery")
+  __asm__(GOSYM_PREFIX "internal_1cpu.kmctrQuery")
   __attribute__((no_split_stack));
 
 struct queryResult kmctrQuery() {
@@ -140,7 +140,7 @@
 }
 
 struct queryResult kmaQuery(void)
-  __asm__(GOSYM_PREFIX "internal..z2fcpu.kmaQuery")
+  __asm__(GOSYM_PREFIX "internal_1cpu.kmaQuery")
   __attribute__((no_split_stack));
 
 struct queryResult kmaQuery() {
@@ -155,7 +155,7 @@
 }
 
 struct queryResult kimdQuery(void)
-  __asm__(GOSYM_PREFIX "internal..z2fcpu.kimdQuery")
+  __asm__(GOSYM_PREFIX "internal_1cpu.kimdQuery")
   __attribute__((no_split_stack));
 
 struct queryResult kimdQuery() {
@@ -170,7 +170,7 @@
 }
 
 struct queryResult klmdQuery(void)
-  __asm__(GOSYM_PREFIX "internal..z2fcpu.klmdQuery")
+  __asm__(GOSYM_PREFIX "internal_1cpu.klmdQuery")
   __attribute__((no_split_stack));
 
 struct queryResult klmdQuery() {
@@ -185,7 +185,7 @@
 }
 
 struct queryResult kdsaQuery(void)
-  __asm__(GOSYM_PREFIX "internal..z2fcpu.kdsaQuery")
+  __asm__(GOSYM_PREFIX "internal_1cpu.kdsaQuery")
   __attribute__((no_split_stack));
 
 struct queryResult kdsaQuery() {
diff --git a/libgo/go/log/syslog/syslog_c.c b/libgo/go/log/syslog/syslog_c.c
index 36e7694..329df40 100644
--- a/libgo/go/log/syslog/syslog_c.c
+++ b/libgo/go/log/syslog/syslog_c.c
@@ -12,7 +12,7 @@
    can't represent a C varargs function in Go.  */
 
 void syslog_c(intgo, const char*)
-  __asm__ (GOSYM_PREFIX "log..z2fsyslog.syslog_c");
+  __asm__ (GOSYM_PREFIX "log_1syslog.syslog_c");
 
 void
 syslog_c (intgo priority, const char *msg)
diff --git a/libgo/go/runtime/atomic_pointer.go b/libgo/go/runtime/atomic_pointer.go
index 49b0f2b..0295913 100644
--- a/libgo/go/runtime/atomic_pointer.go
+++ b/libgo/go/runtime/atomic_pointer.go
@@ -39,10 +39,10 @@
 // We cannot just call the runtime routines, because the race detector expects
 // to be able to intercept the sync/atomic forms but not the runtime forms.
 
-//go:linkname sync_atomic_StoreUintptr sync..z2fatomic.StoreUintptr
+//go:linkname sync_atomic_StoreUintptr sync_1atomic.StoreUintptr
 func sync_atomic_StoreUintptr(ptr *uintptr, new uintptr)
 
-//go:linkname sync_atomic_StorePointer sync..z2fatomic.StorePointer
+//go:linkname sync_atomic_StorePointer sync_1atomic.StorePointer
 //go:nosplit
 func sync_atomic_StorePointer(ptr *unsafe.Pointer, new unsafe.Pointer) {
 	if writeBarrier.enabled {
@@ -51,10 +51,10 @@
 	sync_atomic_StoreUintptr((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
 }
 
-//go:linkname sync_atomic_SwapUintptr sync..z2fatomic.SwapUintptr
+//go:linkname sync_atomic_SwapUintptr sync_1atomic.SwapUintptr
 func sync_atomic_SwapUintptr(ptr *uintptr, new uintptr) uintptr
 
-//go:linkname sync_atomic_SwapPointer sync..z2fatomic.SwapPointer
+//go:linkname sync_atomic_SwapPointer sync_1atomic.SwapPointer
 //go:nosplit
 func sync_atomic_SwapPointer(ptr *unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer {
 	if writeBarrier.enabled {
@@ -64,10 +64,10 @@
 	return old
 }
 
-//go:linkname sync_atomic_CompareAndSwapUintptr sync..z2fatomic.CompareAndSwapUintptr
+//go:linkname sync_atomic_CompareAndSwapUintptr sync_1atomic.CompareAndSwapUintptr
 func sync_atomic_CompareAndSwapUintptr(ptr *uintptr, old, new uintptr) bool
 
-//go:linkname sync_atomic_CompareAndSwapPointer sync..z2fatomic.CompareAndSwapPointer
+//go:linkname sync_atomic_CompareAndSwapPointer sync_1atomic.CompareAndSwapPointer
 //go:nosplit
 func sync_atomic_CompareAndSwapPointer(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool {
 	if writeBarrier.enabled {
diff --git a/libgo/go/runtime/chan.go b/libgo/go/runtime/chan.go
index b909d47..8e104f1 100644
--- a/libgo/go/runtime/chan.go
+++ b/libgo/go/runtime/chan.go
@@ -774,7 +774,7 @@
 	return int(c.qcount)
 }
 
-//go:linkname reflectlite_chanlen internal..z2freflectlite.chanlen
+//go:linkname reflectlite_chanlen internal_1reflectlite.chanlen
 func reflectlite_chanlen(c *hchan) int {
 	if c == nil {
 		return 0
diff --git a/libgo/go/runtime/cpuprof.go b/libgo/go/runtime/cpuprof.go
index d395210..43f0a67 100644
--- a/libgo/go/runtime/cpuprof.go
+++ b/libgo/go/runtime/cpuprof.go
@@ -189,7 +189,7 @@
 	panic("CPUProfile no longer available")
 }
 
-//go:linkname runtime_pprof_runtime_cyclesPerSecond runtime..z2fpprof.runtime_cyclesPerSecond
+//go:linkname runtime_pprof_runtime_cyclesPerSecond runtime_1pprof.runtime__cyclesPerSecond
 func runtime_pprof_runtime_cyclesPerSecond() int64 {
 	return tickspersecond()
 }
@@ -200,7 +200,7 @@
 // on has been returned, readProfile returns eof=true.
 // The caller must save the returned data and tags before calling readProfile again.
 //
-//go:linkname runtime_pprof_readProfile runtime..z2fpprof.readProfile
+//go:linkname runtime_pprof_readProfile runtime_1pprof.readProfile
 func runtime_pprof_readProfile() ([]uint64, []unsafe.Pointer, bool) {
 	lock(&cpuprof.lock)
 	log := cpuprof.log
diff --git a/libgo/go/runtime/debug.go b/libgo/go/runtime/debug.go
index 1202e36..ff76580 100644
--- a/libgo/go/runtime/debug.go
+++ b/libgo/go/runtime/debug.go
@@ -66,7 +66,7 @@
 // added.
 func Fieldtrack(map[string]bool)
 
-//go:linkname debug_modinfo runtime..z2fdebug.modinfo
+//go:linkname debug_modinfo runtime_1debug.modinfo
 func debug_modinfo() string {
 	return modinfo
 }
diff --git a/libgo/go/runtime/heapdump.go b/libgo/go/runtime/heapdump.go
index e8f16e9..816d93c 100644
--- a/libgo/go/runtime/heapdump.go
+++ b/libgo/go/runtime/heapdump.go
@@ -16,7 +16,7 @@
 	"unsafe"
 )
 
-//go:linkname runtime_debug_WriteHeapDump runtime..z2fdebug.WriteHeapDump
+//go:linkname runtime_debug_WriteHeapDump runtime_1debug.WriteHeapDump
 func runtime_debug_WriteHeapDump(fd uintptr) {
 	stopTheWorld("write heap dump")
 
diff --git a/libgo/go/runtime/iface.go b/libgo/go/runtime/iface.go
index 5667ddb..f9df1e0 100644
--- a/libgo/go/runtime/iface.go
+++ b/libgo/go/runtime/iface.go
@@ -505,7 +505,7 @@
 	dst.data = e.data
 }
 
-//go:linkname reflectlite_ifaceE2I internal..z2freflectlite.ifaceE2I
+//go:linkname reflectlite_ifaceE2I internal_1reflectlite.ifaceE2I
 func reflectlite_ifaceE2I(inter *interfacetype, e eface, dst *iface) {
 	t := e._type
 	if t == nil {
diff --git a/libgo/go/runtime/internal/atomic/atomic.c b/libgo/go/runtime/internal/atomic/atomic.c
index 8ae4d7b..9fed1a8 100644
--- a/libgo/go/runtime/internal/atomic/atomic.c
+++ b/libgo/go/runtime/internal/atomic/atomic.c
@@ -7,7 +7,7 @@
 #include "runtime.h"
 
 uint32_t Load (uint32_t *ptr)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Load")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Load")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -17,7 +17,7 @@
 }
 
 void *Loadp (void *ptr)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Loadp")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Loadp")
   __attribute__ ((no_split_stack));
 
 void *
@@ -27,7 +27,7 @@
 }
 
 uint8_t Load8 (uint8_t *ptr)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Load8")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Load8")
   __attribute__ ((no_split_stack));
 
 uint8_t
@@ -37,7 +37,7 @@
 }
 
 uint64_t Load64 (uint64_t *ptr)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Load64")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Load64")
   __attribute__ ((no_split_stack));
 
 uint64_t
@@ -49,7 +49,7 @@
 }
 
 uint32_t LoadAcq (uint32_t *ptr)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.LoadAcq")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.LoadAcq")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -59,7 +59,7 @@
 }
 
 uintptr_t Loaduintptr (uintptr_t *ptr)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Loaduintptr")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Loaduintptr")
   __attribute__ ((no_split_stack));
 
 uintptr_t
@@ -69,7 +69,7 @@
 }
 
 uintgo Loaduint (uintgo *ptr)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Loaduint")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Loaduint")
   __attribute__ ((no_split_stack));
 
 uintgo
@@ -79,7 +79,7 @@
 }
 
 int64_t Loadint64 (int64_t *ptr)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Loadint64")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Loadint64")
   __attribute__ ((no_split_stack));
 
 int64_t
@@ -91,7 +91,7 @@
 }
 
 uint32_t Xadd (uint32_t *ptr, int32_t delta)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xadd")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Xadd")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -101,7 +101,7 @@
 }
 
 uint64_t Xadd64 (uint64_t *ptr, int64_t delta)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xadd64")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Xadd64")
   __attribute__ ((no_split_stack));
 
 uint64_t
@@ -113,7 +113,7 @@
 }
 
 uintptr_t Xadduintptr (uintptr_t *ptr, uintptr_t delta)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xadduintptr")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Xadduintptr")
   __attribute__ ((no_split_stack));
 
 uintptr_t
@@ -123,7 +123,7 @@
 }
 
 int64_t Xaddint64 (int64_t *ptr, int64_t delta)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xaddint64")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Xaddint64")
   __attribute__ ((no_split_stack));
 
 int64_t
@@ -135,7 +135,7 @@
 }
 
 uint32_t Xchg (uint32_t *ptr, uint32_t new)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xchg")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Xchg")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -145,7 +145,7 @@
 }
 
 uint64_t Xchg64 (uint64_t *ptr, uint64_t new)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xchg64")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Xchg64")
   __attribute__ ((no_split_stack));
 
 uint64_t
@@ -157,7 +157,7 @@
 }
 
 uintptr_t Xchguintptr (uintptr_t *ptr, uintptr_t new)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xchguintptr")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Xchguintptr")
   __attribute__ ((no_split_stack));
 
 uintptr_t
@@ -167,7 +167,7 @@
 }
 
 void And8 (uint8_t *ptr, uint8_t val)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.And8")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.And8")
   __attribute__ ((no_split_stack));
 
 void
@@ -177,7 +177,7 @@
 }
 
 void Or8 (uint8_t *ptr, uint8_t val)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Or8")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Or8")
   __attribute__ ((no_split_stack));
 
 void
@@ -187,7 +187,7 @@
 }
 
 _Bool Cas (uint32_t *ptr, uint32_t old, uint32_t new)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Cas")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Cas")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -197,7 +197,7 @@
 }
 
 _Bool Cas64 (uint64_t *ptr, uint64_t old, uint64_t new)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Cas64")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Cas64")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -209,7 +209,7 @@
 }
 
 _Bool CasRel (uint32_t *ptr, uint32_t old, uint32_t new)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.CasRel")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.CasRel")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -219,7 +219,7 @@
 }
 
 _Bool Casp1 (void **ptr, void *old, void *new)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Casp1")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Casp1")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -229,7 +229,7 @@
 }
 
 _Bool Casuintptr (uintptr_t *ptr, uintptr_t old, uintptr_t new)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Casuintptr")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Casuintptr")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -239,7 +239,7 @@
 }
 
 void Store (uint32_t *ptr, uint32_t val)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Store")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Store")
   __attribute__ ((no_split_stack));
 
 void
@@ -249,7 +249,7 @@
 }
 
 void Store8 (uint8_t *ptr, uint8_t val)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Store8")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Store8")
   __attribute__ ((no_split_stack));
 
 void
@@ -259,7 +259,7 @@
 }
 
 void Store64 (uint64_t *ptr, uint64_t val)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Store64")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Store64")
   __attribute__ ((no_split_stack));
 
 void
@@ -271,7 +271,7 @@
 }
 
 void StoreRel (uint32_t *ptr, uint32_t val)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.StoreRel")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.StoreRel")
   __attribute__ ((no_split_stack));
 
 void
@@ -281,7 +281,7 @@
 }
 
 void Storeuintptr (uintptr_t *ptr, uintptr_t val)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Storeuintptr")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.Storeuintptr")
   __attribute__ ((no_split_stack));
 
 void
@@ -291,7 +291,7 @@
 }
 
 void StorepNoWB (void *ptr, void *val)
-  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.StorepNoWB")
+  __asm__ (GOSYM_PREFIX "runtime_1internal_1atomic.StorepNoWB")
   __attribute__ ((no_split_stack));
 
 void
diff --git a/libgo/go/runtime/malloc.go b/libgo/go/runtime/malloc.go
index 81351ee..feb043a 100644
--- a/libgo/go/runtime/malloc.go
+++ b/libgo/go/runtime/malloc.go
@@ -1220,12 +1220,12 @@
 	return mallocgc(typ.size, typ, true)
 }
 
-//go:linkname reflect_unsafe_New reflect.unsafe_New
+//go:linkname reflect_unsafe_New reflect.unsafe__New
 func reflect_unsafe_New(typ *_type) unsafe.Pointer {
 	return mallocgc(typ.size, typ, true)
 }
 
-//go:linkname reflectlite_unsafe_New internal..z2freflectlite.unsafe_New
+//go:linkname reflectlite_unsafe_New internal_1reflectlite.unsafe__New
 func reflectlite_unsafe_New(typ *_type) unsafe.Pointer {
 	return mallocgc(typ.size, typ, true)
 }
@@ -1242,7 +1242,7 @@
 	return mallocgc(mem, typ, true)
 }
 
-//go:linkname reflect_unsafe_NewArray reflect.unsafe_NewArray
+//go:linkname reflect_unsafe_NewArray reflect.unsafe__NewArray
 func reflect_unsafe_NewArray(typ *_type, n int) unsafe.Pointer {
 	return newarray(typ, n)
 }
diff --git a/libgo/go/runtime/map.go b/libgo/go/runtime/map.go
index b829771..1155fee 100644
--- a/libgo/go/runtime/map.go
+++ b/libgo/go/runtime/map.go
@@ -1417,7 +1417,7 @@
 	return h.count
 }
 
-//go:linkname reflectlite_maplen internal..z2freflectlite.maplen
+//go:linkname reflectlite_maplen internal_1reflectlite.maplen
 func reflectlite_maplen(h *hmap) int {
 	if h == nil {
 		return 0
diff --git a/libgo/go/runtime/mbarrier.go b/libgo/go/runtime/mbarrier.go
index 836f85a..3bd8b34 100644
--- a/libgo/go/runtime/mbarrier.go
+++ b/libgo/go/runtime/mbarrier.go
@@ -192,7 +192,7 @@
 	typedmemmove(typ, dst, src)
 }
 
-//go:linkname reflectlite_typedmemmove internal..z2freflectlite.typedmemmove
+//go:linkname reflectlite_typedmemmove internal_1reflectlite.typedmemmove
 func reflectlite_typedmemmove(typ *_type, dst, src unsafe.Pointer) {
 	reflect_typedmemmove(typ, dst, src)
 }
diff --git a/libgo/go/runtime/mgc.go b/libgo/go/runtime/mgc.go
index 9dd7bff..72479c2 100644
--- a/libgo/go/runtime/mgc.go
+++ b/libgo/go/runtime/mgc.go
@@ -223,7 +223,7 @@
 	memstats.enablegc = true // now that runtime is initialized, GC is okay
 }
 
-//go:linkname setGCPercent runtime..z2fdebug.setGCPercent
+//go:linkname setGCPercent runtime_1debug.setGCPercent
 func setGCPercent(in int32) (out int32) {
 	// Run on the system stack since we grab the heap lock.
 	systemstack(func() {
@@ -2238,7 +2238,7 @@
 
 var poolcleanup func()
 
-//go:linkname sync_runtime_registerPoolCleanup sync.runtime_registerPoolCleanup
+//go:linkname sync_runtime_registerPoolCleanup sync.runtime__registerPoolCleanup
 func sync_runtime_registerPoolCleanup(f func()) {
 	poolcleanup = f
 }
diff --git a/libgo/go/runtime/mheap.go b/libgo/go/runtime/mheap.go
index e73ee32..755efd1 100644
--- a/libgo/go/runtime/mheap.go
+++ b/libgo/go/runtime/mheap.go
@@ -1502,7 +1502,7 @@
 	}
 }
 
-//go:linkname runtime_debug_freeOSMemory runtime..z2fdebug.freeOSMemory
+//go:linkname runtime_debug_freeOSMemory runtime_1debug.freeOSMemory
 func runtime_debug_freeOSMemory() {
 	GC()
 	systemstack(func() { mheap_.scavengeAll() })
diff --git a/libgo/go/runtime/mprof.go b/libgo/go/runtime/mprof.go
index a4b135d..afacf8f 100644
--- a/libgo/go/runtime/mprof.go
+++ b/libgo/go/runtime/mprof.go
@@ -942,7 +942,7 @@
 	return
 }
 
-//go:linkname runtime_goroutineProfileWithLabels runtime..z2fpprof.runtime_goroutineProfileWithLabels
+//go:linkname runtime_goroutineProfileWithLabels runtime_1pprof.runtime__goroutineProfileWithLabels
 func runtime_goroutineProfileWithLabels(p []StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
 	return goroutineProfileWithLabels(p, labels)
 }
diff --git a/libgo/go/runtime/mstats.go b/libgo/go/runtime/mstats.go
index 4e2c66c..85a0861 100644
--- a/libgo/go/runtime/mstats.go
+++ b/libgo/go/runtime/mstats.go
@@ -468,7 +468,7 @@
 	stats.StackSys += stats.StackInuse
 }
 
-//go:linkname readGCStats runtime..z2fdebug.readGCStats
+//go:linkname readGCStats runtime_1debug.readGCStats
 func readGCStats(pauses *[]uint64) {
 	systemstack(func() {
 		readGCStats_m(pauses)
diff --git a/libgo/go/runtime/net_plan9.go b/libgo/go/runtime/net_plan9.go
index 907c319..38ff5a4 100644
--- a/libgo/go/runtime/net_plan9.go
+++ b/libgo/go/runtime/net_plan9.go
@@ -8,12 +8,12 @@
 	_ "unsafe"
 )
 
-//go:linkname runtime_ignoreHangup internal..z2fpoll.runtime_ignoreHangup
+//go:linkname runtime_ignoreHangup internal_1poll.runtime__ignoreHangup
 func runtime_ignoreHangup() {
 	getg().m.ignoreHangup = true
 }
 
-//go:linkname runtime_unignoreHangup internal..z2fpoll.runtime_unignoreHangup
+//go:linkname runtime_unignoreHangup internal_1poll.runtime__unignoreHangup
 func runtime_unignoreHangup(sig string) {
 	getg().m.ignoreHangup = false
 }
diff --git a/libgo/go/runtime/netpoll.go b/libgo/go/runtime/netpoll.go
index 72a136d..da00b57 100644
--- a/libgo/go/runtime/netpoll.go
+++ b/libgo/go/runtime/netpoll.go
@@ -113,7 +113,7 @@
 	netpollWaiters uint32
 )
 
-//go:linkname poll_runtime_pollServerInit internal..z2fpoll.runtime_pollServerInit
+//go:linkname poll_runtime_pollServerInit internal_1poll.runtime__pollServerInit
 func poll_runtime_pollServerInit() {
 	netpollGenericInit()
 }
@@ -134,7 +134,7 @@
 	return atomic.Load(&netpollInited) != 0
 }
 
-//go:linkname poll_runtime_isPollServerDescriptor internal..z2fpoll.runtime_isPollServerDescriptor
+//go:linkname poll_runtime_isPollServerDescriptor internal_1poll.runtime__isPollServerDescriptor
 
 // poll_runtime_isPollServerDescriptor reports whether fd is a
 // descriptor being used by netpoll.
@@ -142,7 +142,7 @@
 	return netpollIsPollDescriptor(fd)
 }
 
-//go:linkname poll_runtime_pollOpen internal..z2fpoll.runtime_pollOpen
+//go:linkname poll_runtime_pollOpen internal_1poll.runtime__pollOpen
 func poll_runtime_pollOpen(fd uintptr) (uintptr, int) {
 	pd := pollcache.alloc()
 	lock(&pd.lock)
@@ -169,7 +169,7 @@
 	return uintptr(unsafe.Pointer(pd)), int(errno)
 }
 
-//go:linkname poll_runtime_pollClose internal..z2fpoll.runtime_pollClose
+//go:linkname poll_runtime_pollClose internal_1poll.runtime__pollClose
 func poll_runtime_pollClose(ctx uintptr) {
 	pd := (*pollDesc)(unsafe.Pointer(ctx))
 	if !pd.closing {
@@ -195,7 +195,7 @@
 // poll_runtime_pollReset, which is internal/poll.runtime_pollReset,
 // prepares a descriptor for polling in mode, which is 'r' or 'w'.
 // This returns an error code; the codes are defined above.
-//go:linkname poll_runtime_pollReset internal..z2fpoll.runtime_pollReset
+//go:linkname poll_runtime_pollReset internal_1poll.runtime__pollReset
 func poll_runtime_pollReset(ctx uintptr, mode int) int {
 	pd := (*pollDesc)(unsafe.Pointer(ctx))
 	errcode := netpollcheckerr(pd, int32(mode))
@@ -214,7 +214,7 @@
 // waits for a descriptor to be ready for reading or writing,
 // according to mode, which is 'r' or 'w'.
 // This returns an error code; the codes are defined above.
-//go:linkname poll_runtime_pollWait internal..z2fpoll.runtime_pollWait
+//go:linkname poll_runtime_pollWait internal_1poll.runtime__pollWait
 func poll_runtime_pollWait(ctx uintptr, mode int) int {
 	pd := (*pollDesc)(unsafe.Pointer(ctx))
 	errcode := netpollcheckerr(pd, int32(mode))
@@ -237,7 +237,7 @@
 	return pollNoError
 }
 
-//go:linkname poll_runtime_pollWaitCanceled internal..z2fpoll.runtime_pollWaitCanceled
+//go:linkname poll_runtime_pollWaitCanceled internal_1poll.runtime__pollWaitCanceled
 func poll_runtime_pollWaitCanceled(ctx uintptr, mode int) {
 	pd := (*pollDesc)(unsafe.Pointer(ctx))
 	// This function is used only on windows after a failed attempt to cancel
@@ -246,7 +246,7 @@
 	}
 }
 
-//go:linkname poll_runtime_pollSetDeadline internal..z2fpoll.runtime_pollSetDeadline
+//go:linkname poll_runtime_pollSetDeadline internal_1poll.runtime__pollSetDeadline
 func poll_runtime_pollSetDeadline(ctx uintptr, d int64, mode int) {
 	pd := (*pollDesc)(unsafe.Pointer(ctx))
 	lock(&pd.lock)
@@ -330,7 +330,7 @@
 	}
 }
 
-//go:linkname poll_runtime_pollUnblock internal..z2fpoll.runtime_pollUnblock
+//go:linkname poll_runtime_pollUnblock internal_1poll.runtime__pollUnblock
 func poll_runtime_pollUnblock(ctx uintptr) {
 	pd := (*pollDesc)(unsafe.Pointer(ctx))
 	lock(&pd.lock)
diff --git a/libgo/go/runtime/pprof/mprof_test.go b/libgo/go/runtime/pprof/mprof_test.go
index 625ab7d..83bf572 100644
--- a/libgo/go/runtime/pprof/mprof_test.go
+++ b/libgo/go/runtime/pprof/mprof_test.go
@@ -91,35 +91,35 @@
 		stk    []string
 		legacy string
 	}{{
-		stk: []string{"pprof.allocatePersistent1K", "runtime/pprof.TestMemoryProfiler"},
+		stk: []string{"runtime/pprof.allocatePersistent1K", "runtime/pprof.TestMemoryProfiler"},
 		legacy: fmt.Sprintf(`%v: %v \[%v: %v\] @ 0x[0-9,a-f x]+
-#	0x[0-9,a-f]+	pprof\.allocatePersistent1K\+0x[0-9,a-f]+	.*/mprof_test\.go:47
+#	0x[0-9,a-f]+	runtime/pprof\.allocatePersistent1K\+0x[0-9,a-f]+	.*/mprof_test\.go:47
 #	0x[0-9,a-f]+	runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/mprof_test\.go:82
 `, 32*memoryProfilerRun, 1024*memoryProfilerRun, 32*memoryProfilerRun, 1024*memoryProfilerRun),
 	}, {
-		stk: []string{"pprof.allocateTransient1M", "runtime/pprof.TestMemoryProfiler"},
+		stk: []string{"runtime/pprof.allocateTransient1M", "runtime/pprof.TestMemoryProfiler"},
 		legacy: fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @ 0x[0-9,a-f x]+
-#	0x[0-9,a-f]+	pprof\.allocateTransient1M\+0x[0-9,a-f]+	.*/mprof_test.go:24
+#	0x[0-9,a-f]+	runtime/pprof\.allocateTransient1M\+0x[0-9,a-f]+	.*/mprof_test.go:24
 #	0x[0-9,a-f]+	runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/mprof_test.go:79
 `, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun),
 	}, {
-		stk: []string{"pprof.allocateTransient2M", "runtime/pprof.TestMemoryProfiler"},
+		stk: []string{"runtime/pprof.allocateTransient2M", "runtime/pprof.TestMemoryProfiler"},
 		// This should start with "0: 0" but gccgo's imprecise
 		// GC means that sometimes the value is not collected.
 		legacy: fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @ 0x[0-9,a-f x]+
-#	0x[0-9,a-f]+	pprof\.allocateTransient2M\+0x[0-9,a-f]+	.*/mprof_test.go:30
+#	0x[0-9,a-f]+	runtime/pprof\.allocateTransient2M\+0x[0-9,a-f]+	.*/mprof_test.go:30
 #	0x[0-9,a-f]+	runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/mprof_test.go:80
 `, memoryProfilerRun, (2<<20)*memoryProfilerRun, memoryProfilerRun, (2<<20)*memoryProfilerRun),
 	}, {
-		stk: []string{"pprof.allocateTransient2MInline", "runtime/pprof.TestMemoryProfiler"},
+		stk: []string{"runtime/pprof.allocateTransient2MInline", "runtime/pprof.TestMemoryProfiler"},
 		legacy: fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @ 0x[0-9,a-f x]+
-#	0x[0-9,a-f]+	pprof\.allocateTransient2MInline\+0x[0-9,a-f]+	.*/mprof_test.go:34
+#	0x[0-9,a-f]+	runtime/pprof\.allocateTransient2MInline\+0x[0-9,a-f]+	.*/mprof_test.go:34
 #	0x[0-9,a-f]+	runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/mprof_test.go:81
 `, memoryProfilerRun, (4<<20)*memoryProfilerRun, memoryProfilerRun, (4<<20)*memoryProfilerRun),
 	}, {
-		stk: []string{"pprof.allocateReflectTransient"},
+		stk: []string{"runtime/pprof.allocateReflectTransient"},
 		legacy: fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @( 0x[0-9,a-f]+)+
-#	0x[0-9,a-f]+	pprof\.allocateReflectTransient\+0x[0-9,a-f]+	.*/mprof_test.go:55
+#	0x[0-9,a-f]+	runtime/pprof\.allocateReflectTransient\+0x[0-9,a-f]+	.*/mprof_test.go:55
 `, memoryProfilerRun, (3<<20)*memoryProfilerRun, memoryProfilerRun, (3<<20)*memoryProfilerRun),
 	}}
 
diff --git a/libgo/go/runtime/pprof/pprof_test.go b/libgo/go/runtime/pprof/pprof_test.go
index ff86bce..7adf891 100644
--- a/libgo/go/runtime/pprof/pprof_test.go
+++ b/libgo/go/runtime/pprof/pprof_test.go
@@ -958,8 +958,8 @@
 
 		stks := stacks(p)
 		for _, want := range [][]string{
-			// {"sync.(*Mutex).Unlock", "pprof.blockMutex.func1"},
-			{"sync.Mutex.Unlock", "pprof.blockMutex..func1"},
+			// {"sync.(*Mutex).Unlock", "runtime/pprof.blockMutex.func1"},
+			{"sync.Mutex.Unlock", "runtime/pprof.blockMutex..func1"},
 		} {
 			if !containsStack(stks, want) {
 				t.Errorf("No matching stack entry for %+v", want)
diff --git a/libgo/go/runtime/preempt.go b/libgo/go/runtime/preempt.go
index 9a78bcf..8452076 100644
--- a/libgo/go/runtime/preempt.go
+++ b/libgo/go/runtime/preempt.go
@@ -360,7 +360,7 @@
 	}
 	name := f.Name()
 	if hasPrefix(name, "runtime.") ||
-		hasPrefix(name, "runtime..z2finternal..z2f") ||
+		hasPrefix(name, "runtime_1internal_1") ||
 		hasPrefix(name, "reflect.") {
 		// For now we never async preempt the runtime or
 		// anything closely tied to the runtime. Known issues
diff --git a/libgo/go/runtime/proc.go b/libgo/go/runtime/proc.go
index 0ca6c02..6c72050 100644
--- a/libgo/go/runtime/proc.go
+++ b/libgo/go/runtime/proc.go
@@ -263,7 +263,7 @@
 }
 
 // os_beforeExit is called from os.Exit(0).
-//go:linkname os_beforeExit os.runtime_beforeExit
+//go:linkname os_beforeExit os.runtime__beforeExit
 func os_beforeExit() {
 	if raceenabled {
 		racefini()
@@ -3305,7 +3305,7 @@
 }
 
 // Called from syscall package before fork.
-//go:linkname syscall_runtime_BeforeFork syscall.runtime_BeforeFork
+//go:linkname syscall_runtime_BeforeFork syscall.runtime__BeforeFork
 //go:nosplit
 func syscall_runtime_BeforeFork() {
 	systemstack(beforefork)
@@ -3320,7 +3320,7 @@
 }
 
 // Called from syscall package after fork in parent.
-//go:linkname syscall_runtime_AfterFork syscall.runtime_AfterFork
+//go:linkname syscall_runtime_AfterFork syscall.runtime__AfterFork
 //go:nosplit
 func syscall_runtime_AfterFork() {
 	systemstack(afterfork)
@@ -3338,7 +3338,7 @@
 // temporarily sharing address space with the parent process, this must
 // not change any global variables or calling into C code that may do so.
 //
-//go:linkname syscall_runtime_AfterForkInChild syscall.runtime_AfterForkInChild
+//go:linkname syscall_runtime_AfterForkInChild syscall.runtime__AfterForkInChild
 //go:nosplit
 //go:nowritebarrierrec
 func syscall_runtime_AfterForkInChild() {
@@ -3363,7 +3363,7 @@
 var pendingPreemptSignals uint32
 
 // Called from syscall package before Exec.
-//go:linkname syscall_runtime_BeforeExec syscall.runtime_BeforeExec
+//go:linkname syscall_runtime_BeforeExec syscall.runtime__BeforeExec
 func syscall_runtime_BeforeExec() {
 	// Prevent thread creation during exec.
 	execLock.lock()
@@ -3378,7 +3378,7 @@
 }
 
 // Called from syscall package after Exec.
-//go:linkname syscall_runtime_AfterExec syscall.runtime_AfterExec
+//go:linkname syscall_runtime_AfterExec syscall.runtime__AfterExec
 func syscall_runtime_AfterExec() {
 	execLock.unlock()
 }
@@ -5165,7 +5165,7 @@
 	return gp
 }
 
-//go:linkname setMaxThreads runtime..z2fdebug.setMaxThreads
+//go:linkname setMaxThreads runtime_1debug.setMaxThreads
 func setMaxThreads(in int) (out int) {
 	lock(&sched.lock)
 	out = int(sched.maxmcount)
@@ -5199,32 +5199,32 @@
 	_g_.m.locks--
 }
 
-//go:linkname sync_runtime_procPin sync.runtime_procPin
+//go:linkname sync_runtime_procPin sync.runtime__procPin
 //go:nosplit
 func sync_runtime_procPin() int {
 	return procPin()
 }
 
-//go:linkname sync_runtime_procUnpin sync.runtime_procUnpin
+//go:linkname sync_runtime_procUnpin sync.runtime__procUnpin
 //go:nosplit
 func sync_runtime_procUnpin() {
 	procUnpin()
 }
 
-//go:linkname sync_atomic_runtime_procPin sync..z2fatomic.runtime_procPin
+//go:linkname sync_atomic_runtime_procPin sync_1atomic.runtime__procPin
 //go:nosplit
 func sync_atomic_runtime_procPin() int {
 	return procPin()
 }
 
-//go:linkname sync_atomic_runtime_procUnpin sync..z2fatomic.runtime_procUnpin
+//go:linkname sync_atomic_runtime_procUnpin sync_1atomic.runtime__procUnpin
 //go:nosplit
 func sync_atomic_runtime_procUnpin() {
 	procUnpin()
 }
 
 // Active spinning for sync.Mutex.
-//go:linkname sync_runtime_canSpin sync.runtime_canSpin
+//go:linkname sync_runtime_canSpin sync.runtime__canSpin
 //go:nosplit
 func sync_runtime_canSpin(i int) bool {
 	// sync.Mutex is cooperative, so we are conservative with spinning.
@@ -5241,7 +5241,7 @@
 	return true
 }
 
-//go:linkname sync_runtime_doSpin sync.runtime_doSpin
+//go:linkname sync_runtime_doSpin sync.runtime__doSpin
 //go:nosplit
 func sync_runtime_doSpin() {
 	procyield(active_spin_cnt)
diff --git a/libgo/go/runtime/proflabel.go b/libgo/go/runtime/proflabel.go
index fc655cc..1e1f3bf 100644
--- a/libgo/go/runtime/proflabel.go
+++ b/libgo/go/runtime/proflabel.go
@@ -8,7 +8,7 @@
 
 var labelSync uintptr
 
-//go:linkname runtime_setProfLabel runtime..z2fpprof.runtime_setProfLabel
+//go:linkname runtime_setProfLabel runtime_1pprof.runtime__setProfLabel
 func runtime_setProfLabel(labels unsafe.Pointer) {
 	// Introduce race edge for read-back via profile.
 	// This would more properly use &getg().labels as the sync address,
@@ -34,7 +34,7 @@
 	getg().labels = labels
 }
 
-//go:linkname runtime_getProfLabel runtime..z2fpprof.runtime_getProfLabel
+//go:linkname runtime_getProfLabel runtime_1pprof.runtime__getProfLabel
 func runtime_getProfLabel() unsafe.Pointer {
 	return getg().labels
 }
diff --git a/libgo/go/runtime/rdebug.go b/libgo/go/runtime/rdebug.go
index 358df11..9c43ce5 100644
--- a/libgo/go/runtime/rdebug.go
+++ b/libgo/go/runtime/rdebug.go
@@ -11,14 +11,14 @@
 // maxstacksize.
 var maxstacksize uintptr = 1 << 20 // enough until runtime.main sets it for real
 
-//go:linkname setMaxStack runtime..z2fdebug.setMaxStack
+//go:linkname setMaxStack runtime_1debug.setMaxStack
 func setMaxStack(in int) (out int) {
 	out = int(maxstacksize)
 	maxstacksize = uintptr(in)
 	return out
 }
 
-//go:linkname setPanicOnFault runtime..z2fdebug.setPanicOnFault
+//go:linkname setPanicOnFault runtime_1debug.setPanicOnFault
 func setPanicOnFault(new bool) (old bool) {
 	_g_ := getg()
 	old = _g_.paniconfault
diff --git a/libgo/go/runtime/runtime.go b/libgo/go/runtime/runtime.go
index abc5eab..5af28ae 100644
--- a/libgo/go/runtime/runtime.go
+++ b/libgo/go/runtime/runtime.go
@@ -51,13 +51,13 @@
 var envs []string
 var argslice []string
 
-//go:linkname syscall_runtime_envs syscall.runtime_envs
+//go:linkname syscall_runtime_envs syscall.runtime__envs
 func syscall_runtime_envs() []string { return append([]string{}, envs...) }
 
 //go:linkname syscall_Getpagesize syscall.Getpagesize
 func syscall_Getpagesize() int { return int(physPageSize) }
 
-//go:linkname os_runtime_args os.runtime_args
+//go:linkname os_runtime_args os.runtime__args
 func os_runtime_args() []string { return append([]string{}, argslice...) }
 
 //go:linkname syscall_Exit syscall.Exit
diff --git a/libgo/go/runtime/runtime1.go b/libgo/go/runtime/runtime1.go
index a8a53d3..39969d1 100644
--- a/libgo/go/runtime/runtime1.go
+++ b/libgo/go/runtime/runtime1.go
@@ -398,7 +398,7 @@
 	traceback_env = traceback_cache
 }
 
-//go:linkname setTraceback runtime..z2fdebug.SetTraceback
+//go:linkname setTraceback runtime_1debug.SetTraceback
 func setTraceback(level string) {
 	var t uint32
 	switch level {
diff --git a/libgo/go/runtime/sema.go b/libgo/go/runtime/sema.go
index c1418b3..9a28880 100644
--- a/libgo/go/runtime/sema.go
+++ b/libgo/go/runtime/sema.go
@@ -51,27 +51,27 @@
 	pad  [cpu.CacheLinePadSize - unsafe.Sizeof(semaRoot{})]byte
 }
 
-//go:linkname sync_runtime_Semacquire sync.runtime_Semacquire
+//go:linkname sync_runtime_Semacquire sync.runtime__Semacquire
 func sync_runtime_Semacquire(addr *uint32) {
 	semacquire1(addr, false, semaBlockProfile, 0)
 }
 
-//go:linkname poll_runtime_Semacquire internal..z2fpoll.runtime_Semacquire
+//go:linkname poll_runtime_Semacquire internal_1poll.runtime__Semacquire
 func poll_runtime_Semacquire(addr *uint32) {
 	semacquire1(addr, false, semaBlockProfile, 0)
 }
 
-//go:linkname sync_runtime_Semrelease sync.runtime_Semrelease
+//go:linkname sync_runtime_Semrelease sync.runtime__Semrelease
 func sync_runtime_Semrelease(addr *uint32, handoff bool, skipframes int) {
 	semrelease1(addr, handoff, skipframes)
 }
 
-//go:linkname sync_runtime_SemacquireMutex sync.runtime_SemacquireMutex
+//go:linkname sync_runtime_SemacquireMutex sync.runtime__SemacquireMutex
 func sync_runtime_SemacquireMutex(addr *uint32, lifo bool, skipframes int) {
 	semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile, skipframes)
 }
 
-//go:linkname poll_runtime_Semrelease internal..z2fpoll.runtime_Semrelease
+//go:linkname poll_runtime_Semrelease internal_1poll.runtime__Semrelease
 func poll_runtime_Semrelease(addr *uint32) {
 	semrelease(addr)
 }
@@ -475,7 +475,7 @@
 // notifyListAdd adds the caller to a notify list such that it can receive
 // notifications. The caller must eventually call notifyListWait to wait for
 // such a notification, passing the returned ticket number.
-//go:linkname notifyListAdd sync.runtime_notifyListAdd
+//go:linkname notifyListAdd sync.runtime__notifyListAdd
 func notifyListAdd(l *notifyList) uint32 {
 	// This may be called concurrently, for example, when called from
 	// sync.Cond.Wait while holding a RWMutex in read mode.
@@ -484,7 +484,7 @@
 
 // notifyListWait waits for a notification. If one has been sent since
 // notifyListAdd was called, it returns immediately. Otherwise, it blocks.
-//go:linkname notifyListWait sync.runtime_notifyListWait
+//go:linkname notifyListWait sync.runtime__notifyListWait
 func notifyListWait(l *notifyList, t uint32) {
 	lockWithRank(&l.lock, lockRankNotifyList)
 
@@ -518,7 +518,7 @@
 }
 
 // notifyListNotifyAll notifies all entries in the list.
-//go:linkname notifyListNotifyAll sync.runtime_notifyListNotifyAll
+//go:linkname notifyListNotifyAll sync.runtime__notifyListNotifyAll
 func notifyListNotifyAll(l *notifyList) {
 	// Fast-path: if there are no new waiters since the last notification
 	// we don't need to acquire the lock.
@@ -550,7 +550,7 @@
 }
 
 // notifyListNotifyOne notifies one entry in the list.
-//go:linkname notifyListNotifyOne sync.runtime_notifyListNotifyOne
+//go:linkname notifyListNotifyOne sync.runtime__notifyListNotifyOne
 func notifyListNotifyOne(l *notifyList) {
 	// Fast-path: if there are no new waiters since the last notification
 	// we don't need to acquire the lock at all.
@@ -603,7 +603,7 @@
 	unlock(&l.lock)
 }
 
-//go:linkname notifyListCheck sync.runtime_notifyListCheck
+//go:linkname notifyListCheck sync.runtime__notifyListCheck
 func notifyListCheck(sz uintptr) {
 	if sz != unsafe.Sizeof(notifyList{}) {
 		print("runtime: bad notifyList size - sync=", sz, " runtime=", unsafe.Sizeof(notifyList{}), "\n")
@@ -611,7 +611,7 @@
 	}
 }
 
-//go:linkname sync_nanotime sync.runtime_nanotime
+//go:linkname sync_nanotime sync.runtime__nanotime
 func sync_nanotime() int64 {
 	return nanotime()
 }
diff --git a/libgo/go/runtime/sigqueue.go b/libgo/go/runtime/sigqueue.go
index 7d1028e..ed024e1 100644
--- a/libgo/go/runtime/sigqueue.go
+++ b/libgo/go/runtime/sigqueue.go
@@ -121,7 +121,7 @@
 
 // Called to receive the next queued signal.
 // Must only be called from a single goroutine at a time.
-//go:linkname signal_recv os..z2fsignal.signal_recv
+//go:linkname signal_recv os_1signal.signal__recv
 func signal_recv() uint32 {
 	for {
 		// Serve any signals from local copy.
@@ -169,7 +169,7 @@
 // the signal(s) in question, and here we are just waiting to make sure
 // that all the signals have been delivered to the user channels
 // by the os/signal package.
-//go:linkname signalWaitUntilIdle os..z2fsignal.signalWaitUntilIdle
+//go:linkname signalWaitUntilIdle os_1signal.signalWaitUntilIdle
 func signalWaitUntilIdle() {
 	// Although the signals we care about have been removed from
 	// sig.wanted, it is possible that another thread has received
@@ -189,7 +189,7 @@
 }
 
 // Must only be called from a single goroutine at a time.
-//go:linkname signal_enable os..z2fsignal.signal_enable
+//go:linkname signal_enable os_1signal.signal__enable
 func signal_enable(s uint32) {
 	if !sig.inuse {
 		// This is the first call to signal_enable. Initialize.
@@ -217,7 +217,7 @@
 }
 
 // Must only be called from a single goroutine at a time.
-//go:linkname signal_disable os..z2fsignal.signal_disable
+//go:linkname signal_disable os_1signal.signal__disable
 func signal_disable(s uint32) {
 	if s >= uint32(len(sig.wanted)*32) {
 		return
@@ -230,7 +230,7 @@
 }
 
 // Must only be called from a single goroutine at a time.
-//go:linkname signal_ignore os..z2fsignal.signal_ignore
+//go:linkname signal_ignore os_1signal.signal__ignore
 func signal_ignore(s uint32) {
 	if s >= uint32(len(sig.wanted)*32) {
 		return
@@ -257,7 +257,7 @@
 }
 
 // Checked by signal handlers.
-//go:linkname signal_ignored os..z2fsignal.signal_ignored
+//go:linkname signal_ignored os_1signal.signal__ignored
 func signal_ignored(s uint32) bool {
 	i := atomic.Load(&sig.ignored[s/32])
 	return i&(1<<(s&31)) != 0
diff --git a/libgo/go/runtime/symtab.go b/libgo/go/runtime/symtab.go
index bb0b61d..22a2b13 100644
--- a/libgo/go/runtime/symtab.go
+++ b/libgo/go/runtime/symtab.go
@@ -5,6 +5,7 @@
 package runtime
 
 import (
+	"internal/bytealg"
 	_ "unsafe" // for go:linkname
 )
 
@@ -119,7 +120,7 @@
 // runtime_expandFinalInlineFrame expands the final pc in stk to include all
 // "callers" if pc is inline.
 //
-//go:linkname runtime_expandFinalInlineFrame runtime..z2fpprof.runtime_expandFinalInlineFrame
+//go:linkname runtime_expandFinalInlineFrame runtime_1pprof.runtime__expandFinalInlineFrame
 func runtime_expandFinalInlineFrame(stk []uintptr) []uintptr {
 	if len(stk) == 0 {
 		return stk
@@ -210,42 +211,62 @@
 	return rune(result)
 }
 
-// Perform an in-place decoding on the input byte slice. This looks
-// for "..z<hex 2 >", "..u<hex x 4>" and "..U<hex x 8>" and overwrites
-// with the encoded bytes corresponding to the unicode in question.
-// Return value is the number of bytes taken by the result.
-
+// decodeIdentifier performs an in-place decoding on the input byte slice.
+// This undoes the compiler underscore mangling.
+// Returns the number of bytes used by the result.
 func decodeIdentifier(bsl []byte) int {
+	underscoreCodes := map[byte]byte{
+		'_': '_',
+		'0': '.',
+		'1': '/',
+		'2': '*',
+		'3': ',',
+		'4': '{',
+		'5': '}',
+		'6': '[',
+		'7': ']',
+		'8': '(',
+		'9': ')',
+		'a': '"',
+		'b': ' ',
+		'c': ';',
+	}
+
 	j := 0
 	for i := 0; i < len(bsl); i++ {
 		b := bsl[i]
-
-		if i+1 < len(bsl) && bsl[i] == '.' && bsl[i+1] == '.' {
-			if i+4 < len(bsl) && bsl[i+2] == 'z' {
-				digits := bsl[i+3:]
-				r := hexDigitsToRune(digits, 2)
-				nc := encoderune(bsl[j:], r)
-				j += nc
-				i += 4
-				continue
-			} else if i+6 < len(bsl) && bsl[i+2] == 'u' {
-				digits := bsl[i+3:]
-				r := hexDigitsToRune(digits, 4)
-				nc := encoderune(bsl[j:], r)
-				j += nc
-				i += 6
-				continue
-			} else if i+10 < len(bsl) && bsl[i+2] == 'U' {
-				digits := bsl[i+3:]
-				r := hexDigitsToRune(digits, 8)
-				nc := encoderune(bsl[j:], r)
-				j += nc
-				i += 10
-				continue
-			}
+		if b != '_' || i+1 >= len(bsl) {
+			bsl[j] = b
+			j++
+			continue
 		}
-		bsl[j] = b
-		j += 1
+
+		if d, ok := underscoreCodes[bsl[i+1]]; ok {
+			i++
+			bsl[j] = d
+			j++
+			continue
+		}
+
+		rlen := 0
+		switch bsl[i+1] {
+		case 'x':
+			rlen = 2
+		case 'u':
+			rlen = 4
+		case 'U':
+			rlen = 8
+		}
+
+		if rlen > 0 && i+1+rlen < len(bsl) {
+			r := hexDigitsToRune(bsl[i+2:], rlen)
+			nc := encoderune(bsl[j:], r)
+			j += nc
+			i += rlen + 1
+		} else {
+			bsl[j] = b
+			j++
+		}
 	}
 	return j
 }
@@ -254,6 +275,11 @@
 // as used in the compiler.
 
 func demangleSymbol(s string) string {
+	if bytealg.IndexByteString(s, '.') < 0 {
+		// A symbol with no '.' is not a Go symbol.
+		return s
+	}
+
 	bsl := []byte(s)
 	nchars := decodeIdentifier(bsl)
 	bsl = bsl[:nchars]
diff --git a/libgo/go/runtime/trace.go b/libgo/go/runtime/trace.go
index ce185fc..b05f30a 100644
--- a/libgo/go/runtime/trace.go
+++ b/libgo/go/runtime/trace.go
@@ -1152,7 +1152,7 @@
 // To access runtime functions from runtime/trace.
 // See runtime/trace/annotation.go
 
-//go:linkname trace_userTaskCreate runtime..z2ftrace.userTaskCreate
+//go:linkname trace_userTaskCreate runtime_1trace.userTaskCreate
 func trace_userTaskCreate(id, parentID uint64, taskType string) {
 	if !trace.enabled {
 		return
@@ -1170,12 +1170,12 @@
 	traceReleaseBuffer(pid)
 }
 
-//go:linkname trace_userTaskEnd runtime..z2ftrace.userTaskEnd
+//go:linkname trace_userTaskEnd runtime_1trace.userTaskEnd
 func trace_userTaskEnd(id uint64) {
 	traceEvent(traceEvUserTaskEnd, 2, id)
 }
 
-//go:linkname trace_userRegion runtime..z2ftrace.userRegion
+//go:linkname trace_userRegion runtime_1trace.userRegion
 func trace_userRegion(id, mode uint64, name string) {
 	if !trace.enabled {
 		return
@@ -1192,7 +1192,7 @@
 	traceReleaseBuffer(pid)
 }
 
-//go:linkname trace_userLog runtime..z2ftrace.userLog
+//go:linkname trace_userLog runtime_1trace.userLog
 func trace_userLog(id uint64, category, message string) {
 	if !trace.enabled {
 		return
diff --git a/libgo/go/runtime/traceback_gccgo.go b/libgo/go/runtime/traceback_gccgo.go
index 1ba91af..ebdbefc 100644
--- a/libgo/go/runtime/traceback_gccgo.go
+++ b/libgo/go/runtime/traceback_gccgo.go
@@ -184,10 +184,10 @@
 // isExportedRuntime reports whether name is an exported runtime function.
 // It is only for runtime functions, so ASCII A-Z is fine. Here also check
 // for mangled functions from runtime/<...>, which will be prefixed with
-// "runtime..z2f".
+// "runtime_1".
 func isExportedRuntime(name string) bool {
 	const n = len("runtime.")
-	if hasPrefix(name, "runtime..z2f") {
+	if hasPrefix(name, "runtime_1") {
 		return true
 	}
 	return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z'
diff --git a/libgo/go/sync/atomic/atomic.c b/libgo/go/sync/atomic/atomic.c
index 90a4ff3..71d51aa 100644
--- a/libgo/go/sync/atomic/atomic.c
+++ b/libgo/go/sync/atomic/atomic.c
@@ -9,7 +9,7 @@
 #include "runtime.h"
 
 int32_t SwapInt32 (int32_t *, int32_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.SwapInt32")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.SwapInt32")
   __attribute__ ((no_split_stack));
 
 int32_t
@@ -19,7 +19,7 @@
 }
 
 int64_t SwapInt64 (int64_t *, int64_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.SwapInt64")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.SwapInt64")
   __attribute__ ((no_split_stack));
 
 int64_t
@@ -31,7 +31,7 @@
 }
 
 uint32_t SwapUint32 (uint32_t *, uint32_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.SwapUint32")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.SwapUint32")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -41,7 +41,7 @@
 }
 
 uint64_t SwapUint64 (uint64_t *, uint64_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.SwapUint64")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.SwapUint64")
   __attribute__ ((no_split_stack));
 
 uint64_t
@@ -53,7 +53,7 @@
 }
 
 uintptr_t SwapUintptr (uintptr_t *, uintptr_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.SwapUintptr")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.SwapUintptr")
   __attribute__ ((no_split_stack));
 
 uintptr_t
@@ -63,7 +63,7 @@
 }
 
 _Bool CompareAndSwapInt32 (int32_t *, int32_t, int32_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.CompareAndSwapInt32")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.CompareAndSwapInt32")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -74,7 +74,7 @@
 }
 
 _Bool CompareAndSwapInt64 (int64_t *, int64_t, int64_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.CompareAndSwapInt64")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.CompareAndSwapInt64")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -87,7 +87,7 @@
 }
 
 _Bool CompareAndSwapUint32 (uint32_t *, uint32_t, uint32_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.CompareAndSwapUint32")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.CompareAndSwapUint32")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -98,7 +98,7 @@
 }
 
 _Bool CompareAndSwapUint64 (uint64_t *, uint64_t, uint64_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.CompareAndSwapUint64")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.CompareAndSwapUint64")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -111,7 +111,7 @@
 }
 
 _Bool CompareAndSwapUintptr (uintptr_t *, uintptr_t, uintptr_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.CompareAndSwapUintptr")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.CompareAndSwapUintptr")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -122,7 +122,7 @@
 }
 
 int32_t AddInt32 (int32_t *, int32_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.AddInt32")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.AddInt32")
   __attribute__ ((no_split_stack));
 
 int32_t
@@ -132,7 +132,7 @@
 }
 
 uint32_t AddUint32 (uint32_t *, uint32_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.AddUint32")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.AddUint32")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -142,7 +142,7 @@
 }
 
 int64_t AddInt64 (int64_t *, int64_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.AddInt64")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.AddInt64")
   __attribute__ ((no_split_stack));
 
 int64_t
@@ -154,7 +154,7 @@
 }
 
 uint64_t AddUint64 (uint64_t *, uint64_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.AddUint64")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.AddUint64")
   __attribute__ ((no_split_stack));
 
 uint64_t
@@ -166,7 +166,7 @@
 }
 
 uintptr_t AddUintptr (uintptr_t *, uintptr_t)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.AddUintptr")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.AddUintptr")
   __attribute__ ((no_split_stack));
 
 uintptr_t
@@ -176,7 +176,7 @@
 }
 
 int32_t LoadInt32 (int32_t *addr)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.LoadInt32")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.LoadInt32")
   __attribute__ ((no_split_stack));
 
 int32_t
@@ -186,7 +186,7 @@
 }
 
 int64_t LoadInt64 (int64_t *addr)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.LoadInt64")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.LoadInt64")
   __attribute__ ((no_split_stack));
 
 int64_t
@@ -198,7 +198,7 @@
 }
 
 uint32_t LoadUint32 (uint32_t *addr)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.LoadUint32")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.LoadUint32")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -208,7 +208,7 @@
 }
 
 uint64_t LoadUint64 (uint64_t *addr)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.LoadUint64")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.LoadUint64")
   __attribute__ ((no_split_stack));
 
 uint64_t
@@ -220,7 +220,7 @@
 }
 
 uintptr_t LoadUintptr (uintptr_t *addr)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.LoadUintptr")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.LoadUintptr")
   __attribute__ ((no_split_stack));
 
 uintptr_t
@@ -230,7 +230,7 @@
 }
 
 void *LoadPointer (void **addr)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.LoadPointer")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.LoadPointer")
   __attribute__ ((no_split_stack));
 
 void *
@@ -240,7 +240,7 @@
 }
 
 void StoreInt32 (int32_t *addr, int32_t val)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.StoreInt32")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.StoreInt32")
   __attribute__ ((no_split_stack));
 
 void
@@ -250,7 +250,7 @@
 }
 
 void StoreInt64 (int64_t *addr, int64_t val)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.StoreInt64")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.StoreInt64")
   __attribute__ ((no_split_stack));
 
 void
@@ -262,7 +262,7 @@
 }
 
 void StoreUint32 (uint32_t *addr, uint32_t val)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.StoreUint32")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.StoreUint32")
   __attribute__ ((no_split_stack));
 
 void
@@ -272,7 +272,7 @@
 }
 
 void StoreUint64 (uint64_t *addr, uint64_t val)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.StoreUint64")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.StoreUint64")
   __attribute__ ((no_split_stack));
 
 void
@@ -284,7 +284,7 @@
 }
 
 void StoreUintptr (uintptr_t *addr, uintptr_t val)
-  __asm__ (GOSYM_PREFIX "sync..z2fatomic.StoreUintptr")
+  __asm__ (GOSYM_PREFIX "sync_1atomic.StoreUintptr")
   __attribute__ ((no_split_stack));
 
 void
diff --git a/libgo/runtime/go-ffi.c b/libgo/runtime/go-ffi.c
index b030f5e..1ec5f87 100644
--- a/libgo/runtime/go-ffi.c
+++ b/libgo/runtime/go-ffi.c
@@ -16,36 +16,36 @@
    the libffi type values.  */
 
 ffi_type *go_ffi_type_pointer(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_pointer(void) __asm__ ("runtime.ffi_type_pointer");
+ffi_type *go_ffi_type_pointer(void) __asm__ ("runtime.ffi__type__pointer");
 ffi_type *go_ffi_type_sint8(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_sint8(void) __asm__ ("runtime.ffi_type_sint8");
+ffi_type *go_ffi_type_sint8(void) __asm__ ("runtime.ffi__type__sint8");
 ffi_type *go_ffi_type_sint16(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_sint16(void) __asm__ ("runtime.ffi_type_sint16");
+ffi_type *go_ffi_type_sint16(void) __asm__ ("runtime.ffi__type__sint16");
 ffi_type *go_ffi_type_sint32(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_sint32(void) __asm__ ("runtime.ffi_type_sint32");
+ffi_type *go_ffi_type_sint32(void) __asm__ ("runtime.ffi__type__sint32");
 ffi_type *go_ffi_type_sint64(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_sint64(void) __asm__ ("runtime.ffi_type_sint64");
+ffi_type *go_ffi_type_sint64(void) __asm__ ("runtime.ffi__type__sint64");
 ffi_type *go_ffi_type_uint8(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_uint8(void) __asm__ ("runtime.ffi_type_uint8");
+ffi_type *go_ffi_type_uint8(void) __asm__ ("runtime.ffi__type__uint8");
 ffi_type *go_ffi_type_uint16(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_uint16(void) __asm__ ("runtime.ffi_type_uint16");
+ffi_type *go_ffi_type_uint16(void) __asm__ ("runtime.ffi__type__uint16");
 ffi_type *go_ffi_type_uint32(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_uint32(void) __asm__ ("runtime.ffi_type_uint32");
+ffi_type *go_ffi_type_uint32(void) __asm__ ("runtime.ffi__type__uint32");
 ffi_type *go_ffi_type_uint64(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_uint64(void) __asm__ ("runtime.ffi_type_uint64");
+ffi_type *go_ffi_type_uint64(void) __asm__ ("runtime.ffi__type__uint64");
 ffi_type *go_ffi_type_float(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_float(void) __asm__ ("runtime.ffi_type_float");
+ffi_type *go_ffi_type_float(void) __asm__ ("runtime.ffi__type__float");
 ffi_type *go_ffi_type_double(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_double(void) __asm__ ("runtime.ffi_type_double");
+ffi_type *go_ffi_type_double(void) __asm__ ("runtime.ffi__type__double");
 ffi_type *go_ffi_type_complex_float(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_complex_float(void) __asm__ ("runtime.ffi_type_complex_float");
+ffi_type *go_ffi_type_complex_float(void) __asm__ ("runtime.ffi__type__complex__float");
 ffi_type *go_ffi_type_complex_double(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_complex_double(void) __asm__ ("runtime.ffi_type_complex_double");
+ffi_type *go_ffi_type_complex_double(void) __asm__ ("runtime.ffi__type__complex__double");
 ffi_type *go_ffi_type_void(void) __attribute__ ((no_split_stack));
-ffi_type *go_ffi_type_void(void) __asm__ ("runtime.ffi_type_void");
+ffi_type *go_ffi_type_void(void) __asm__ ("runtime.ffi__type__void");
 
 _Bool go_ffi_supports_complex(void) __attribute__ ((no_split_stack));
-_Bool go_ffi_supports_complex(void) __asm__ ("runtime.ffi_supports_complex");
+_Bool go_ffi_supports_complex(void) __asm__ ("runtime.ffi__supports__complex");
 
 ffi_type *
 go_ffi_type_pointer(void)
diff --git a/libgo/runtime/go-setenv.c b/libgo/runtime/go-setenv.c
index 81b1775..08987de 100644
--- a/libgo/runtime/go-setenv.c
+++ b/libgo/runtime/go-setenv.c
@@ -13,7 +13,7 @@
 
 /* Set the C environment from Go.  This is called by syscall.Setenv.  */
 
-void setenv_c (String, String) __asm__ (GOSYM_PREFIX "syscall.setenv_c");
+void setenv_c (String, String) __asm__ (GOSYM_PREFIX "syscall.setenv__c");
 
 void
 setenv_c (String k, String v)
diff --git a/libgo/runtime/go-unsafe-pointer.c b/libgo/runtime/go-unsafe-pointer.c
index 364878e..e24bfb2 100644
--- a/libgo/runtime/go-unsafe-pointer.c
+++ b/libgo/runtime/go-unsafe-pointer.c
@@ -72,7 +72,7 @@
    it to be defined elsewhere.  */
 
 extern const struct ptrtype pointer_unsafe_Pointer
-  __asm__ (GOSYM_PREFIX "type...1unsafe.Pointer");
+  __asm__ (GOSYM_PREFIX "unsafe.Pointer..p");
 
 /* The reflection string.  */
 #define PREFLECTION "*unsafe.Pointer"
@@ -83,7 +83,7 @@
 };
 
 extern const byte pointer_unsafe_Pointer_gc[]
-  __asm__ (GOSYM_PREFIX "type...1unsafe.Pointer..g");
+  __asm__ (GOSYM_PREFIX "unsafe.Pointer..p..g");
 
 const byte pointer_unsafe_Pointer_gc[] = { 1 };
 
diff --git a/libgo/runtime/go-unsetenv.c b/libgo/runtime/go-unsetenv.c
index 2135997..4b5058a 100644
--- a/libgo/runtime/go-unsetenv.c
+++ b/libgo/runtime/go-unsetenv.c
@@ -14,7 +14,7 @@
 /* Unset an environment variable from Go.  This is called by
    syscall.Unsetenv.  */
 
-void unsetenv_c (String) __asm__ (GOSYM_PREFIX "syscall.unsetenv_c");
+void unsetenv_c (String) __asm__ (GOSYM_PREFIX "syscall.unsetenv__c");
 
 void
 unsetenv_c (String k)
diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h
index f46eaea..3a65d44 100644
--- a/libgo/runtime/runtime.h
+++ b/libgo/runtime/runtime.h
@@ -440,7 +440,7 @@
 
 extern void _cgo_wait_runtime_init_done (void);
 extern void _cgo_notify_runtime_init_done (void)
-  __asm__ (GOSYM_PREFIX "runtime._cgo_notify_runtime_init_done");
+  __asm__ (GOSYM_PREFIX "runtime.__cgo__notify__runtime__init__done");
 extern _Bool runtime_iscgo;
 extern uintptr __go_end __attribute__ ((weak));
 extern void *getitab(const struct _type *, const struct _type *, _Bool)
diff --git a/libgo/testsuite/gotest b/libgo/testsuite/gotest
index 0fd6419..8c3c5ca 100755
--- a/libgo/testsuite/gotest
+++ b/libgo/testsuite/gotest
@@ -496,7 +496,8 @@
 localname() {
 	# The package main has been renamed to __main__ when imported.
 	# Adjust its uses.
-	echo $1 | sed 's/^main\./__main__./'
+	# Also demangle underscores.
+	echo $1 | sed 's/^main\./__main__./' | sed 's/__/_/'
 }
 
 # Takes a list of tests derived from 'nm' output (whose symbols are mangled)
@@ -504,7 +505,7 @@
 # Example:
 #
 #    Original symbol:   foo/bar/leaf.Mumble
-#    Mangled symbol:    foo..z2fbar..z2fleaf.Mumble
+#    Mangled symbol:    foo_1fbar_1leaf.Mumble
 #    Returned:          leaf.Mumble
 #
 symtogo() {
@@ -522,7 +523,7 @@
     if expr "$tp" : '^type\.\.' >/dev/null 2>&1; then
       continue
     fi
-    s=$(echo "$tp" | sed -e 's/\.\.z2f/%/g' | sed -e 's/.*%//')
+    s=$(echo "$tp" | sed -e 's/_1/%/g' | sed -e 's/.*%//')
     # Screen out methods (X.Y.Z).
     if ! expr "$s" : '^[^.]*\.[^.]*$' >/dev/null 2>&1; then
       continue