diff mbox series

Go patch committed: Improve name mangling for package paths

Message ID CAOyqgcXahY72v_Cy7XOUHs4CqoJy_rmOR5mP-cxDaNHUiB4Yyw@mail.gmail.com
State New
Headers show
Series Go patch committed: Improve name mangling for package paths | expand

Commit Message

Ian Lance Taylor Oct. 25, 2018, 10:18 p.m. UTC
This patch by Than McIntosh improves the mangling of package paths in
the Go frontend.

The current implementation of Gogo::pkgpath_for_symbol was written in
a way that allowed two distinct package paths to map to the same
symbol, which could cause collisions at link- time or compile-time.

This patch switches to a better mangling scheme to ensure that we get
a unique packagepath symbol for each package.  In the new scheme
instead of having separate mangling schemes for identifiers and
package paths, the main identifier mangler ("go_encode_id") now
handles mangling of both packagepath characters and identifier
characters.

The new mangling scheme is more intrusive: "foo/bar.Baz" is mangled as
"foo..z2fbar.Baz" instead of "foo_bar.Baz".  To mitigate this, this
patch also adds a demangling capability so that function names
returned from runtime.CallersFrames are converted back to their
original unmangled form.

Changing the pkgpath_for_symbol scheme requires updating a number of
//go:linkname directives and C "__asm__" directives to match the new
scheme, as well as updating the 'gotest' driver (which makes
assumptions about the correct mapping from pkgpath symbol to package
name).

This fixes https://golang.org/issue/27534.

Note that this changes the name mangling of many symbols in libgo.

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

Ian

Comments

Rainer Orth Oct. 26, 2018, 11:55 a.m. UTC | #1
Hi Ian,

> This patch by Than McIntosh improves the mangling of package paths in
> the Go frontend.
>
> The current implementation of Gogo::pkgpath_for_symbol was written in
> a way that allowed two distinct package paths to map to the same
> symbol, which could cause collisions at link- time or compile-time.
>
> This patch switches to a better mangling scheme to ensure that we get
> a unique packagepath symbol for each package.  In the new scheme
> instead of having separate mangling schemes for identifiers and
> package paths, the main identifier mangler ("go_encode_id") now
> handles mangling of both packagepath characters and identifier
> characters.
>
> The new mangling scheme is more intrusive: "foo/bar.Baz" is mangled as
> "foo..z2fbar.Baz" instead of "foo_bar.Baz".  To mitigate this, this
> patch also adds a demangling capability so that function names
> returned from runtime.CallersFrames are converted back to their
> original unmangled form.
>
> Changing the pkgpath_for_symbol scheme requires updating a number of
> //go:linkname directives and C "__asm__" directives to match the new
> scheme, as well as updating the 'gotest' driver (which makes
> assumptions about the correct mapping from pkgpath symbol to package
> name).

it seems you missed a case here: both i386-pc-solaris2.* and
sparc-sun-solaris2.* bootstraps broke linking the gotools:

Undefined                       first referenced
 symbol                             in file
log..z2fsyslog.syslog_c             ../sparc-sun-solaris2.11/libgo/.libs/libgo.so
ld: fatal: symbol referencing errors
collect2: error: ld returned 1 exit status
make[2]: *** [Makefile:751: go] Error 1

The following patch fixes this allowing the links to succeed, though
I've not run the testsuite yet.

	Rainer
Li, Pan2 via Gcc-patches Oct. 26, 2018, 12:04 p.m. UTC | #2
Thanks for reporting this.

Sent https://go-review.googlesource.com/c/gofrontend/+/145017 with a
tentative fix.

Than

On Fri, Oct 26, 2018 at 7:55 AM Rainer Orth <ro@cebitec.uni-bielefeld.de> wrote:
>
> Hi Ian,
>
> > This patch by Than McIntosh improves the mangling of package paths in
> > the Go frontend.
> >
> > The current implementation of Gogo::pkgpath_for_symbol was written in
> > a way that allowed two distinct package paths to map to the same
> > symbol, which could cause collisions at link- time or compile-time.
> >
> > This patch switches to a better mangling scheme to ensure that we get
> > a unique packagepath symbol for each package.  In the new scheme
> > instead of having separate mangling schemes for identifiers and
> > package paths, the main identifier mangler ("go_encode_id") now
> > handles mangling of both packagepath characters and identifier
> > characters.
> >
> > The new mangling scheme is more intrusive: "foo/bar.Baz" is mangled as
> > "foo..z2fbar.Baz" instead of "foo_bar.Baz".  To mitigate this, this
> > patch also adds a demangling capability so that function names
> > returned from runtime.CallersFrames are converted back to their
> > original unmangled form.
> >
> > Changing the pkgpath_for_symbol scheme requires updating a number of
> > //go:linkname directives and C "__asm__" directives to match the new
> > scheme, as well as updating the 'gotest' driver (which makes
> > assumptions about the correct mapping from pkgpath symbol to package
> > name).
>
> it seems you missed a case here: both i386-pc-solaris2.* and
> sparc-sun-solaris2.* bootstraps broke linking the gotools:
>
> Undefined                       first referenced
>  symbol                             in file
> log..z2fsyslog.syslog_c             ../sparc-sun-solaris2.11/libgo/.libs/libgo.so
> ld: fatal: symbol referencing errors
> collect2: error: ld returned 1 exit status
> make[2]: *** [Makefile:751: go] Error 1
>
> The following patch fixes this allowing the links to succeed, though
> I've not run the testsuite yet.
>
>         Rainer
>
> --
> -----------------------------------------------------------------------------
> Rainer Orth, Center for Biotechnology, Bielefeld University
>
>
> --
> You received this message because you are subscribed to the Google Groups "gofrontend-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to gofrontend-dev+unsubscribe@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
Rainer Orth Oct. 26, 2018, 2:19 p.m. UTC | #3
Hi Than,

> Thanks for reporting this.
>
> Sent https://go-review.googlesource.com/c/gofrontend/+/145017 with a
> tentative fix.

fine, thanks.

While actually running the libgo testsuite, another issue came up: all
tests FAIL like this:

/vol/gcc/src/hg/trunk/local/libgo/testsuite/gotest[516]: local: not found [No such file or directory]
FAIL: crypto/sha512

The Solaris 11 /bin/sh is ksh93, and Solaris 10 even comes with the
original Bourne shell, neither of which support the unportable local
command.  AFAICS, there's no need at all to use it, and indeed with the
following patch libgo testsuite results are as before the patch.

	Rainer
Li, Pan2 via Gcc-patches Oct. 26, 2018, 2:44 p.m. UTC | #4
OK, thanks again. Another fix sent:

 https://go-review.googlesource.com/c/gofrontend/+/145021

Cheers, Than

On Fri, Oct 26, 2018 at 10:20 AM Rainer Orth
<ro@cebitec.uni-bielefeld.de> wrote:
>
> Hi Than,
>
> > Thanks for reporting this.
> >
> > Sent https://go-review.googlesource.com/c/gofrontend/+/145017 with a
> > tentative fix.
>
> fine, thanks.
>
> While actually running the libgo testsuite, another issue came up: all
> tests FAIL like this:
>
> /vol/gcc/src/hg/trunk/local/libgo/testsuite/gotest[516]: local: not found [No such file or directory]
> FAIL: crypto/sha512
>
> The Solaris 11 /bin/sh is ksh93, and Solaris 10 even comes with the
> original Bourne shell, neither of which support the unportable local
> command.  AFAICS, there's no need at all to use it, and indeed with the
> following patch libgo testsuite results are as before the patch.
>
>         Rainer
>
> --
> -----------------------------------------------------------------------------
> Rainer Orth, Center for Biotechnology, Bielefeld University
>
>
Rainer Orth Oct. 26, 2018, 4:13 p.m. UTC | #5
Hi Than,

> OK, thanks again. Another fix sent:
>
>  https://go-review.googlesource.com/c/gofrontend/+/145021

great, thanks again.  While the two previous patches were enough to get
decent Solaris 11 results, on Solaris 10 all libgo tests still FAIL like
this:

_testmain.go:9:25: error: reference to undefined identifier 'tar.TestReaderntar'
    9 |  {"TestFileWriter", tar.TestReaderntar.TestPartialReadntar.TestUninitializedReadntar.TestReadTruncationntar.TestReadHeaderOnlyntar.TestMergePAXntar.TestParsePAXntar.TestReadOldGNUSparseMapntar.TestReadGNUSparsePAXHeadersntar.TestFileReaderntar.TestFitsInBase256ntar.TestParseNumericntar.TestFormatNumericntar.TestFitsInOctalntar.TestParsePAXTimentar.TestFormatPAXTimentar.TestParsePAXRecordntar.TestFormatPAXRecordntar.TestSparseEntriesntar.TestFileInfoHeaderntar.TestFileInfoHeaderDirntar.TestFileInfoHeaderSymlinkntar.TestRoundTripntar.TestHeaderRoundTripntar.TestHeaderAllowedFormatsntar.TestWriterntar.TestPaxntar.TestPaxSymlinkntar.TestPaxNonAsciintar.TestPaxXattrsntar.TestPaxHeadersSortedntar.TestUSTARLongNamentar.TestValidTypeflagWithPAXHeaderntar.TestWriterErrorsntar.TestSplitUSTARPathntar.TestIssue12594ntar.TestFileWriter},
      |                         ^
FAIL: archive/tar

This is due to gotest (symtogo):

  echo "$result" | sed -e 's/ /\n/g'

which even Solaris 11.5 Beta /bin/sed treats like this:

$ echo 'a b' | /bin/sed -e 's/ /\n/g'
anb

I still got decent results because GNU sed is earlier in my PATH on
Solaris 11, but Solaris 10 lacks it.  However, this seems to work (both
with Solaris sed and GNU sed):

echo "$result" | /bin/sed -e 's/ /\
/g'

It allows the Solaris 10 libgo testing to work, but can't tell if that's
the most portable way to do this.

	Rainer
Ian Lance Taylor Oct. 26, 2018, 4:53 p.m. UTC | #6
On Fri, Oct 26, 2018 at 5:04 AM, Than McIntosh <thanm@google.com> wrote:
>
> Thanks for reporting this.
>
> Sent https://go-review.googlesource.com/c/gofrontend/+/145017 with a
> tentative fix.

