]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/cgo: handle new-style gccgo packagepath mangling
authorThan McIntosh <thanm@google.com>
Wed, 24 Oct 2018 13:25:05 +0000 (09:25 -0400)
committerThan McIntosh <thanm@google.com>
Fri, 26 Oct 2018 12:37:30 +0000 (12:37 +0000)
With https://golang.org/cl/135455, gccgo now uses a different mangling
scheme for package paths; add code to use this new scheme for function
and variable symbols. Since users sometimes use older versions of
gccgo with newer versions of go, perform a test at runtime to see
which mangling scheme is in effect for the version of 'gccgo' in the
path.

Updates #27534.

Change-Id: If7ecab06a72e1361129fe40ca6582070a3e8e737
Reviewed-on: https://go-review.googlesource.com/c/144418
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/cmd/cgo/main.go
src/cmd/cgo/out.go

index b6f059001f7b0a88833991ab605a8636037ea50e..5bcb9754d71e5c0d224554572f1d3222eaaadc89 100644 (file)
@@ -211,6 +211,8 @@ var exportHeader = flag.String("exportheader", "", "where to write export header
 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 8a26d5c0632101b6826f4ff769b9a9b594a10606..a93ff365b019cc3a4bb96c688323507d4ebd05d6 100644 (file)
@@ -15,7 +15,9 @@ import (
        "go/printer"
        "go/token"
        "io"
+       "io/ioutil"
        "os"
+       "os/exec"
        "path/filepath"
        "regexp"
        "sort"
@@ -1186,12 +1188,91 @@ func (p *Package) writeExportHeader(fgcch io.Writer) {
        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',
@@ -1200,14 +1281,32 @@ func (p *Package) gccgoSymbolPrefix() string {
                }
                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"
        }