| |
| |
| |
|
|
| |
|
|
| |
| |
| |
|
|
| package main |
|
|
| import ( |
| "flag" |
| "fmt" |
| "go/ast" |
| "go/printer" |
| "go/token" |
| "internal/buildcfg" |
| "io" |
| "maps" |
| "os" |
| "path/filepath" |
| "reflect" |
| "runtime" |
| "sort" |
| "strings" |
| "sync" |
|
|
| "cmd/internal/edit" |
| "cmd/internal/hash" |
| "cmd/internal/objabi" |
| "cmd/internal/par" |
| "cmd/internal/telemetry/counter" |
| ) |
|
|
| |
| type Package struct { |
| PackageName string |
| PackagePath string |
| PtrSize int64 |
| IntSize int64 |
| GccOptions []string |
| GccIsClang bool |
| LdFlags []string |
| Written map[string]bool |
| Name map[string]*Name |
| ExpFunc []*ExpFunc |
| Decl []ast.Decl |
| GoFiles []string |
| GccFiles []string |
| Preamble string |
| noCallbacks map[string]bool |
| noEscapes map[string]bool |
| } |
|
|
| |
| |
| type typedefInfo struct { |
| typedef string |
| pos token.Pos |
| } |
|
|
| |
| type File struct { |
| AST *ast.File |
| Comments []*ast.CommentGroup |
| Package string |
| Preamble string |
| Ref []*Ref |
| Calls []*Call |
| ExpFunc []*ExpFunc |
| Name map[string]*Name |
| NamePos map[*Name]token.Pos |
| NoCallbacks map[string]bool |
| NoEscapes map[string]bool |
| Edit *edit.Buffer |
|
|
| debugs []*debug |
| } |
|
|
| func (f *File) offset(p token.Pos) int { |
| return fset.Position(p).Offset |
| } |
|
|
| func nameKeys(m map[string]*Name) []string { |
| ks := make([]string, 0, len(m)) |
| for k := range m { |
| ks = append(ks, k) |
| } |
| sort.Strings(ks) |
| return ks |
| } |
|
|
| |
| type Call struct { |
| Call *ast.CallExpr |
| Deferred bool |
| Done bool |
| } |
|
|
| |
| type Ref struct { |
| Name *Name |
| Expr *ast.Expr |
| Context astContext |
| Done bool |
| } |
|
|
| func (r *Ref) Pos() token.Pos { |
| return (*r.Expr).Pos() |
| } |
|
|
| var nameKinds = []string{"iconst", "fconst", "sconst", "type", "var", "fpvar", "func", "macro", "not-type"} |
|
|
| |
| type Name struct { |
| Go string |
| Mangle string |
| C string |
| Define string |
| Kind string |
| Type *Type |
| FuncType *FuncType |
| AddError bool |
| Const string |
| } |
|
|
| |
| func (n *Name) IsVar() bool { |
| return n.Kind == "var" || n.Kind == "fpvar" |
| } |
|
|
| |
| func (n *Name) IsConst() bool { |
| return strings.HasSuffix(n.Kind, "const") |
| } |
|
|
| |
| |
| |
| type ExpFunc struct { |
| Func *ast.FuncDecl |
| ExpName string |
| Doc string |
| } |
|
|
| |
| type TypeRepr struct { |
| Repr string |
| FormatArgs []any |
| } |
|
|
| |
| type Type struct { |
| Size int64 |
| Align int64 |
| C *TypeRepr |
| Go ast.Expr |
| EnumValues map[string]int64 |
| Typedef string |
| BadPointer bool |
| } |
|
|
| func (t *Type) fuzzyMatch(t2 *Type) bool { |
| if t == nil || t2 == nil { |
| return false |
| } |
| return t.Size == t2.Size && t.Align == t2.Align |
| } |
|
|
| |
| type FuncType struct { |
| Params []*Type |
| Result *Type |
| Go *ast.FuncType |
| } |
|
|
| func (t *FuncType) fuzzyMatch(t2 *FuncType) bool { |
| if t == nil || t2 == nil { |
| return false |
| } |
| if !t.Result.fuzzyMatch(t2.Result) { |
| return false |
| } |
| if len(t.Params) != len(t2.Params) { |
| return false |
| } |
| for i := range t.Params { |
| if !t.Params[i].fuzzyMatch(t2.Params[i]) { |
| return false |
| } |
| } |
| return true |
| } |
|
|
| func usage() { |
| fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n") |
| flag.PrintDefaults() |
| os.Exit(2) |
| } |
|
|
| var ptrSizeMap = map[string]int64{ |
| "386": 4, |
| "alpha": 8, |
| "amd64": 8, |
| "arm": 4, |
| "arm64": 8, |
| "loong64": 8, |
| "m68k": 4, |
| "mips": 4, |
| "mipsle": 4, |
| "mips64": 8, |
| "mips64le": 8, |
| "nios2": 4, |
| "ppc": 4, |
| "ppc64": 8, |
| "ppc64le": 8, |
| "riscv": 4, |
| "riscv64": 8, |
| "s390": 4, |
| "s390x": 8, |
| "sh": 4, |
| "shbe": 4, |
| "sparc": 4, |
| "sparc64": 8, |
| } |
|
|
| var intSizeMap = map[string]int64{ |
| "386": 4, |
| "alpha": 8, |
| "amd64": 8, |
| "arm": 4, |
| "arm64": 8, |
| "loong64": 8, |
| "m68k": 4, |
| "mips": 4, |
| "mipsle": 4, |
| "mips64": 8, |
| "mips64le": 8, |
| "nios2": 4, |
| "ppc": 4, |
| "ppc64": 8, |
| "ppc64le": 8, |
| "riscv": 4, |
| "riscv64": 8, |
| "s390": 4, |
| "s390x": 8, |
| "sh": 4, |
| "shbe": 4, |
| "sparc": 4, |
| "sparc64": 8, |
| } |
|
|
| var cPrefix string |
|
|
| var fset = token.NewFileSet() |
|
|
| var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") |
| var dynout = flag.String("dynout", "", "write -dynimport output to this file") |
| var dynpackage = flag.String("dynpackage", "main", "set Go package for -dynimport output") |
| var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in -dynimport mode") |
|
|
| |
| |
| |
| var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output") |
|
|
| var srcDir = flag.String("srcdir", "", "source directory") |
| var objDir = flag.String("objdir", "", "object directory") |
| var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)") |
| var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions") |
|
|
| var ldflags = flag.String("ldflags", "", "flags to pass to C linker") |
|
|
| 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 gccgoMangler func(string) string |
| var gccgoDefineCgoIncomplete = flag.Bool("gccgo_define_cgoincomplete", false, "define cgo.Incomplete for older gccgo/GoLLVM") |
| 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 trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths") |
|
|
| var goarch, goos, gomips, gomips64 string |
| var gccBaseCmd []string |
|
|
| func main() { |
| counter.Open() |
| objabi.AddVersionFlag() |
| objabi.Flagparse(usage) |
| counter.Inc("cgo/invocations") |
| counter.CountFlags("cgo/flag:", *flag.CommandLine) |
|
|
| if *gccgoDefineCgoIncomplete { |
| if !*gccgo { |
| fmt.Fprintf(os.Stderr, "cgo: -gccgo_define_cgoincomplete without -gccgo\n") |
| os.Exit(2) |
| } |
| incomplete = "_cgopackage_Incomplete" |
| } |
|
|
| if *dynobj != "" { |
| |
| |
| |
| |
| |
| |
| |
| |
| dynimport(*dynobj) |
| return |
| } |
|
|
| if *godefs { |
| |
| |
| |
| conf.Mode &^= printer.SourcePos |
| } |
|
|
| args := flag.Args() |
| if len(args) < 1 { |
| usage() |
| } |
|
|
| |
| |
| var i int |
| for i = len(args); i > 0; i-- { |
| if !strings.HasSuffix(args[i-1], ".go") { |
| break |
| } |
| } |
| if i == len(args) { |
| usage() |
| } |
|
|
| |
| |
| osArgs := make([]string, len(os.Args)) |
| copy(osArgs, os.Args[:]) |
| goFiles := args[i:] |
|
|
| for _, arg := range args[:i] { |
| if arg == "-fsanitize=thread" { |
| tsanProlog = yesTsanProlog |
| } |
| if arg == "-fsanitize=memory" { |
| msanProlog = yesMsanProlog |
| } |
| } |
|
|
| p := newPackage(args[:i]) |
|
|
| |
| var err error |
| gccBaseCmd, err = checkGCCBaseCmd() |
| if err != nil { |
| fatalf("%v", err) |
| os.Exit(2) |
| } |
|
|
| |
| if *ldflags != "" { |
| args, err := splitQuoted(*ldflags) |
| if err != nil { |
| fatalf("bad -ldflags option: %q (%s)", *ldflags, err) |
| } |
| p.addToFlag("LDFLAGS", args) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| if envFlags := os.Getenv("CGO_LDFLAGS"); envFlags != "" { |
| args, err := splitQuoted(envFlags) |
| if err != nil { |
| fatalf("bad CGO_LDFLAGS: %q (%s)", envFlags, err) |
| } |
| p.addToFlag("LDFLAGS", args) |
| } |
|
|
| |
| |
| |
| |
| |
| h := hash.New32() |
| io.WriteString(h, *importPath) |
| var once sync.Once |
| q := par.NewQueue(runtime.GOMAXPROCS(0)) |
| fs := make([]*File, len(goFiles)) |
| for i, input := range goFiles { |
| if *srcDir != "" { |
| input = filepath.Join(*srcDir, input) |
| } |
|
|
| |
| |
| |
| if aname, err := filepath.Abs(input); err == nil { |
| input = aname |
| } |
|
|
| b, err := os.ReadFile(input) |
| if err != nil { |
| fatalf("%s", err) |
| } |
| if _, err = h.Write(b); err != nil { |
| fatalf("%s", err) |
| } |
|
|
| q.Add(func() { |
| |
| input, _ = objabi.ApplyRewrites(input, *trimpath) |
| if strings.ContainsAny(input, "\r\n") { |
| |
| |
| |
| fatalf("input path contains newline character: %q", input) |
| } |
| goFiles[i] = input |
|
|
| f := new(File) |
| f.Edit = edit.NewBuffer(b) |
| f.ParseGo(input, b) |
| f.ProcessCgoDirectives() |
| gccIsClang := f.loadDefines(p.GccOptions) |
| once.Do(func() { |
| p.GccIsClang = gccIsClang |
| }) |
|
|
| fs[i] = f |
|
|
| f.loadDebug(p) |
| }) |
| } |
|
|
| <-q.Idle() |
|
|
| cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6]) |
|
|
| if *objDir == "" { |
| *objDir = "_obj" |
| } |
| |
| |
| os.MkdirAll(*objDir, 0o700) |
| *objDir += string(filepath.Separator) |
|
|
| for i, input := range goFiles { |
| f := fs[i] |
| p.Translate(f) |
| for _, cref := range f.Ref { |
| switch cref.Context { |
| case ctxCall, ctxCall2: |
| if cref.Name.Kind != "type" { |
| break |
| } |
| old := *cref.Expr |
| *cref.Expr = cref.Name.Type.Go |
| f.Edit.Replace(f.offset(old.Pos()), f.offset(old.End()), gofmt(cref.Name.Type.Go)) |
| } |
| } |
| if nerrors > 0 { |
| os.Exit(2) |
| } |
| p.PackagePath = f.Package |
| p.Record(f) |
| if *godefs { |
| os.Stdout.WriteString(p.godefs(f, osArgs)) |
| } else { |
| p.writeOutput(f, input) |
| } |
| } |
| cFunctions := make(map[string]bool) |
| for _, key := range nameKeys(p.Name) { |
| n := p.Name[key] |
| if n.FuncType != nil { |
| cFunctions[n.C] = true |
| } |
| } |
|
|
| for funcName := range p.noEscapes { |
| if _, found := cFunctions[funcName]; !found { |
| error_(token.NoPos, "#cgo noescape %s: no matched C function", funcName) |
| } |
| } |
|
|
| for funcName := range p.noCallbacks { |
| if _, found := cFunctions[funcName]; !found { |
| error_(token.NoPos, "#cgo nocallback %s: no matched C function", funcName) |
| } |
| } |
|
|
| if !*godefs { |
| p.writeDefs() |
| } |
| if nerrors > 0 { |
| os.Exit(2) |
| } |
| } |
|
|
| |
| |
| func newPackage(args []string) *Package { |
| goarch = runtime.GOARCH |
| if s := os.Getenv("GOARCH"); s != "" { |
| goarch = s |
| } |
| goos = runtime.GOOS |
| if s := os.Getenv("GOOS"); s != "" { |
| goos = s |
| } |
| buildcfg.Check() |
| gomips = buildcfg.GOMIPS |
| gomips64 = buildcfg.GOMIPS64 |
| ptrSize := ptrSizeMap[goarch] |
| if ptrSize == 0 { |
| fatalf("unknown ptrSize for $GOARCH %q", goarch) |
| } |
| intSize := intSizeMap[goarch] |
| if intSize == 0 { |
| fatalf("unknown intSize for $GOARCH %q", goarch) |
| } |
|
|
| |
| os.Setenv("LANG", "en_US.UTF-8") |
| os.Setenv("LC_ALL", "C") |
|
|
| p := &Package{ |
| PtrSize: ptrSize, |
| IntSize: intSize, |
| Written: make(map[string]bool), |
| noCallbacks: make(map[string]bool), |
| noEscapes: make(map[string]bool), |
| } |
| p.addToFlag("CFLAGS", args) |
| return p |
| } |
|
|
| |
| func (p *Package) Record(f *File) { |
| if p.PackageName == "" { |
| p.PackageName = f.Package |
| } else if p.PackageName != f.Package { |
| error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package) |
| } |
|
|
| if p.Name == nil { |
| p.Name = f.Name |
| } else { |
| |
| for k, v := range f.Name { |
| if p.Name[k] == nil { |
| |
| p.Name[k] = v |
| } else if p.incompleteTypedef(p.Name[k].Type) && p.Name[k].FuncType == nil { |
| |
| p.Name[k] = v |
| } else if p.incompleteTypedef(v.Type) && v.FuncType == nil { |
| |
| |
| } else if _, ok := nameToC[k]; ok { |
| |
| |
| |
| } else if !reflect.DeepEqual(p.Name[k], v) { |
| |
| |
| |
| |
| |
| |
| |
| ok := false |
| ft1 := p.Name[k].FuncType |
| ft2 := v.FuncType |
| if ft1.fuzzyMatch(ft2) { |
| |
| x1 := *p.Name[k] |
| x2 := *v |
| x1.FuncType = nil |
| x2.FuncType = nil |
| if reflect.DeepEqual(&x1, &x2) { |
| ok = true |
| } |
| } |
| if !ok { |
| error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k)) |
| } |
| } |
| } |
| } |
|
|
| |
| maps.Copy(p.noCallbacks, f.NoCallbacks) |
| maps.Copy(p.noEscapes, f.NoEscapes) |
|
|
| if f.ExpFunc != nil { |
| p.ExpFunc = append(p.ExpFunc, f.ExpFunc...) |
| p.Preamble += "\n" + f.Preamble |
| } |
| p.Decl = append(p.Decl, f.AST.Decls...) |
| } |
|
|
| |
| |
| func (p *Package) incompleteTypedef(t *Type) bool { |
| return t == nil || (t.Size == 0 && t.Align == -1) |
| } |
|
|