Thanks, committed to mainline.

Ian
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 265515)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@
-0a58bd7d820dac8931e8da5b291f19c3c7e6bee3
+ad50884d2a4b653f7f20edc8b441fe6ad6570d55
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: libgo/go/log/syslog/syslog_c.c
===================================================================
--- libgo/go/log/syslog/syslog_c.c	(revision 265460)
+++ libgo/go/log/syslog/syslog_c.c	(working copy)
@@ -12,7 +12,7 @@
    can't represent a C varargs function in Go.  */
 
 void syslog_c(intgo, const char*)
-  __asm__ (GOSYM_PREFIX "log_syslog.syslog_c");
+  __asm__ (GOSYM_PREFIX "log..z2fsyslog.syslog_c");
 
 void
 syslog_c (intgo priority, const char *msg)
Ian Lance Taylor Oct. 26, 2018, 4:58 p.m. UTC | #7
On Fri, Oct 26, 2018 at 7:44 AM, Than McIntosh <thanm@google.com> wrote:
> OK, thanks again. Another fix sent:
>
>  https://go-review.googlesource.com/c/gofrontend/+/145021

Thanks.  Sorry for missing that in code review.  Committed to mainline.

Ian
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 265533)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@
-ad50884d2a4b653f7f20edc8b441fe6ad6570d55
+9785e5c4e868ba55efdb33fc51872b4821770167
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: libgo/testsuite/gotest
===================================================================
--- libgo/testsuite/gotest	(revision 265515)
+++ libgo/testsuite/gotest	(working copy)
@@ -513,9 +513,7 @@ localname() {
 #    Returned:          leaf.Mumble
 #
 symtogo() {
-  local s=""
-  local result=""
-  local ndots=""
+  result=""
   for tp in $*
   do
     s=$(echo "$tp" | sed -e 's/\.\.z2f/%/g' | sed -e 's/.*%//')
Bernhard Reutner-Fischer Oct. 26, 2018, 6:09 p.m. UTC | #8
On 26 October 2018 18:13:32 CEST, Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> wrote:
>Hi Than,
>
>> OK, thanks again. Another fix sent:
>>
>>  https://go-review.googlesource.com/c/gofrontend/+/145021
>
>great, thanks again.  While the two previous patches were enough to get
>decent Solaris 11 results, on Solaris 10 all libgo tests still FAIL
>like
>this:
>
>_testmain.go:9:25: error: reference to undefined identifier
>'tar.TestReaderntar'
>9 |  {"TestFileWriter",
>tar.TestReaderntar.TestPartialReadntar.TestUninitializedReadntar.TestReadTruncationntar.TestReadHeaderOnlyntar.TestMergePAXntar.TestParsePAXntar.TestReadOldGNUSparseMapntar.TestReadGNUSparsePAXHeadersntar.TestFileReaderntar.TestFitsInBase256ntar.TestParseNumericntar.TestFormatNumericntar.TestFitsInOctalntar.TestParsePAXTimentar.TestFormatPAXTimentar.TestParsePAXRecordntar.TestFormatPAXRecordntar.TestSparseEntriesntar.TestFileInfoHeaderntar.TestFileInfoHeaderDirntar.TestFileInfoHeaderSymlinkntar.TestRoundTripntar.TestHeaderRoundTripntar.TestHeaderAllowedFormatsntar.TestWriterntar.TestPaxntar.TestPaxSymlinkntar.TestPaxNonAsciintar.TestPaxXattrsntar.TestPaxHeadersSortedntar.TestUSTARLongNamentar.TestValidTypeflagWithPAXHeaderntar.TestWriterErrorsntar.TestSplitUSTARPathntar.TestIssue12594ntar.TestFileWriter},
>      |                         ^
>FAIL: archive/tar
>
>This is due to gotest (symtogo):
>
>  echo "$result" | sed -e 's/ /\n/g'
>
>which even Solaris 11.5 Beta /bin/sed treats like this:
>
>$ echo 'a b' | /bin/sed -e 's/ /\n/g'
>anb

Just curious if either 
   | sed -e "s/ /\n/g"
or
  |  tr " " "\n"

would work.

thanks,
Ian Lance Taylor Oct. 26, 2018, 6:37 p.m. UTC | #9
On Fri, Oct 26, 2018 at 9:13 AM, Rainer Orth
<ro@cebitec.uni-bielefeld.de> wrote:
> Hi Than,
>
>> OK, thanks again. Another fix sent:
>>
>>  https://go-review.googlesource.com/c/gofrontend/+/145021
>
> great, thanks again.  While the two previous patches were enough to get
> decent Solaris 11 results, on Solaris 10 all libgo tests still FAIL like
> this:
>
> _testmain.go:9:25: error: reference to undefined identifier 'tar.TestReaderntar'
>     9 |  {"TestFileWriter", tar.TestReaderntar.TestPartialReadntar.TestUninitializedReadntar.TestReadTruncationntar.TestReadHeaderOnlyntar.TestMergePAXntar.TestParsePAXntar.TestReadOldGNUSparseMapntar.TestReadGNUSparsePAXHeadersntar.TestFileReaderntar.TestFitsInBase256ntar.TestParseNumericntar.TestFormatNumericntar.TestFitsInOctalntar.TestParsePAXTimentar.TestFormatPAXTimentar.TestParsePAXRecordntar.TestFormatPAXRecordntar.TestSparseEntriesntar.TestFileInfoHeaderntar.TestFileInfoHeaderDirntar.TestFileInfoHeaderSymlinkntar.TestRoundTripntar.TestHeaderRoundTripntar.TestHeaderAllowedFormatsntar.TestWriterntar.TestPaxntar.TestPaxSymlinkntar.TestPaxNonAsciintar.TestPaxXattrsntar.TestPaxHeadersSortedntar.TestUSTARLongNamentar.TestValidTypeflagWithPAXHeaderntar.TestWriterErrorsntar.TestSplitUSTARPathntar.TestIssue12594ntar.TestFileWriter},
>       |                         ^
> FAIL: archive/tar
>
> This is due to gotest (symtogo):
>
>   echo "$result" | sed -e 's/ /\n/g'
>
> which even Solaris 11.5 Beta /bin/sed treats like this:
>
> $ echo 'a b' | /bin/sed -e 's/ /\n/g'
> anb
>
> I still got decent results because GNU sed is earlier in my PATH on
> Solaris 11, but Solaris 10 lacks it.  However, this seems to work (both
> with Solaris sed and GNU sed):
>
> echo "$result" | /bin/sed -e 's/ /\
> /g'
>
> It allows the Solaris 10 libgo testing to work, but can't tell if that's
> the most portable way to do this.

Thanks for the report.  Fixed by simplifying the code, like so.
Committed to mainline.

Ian
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 265534)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@
-9785e5c4e868ba55efdb33fc51872b4821770167
+8902fb43c569e4d3ec5bd143bfa8cb6bf2836780
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: libgo/testsuite/gotest
===================================================================
--- libgo/testsuite/gotest	(revision 265534)
+++ libgo/testsuite/gotest	(working copy)
@@ -521,13 +521,8 @@ symtogo() {
     if ! expr "$s" : '^[^.]*\.[^.]*$' >/dev/null 2>&1; then
       continue
     fi
-    if [ -z "${result}" ]; then
-      result="${s}"
-    else
-      result="${result} ${s}"
-    fi
+    echo "$s"
   done
-  echo "$result" | sed -e 's/ /\n/g'
 }
 
 {
diff mbox series

Patch

Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 265460)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@ 
-771668f7137e560b2ef32c8799e5f8b4c4ee14a9
+407a59831ea4fbfe03f0887c40497b73939e7c44
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/go-encode-id.cc
===================================================================
--- gcc/go/gofrontend/go-encode-id.cc	(revision 265460)
+++ gcc/go/gofrontend/go-encode-id.cc	(working copy)
@@ -1,4 +1,4 @@ 
-// go-encode-id.cc -- Go identifier encoding hooks
+// go-encode-id.cc -- Go identifier and packagepath encoding/decoding hooks
 
 // Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
@@ -82,10 +82,10 @@  fetch_utf8_char(const char* p, unsigned
   return len;
 }
 
-// Encode an identifier using ASCII 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.
+// 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".
 
 std::string
 go_encode_id(const std::string &id)
@@ -97,7 +97,8 @@  go_encode_id(const std::string &id)
     }
 
   // The encoding is only unambiguous if the input string does not
-  // contain ..u or ..U.
+  // 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);
 
