diff options
Diffstat (limited to 'src/cmd/cgo/out.go')
| -rw-r--r-- | src/cmd/cgo/out.go | 339 |
1 files changed, 119 insertions, 220 deletions
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 50d2811f1b..11c53facf8 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -6,6 +6,7 @@ package main import ( "bytes" + "cmd/internal/pkgpath" "debug/elf" "debug/macho" "debug/pe" @@ -15,7 +16,6 @@ import ( "go/token" "internal/xcoff" "io" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -59,14 +59,14 @@ func (p *Package) writeDefs() { // Write C main file for using gcc to resolve imports. fmt.Fprintf(fm, "int main() { return 0; }\n") if *importRuntimeCgo { - fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }\n") + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*), void *a, int c, __SIZE_TYPE__ ctxt) { }\n") fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void) { return 0; }\n") fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__ ctxt) { }\n") fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n") } else { // If we're not importing runtime/cgo, we *are* runtime/cgo, // which provides these functions. We just need a prototype. - fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt);\n") + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*), void *a, int c, __SIZE_TYPE__ ctxt);\n") fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void);\n") fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__);\n") } @@ -108,6 +108,9 @@ func (p *Package) writeDefs() { sort.Strings(typedefNames) for _, name := range typedefNames { def := typedef[name] + if def.NotInHeap { + fmt.Fprintf(fgo2, "//go:notinheap\n") + } fmt.Fprintf(fgo2, "type %s ", name) // We don't have source info for these types, so write them out without source info. // Otherwise types would look like: @@ -183,7 +186,7 @@ func (p *Package) writeDefs() { 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") } @@ -334,6 +337,8 @@ func dynimport(obj string) { if s.Version != "" { targ += "#" + s.Version } + checkImportSymName(s.Name) + checkImportSymName(targ) fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library) } lib, _ := f.ImportedLibraries() @@ -349,6 +354,7 @@ func dynimport(obj string) { if len(s) > 0 && s[0] == '_' { s = s[1:] } + checkImportSymName(s) fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s, s, "") } lib, _ := f.ImportedLibraries() @@ -363,6 +369,8 @@ func dynimport(obj string) { for _, s := range sym { ss := strings.Split(s, ":") name := strings.Split(ss[0], "@")[0] + checkImportSymName(name) + checkImportSymName(ss[0]) fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", name, ss[0], strings.ToLower(ss[1])) } return @@ -380,6 +388,7 @@ func dynimport(obj string) { // Go symbols. continue } + checkImportSymName(s.Name) fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, s.Name, s.Library) } lib, err := f.ImportedLibraries() @@ -395,6 +404,23 @@ func dynimport(obj string) { fatalf("cannot parse %s as ELF, Mach-O, PE or XCOFF", obj) } +// checkImportSymName checks a symbol name we are going to emit as part +// of a //go:cgo_import_dynamic pragma. These names come from object +// files, so they may be corrupt. We are going to emit them unquoted, +// so while they don't need to be valid symbol names (and in some cases, +// involving symbol versions, they won't be) they must contain only +// graphic characters and must not contain Go comments. +func checkImportSymName(s string) { + for _, c := range s { + if !unicode.IsGraphic(c) || unicode.IsSpace(c) { + fatalf("dynamic symbol %q contains unsupported character", s) + } + } + if strings.Index(s, "//") >= 0 || strings.Index(s, "/*") >= 0 { + fatalf("dynamic symbol %q contains Go comment") + } +} + // Construct a gcc struct matching the gc argument frame. // Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes. // These assumptions are checked by the gccProlog. @@ -849,7 +875,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprintf(fgcc, "#pragma GCC diagnostic ignored \"-Wpragmas\"\n") fmt.Fprintf(fgcc, "#pragma GCC diagnostic ignored \"-Waddress-of-packed-member\"\n") - fmt.Fprintf(fgcc, "extern void crosscall2(void (*fn)(void *, int, __SIZE_TYPE__), void *, int, __SIZE_TYPE__);\n") + fmt.Fprintf(fgcc, "extern void crosscall2(void (*fn)(void *), void *, int, __SIZE_TYPE__);\n") fmt.Fprintf(fgcc, "extern __SIZE_TYPE__ _cgo_wait_runtime_init_done(void);\n") fmt.Fprintf(fgcc, "extern void _cgo_release_context(__SIZE_TYPE__);\n\n") fmt.Fprintf(fgcc, "extern char* _cgo_topofstack(void);") @@ -859,59 +885,48 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { for _, exp := range p.ExpFunc { fn := exp.Func - // Construct a gcc struct matching the gc argument and - // result frame. The gcc struct will be compiled with - // __attribute__((packed)) so all padding must be accounted - // for explicitly. + // Construct a struct that will be used to communicate + // arguments from C to Go. The C and Go definitions + // just have to agree. The gcc struct will be compiled + // with __attribute__((packed)) so all padding must be + // accounted for explicitly. ctype := "struct {\n" + gotype := new(bytes.Buffer) + fmt.Fprintf(gotype, "struct {\n") off := int64(0) npad := 0 - if fn.Recv != nil { - t := p.cgoType(fn.Recv.List[0].Type) - ctype += fmt.Sprintf("\t\t%s recv;\n", t.C) + argField := func(typ ast.Expr, namePat string, args ...interface{}) { + name := fmt.Sprintf(namePat, args...) + t := p.cgoType(typ) + if off%t.Align != 0 { + pad := t.Align - off%t.Align + ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + off += pad + npad++ + } + ctype += fmt.Sprintf("\t\t%s %s;\n", t.C, name) + fmt.Fprintf(gotype, "\t\t%s ", name) + noSourceConf.Fprint(gotype, fset, typ) + fmt.Fprintf(gotype, "\n") off += t.Size } + if fn.Recv != nil { + argField(fn.Recv.List[0].Type, "recv") + } fntype := fn.Type forFieldList(fntype.Params, func(i int, aname string, atype ast.Expr) { - t := p.cgoType(atype) - if off%t.Align != 0 { - pad := t.Align - off%t.Align - ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - ctype += fmt.Sprintf("\t\t%s p%d;\n", t.C, i) - off += t.Size + argField(atype, "p%d", i) }) - if off%p.PtrSize != 0 { - pad := p.PtrSize - off%p.PtrSize - ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } forFieldList(fntype.Results, func(i int, aname string, atype ast.Expr) { - t := p.cgoType(atype) - if off%t.Align != 0 { - pad := t.Align - off%t.Align - ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } - ctype += fmt.Sprintf("\t\t%s r%d;\n", t.C, i) - off += t.Size + argField(atype, "r%d", i) }) - if off%p.PtrSize != 0 { - pad := p.PtrSize - off%p.PtrSize - ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) - off += pad - npad++ - } if ctype == "struct {\n" { ctype += "\t\tchar unused;\n" // avoid empty struct } ctype += "\t}" + fmt.Fprintf(gotype, "\t}") // Get the return type of the wrapper function // compiled by gcc. @@ -936,7 +951,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { } // Build the wrapper function compiled by gcc. - s := fmt.Sprintf("%s %s(", gccResult, exp.ExpName) + gccExport := "" + if goos == "windows" { + gccExport = "__declspec(dllexport)" + } + s := fmt.Sprintf("%s %s %s(", gccExport, gccResult, exp.ExpName) if fn.Recv != nil { s += p.cgoType(fn.Recv.List[0].Type).C.String() s += " recv" @@ -958,12 +977,23 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { } fmt.Fprintf(fgcch, "extern %s;\n", s) - fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int, __SIZE_TYPE__);\n", cPrefix, exp.ExpName) + fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *);\n", cPrefix, exp.ExpName) fmt.Fprintf(fgcc, "\nCGO_NO_SANITIZE_THREAD") fmt.Fprintf(fgcc, "\n%s\n", s) fmt.Fprintf(fgcc, "{\n") fmt.Fprintf(fgcc, "\t__SIZE_TYPE__ _cgo_ctxt = _cgo_wait_runtime_init_done();\n") - fmt.Fprintf(fgcc, "\t%s %v _cgo_a;\n", ctype, p.packedAttribute()) + // The results part of the argument structure must be + // initialized to 0 so the write barriers generated by + // the assignments to these fields in Go are safe. + // + // We use a local static variable to get the zeroed + // value of the argument type. This avoids including + // string.h for memset, and is also robust to C++ + // types with constructors. Both GCC and LLVM optimize + // this into just zeroing _cgo_a. + fmt.Fprintf(fgcc, "\ttypedef %s %v _cgo_argtype;\n", ctype, p.packedAttribute()) + fmt.Fprintf(fgcc, "\tstatic _cgo_argtype _cgo_zero;\n") + fmt.Fprintf(fgcc, "\t_cgo_argtype _cgo_a = _cgo_zero;\n") if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) { fmt.Fprintf(fgcc, "\t%s r;\n", gccResult) } @@ -992,82 +1022,28 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprintf(fgcc, "}\n") // Build the wrapper function compiled by cmd/compile. - goname := "_cgoexpwrap" + cPrefix + "_" - if fn.Recv != nil { - goname += fn.Recv.List[0].Names[0].Name + "_" - } - goname += exp.Func.Name.Name + // This unpacks the argument struct above and calls the Go function. fmt.Fprintf(fgo2, "//go:cgo_export_dynamic %s\n", exp.ExpName) fmt.Fprintf(fgo2, "//go:linkname _cgoexp%s_%s _cgoexp%s_%s\n", cPrefix, exp.ExpName, cPrefix, exp.ExpName) fmt.Fprintf(fgo2, "//go:cgo_export_static _cgoexp%s_%s\n", cPrefix, exp.ExpName) - fmt.Fprintf(fgo2, "//go:nosplit\n") // no split stack, so no use of m or g - fmt.Fprintf(fgo2, "//go:norace\n") // must not have race detector calls inserted - fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a unsafe.Pointer, n int32, ctxt uintptr) {\n", cPrefix, exp.ExpName) - fmt.Fprintf(fgo2, "\tfn := %s\n", goname) - // The indirect here is converting from a Go function pointer to a C function pointer. - fmt.Fprintf(fgo2, "\t_cgo_runtime_cgocallback(**(**unsafe.Pointer)(unsafe.Pointer(&fn)), a, uintptr(n), ctxt);\n") - fmt.Fprintf(fgo2, "}\n") + fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a *%s) {\n", cPrefix, exp.ExpName, gotype) fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName) - // This code uses printer.Fprint, not conf.Fprint, - // because we don't want //line comments in the middle - // of the function types. - fmt.Fprintf(fgo2, "\n") - fmt.Fprintf(fgo2, "func %s(", goname) - comma := false - if fn.Recv != nil { - fmt.Fprintf(fgo2, "recv ") - printer.Fprint(fgo2, fset, fn.Recv.List[0].Type) - comma = true - } - forFieldList(fntype.Params, - func(i int, aname string, atype ast.Expr) { - if comma { - fmt.Fprintf(fgo2, ", ") - } - fmt.Fprintf(fgo2, "p%d ", i) - printer.Fprint(fgo2, fset, atype) - comma = true - }) - fmt.Fprintf(fgo2, ")") if gccResult != "void" { - fmt.Fprint(fgo2, " (") + // Write results back to frame. + fmt.Fprintf(fgo2, "\t") forFieldList(fntype.Results, func(i int, aname string, atype ast.Expr) { if i > 0 { - fmt.Fprint(fgo2, ", ") + fmt.Fprintf(fgo2, ", ") } - fmt.Fprintf(fgo2, "r%d ", i) - printer.Fprint(fgo2, fset, atype) + fmt.Fprintf(fgo2, "a.r%d", i) }) - fmt.Fprint(fgo2, ")") - } - fmt.Fprint(fgo2, " {\n") - if gccResult == "void" { - fmt.Fprint(fgo2, "\t") - } else { - // Verify that any results don't contain any - // Go pointers. - addedDefer := false - forFieldList(fntype.Results, - func(i int, aname string, atype ast.Expr) { - if !p.hasPointer(nil, atype, false) { - return - } - if !addedDefer { - fmt.Fprint(fgo2, "\tdefer func() {\n") - addedDefer = true - } - fmt.Fprintf(fgo2, "\t\t_cgoCheckResult(r%d)\n", i) - }) - if addedDefer { - fmt.Fprint(fgo2, "\t}()\n") - } - fmt.Fprint(fgo2, "\treturn ") + fmt.Fprintf(fgo2, " = ") } if fn.Recv != nil { - fmt.Fprintf(fgo2, "recv.") + fmt.Fprintf(fgo2, "a.recv.") } fmt.Fprintf(fgo2, "%s(", exp.Func.Name) forFieldList(fntype.Params, @@ -1075,9 +1051,20 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { if i > 0 { fmt.Fprint(fgo2, ", ") } - fmt.Fprintf(fgo2, "p%d", i) + fmt.Fprintf(fgo2, "a.p%d", i) }) fmt.Fprint(fgo2, ")\n") + if gccResult != "void" { + // Verify that any results don't contain any + // Go pointers. + forFieldList(fntype.Results, + func(i int, aname string, atype ast.Expr) { + if !p.hasPointer(nil, atype, false) { + return + } + fmt.Fprintf(fgo2, "\t_cgoCheckResult(a.r%d)\n", i) + }) + } fmt.Fprint(fgo2, "}\n") } @@ -1161,7 +1148,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) { // 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") @@ -1195,7 +1182,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) { 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 @@ -1279,112 +1266,23 @@ func (p *Package) writeExportHeader(fgcch io.Writer) { fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog()) } -// gccgoUsesNewMangling reports 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", cerr) - } - - // 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) - case c == '.': - bsl = append(bsl, ".x2e"...) - default: - changed = true - encbytes := []byte(fmt.Sprintf("..z%02x", c)) - bsl = append(bsl, encbytes...) +// 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") + if cmd == "" { + cmd, err = exec.LookPath("gccgo") + if err != nil { + fatalf("unable to locate gccgo: %v", err) + } } - } - 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', - '0' <= r && r <= '9': - return r + gccgoMangler, err = pkgpath.ToSymbolFunc(cmd, *objDir) + if err != nil { + fatalf("%v", err) } - 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 gccgoMangler(ppath) } // Return the package prefix when using gccgo. @@ -1394,12 +1292,12 @@ func (p *Package) gccgoSymbolPrefix() string { } 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" } @@ -1663,9 +1561,6 @@ const goProlog = ` //go:linkname _cgo_runtime_cgocall runtime.cgocall func _cgo_runtime_cgocall(unsafe.Pointer, uintptr) int32 -//go:linkname _cgo_runtime_cgocallback runtime.cgocallback -func _cgo_runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr, uintptr) - //go:linkname _cgoCheckPointer runtime.cgoCheckPointer func _cgoCheckPointer(interface{}, interface{}) @@ -1791,8 +1686,12 @@ void _cgoPREFIX_Cfunc__Cmalloc(void *v) { ` 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 = ` |
