1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Cgo; see doc.go for an overview.
6
7// TODO(rsc):
8//	Emit correct line number annotations.
9//	Make gc understand the annotations.
10
11package main
12
13import (
14	"flag"
15	"fmt"
16	"go/ast"
17	"go/printer"
18	"go/token"
19	"internal/buildcfg"
20	"io"
21	"os"
22	"path/filepath"
23	"reflect"
24	"runtime"
25	"sort"
26	"strings"
27
28	"cmd/internal/edit"
29	"cmd/internal/notsha256"
30	"cmd/internal/objabi"
31	"cmd/internal/telemetry/counter"
32)
33
34// A Package collects information about the package we're going to write.
35type Package struct {
36	PackageName string // name of package
37	PackagePath string
38	PtrSize     int64
39	IntSize     int64
40	GccOptions  []string
41	GccIsClang  bool
42	LdFlags     []string // #cgo LDFLAGS
43	Written     map[string]bool
44	Name        map[string]*Name // accumulated Name from Files
45	ExpFunc     []*ExpFunc       // accumulated ExpFunc from Files
46	Decl        []ast.Decl
47	GoFiles     []string        // list of Go files
48	GccFiles    []string        // list of gcc output files
49	Preamble    string          // collected preamble for _cgo_export.h
50	typedefs    map[string]bool // type names that appear in the types of the objects we're interested in
51	typedefList []typedefInfo
52	noCallbacks map[string]bool // C function names with #cgo nocallback directive
53	noEscapes   map[string]bool // C function names with #cgo noescape directive
54}
55
56// A typedefInfo is an element on Package.typedefList: a typedef name
57// and the position where it was required.
58type typedefInfo struct {
59	typedef string
60	pos     token.Pos
61}
62
63// A File collects information about a single Go input file.
64type File struct {
65	AST         *ast.File           // parsed AST
66	Comments    []*ast.CommentGroup // comments from file
67	Package     string              // Package name
68	Preamble    string              // C preamble (doc comment on import "C")
69	Ref         []*Ref              // all references to C.xxx in AST
70	Calls       []*Call             // all calls to C.xxx in AST
71	ExpFunc     []*ExpFunc          // exported functions for this file
72	Name        map[string]*Name    // map from Go name to Name
73	NamePos     map[*Name]token.Pos // map from Name to position of the first reference
74	NoCallbacks map[string]bool     // C function names that with #cgo nocallback directive
75	NoEscapes   map[string]bool     // C function names that with #cgo noescape directive
76	Edit        *edit.Buffer
77}
78
79func (f *File) offset(p token.Pos) int {
80	return fset.Position(p).Offset
81}
82
83func nameKeys(m map[string]*Name) []string {
84	var ks []string
85	for k := range m {
86		ks = append(ks, k)
87	}
88	sort.Strings(ks)
89	return ks
90}
91
92// A Call refers to a call of a C.xxx function in the AST.
93type Call struct {
94	Call     *ast.CallExpr
95	Deferred bool
96	Done     bool
97}
98
99// A Ref refers to an expression of the form C.xxx in the AST.
100type Ref struct {
101	Name    *Name
102	Expr    *ast.Expr
103	Context astContext
104	Done    bool
105}
106
107func (r *Ref) Pos() token.Pos {
108	return (*r.Expr).Pos()
109}
110
111var nameKinds = []string{"iconst", "fconst", "sconst", "type", "var", "fpvar", "func", "macro", "not-type"}
112
113// A Name collects information about C.xxx.
114type Name struct {
115	Go       string // name used in Go referring to package C
116	Mangle   string // name used in generated Go
117	C        string // name used in C
118	Define   string // #define expansion
119	Kind     string // one of the nameKinds
120	Type     *Type  // the type of xxx
121	FuncType *FuncType
122	AddError bool
123	Const    string // constant definition
124}
125
126// IsVar reports whether Kind is either "var" or "fpvar"
127func (n *Name) IsVar() bool {
128	return n.Kind == "var" || n.Kind == "fpvar"
129}
130
131// IsConst reports whether Kind is either "iconst", "fconst" or "sconst"
132func (n *Name) IsConst() bool {
133	return strings.HasSuffix(n.Kind, "const")
134}
135
136// An ExpFunc is an exported function, callable from C.
137// Such functions are identified in the Go input file
138// by doc comments containing the line //export ExpName
139type ExpFunc struct {
140	Func    *ast.FuncDecl
141	ExpName string // name to use from C
142	Doc     string
143}
144
145// A TypeRepr contains the string representation of a type.
146type TypeRepr struct {
147	Repr       string
148	FormatArgs []interface{}
149}
150
151// A Type collects information about a type in both the C and Go worlds.
152type Type struct {
153	Size       int64
154	Align      int64
155	C          *TypeRepr
156	Go         ast.Expr
157	EnumValues map[string]int64
158	Typedef    string
159	BadPointer bool // this pointer type should be represented as a uintptr (deprecated)
160}
161
162// A FuncType collects information about a function type in both the C and Go worlds.
163type FuncType struct {
164	Params []*Type
165	Result *Type
166	Go     *ast.FuncType
167}
168
169func usage() {
170	fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n")
171	flag.PrintDefaults()
172	os.Exit(2)
173}
174
175var ptrSizeMap = map[string]int64{
176	"386":      4,
177	"alpha":    8,
178	"amd64":    8,
179	"arm":      4,
180	"arm64":    8,
181	"loong64":  8,
182	"m68k":     4,
183	"mips":     4,
184	"mipsle":   4,
185	"mips64":   8,
186	"mips64le": 8,
187	"nios2":    4,
188	"ppc":      4,
189	"ppc64":    8,
190	"ppc64le":  8,
191	"riscv":    4,
192	"riscv64":  8,
193	"s390":     4,
194	"s390x":    8,
195	"sh":       4,
196	"shbe":     4,
197	"sparc":    4,
198	"sparc64":  8,
199}
200
201var intSizeMap = map[string]int64{
202	"386":      4,
203	"alpha":    8,
204	"amd64":    8,
205	"arm":      4,
206	"arm64":    8,
207	"loong64":  8,
208	"m68k":     4,
209	"mips":     4,
210	"mipsle":   4,
211	"mips64":   8,
212	"mips64le": 8,
213	"nios2":    4,
214	"ppc":      4,
215	"ppc64":    8,
216	"ppc64le":  8,
217	"riscv":    4,
218	"riscv64":  8,
219	"s390":     4,
220	"s390x":    8,
221	"sh":       4,
222	"shbe":     4,
223	"sparc":    4,
224	"sparc64":  8,
225}
226
227var cPrefix string
228
229var fset = token.NewFileSet()
230
231var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
232var dynout = flag.String("dynout", "", "write -dynimport output to this file")
233var dynpackage = flag.String("dynpackage", "main", "set Go package for -dynimport output")
234var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in -dynimport mode")
235
236// This flag is for bootstrapping a new Go implementation,
237// to generate Go types that match the data layout and
238// constant values used in the host's C libraries and system calls.
239var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
240
241var srcDir = flag.String("srcdir", "", "source directory")
242var objDir = flag.String("objdir", "", "object directory")
243var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)")
244var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions")
245
246var ldflags = flag.String("ldflags", "", "flags to pass to C linker")
247
248var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
249var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
250var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
251var gccgoMangler func(string) string
252var gccgoDefineCgoIncomplete = flag.Bool("gccgo_define_cgoincomplete", false, "define cgo.Incomplete for older gccgo/GoLLVM")
253var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
254var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
255var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths")
256
257var goarch, goos, gomips, gomips64 string
258var gccBaseCmd []string
259
260func main() {
261	counter.Open()
262	objabi.AddVersionFlag() // -V
263	objabi.Flagparse(usage)
264	counter.Inc("cgo/invocations")
265	counter.CountFlags("cgo/flag:", *flag.CommandLine)
266
267	if *gccgoDefineCgoIncomplete {
268		if !*gccgo {
269			fmt.Fprintf(os.Stderr, "cgo: -gccgo_define_cgoincomplete without -gccgo\n")
270			os.Exit(2)
271		}
272		incomplete = "_cgopackage_Incomplete"
273	}
274
275	if *dynobj != "" {
276		// cgo -dynimport is essentially a separate helper command
277		// built into the cgo binary. It scans a gcc-produced executable
278		// and dumps information about the imported symbols and the
279		// imported libraries. The 'go build' rules for cgo prepare an
280		// appropriate executable and then use its import information
281		// instead of needing to make the linkers duplicate all the
282		// specialized knowledge gcc has about where to look for imported
283		// symbols and which ones to use.
284		dynimport(*dynobj)
285		return
286	}
287
288	if *godefs {
289		// Generating definitions pulled from header files,
290		// to be checked into Go repositories.
291		// Line numbers are just noise.
292		conf.Mode &^= printer.SourcePos
293	}
294
295	args := flag.Args()
296	if len(args) < 1 {
297		usage()
298	}
299
300	// Find first arg that looks like a go file and assume everything before
301	// that are options to pass to gcc.
302	var i int
303	for i = len(args); i > 0; i-- {
304		if !strings.HasSuffix(args[i-1], ".go") {
305			break
306		}
307	}
308	if i == len(args) {
309		usage()
310	}
311
312	// Save original command line arguments for the godefs generated comment. Relative file
313	// paths in os.Args will be rewritten to absolute file paths in the loop below.
314	osArgs := make([]string, len(os.Args))
315	copy(osArgs, os.Args[:])
316	goFiles := args[i:]
317
318	for _, arg := range args[:i] {
319		if arg == "-fsanitize=thread" {
320			tsanProlog = yesTsanProlog
321		}
322		if arg == "-fsanitize=memory" {
323			msanProlog = yesMsanProlog
324		}
325	}
326
327	p := newPackage(args[:i])
328
329	// We need a C compiler to be available. Check this.
330	var err error
331	gccBaseCmd, err = checkGCCBaseCmd()
332	if err != nil {
333		fatalf("%v", err)
334		os.Exit(2)
335	}
336
337	// Record linker flags for external linking.
338	if *ldflags != "" {
339		args, err := splitQuoted(*ldflags)
340		if err != nil {
341			fatalf("bad -ldflags option: %q (%s)", *ldflags, err)
342		}
343		p.addToFlag("LDFLAGS", args)
344	}
345
346	// For backward compatibility for Bazel, record CGO_LDFLAGS
347	// from the environment for external linking.
348	// This should not happen with cmd/go, which removes CGO_LDFLAGS
349	// from the environment when invoking cgo.
350	// This can be removed when we no longer need to support
351	// older versions of Bazel. See issue #66456 and
352	// https://github.com/bazelbuild/rules_go/issues/3979.
353	if envFlags := os.Getenv("CGO_LDFLAGS"); envFlags != "" {
354		args, err := splitQuoted(envFlags)
355		if err != nil {
356			fatalf("bad CGO_LDFLAGS: %q (%s)", envFlags, err)
357		}
358		p.addToFlag("LDFLAGS", args)
359	}
360
361	// Need a unique prefix for the global C symbols that
362	// we use to coordinate between gcc and ourselves.
363	// We already put _cgo_ at the beginning, so the main
364	// concern is other cgo wrappers for the same functions.
365	// Use the beginning of the notsha256 of the input to disambiguate.
366	h := notsha256.New()
367	io.WriteString(h, *importPath)
368	fs := make([]*File, len(goFiles))
369	for i, input := range goFiles {
370		if *srcDir != "" {
371			input = filepath.Join(*srcDir, input)
372		}
373
374		// Create absolute path for file, so that it will be used in error
375		// messages and recorded in debug line number information.
376		// This matches the rest of the toolchain. See golang.org/issue/5122.
377		if aname, err := filepath.Abs(input); err == nil {
378			input = aname
379		}
380
381		b, err := os.ReadFile(input)
382		if err != nil {
383			fatalf("%s", err)
384		}
385		if _, err = h.Write(b); err != nil {
386			fatalf("%s", err)
387		}
388
389		// Apply trimpath to the file path. The path won't be read from after this point.
390		input, _ = objabi.ApplyRewrites(input, *trimpath)
391		if strings.ContainsAny(input, "\r\n") {
392			// ParseGo, (*Package).writeOutput, and printer.Fprint in SourcePos mode
393			// all emit line directives, which don't permit newlines in the file path.
394			// Bail early if we see anything newline-like in the trimmed path.
395			fatalf("input path contains newline character: %q", input)
396		}
397		goFiles[i] = input
398
399		f := new(File)
400		f.Edit = edit.NewBuffer(b)
401		f.ParseGo(input, b)
402		f.ProcessCgoDirectives()
403		fs[i] = f
404	}
405
406	cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])
407
408	if *objDir == "" {
409		*objDir = "_obj"
410	}
411	// make sure that `objDir` directory exists, so that we can write
412	// all the output files there.
413	os.MkdirAll(*objDir, 0o700)
414	*objDir += string(filepath.Separator)
415
416	for i, input := range goFiles {
417		f := fs[i]
418		p.Translate(f)
419		for _, cref := range f.Ref {
420			switch cref.Context {
421			case ctxCall, ctxCall2:
422				if cref.Name.Kind != "type" {
423					break
424				}
425				old := *cref.Expr
426				*cref.Expr = cref.Name.Type.Go
427				f.Edit.Replace(f.offset(old.Pos()), f.offset(old.End()), gofmt(cref.Name.Type.Go))
428			}
429		}
430		if nerrors > 0 {
431			os.Exit(2)
432		}
433		p.PackagePath = f.Package
434		p.Record(f)
435		if *godefs {
436			os.Stdout.WriteString(p.godefs(f, osArgs))
437		} else {
438			p.writeOutput(f, input)
439		}
440	}
441	cFunctions := make(map[string]bool)
442	for _, key := range nameKeys(p.Name) {
443		n := p.Name[key]
444		if n.FuncType != nil {
445			cFunctions[n.C] = true
446		}
447	}
448
449	for funcName := range p.noEscapes {
450		if _, found := cFunctions[funcName]; !found {
451			error_(token.NoPos, "#cgo noescape %s: no matched C function", funcName)
452		}
453	}
454
455	for funcName := range p.noCallbacks {
456		if _, found := cFunctions[funcName]; !found {
457			error_(token.NoPos, "#cgo nocallback %s: no matched C function", funcName)
458		}
459	}
460
461	if !*godefs {
462		p.writeDefs()
463	}
464	if nerrors > 0 {
465		os.Exit(2)
466	}
467}
468
469// newPackage returns a new Package that will invoke
470// gcc with the additional arguments specified in args.
471func newPackage(args []string) *Package {
472	goarch = runtime.GOARCH
473	if s := os.Getenv("GOARCH"); s != "" {
474		goarch = s
475	}
476	goos = runtime.GOOS
477	if s := os.Getenv("GOOS"); s != "" {
478		goos = s
479	}
480	buildcfg.Check()
481	gomips = buildcfg.GOMIPS
482	gomips64 = buildcfg.GOMIPS64
483	ptrSize := ptrSizeMap[goarch]
484	if ptrSize == 0 {
485		fatalf("unknown ptrSize for $GOARCH %q", goarch)
486	}
487	intSize := intSizeMap[goarch]
488	if intSize == 0 {
489		fatalf("unknown intSize for $GOARCH %q", goarch)
490	}
491
492	// Reset locale variables so gcc emits English errors [sic].
493	os.Setenv("LANG", "en_US.UTF-8")
494	os.Setenv("LC_ALL", "C")
495
496	p := &Package{
497		PtrSize:     ptrSize,
498		IntSize:     intSize,
499		Written:     make(map[string]bool),
500		noCallbacks: make(map[string]bool),
501		noEscapes:   make(map[string]bool),
502	}
503	p.addToFlag("CFLAGS", args)
504	return p
505}
506
507// Record what needs to be recorded about f.
508func (p *Package) Record(f *File) {
509	if p.PackageName == "" {
510		p.PackageName = f.Package
511	} else if p.PackageName != f.Package {
512		error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
513	}
514
515	if p.Name == nil {
516		p.Name = f.Name
517	} else {
518		for k, v := range f.Name {
519			if p.Name[k] == nil {
520				p.Name[k] = v
521			} else if p.incompleteTypedef(p.Name[k].Type) {
522				p.Name[k] = v
523			} else if p.incompleteTypedef(v.Type) {
524				// Nothing to do.
525			} else if _, ok := nameToC[k]; ok {
526				// Names we predefine may appear inconsistent
527				// if some files typedef them and some don't.
528				// Issue 26743.
529			} else if !reflect.DeepEqual(p.Name[k], v) {
530				error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
531			}
532		}
533	}
534
535	// merge nocallback & noescape
536	for k, v := range f.NoCallbacks {
537		p.noCallbacks[k] = v
538	}
539	for k, v := range f.NoEscapes {
540		p.noEscapes[k] = v
541	}
542
543	if f.ExpFunc != nil {
544		p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
545		p.Preamble += "\n" + f.Preamble
546	}
547	p.Decl = append(p.Decl, f.AST.Decls...)
548}
549
550// incompleteTypedef reports whether t appears to be an incomplete
551// typedef definition.
552func (p *Package) incompleteTypedef(t *Type) bool {
553	return t == nil || (t.Size == 0 && t.Align == -1)
554}
555