@@ -116,17 +117,16 @@  go_encode_id(const std::string &id)
     {
       unsigned int c;
       size_t len = fetch_utf8_char(p, &c);
-      if (len == 1)
+      if (len == 1 && !char_needs_encoding(c))
 	{
-	  // At this point we should only be seeing alphanumerics or
-	  // underscore or dot.
-	  go_assert(!char_needs_encoding(c));
 	  ret += c;
 	}
       else
 	{
 	  char buf[16];
-	  if (c < 0x10000)
+          if (len == 1)
+            snprintf(buf, sizeof buf, "..z%02x", c);
+	  else if (c < 0x10000)
 	    snprintf(buf, sizeof buf, "..u%04x", c);
 	  else
 	    snprintf(buf, sizeof buf, "..U%08x", c);
@@ -143,6 +143,77 @@  go_encode_id(const std::string &id)
   return ret;
 }
 
+// Convert a hex digit string to a unicode codepoint. No checking
+// to insure that the hex digit is meaningful.
+
+static unsigned
+hex_digits_to_unicode_codepoint(const char *digits, unsigned ndig)
+{
+  unsigned result = 0;
+  for (unsigned i = 0; i < ndig; ++i) {
+    result <<= 4;
+    result |= Lex::hex_val(digits[i]);
+  }
+  return result;
+}
+
+// Decode/demangle a mangled string produced by go_encode_id(). Returns
+// empty string if demangling process fails in some way.  At the moment
+// this routine is unused; there is an equivalent routine in the runtime
+// used for demangling symbols appearing in stack traces.
+
+std::string
+go_decode_id(const std::string &encoded)
+{
+  std::string ret;
+  const char* p = encoded.c_str();
+  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;
+        }
+    }
+
+  return ret;
+}
+
 std::string
 go_selectively_encode_id(const std::string &id)
 {
Index: gcc/go/gofrontend/go-encode-id.h
===================================================================
--- gcc/go/gofrontend/go-encode-id.h	(revision 265460)
+++ gcc/go/gofrontend/go-encode-id.h	(working copy)
@@ -20,6 +20,11 @@  go_id_needs_encoding(const std::string&
 extern std::string
 go_encode_id(const std::string &id);
 
+// Decodes an encoded ID, returning the original string handed off to
+// go_encode_id().
+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
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc	(revision 265460)
+++ gcc/go/gofrontend/gogo.cc	(working copy)
@@ -256,26 +256,11 @@  Gogo::Gogo(Backend* backend, Linemap* li
   this->globals_->add_function_declaration("delete", NULL, delete_type, loc);
 }
 
-// Convert a pkgpath into a string suitable for a symbol.  Note that
-// this transformation is convenient but imperfect.  A -fgo-pkgpath
-// option of a/b_c will conflict with a -fgo-pkgpath option of a_b/c,
-// possibly leading to link time errors.
-
 std::string
 Gogo::pkgpath_for_symbol(const std::string& pkgpath)
 {
-  std::string s = pkgpath;
-  for (size_t i = 0; i < s.length(); ++i)
-    {
-      char c = s[i];
-      if ((c >= 'a' && c <= 'z')
-	  || (c >= 'A' && c <= 'Z')
-	  || (c >= '0' && c <= '9'))
-	;
-      else
-	s[i] = '_';
-    }
-  return s;
+  go_assert(!pkgpath.empty());
+  return go_encode_id(pkgpath);
 }
 
 // Get the package path to use for type reflection data.  This should
@@ -319,6 +304,32 @@  Gogo::set_prefix(const std::string& arg)
   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
Index: gcc/go/gofrontend/gogo.h
===================================================================
--- gcc/go/gofrontend/gogo.h	(revision 265460)
+++ gcc/go/gofrontend/gogo.h	(working copy)
@@ -199,26 +199,10 @@  class Gogo
     return name.substr(1, name.rfind('.') - 1);
   }
 
-  // Given a name which may or may not have been hidden, return the
-  // name to use within a mangled symbol name.
-  static std::string
-  mangle_possibly_hidden_name(const std::string& name)
-  { 
-    // FIXME: This adds in pkgpath twice for hidden symbols, which is
-    // less than ideal.
-    std::string n;
-    if (!Gogo::is_hidden_name(name))
-      n = name;
-    else
-      {
-        n = ".";
-        std::string pkgpath = Gogo::hidden_name_pkgpath(name);
-        n.append(Gogo::pkgpath_for_symbol(pkgpath));
-        n.append(1, '.');
-        n.append(Gogo::unpack_hidden_name(name));
-      }
-    return n;
-  }
+  // Given a name which may or may not have been hidden, append the
+  // appropriate version of the name to the result string.
+  static void
+  append_possibly_hidden_name(std::string *result, const std::string& name);
 
   // Given a name which may or may not have been hidden, return the
   // name to use in an error message.
Index: gcc/go/gofrontend/lex.h
===================================================================
--- gcc/go/gofrontend/lex.h	(revision 265460)
+++ gcc/go/gofrontend/lex.h	(working copy)
@@ -440,6 +440,10 @@  class Lex
   static bool
   is_unicode_space(unsigned int c);
 
+  // Convert the specified hex char into an unsigned integer value.
+  static unsigned
+  hex_val(char c);
+
  private:
   ssize_t
   get_line();
@@ -462,9 +466,6 @@  class Lex
   octal_value(char c)
   { return c - '0'; }
 
-  static unsigned
-  hex_val(char c);
-
   Token
   make_invalid_token()
   { return Token::make_invalid_token(this->location()); }
Index: gcc/go/gofrontend/names.cc
===================================================================
--- gcc/go/gofrontend/names.cc	(revision 265460)
+++ gcc/go/gofrontend/names.cc	(working copy)
@@ -33,7 +33,7 @@ 
 // 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
+// -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
@@ -188,12 +188,17 @@ 
 // 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.  Since before this encoding names can never
-// contain consecutive dots followed by 'u' or 'U', and after this
-// encoding "..u" and "..U" are followed by a known number of
+// 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.
 //
 // 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
@@ -215,9 +220,9 @@  Gogo::function_asm_name(const std::strin
   if (rtype != NULL)
     ret = rtype->deref()->mangled_name(this);
   else if (package == NULL)
-    ret = this->pkgpath_symbol();
+    ret = this->pkgpath();
   else
-    ret = package->pkgpath_symbol();
+    ret = package->pkgpath();
   ret.push_back('.');
   // Check for special names that will break if we use
   // Gogo::unpack_hidden_name.
@@ -268,7 +273,7 @@  Gogo::stub_method_name(const Package* pa
 
   // 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 = this->pkgpath_symbol_for_package(mpkgpath);
+  std::string ret = mpkgpath;
   ret.push_back('.');
   ret.append(Gogo::unpack_hidden_name(mname));
   ret.append("..stub");
@@ -302,9 +307,9 @@  Gogo::global_var_asm_name(const std::str
 {
   std::string ret;
   if (package == NULL)
-    ret = this->pkgpath_symbol();
+    ret = this->pkgpath();
   else
-    ret = package->pkgpath_symbol();
+    ret = package->pkgpath();
   ret.append(1, '.');
   ret.append(Gogo::unpack_hidden_name(go_name));
   return go_encode_id(ret);
@@ -341,7 +346,7 @@  Gogo::thunk_name()
   char thunk_name[50];
   snprintf(thunk_name, sizeof thunk_name, "..thunk%d", thunk_count);
   ++thunk_count;
-  std::string ret = this->pkgpath_symbol();
+  std::string ret = this->pkgpath();
   return ret + thunk_name;
 }
 
@@ -370,7 +375,7 @@  Gogo::init_function_name()
   char buf[30];
   snprintf(buf, sizeof buf, "..init%d", init_count);
   ++init_count;
-  std::string ret = this->pkgpath_symbol();
+  std::string ret = this->pkgpath();
   return ret + buf;
 }
 
@@ -726,7 +731,7 @@  Struct_type::do_mangled_name(Gogo* gogo,
 
 	  if (!p->is_anonymous())
 	    {
-	      ret->append(Gogo::mangle_possibly_hidden_name(p->field_name()));
+              Gogo::append_possibly_hidden_name(ret, p->field_name());
 	      ret->push_back(' ');
 	    }
 
@@ -827,7 +832,7 @@  Interface_type::do_mangled_name(Gogo* go
 
 	  if (!p->name().empty())
 	    {
-	      ret->append(Gogo::mangle_possibly_hidden_name(p->name()));
+              Gogo::append_possibly_hidden_name(ret, p->name());
 	      ret->push_back(' ');
 	    }
 
@@ -854,9 +859,9 @@  Forward_declaration_type::do_mangled_nam
     {
       const Named_object* no = this->named_object();
       if (no->package() == NULL)
-	ret->append(gogo->pkgpath_symbol());
+	ret->append(gogo->pkgpath());
       else
-	ret->append(no->package()->pkgpath_symbol());
+	ret->append(no->package()->pkgpath());
       ret->push_back('.');
       ret->append(Gogo::unpack_hidden_name(no->name()));
     }
@@ -894,18 +899,18 @@  Named_type::append_mangled_type_name(Gog
 	  if (rcvr != NULL)
 	    ret->append(rcvr->type()->deref()->mangled_name(gogo));
 	  else if (this->in_function_->package() == NULL)
-	    ret->append(gogo->pkgpath_symbol());
+	    ret->append(gogo->pkgpath());
 	  else
-	    ret->append(this->in_function_->package()->pkgpath_symbol());
+	    ret->append(this->in_function_->package()->pkgpath());
 	  ret->push_back('.');
 	  ret->append(Gogo::unpack_hidden_name(this->in_function_->name()));
 	}
       else
 	{
 	  if (no->package() == NULL)
-	    ret->append(gogo->pkgpath_symbol());
+	    ret->append(gogo->pkgpath());
 	  else
-	    ret->append(no->package()->pkgpath_symbol());
+	    ret->append(no->package()->pkgpath());
 	}
       ret->push_back('.');
     }
@@ -951,22 +956,22 @@  Gogo::type_descriptor_name(Type* type, N
 	  if (rcvr != NULL)
 	    ret.append(rcvr->type()->deref()->mangled_name(this));
 	  else if (in_function->package() == NULL)
-	    ret.append(this->pkgpath_symbol());
+	    ret.append(this->pkgpath());
 	  else
-	    ret.append(in_function->package()->pkgpath_symbol());
+	    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_symbol());
+	ret.append(this->pkgpath());
       else
-	ret.append(no->package()->pkgpath_symbol());
+	ret.append(no->package()->pkgpath());
       ret.push_back('.');
     }
 
-  ret.append(Gogo::mangle_possibly_hidden_name(no->name()));
+  Gogo::append_possibly_hidden_name(&ret, no->name());
 
   if (in_function != NULL && index > 0)
     {
Index: libgo/go/cmd/cgo/main.go
===================================================================
--- libgo/go/cmd/cgo/main.go	(revision 265460)
+++ libgo/go/cmd/cgo/main.go	(working copy)
@@ -229,6 +229,8 @@  var exportHeader = flag.String("exporthe
 var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
 var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
 var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
+var gccgoMangleCheckDone bool
+var gccgoNewmanglingInEffect bool
 var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
 var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
 var goarch, goos string
Index: libgo/go/cmd/cgo/out.go
===================================================================
--- libgo/go/cmd/cgo/out.go	(revision 265460)
+++ libgo/go/cmd/cgo/out.go	(working copy)
@@ -15,7 +15,9 @@  import (
 	"go/printer"
 	"go/token"
 	"io"
+	"io/ioutil"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"regexp"
 	"sort"
@@ -1191,12 +1193,91 @@  func (p *Package) writeExportHeader(fgcc
 	fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog())
 }
 
-// Return the package prefix when using gccgo.
-func (p *Package) gccgoSymbolPrefix() string {
-	if !*gccgo {
-		return ""
+// gccgoUsesNewMangling returns whether gccgo uses the new collision-free
+// packagepath mangling scheme (see determineGccgoManglingScheme for more
+// info).
+func gccgoUsesNewMangling() bool {
+	if !gccgoMangleCheckDone {
+		gccgoNewmanglingInEffect = determineGccgoManglingScheme()
+		gccgoMangleCheckDone = true
+	}
+	return gccgoNewmanglingInEffect
+}
+
+const mangleCheckCode = `
+package läufer
+func Run(x int) int {
+  return 1
+}
+`
+
+// determineGccgoManglingScheme performs a runtime test to see which
+// flavor of packagepath mangling gccgo is using. Older versions of
+// gccgo use a simple mangling scheme where there can be collisions
+// between packages whose paths are different but mangle to the same
+// string. More recent versions of gccgo use a new mangler that avoids
+// these collisions. Return value is whether gccgo uses the new mangling.
+func determineGccgoManglingScheme() bool {
+
+	// Emit a small Go file for gccgo to compile.
+	filepat := "*_gccgo_manglecheck.go"
+	var f *os.File
+	var err error
+	if f, err = ioutil.TempFile(*objDir, filepat); err != nil {
+		fatalf("%v", err)
+	}
+	gofilename := f.Name()
+	defer os.Remove(gofilename)
+
+	if err = ioutil.WriteFile(gofilename, []byte(mangleCheckCode), 0666); err != nil {
+		fatalf("%v", err)
+	}
+
+	// Compile with gccgo, capturing generated assembly.
+	gccgocmd := os.Getenv("GCCGO")
+	if gccgocmd == "" {
+		gpath, gerr := exec.LookPath("gccgo")
+		if gerr != nil {
+			fatalf("unable to locate gccgo: %v", gerr)
+		}
+		gccgocmd = gpath
+	}
+	cmd := exec.Command(gccgocmd, "-S", "-o", "-", gofilename)
+	buf, cerr := cmd.CombinedOutput()
+	if cerr != nil {
+		fatalf("%s", err)
+	}
+
+	// New mangling: expect go.l..u00e4ufer.Run
+	// Old mangling: expect go.l__ufer.Run
+	return regexp.MustCompile(`go\.l\.\.u00e4ufer\.Run`).Match(buf)
+}
+
+// gccgoPkgpathToSymbolNew converts a package path to a gccgo-style
+// package symbol.
+func gccgoPkgpathToSymbolNew(ppath string) string {
+	bsl := []byte{}
+	changed := false
+	for _, c := range []byte(ppath) {
+		switch {
+		case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z',
+			'0' <= c && c <= '9', '_' == c:
+			bsl = append(bsl, c)
+		default:
+			changed = true
+			encbytes := []byte(fmt.Sprintf("..z%02x", c))
+			bsl = append(bsl, encbytes...)
+		}
 	}
+	if !changed {
+		return ppath
+	}
+	return string(bsl)
+}
 
+// gccgoPkgpathToSymbolOld converts a package path to a gccgo-style
+// package symbol using the older mangling scheme.
+func gccgoPkgpathToSymbolOld(ppath string) string {
 	clean := func(r rune) rune {
 		switch {
 		case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z',
@@ -1205,14 +1286,32 @@  func (p *Package) gccgoSymbolPrefix() st
 		}
 		return '_'
 	}
+	return strings.Map(clean, ppath)
+}
+
+// gccgoPkgpathToSymbol converts a package path to a mangled packagepath
+// symbol.
+func gccgoPkgpathToSymbol(ppath string) string {
+	if gccgoUsesNewMangling() {
+		return gccgoPkgpathToSymbolNew(ppath)
+	} else {
+		return gccgoPkgpathToSymbolOld(ppath)
+	}
+}
+
+// Return the package prefix when using gccgo.
+func (p *Package) gccgoSymbolPrefix() string {
+	if !*gccgo {
+		return ""
+	}
 
 	if *gccgopkgpath != "" {
-		return strings.Map(clean, *gccgopkgpath)
+		return gccgoPkgpathToSymbol(*gccgopkgpath)
 	}
 	if *gccgoprefix == "" && p.PackageName == "main" {
 		return "main"
 	}
-	prefix := strings.Map(clean, *gccgoprefix)
+	prefix := gccgoPkgpathToSymbol(*gccgoprefix)
 	if prefix == "" {
 		prefix = "go"
 	}
Index: libgo/go/internal/bytealg/bytealg.c
===================================================================
--- libgo/go/internal/bytealg/bytealg.c	(revision 265460)
+++ libgo/go/internal/bytealg/bytealg.c	(working copy)
@@ -38,7 +38,7 @@  static const void *goMemmem(const void *
 #endif
 
 intgo Compare(struct __go_open_array, struct __go_open_array)
-  __asm__(GOSYM_PREFIX "internal_bytealg.Compare")
+  __asm__(GOSYM_PREFIX "internal..z2fbytealg.Compare")
   __attribute__((no_split_stack));
 
 intgo Compare(struct __go_open_array a, struct __go_open_array b)
@@ -67,7 +67,7 @@  intgo Compare(struct __go_open_array a,
 }
 
 _Bool Equal(struct __go_open_array, struct __go_open_array)
-  __asm__(GOSYM_PREFIX "internal_bytealg.Equal")
+  __asm__(GOSYM_PREFIX "internal..z2fbytealg.Equal")
   __attribute__((no_split_stack));
 
 _Bool Equal(struct __go_open_array a, struct __go_open_array b)
@@ -82,7 +82,7 @@  _Bool Equal(struct __go_open_array a, st
 }
 
 intgo IndexByte(struct __go_open_array, byte)
-  __asm__(GOSYM_PREFIX "internal_bytealg.IndexByte")
+  __asm__(GOSYM_PREFIX "internal..z2fbytealg.IndexByte")
   __attribute__((no_split_stack));
 
 intgo IndexByte(struct __go_open_array b, byte c)
@@ -98,7 +98,7 @@  intgo IndexByte(struct __go_open_array b
 
 
 intgo IndexByteString(String, byte)
-  __asm__(GOSYM_PREFIX "internal_bytealg.IndexByteString")
+  __asm__(GOSYM_PREFIX "internal..z2fbytealg.IndexByteString")
   __attribute__((no_split_stack));
 
 intgo IndexByteString(String s, byte c)
@@ -113,7 +113,7 @@  intgo IndexByteString(String s, byte c)
 }
 
 intgo Index(struct __go_open_array, struct __go_open_array)
-  __asm__(GOSYM_PREFIX "internal_bytealg.Index")
+  __asm__(GOSYM_PREFIX "internal..z2fbytealg.Index")
   __attribute__((no_split_stack));
 
 intgo Index(struct __go_open_array a, struct __go_open_array b)
@@ -128,7 +128,7 @@  intgo Index(struct __go_open_array a, st
 }
 
 intgo IndexString(String, String)
-  __asm__(GOSYM_PREFIX "internal_bytealg.IndexString")
+  __asm__(GOSYM_PREFIX "internal..z2fbytealg.IndexString")
   __attribute__((no_split_stack));
 
 intgo IndexString(String a, String b)
Index: libgo/go/internal/cpu/cpu_gccgo.c
===================================================================
--- libgo/go/internal/cpu/cpu_gccgo.c	(revision 265460)
+++ libgo/go/internal/cpu/cpu_gccgo.c	(working copy)
@@ -21,7 +21,7 @@  struct cpuid_ret {
 };
 
 struct cpuid_ret cpuid(uint32_t, uint32_t)
-  __asm__(GOSYM_PREFIX "internal_cpu.cpuid")
+  __asm__(GOSYM_PREFIX "internal..z2fcpu.cpuid")
   __attribute__((no_split_stack));
 
 struct cpuid_ret cpuid(uint32_t eaxArg, uint32_t ecxArg) {
@@ -45,7 +45,7 @@  struct xgetbv_ret {
 };
 
 struct xgetbv_ret xgetbv(void)
-  __asm__(GOSYM_PREFIX "internal_cpu.xgetbv")
+  __asm__(GOSYM_PREFIX "internal..z2fcpu.xgetbv")
   __attribute__((no_split_stack));
 
 #pragma GCC push_options
Index: libgo/go/runtime/atomic_pointer.go
===================================================================
--- libgo/go/runtime/atomic_pointer.go	(revision 265460)
+++ libgo/go/runtime/atomic_pointer.go	(working copy)
@@ -52,10 +52,10 @@  func casp(ptr *unsafe.Pointer, old, new
 // 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_atomic.StoreUintptr
+//go:linkname sync_atomic_StoreUintptr sync..z2fatomic.StoreUintptr
 func sync_atomic_StoreUintptr(ptr *uintptr, new uintptr)
 
-//go:linkname sync_atomic_StorePointer sync_atomic.StorePointer
+//go:linkname sync_atomic_StorePointer sync..z2fatomic.StorePointer
 //go:nosplit
 func sync_atomic_StorePointer(ptr *unsafe.Pointer, new unsafe.Pointer) {
 	if writeBarrier.enabled {
@@ -64,10 +64,10 @@  func sync_atomic_StorePointer(ptr *unsaf
 	sync_atomic_StoreUintptr((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
 }
 
-//go:linkname sync_atomic_SwapUintptr sync_atomic.SwapUintptr
+//go:linkname sync_atomic_SwapUintptr sync..z2fatomic.SwapUintptr
 func sync_atomic_SwapUintptr(ptr *uintptr, new uintptr) uintptr
 
-//go:linkname sync_atomic_SwapPointer sync_atomic.SwapPointer
+//go:linkname sync_atomic_SwapPointer sync..z2fatomic.SwapPointer
 //go:nosplit
 func sync_atomic_SwapPointer(ptr *unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer {
 	if writeBarrier.enabled {
@@ -77,10 +77,10 @@  func sync_atomic_SwapPointer(ptr *unsafe
 	return old
 }
 
-//go:linkname sync_atomic_CompareAndSwapUintptr sync_atomic.CompareAndSwapUintptr
+//go:linkname sync_atomic_CompareAndSwapUintptr sync..z2fatomic.CompareAndSwapUintptr
 func sync_atomic_CompareAndSwapUintptr(ptr *uintptr, old, new uintptr) bool
 
-//go:linkname sync_atomic_CompareAndSwapPointer sync_atomic.CompareAndSwapPointer
+//go:linkname sync_atomic_CompareAndSwapPointer sync..z2fatomic.CompareAndSwapPointer
 //go:nosplit
 func sync_atomic_CompareAndSwapPointer(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool {
 	if writeBarrier.enabled {
Index: libgo/go/runtime/cpuprof.go
===================================================================
--- libgo/go/runtime/cpuprof.go	(revision 265460)
+++ libgo/go/runtime/cpuprof.go	(working copy)
@@ -186,7 +186,7 @@  func CPUProfile() []byte {
 	panic("CPUProfile no longer available")
 }
 
-//go:linkname runtime_pprof_runtime_cyclesPerSecond runtime_pprof.runtime_cyclesPerSecond
+//go:linkname runtime_pprof_runtime_cyclesPerSecond runtime..z2fpprof.runtime_cyclesPerSecond
 func runtime_pprof_runtime_cyclesPerSecond() int64 {
 	return tickspersecond()
 }
@@ -197,7 +197,7 @@  func runtime_pprof_runtime_cyclesPerSeco
 // 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_pprof.readProfile
+//go:linkname runtime_pprof_readProfile runtime..z2fpprof.readProfile
 func runtime_pprof_readProfile() ([]uint64, []unsafe.Pointer, bool) {
 	lock(&cpuprof.lock)
 	log := cpuprof.log
Index: libgo/go/runtime/debug/stack_test.go
===================================================================
--- libgo/go/runtime/debug/stack_test.go	(revision 265460)
+++ libgo/go/runtime/debug/stack_test.go	(working copy)
@@ -51,10 +51,10 @@  func TestStack(t *testing.T) {
 		n++
 	}
 	n++
-	frame("stack.go", "runtime_debug.Stack")
+	frame("stack.go", "debug.Stack")
 	frame("stack_test.go", "ptrmethod")
 	frame("stack_test.go", "method")
-	frame("stack_test.go", "runtime_debug_test.TestStack")
+	frame("stack_test.go", "test.TestStack")
 	frame("testing.go", "")
 }
 
Index: libgo/go/runtime/heapdump.go
===================================================================
--- libgo/go/runtime/heapdump.go	(revision 265460)
+++ libgo/go/runtime/heapdump.go	(working copy)
@@ -16,7 +16,7 @@  import (
 	"unsafe"
 )
 
-//go:linkname runtime_debug_WriteHeapDump runtime_debug.WriteHeapDump
+//go:linkname runtime_debug_WriteHeapDump runtime..z2fdebug.WriteHeapDump
 func runtime_debug_WriteHeapDump(fd uintptr) {
 	stopTheWorld("write heap dump")
 
Index: libgo/go/runtime/internal/atomic/atomic.c
===================================================================
--- libgo/go/runtime/internal/atomic/atomic.c	(revision 265460)
+++ libgo/go/runtime/internal/atomic/atomic.c	(working copy)
@@ -7,7 +7,7 @@ 
 #include "runtime.h"
 
 uint32_t Load (uint32_t *ptr)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Load")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Load")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -17,7 +17,7 @@  Load (uint32_t *ptr)
 }
 
 void *Loadp (void *ptr)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Loadp")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Loadp")
   __attribute__ ((no_split_stack));
 
 void *
@@ -27,7 +27,7 @@  Loadp (void *ptr)
 }
 
 uint64_t Load64 (uint64_t *ptr)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Load64")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Load64")
   __attribute__ ((no_split_stack));
 
 uint64_t
@@ -39,7 +39,7 @@  Load64 (uint64_t *ptr)
 }
 
 uintptr_t Loaduintptr (uintptr_t *ptr)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Loaduintptr")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Loaduintptr")
   __attribute__ ((no_split_stack));
 
 uintptr_t
@@ -49,7 +49,7 @@  Loaduintptr (uintptr_t *ptr)
 }
 
 uintgo Loaduint (uintgo *ptr)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Loaduint")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Loaduint")
   __attribute__ ((no_split_stack));
 
 uintgo
@@ -59,7 +59,7 @@  Loaduint (uintgo *ptr)
 }
 
 int64_t Loadint64 (int64_t *ptr)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Loadint64")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Loadint64")
   __attribute__ ((no_split_stack));
 
 int64_t
@@ -71,7 +71,7 @@  Loadint64 (int64_t *ptr)
 }
 
 uint32_t Xadd (uint32_t *ptr, int32_t delta)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xadd")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xadd")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -81,7 +81,7 @@  Xadd (uint32_t *ptr, int32_t delta)
 }
 
 uint64_t Xadd64 (uint64_t *ptr, int64_t delta)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xadd64")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xadd64")
   __attribute__ ((no_split_stack));
 
 uint64_t
@@ -93,7 +93,7 @@  Xadd64 (uint64_t *ptr, int64_t delta)
 }
 
 uintptr_t Xadduintptr (uintptr_t *ptr, uintptr_t delta)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xadduintptr")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xadduintptr")
   __attribute__ ((no_split_stack));
 
 uintptr_t
@@ -103,7 +103,7 @@  Xadduintptr (uintptr_t *ptr, uintptr_t d
 }
 
 int64_t Xaddint64 (int64_t *ptr, int64_t delta)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xaddint64")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xaddint64")
   __attribute__ ((no_split_stack));
 
 int64_t
@@ -115,7 +115,7 @@  Xaddint64 (int64_t *ptr, int64_t delta)
 }
 
 uint32_t Xchg (uint32_t *ptr, uint32_t new)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xchg")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xchg")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -125,7 +125,7 @@  Xchg (uint32_t *ptr, uint32_t new)
 }
 
 uint64_t Xchg64 (uint64_t *ptr, uint64_t new)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xchg64")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xchg64")
   __attribute__ ((no_split_stack));
 
 uint64_t
@@ -137,7 +137,7 @@  Xchg64 (uint64_t *ptr, uint64_t new)
 }
 
 uintptr_t Xchguintptr (uintptr_t *ptr, uintptr_t new)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Xchguintptr")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Xchguintptr")
   __attribute__ ((no_split_stack));
 
 uintptr_t
@@ -147,7 +147,7 @@  Xchguintptr (uintptr_t *ptr, uintptr_t n
 }
 
 void And8 (uint8_t *ptr, uint8_t val)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.And8")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.And8")
   __attribute__ ((no_split_stack));
 
 void
@@ -157,7 +157,7 @@  And8 (uint8_t *ptr, uint8_t val)
 }
 
 void Or8 (uint8_t *ptr, uint8_t val)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Or8")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Or8")
   __attribute__ ((no_split_stack));
 
 void
@@ -167,7 +167,7 @@  Or8 (uint8_t *ptr, uint8_t val)
 }
 
 _Bool Cas (uint32_t *ptr, uint32_t old, uint32_t new)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Cas")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Cas")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -177,7 +177,7 @@  Cas (uint32_t *ptr, uint32_t old, uint32
 }
 
 _Bool Cas64 (uint64_t *ptr, uint64_t old, uint64_t new)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Cas64")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Cas64")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -189,7 +189,7 @@  Cas64 (uint64_t *ptr, uint64_t old, uint
 }
 
 _Bool Casp1 (void **ptr, void *old, void *new)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Casp1")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Casp1")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -199,7 +199,7 @@  Casp1 (void **ptr, void *old, void *new)
 }
 
 _Bool Casuintptr (uintptr_t *ptr, uintptr_t old, uintptr_t new)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Casuintptr")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Casuintptr")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -209,7 +209,7 @@  Casuintptr (uintptr_t *ptr, uintptr_t ol
 }
 
 void Store (uint32_t *ptr, uint32_t val)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Store")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Store")
   __attribute__ ((no_split_stack));
 
 void
@@ -219,7 +219,7 @@  Store (uint32_t *ptr, uint32_t val)
 }
 
 void Store64 (uint64_t *ptr, uint64_t val)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Store64")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Store64")
   __attribute__ ((no_split_stack));
 
 void
@@ -231,7 +231,7 @@  Store64 (uint64_t *ptr, uint64_t val)
 }
 
 void Storeuintptr (uintptr_t *ptr, uintptr_t val)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.Storeuintptr")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.Storeuintptr")
   __attribute__ ((no_split_stack));
 
 void
@@ -241,7 +241,7 @@  Storeuintptr (uintptr_t *ptr, uintptr_t
 }
 
 void StorepNoWB (void *ptr, void *val)
-  __asm__ (GOSYM_PREFIX "runtime_internal_atomic.StorepNoWB")
+  __asm__ (GOSYM_PREFIX "runtime..z2finternal..z2fatomic.StorepNoWB")
   __attribute__ ((no_split_stack));
 
 void
Index: libgo/go/runtime/mgc.go
===================================================================
--- libgo/go/runtime/mgc.go	(revision 265460)
+++ libgo/go/runtime/mgc.go	(working copy)
@@ -219,7 +219,7 @@  func gcenable() {
 	memstats.enablegc = true // now that runtime is initialized, GC is okay
 }
 
-//go:linkname setGCPercent runtime_debug.setGCPercent
+//go:linkname setGCPercent runtime..z2fdebug.setGCPercent
 func setGCPercent(in int32) (out int32) {
 	lock(&mheap_.lock)
 	out = gcpercent
Index: libgo/go/runtime/mheap.go
===================================================================
--- libgo/go/runtime/mheap.go	(revision 265460)
+++ libgo/go/runtime/mheap.go	(working copy)
@@ -1165,7 +1165,7 @@  func (h *mheap) scavenge(k int32, now, l
 	}
 }
 
-//go:linkname runtime_debug_freeOSMemory runtime_debug.freeOSMemory
+//go:linkname runtime_debug_freeOSMemory runtime..z2fdebug.freeOSMemory
 func runtime_debug_freeOSMemory() {
 	GC()
 	systemstack(func() { mheap_.scavenge(-1, ^uint64(0), 0) })
Index: libgo/go/runtime/mstats.go
===================================================================
--- libgo/go/runtime/mstats.go	(revision 265460)
+++ libgo/go/runtime/mstats.go	(working copy)
@@ -477,7 +477,7 @@  func readmemstats_m(stats *MemStats) {
 	stats.StackSys += stats.StackInuse
 }
 
-//go:linkname readGCStats runtime_debug.readGCStats
+//go:linkname readGCStats runtime..z2fdebug.readGCStats
 func readGCStats(pauses *[]uint64) {
 	systemstack(func() {
 		readGCStats_m(pauses)
Index: libgo/go/runtime/net_plan9.go
===================================================================
--- libgo/go/runtime/net_plan9.go	(revision 265460)
+++ libgo/go/runtime/net_plan9.go	(working copy)
@@ -8,12 +8,12 @@  import (
 	_ "unsafe"
 )
 
-//go:linkname runtime_ignoreHangup internal_poll.runtime_ignoreHangup
+//go:linkname runtime_ignoreHangup internal..z2fpoll.runtime_ignoreHangup
 func runtime_ignoreHangup() {
 	getg().m.ignoreHangup = true
 }
 
-//go:linkname runtime_unignoreHangup internal_poll.runtime_unignoreHangup
+//go:linkname runtime_unignoreHangup internal..z2fpoll.runtime_unignoreHangup
 func runtime_unignoreHangup(sig string) {
 	getg().m.ignoreHangup = false
 }
Index: libgo/go/runtime/netpoll.go
===================================================================
--- libgo/go/runtime/netpoll.go	(revision 265460)
+++ libgo/go/runtime/netpoll.go	(working copy)
@@ -85,7 +85,7 @@  var (
 	netpollWaiters uint32
 )
 
-//go:linkname poll_runtime_pollServerInit internal_poll.runtime_pollServerInit
+//go:linkname poll_runtime_pollServerInit internal..z2fpoll.runtime_pollServerInit
 func poll_runtime_pollServerInit() {
 	netpollinit()
 	atomic.Store(&netpollInited, 1)
@@ -95,7 +95,7 @@  func netpollinited() bool {
 	return atomic.Load(&netpollInited) != 0
 }
 
-//go:linkname poll_runtime_pollServerDescriptor internal_poll.runtime_pollServerDescriptor
+//go:linkname poll_runtime_pollServerDescriptor internal..z2fpoll.runtime_pollServerDescriptor
 
 // poll_runtime_pollServerDescriptor returns the descriptor being used,
 // or ^uintptr(0) if the system does not use a poll descriptor.
@@ -103,7 +103,7 @@  func poll_runtime_pollServerDescriptor()
 	return netpolldescriptor()
 }
 
-//go:linkname poll_runtime_pollOpen internal_poll.runtime_pollOpen
+//go:linkname poll_runtime_pollOpen internal..z2fpoll.runtime_pollOpen
 func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) {
 	pd := pollcache.alloc()
 	lock(&pd.lock)
@@ -127,7 +127,7 @@  func poll_runtime_pollOpen(fd uintptr) (
 	return pd, int(errno)
 }
 
-//go:linkname poll_runtime_pollClose internal_poll.runtime_pollClose
+//go:linkname poll_runtime_pollClose internal..z2fpoll.runtime_pollClose
 func poll_runtime_pollClose(pd *pollDesc) {
 	if !pd.closing {
 		throw("runtime: close polldesc w/o unblock")
@@ -149,7 +149,7 @@  func (c *pollCache) free(pd *pollDesc) {
 	unlock(&c.lock)
 }
 
-//go:linkname poll_runtime_pollReset internal_poll.runtime_pollReset
+//go:linkname poll_runtime_pollReset internal..z2fpoll.runtime_pollReset
 func poll_runtime_pollReset(pd *pollDesc, mode int) int {
 	err := netpollcheckerr(pd, int32(mode))
 	if err != 0 {
@@ -163,7 +163,7 @@  func poll_runtime_pollReset(pd *pollDesc
 	return 0
 }
 
-//go:linkname poll_runtime_pollWait internal_poll.runtime_pollWait
+//go:linkname poll_runtime_pollWait internal..z2fpoll.runtime_pollWait
 func poll_runtime_pollWait(pd *pollDesc, mode int) int {
 	err := netpollcheckerr(pd, int32(mode))
 	if err != 0 {
@@ -185,7 +185,7 @@  func poll_runtime_pollWait(pd *pollDesc,
 	return 0
 }
 
-//go:linkname poll_runtime_pollWaitCanceled internal_poll.runtime_pollWaitCanceled
+//go:linkname poll_runtime_pollWaitCanceled internal..z2fpoll.runtime_pollWaitCanceled
 func poll_runtime_pollWaitCanceled(pd *pollDesc, mode int) {
 	// This function is used only on windows after a failed attempt to cancel
 	// a pending async IO operation. Wait for ioready, ignore closing or timeouts.
@@ -193,7 +193,7 @@  func poll_runtime_pollWaitCanceled(pd *p
 	}
 }
 
-//go:linkname poll_runtime_pollSetDeadline internal_poll.runtime_pollSetDeadline
+//go:linkname poll_runtime_pollSetDeadline internal..z2fpoll.runtime_pollSetDeadline
 func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
 	lock(&pd.lock)
 	if pd.closing {
@@ -263,7 +263,7 @@  func poll_runtime_pollSetDeadline(pd *po
 	}
 }
 
-//go:linkname poll_runtime_pollUnblock internal_poll.runtime_pollUnblock
+//go:linkname poll_runtime_pollUnblock internal..z2fpoll.runtime_pollUnblock
 func poll_runtime_pollUnblock(pd *pollDesc) {
 	lock(&pd.lock)
 	if pd.closing {
Index: libgo/go/runtime/pprof/mprof_test.go
===================================================================
--- libgo/go/runtime/pprof/mprof_test.go	(revision 265460)
+++ libgo/go/runtime/pprof/mprof_test.go	(working copy)
@@ -87,19 +87,19 @@  func TestMemoryProfiler(t *testing.T) {
 
 		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:40
-#	0x[0-9,a-f]+	runtime_pprof\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/mprof_test\.go:74
+#	0x[0-9,a-f]+	runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/mprof_test\.go:74
 `, 32*memoryProfilerRun, 1024*memoryProfilerRun, 32*memoryProfilerRun, 1024*memoryProfilerRun),
 
 		fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f x]+
 #	0x[0-9,a-f]+	pprof\.allocateTransient1M\+0x[0-9,a-f]+	.*/mprof_test.go:21
-#	0x[0-9,a-f]+	runtime_pprof\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/mprof_test.go:72
+#	0x[0-9,a-f]+	runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/mprof_test.go:72
 `, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun),
 
 		// This should start with "0: 0" but gccgo's imprecise
 		// GC means that sometimes the value is not collected.
 		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:27
-#	0x[0-9,a-f]+	runtime_pprof\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/mprof_test.go:73
+#	0x[0-9,a-f]+	runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/mprof_test.go:73
 `, memoryProfilerRun, (2<<20)*memoryProfilerRun, memoryProfilerRun, (2<<20)*memoryProfilerRun),
 
 		// This should start with "0: 0" but gccgo's imprecise
Index: libgo/go/runtime/proc.go
===================================================================
--- libgo/go/runtime/proc.go	(revision 265460)
+++ libgo/go/runtime/proc.go	(working copy)
@@ -4670,7 +4670,7 @@  func runqsteal(_p_, p2 *p, stealRunNextG
 	return gp
 }
 
-//go:linkname setMaxThreads runtime_debug.setMaxThreads
+//go:linkname setMaxThreads runtime..z2fdebug.setMaxThreads
 func setMaxThreads(in int) (out int) {
 	lock(&sched.lock)
 	out = int(sched.maxmcount)
@@ -4716,13 +4716,13 @@  func sync_runtime_procUnpin() {
 	procUnpin()
 }
 
-//go:linkname sync_atomic_runtime_procPin sync_atomic.runtime_procPin
+//go:linkname sync_atomic_runtime_procPin sync..z2fatomic.runtime_procPin
 //go:nosplit
 func sync_atomic_runtime_procPin() int {
 	return procPin()
 }
 
-//go:linkname sync_atomic_runtime_procUnpin sync_atomic.runtime_procUnpin
+//go:linkname sync_atomic_runtime_procUnpin sync..z2fatomic.runtime_procUnpin
 //go:nosplit
 func sync_atomic_runtime_procUnpin() {
 	procUnpin()
Index: libgo/go/runtime/proflabel.go
===================================================================
--- libgo/go/runtime/proflabel.go	(revision 265460)
+++ libgo/go/runtime/proflabel.go	(working copy)
@@ -8,7 +8,7 @@  import "unsafe"
 
 var labelSync uintptr
 
-//go:linkname runtime_setProfLabel runtime_pprof.runtime_setProfLabel
+//go:linkname runtime_setProfLabel runtime..z2fpprof.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 @@  func runtime_setProfLabel(labels unsafe.
 	getg().labels = labels
 }
 
-//go:linkname runtime_getProfLabel runtime_pprof.runtime_getProfLabel
+//go:linkname runtime_getProfLabel runtime..z2fpprof.runtime_getProfLabel
 func runtime_getProfLabel() unsafe.Pointer {
 	return getg().labels
 }
Index: libgo/go/runtime/rdebug.go
===================================================================
--- libgo/go/runtime/rdebug.go	(revision 265460)
+++ libgo/go/runtime/rdebug.go	(working copy)
@@ -11,14 +11,14 @@  import _ "unsafe" // for go:linkname
 // maxstacksize.
 var maxstacksize uintptr = 1 << 20 // enough until runtime.main sets it for real
 
-//go:linkname setMaxStack runtime_debug.setMaxStack
+//go:linkname setMaxStack runtime..z2fdebug.setMaxStack
 func setMaxStack(in int) (out int) {
 	out = int(maxstacksize)
 	maxstacksize = uintptr(in)
 	return out
 }
 
-//go:linkname setPanicOnFault runtime_debug.setPanicOnFault
+//go:linkname setPanicOnFault runtime..z2fdebug.setPanicOnFault
 func setPanicOnFault(new bool) (old bool) {
 	_g_ := getg()
 	old = _g_.paniconfault
Index: libgo/go/runtime/runtime1.go
===================================================================
--- libgo/go/runtime/runtime1.go	(revision 265460)
+++ libgo/go/runtime/runtime1.go	(working copy)
@@ -413,7 +413,7 @@  func parsedebugvars() {
 	traceback_env = traceback_cache
 }
 
-//go:linkname setTraceback runtime_debug.SetTraceback
+//go:linkname setTraceback runtime..z2fdebug.SetTraceback
 func setTraceback(level string) {
 	var t uint32
 	switch level {
Index: libgo/go/runtime/sema.go
===================================================================
--- libgo/go/runtime/sema.go	(revision 265460)
+++ libgo/go/runtime/sema.go	(working copy)
@@ -56,7 +56,7 @@  func sync_runtime_Semacquire(addr *uint3
 	semacquire1(addr, false, semaBlockProfile)
 }
 
-//go:linkname poll_runtime_Semacquire internal_poll.runtime_Semacquire
+//go:linkname poll_runtime_Semacquire internal..z2fpoll.runtime_Semacquire
 func poll_runtime_Semacquire(addr *uint32) {
 	semacquire1(addr, false, semaBlockProfile)
 }
@@ -71,7 +71,7 @@  func sync_runtime_SemacquireMutex(addr *
 	semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile)
 }
 
-//go:linkname poll_runtime_Semrelease internal_poll.runtime_Semrelease
+//go:linkname poll_runtime_Semrelease internal..z2fpoll.runtime_Semrelease
 func poll_runtime_Semrelease(addr *uint32) {
 	semrelease(addr)
 }
Index: libgo/go/runtime/sigqueue.go
===================================================================
--- libgo/go/runtime/sigqueue.go	(revision 265460)
+++ libgo/go/runtime/sigqueue.go	(working copy)
@@ -117,7 +117,7 @@  Send:
 
 // Called to receive the next queued signal.
 // Must only be called from a single goroutine at a time.
-//go:linkname signal_recv os_signal.signal_recv
+//go:linkname signal_recv os..z2fsignal.signal_recv
 func signal_recv() uint32 {
 	for {
 		// Serve any signals from local copy.
@@ -161,7 +161,7 @@  func signal_recv() uint32 {
 // 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_signal.signalWaitUntilIdle
+//go:linkname signalWaitUntilIdle os..z2fsignal.signalWaitUntilIdle
 func signalWaitUntilIdle() {
 	// Although the signals we care about have been removed from
 	// sig.wanted, it is possible that another thread has received
@@ -181,7 +181,7 @@  func signalWaitUntilIdle() {
 }
 
 // Must only be called from a single goroutine at a time.
-//go:linkname signal_enable os_signal.signal_enable
+//go:linkname signal_enable os..z2fsignal.signal_enable
 func signal_enable(s uint32) {
 	if !sig.inuse {
 		// The first call to signal_enable is for us
@@ -208,7 +208,7 @@  func signal_enable(s uint32) {
 }
 
 // Must only be called from a single goroutine at a time.
-//go:linkname signal_disable os_signal.signal_disable
+//go:linkname signal_disable os..z2fsignal.signal_disable
 func signal_disable(s uint32) {
 	if s >= uint32(len(sig.wanted)*32) {
 		return
@@ -221,7 +221,7 @@  func signal_disable(s uint32) {
 }
 
 // Must only be called from a single goroutine at a time.
-//go:linkname signal_ignore os_signal.signal_ignore
+//go:linkname signal_ignore os..z2fsignal.signal_ignore
 func signal_ignore(s uint32) {
 	if s >= uint32(len(sig.wanted)*32) {
 		return
@@ -248,7 +248,7 @@  func sigInitIgnored(s uint32) {
 }
 
 // Checked by signal handlers.
-//go:linkname signal_ignored os_signal.signal_ignored
+//go:linkname signal_ignored os..z2fsignal.signal_ignored
 func signal_ignored(s uint32) bool {
 	i := atomic.Load(&sig.ignored[s/32])
 	return i&(1<<(s&31)) != 0
Index: libgo/go/runtime/symtab.go
===================================================================
--- libgo/go/runtime/symtab.go	(revision 265460)
+++ libgo/go/runtime/symtab.go	(working copy)
@@ -83,6 +83,11 @@  func (ci *Frames) Next() (frame Frame, m
 	if function == "" && file == "" {
 		return Frame{}, more
 	}
+
+	// Demangle function name if needed.
+	function = demangleSymbol(function)
+
+	// Create entry.
 	entry := funcentry(pc - 1)
 	f := &Func{name: function, entry: entry}
 
@@ -182,6 +187,75 @@  func (f *Func) FileLine(pc uintptr) (fil
 	return file, line
 }
 
+func hexval(b byte) uint {
+	if b >= '0' && b <= '9' {
+		return uint(b - '0')
+	}
+	if b >= 'a' && b <= 'f' {
+		return uint(b-'a') + 10
+	}
+	return 0
+}
+
+func hexDigitsToRune(digits []byte, ndig int) rune {
+	result := uint(0)
+	for i := 0; i < ndig; i++ {
+		result <<= uint(4)
+		result |= hexval(digits[i])
+	}
+	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.
+
+func decodeIdentifier(bsl []byte) int {
+	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
+			}
+		}
+		bsl[j] = b
+		j += 1
+	}
+	return j
+}
+
+// Demangle a function symbol. Applies the reverse of go_encode_id()
+// as used in the compiler.
+
+func demangleSymbol(s string) string {
+	bsl := []byte(s)
+	nchars := decodeIdentifier(bsl)
+	bsl = bsl[:nchars]
+	return string(bsl)
+}
+
 // implemented in go-caller.c
 func funcfileline(uintptr, int32) (string, string, int)
 func funcentry(uintptr) uintptr
Index: libgo/go/runtime/time.go
===================================================================
--- libgo/go/runtime/time.go	(revision 265460)
+++ libgo/go/runtime/time.go	(working copy)
@@ -441,7 +441,7 @@  func badTimer() {
 
 // Entry points for net, time to call nanotime.
 
-//go:linkname poll_runtimeNano internal_poll.runtimeNano
+//go:linkname poll_runtimeNano internal..z2fpoll.runtimeNano
 func poll_runtimeNano() int64 {
 	return nanotime()
 }
Index: libgo/go/runtime/trace.go
===================================================================
--- libgo/go/runtime/trace.go	(revision 265460)
+++ libgo/go/runtime/trace.go	(working copy)
@@ -1143,7 +1143,7 @@  func traceNextGC() {
 // To access runtime functions from runtime/trace.
 // See runtime/trace/annotation.go
 
-//go:linkname trace_userTaskCreate runtime_trace.userTaskCreate
+//go:linkname trace_userTaskCreate runtime..z2ftrace.userTaskCreate
 func trace_userTaskCreate(id, parentID uint64, taskType string) {
 	if !trace.enabled {
 		return
@@ -1161,12 +1161,12 @@  func trace_userTaskCreate(id, parentID u
 	traceReleaseBuffer(pid)
 }
 
-//go:linkname trace_userTaskEnd runtime_trace.userTaskEnd
+//go:linkname trace_userTaskEnd runtime..z2ftrace.userTaskEnd
 func trace_userTaskEnd(id uint64) {
 	traceEvent(traceEvUserTaskEnd, 2, id)
 }
 
-//go:linkname trace_userRegion runtime_trace.userRegion
+//go:linkname trace_userRegion runtime..z2ftrace.userRegion
 func trace_userRegion(id, mode uint64, name string) {
 	if !trace.enabled {
 		return
@@ -1183,7 +1183,7 @@  func trace_userRegion(id, mode uint64, n
 	traceReleaseBuffer(pid)
 }
 
-//go:linkname trace_userLog runtime_trace.userLog
+//go:linkname trace_userLog runtime..z2ftrace.userLog
 func trace_userLog(id uint64, category, message string) {
 	if !trace.enabled {
 		return
Index: libgo/go/runtime/traceback_gccgo.go
===================================================================
--- libgo/go/runtime/traceback_gccgo.go	(revision 265460)
+++ libgo/go/runtime/traceback_gccgo.go	(working copy)
@@ -110,9 +110,14 @@  func showframe(name string, gp *g) bool
 }
 
 // isExportedRuntime reports whether name is an exported runtime function.
-// It is only for runtime functions, so ASCII A-Z is fine.
+// 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".
 func isExportedRuntime(name string) bool {
 	const n = len("runtime.")
+	if hasprefix(name, "runtime..z2f") {
+		return true
+	}
 	return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z'
 }
 
Index: libgo/go/sync/atomic/atomic.c
===================================================================
--- libgo/go/sync/atomic/atomic.c	(revision 265460)
+++ libgo/go/sync/atomic/atomic.c	(working copy)
@@ -9,7 +9,7 @@ 
 #include "runtime.h"
 
 int32_t SwapInt32 (int32_t *, int32_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.SwapInt32")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.SwapInt32")
   __attribute__ ((no_split_stack));
 
 int32_t
@@ -19,7 +19,7 @@  SwapInt32 (int32_t *addr, int32_t new)
 }
 
 int64_t SwapInt64 (int64_t *, int64_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.SwapInt64")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.SwapInt64")
   __attribute__ ((no_split_stack));
 
 int64_t
@@ -31,7 +31,7 @@  SwapInt64 (int64_t *addr, int64_t new)
 }
 
 uint32_t SwapUint32 (uint32_t *, uint32_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.SwapUint32")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.SwapUint32")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -41,7 +41,7 @@  SwapUint32 (uint32_t *addr, uint32_t new
 }
 
 uint64_t SwapUint64 (uint64_t *, uint64_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.SwapUint64")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.SwapUint64")
   __attribute__ ((no_split_stack));
 
 uint64_t
@@ -53,7 +53,7 @@  SwapUint64 (uint64_t *addr, uint64_t new
 }
 
 uintptr_t SwapUintptr (uintptr_t *, uintptr_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.SwapUintptr")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.SwapUintptr")
   __attribute__ ((no_split_stack));
 
 uintptr_t
@@ -63,7 +63,7 @@  SwapUintptr (uintptr_t *addr, uintptr_t
 }
 
 _Bool CompareAndSwapInt32 (int32_t *, int32_t, int32_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapInt32")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.CompareAndSwapInt32")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -73,7 +73,7 @@  CompareAndSwapInt32 (int32_t *val, int32
 }
 
 _Bool CompareAndSwapInt64 (int64_t *, int64_t, int64_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapInt64")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.CompareAndSwapInt64")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -85,7 +85,7 @@  CompareAndSwapInt64 (int64_t *val, int64
 }
 
 _Bool CompareAndSwapUint32 (uint32_t *, uint32_t, uint32_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUint32")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.CompareAndSwapUint32")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -95,7 +95,7 @@  CompareAndSwapUint32 (uint32_t *val, uin
 }
 
 _Bool CompareAndSwapUint64 (uint64_t *, uint64_t, uint64_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUint64")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.CompareAndSwapUint64")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -107,7 +107,7 @@  CompareAndSwapUint64 (uint64_t *val, uin
 }
 
 _Bool CompareAndSwapUintptr (uintptr_t *, uintptr_t, uintptr_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUintptr")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.CompareAndSwapUintptr")
   __attribute__ ((no_split_stack));
 
 _Bool
@@ -117,7 +117,7 @@  CompareAndSwapUintptr (uintptr_t *val, u
 }
 
 int32_t AddInt32 (int32_t *, int32_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.AddInt32")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.AddInt32")
   __attribute__ ((no_split_stack));
 
 int32_t
@@ -127,7 +127,7 @@  AddInt32 (int32_t *val, int32_t delta)
 }
 
 uint32_t AddUint32 (uint32_t *, uint32_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.AddUint32")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.AddUint32")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -137,7 +137,7 @@  AddUint32 (uint32_t *val, uint32_t delta
 }
 
 int64_t AddInt64 (int64_t *, int64_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.AddInt64")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.AddInt64")
   __attribute__ ((no_split_stack));
 
 int64_t
@@ -149,7 +149,7 @@  AddInt64 (int64_t *val, int64_t delta)
 }
 
 uint64_t AddUint64 (uint64_t *, uint64_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.AddUint64")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.AddUint64")
   __attribute__ ((no_split_stack));
 
 uint64_t
@@ -161,7 +161,7 @@  AddUint64 (uint64_t *val, uint64_t delta
 }
 
 uintptr_t AddUintptr (uintptr_t *, uintptr_t)
-  __asm__ (GOSYM_PREFIX "sync_atomic.AddUintptr")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.AddUintptr")
   __attribute__ ((no_split_stack));
 
 uintptr_t
@@ -171,7 +171,7 @@  AddUintptr (uintptr_t *val, uintptr_t de
 }
 
 int32_t LoadInt32 (int32_t *addr)
-  __asm__ (GOSYM_PREFIX "sync_atomic.LoadInt32")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.LoadInt32")
   __attribute__ ((no_split_stack));
 
 int32_t
@@ -186,7 +186,7 @@  LoadInt32 (int32_t *addr)
 }
 
 int64_t LoadInt64 (int64_t *addr)
-  __asm__ (GOSYM_PREFIX "sync_atomic.LoadInt64")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.LoadInt64")
   __attribute__ ((no_split_stack));
 
 int64_t
@@ -203,7 +203,7 @@  LoadInt64 (int64_t *addr)
 }
 
 uint32_t LoadUint32 (uint32_t *addr)
-  __asm__ (GOSYM_PREFIX "sync_atomic.LoadUint32")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.LoadUint32")
   __attribute__ ((no_split_stack));
 
 uint32_t
@@ -218,7 +218,7 @@  LoadUint32 (uint32_t *addr)
 }
 
 uint64_t LoadUint64 (uint64_t *addr)
-  __asm__ (GOSYM_PREFIX "sync_atomic.LoadUint64")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.LoadUint64")
   __attribute__ ((no_split_stack));
 
 uint64_t
@@ -235,7 +235,7 @@  LoadUint64 (uint64_t *addr)
 }
 
 uintptr_t LoadUintptr (uintptr_t *addr)
-  __asm__ (GOSYM_PREFIX "sync_atomic.LoadUintptr")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.LoadUintptr")
   __attribute__ ((no_split_stack));
 
 uintptr_t
@@ -250,7 +250,7 @@  LoadUintptr (uintptr_t *addr)
 }
 
 void *LoadPointer (void **addr)
-  __asm__ (GOSYM_PREFIX "sync_atomic.LoadPointer")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.LoadPointer")
   __attribute__ ((no_split_stack));
 
 void *
@@ -265,7 +265,7 @@  LoadPointer (void **addr)
 }
 
 void StoreInt32 (int32_t *addr, int32_t val)
-  __asm__ (GOSYM_PREFIX "sync_atomic.StoreInt32")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.StoreInt32")
   __attribute__ ((no_split_stack));
 
 void
@@ -279,7 +279,7 @@  StoreInt32 (int32_t *addr, int32_t val)
 }
 
 void StoreInt64 (int64_t *addr, int64_t val)
-  __asm__ (GOSYM_PREFIX "sync_atomic.StoreInt64")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.StoreInt64")
   __attribute__ ((no_split_stack));
 
 void
@@ -295,7 +295,7 @@  StoreInt64 (int64_t *addr, int64_t val)
 }
 
 void StoreUint32 (uint32_t *addr, uint32_t val)
-  __asm__ (GOSYM_PREFIX "sync_atomic.StoreUint32")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.StoreUint32")
   __attribute__ ((no_split_stack));
 
 void
@@ -309,7 +309,7 @@  StoreUint32 (uint32_t *addr, uint32_t va
 }
 
 void StoreUint64 (uint64_t *addr, uint64_t val)
-  __asm__ (GOSYM_PREFIX "sync_atomic.StoreUint64")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.StoreUint64")
   __attribute__ ((no_split_stack));
 
 void
@@ -325,7 +325,7 @@  StoreUint64 (uint64_t *addr, uint64_t va
 }
 
 void StoreUintptr (uintptr_t *addr, uintptr_t val)
-  __asm__ (GOSYM_PREFIX "sync_atomic.StoreUintptr")
+  __asm__ (GOSYM_PREFIX "sync..z2fatomic.StoreUintptr")
   __attribute__ ((no_split_stack));
 
 void
Index: libgo/testsuite/gotest
===================================================================
--- libgo/testsuite/gotest	(revision 265460)
+++ libgo/testsuite/gotest	(working copy)
@@ -504,6 +504,35 @@  localname() {
 	echo $1 | sed 's/^main\./__main__./'
 }
 
+# Takes a list of tests derived from 'nm' output (whose symbols are mangled)
+# and emits a demangled list of tests, using only the terminal package.
+# Example:
+#
+#    Original symbol:   foo/bar/leaf.Mumble
+#    Mangled symbol:    foo..z2fbar..z2fleaf.Mumble
+#    Returned:          leaf.Mumble
+#
+symtogo() {
+  local s=""
+  local result=""
+  local ndots=""
+  for tp in $*
+  do
+    s=$(echo $tp | sed -e 's/\.\.z2f/%/g' | sed -e 's/.*%//')
+    # screen out methods (X.Y.Z)
+    ndots=$(echo $s | sed -e 's/\./ /g' | wc -w)
+    if [ $ndots -ne 2 ]; then
+      continue
+    fi
+    if [ -z "${result}" ]; then
+      result="${s}"
+    else
+      result="${result} ${s}"
+    fi
+  done
+  echo "$result" | sed -e 's/ /\n/g'
+}
+
 {
 	text="T"
 
@@ -514,26 +543,27 @@  localname() {
 	    text="[TD]"
 	fi
 
-	symtogo='sed -e s/_test\([^A-Za-z0-9]\)/XXXtest\1/ -e s/.*_\([^_]*\.\)/\1/ -e s/XXXtest/_test/'
-
 	# test functions are named TestFoo
 	# the grep -v eliminates methods and other special names
 	# that have multiple dots.
 	pattern='Test([^a-z].*)?'
 	# The -p option tells GNU nm not to sort.
 	# The -v option tells Solaris nm to sort by value.
-	tests=$($NM -p -v _gotest_.o $xofile | egrep " $text .*\."$pattern'$' | grep -v '[^ ]\..*\.' | fgrep -v ' __go_' | sed 's/.* //' | $symtogo)
+        testsyms=$($NM -p -v _gotest_.o $xofile | egrep " $text .*\."$pattern'$' | fgrep -v ' __go_' | egrep -v '\.\.\w+$' | sed 's/.* //')
+        tests=$(symtogo "$testsyms")
 	if [ "x$tests" = x ]; then
 		echo 'gotest: warning: no tests matching '$pattern in _gotest_.o $xofile 1>&2
 		exit 2
 	fi
 	# benchmarks are named BenchmarkFoo.
 	pattern='Benchmark([^a-z].*)?'
-	benchmarks=$($NM -p -v _gotest_.o $xofile | egrep " $text .*\."$pattern'$' | grep -v '[^ ]\..*\.' | fgrep -v ' __go_' | sed 's/.* //' | $symtogo)
+	benchmarksyms=$($NM -p -v _gotest_.o $xofile | egrep " $text .*\."$pattern'$' | fgrep -v ' __go_' | egrep -v '\.\.\w+$' | sed 's/.* //')
+        benchmarks=$(symtogo "$benchmarksyms")
 
 	# examples are named ExampleFoo
 	pattern='Example([^a-z].*)?'
-	examples=$($NM -p -v _gotest_.o $xofile | egrep " $text .*\."$pattern'$' | grep -v '[^ ]\..*\.' | fgrep -v ' __go_' | sed 's/.* //' | $symtogo)
+	examplesyms=$($NM -p -v _gotest_.o $xofile | egrep " $text .*\."$pattern'$' | fgrep -v ' __go_' | egrep -v '\.\.\w+$' | sed 's/.* //')
+	examples=$(symtogo "$examplesyms")
 
 	# package spec
 	echo 'package main'