src/cmd/cgo/gcc.go GO 3,581 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 3,581.
1// Copyright 2009 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45// Annotate Ref in Prog with C types by parsing gcc debug output.6// Conversion of debug output to Go types.78package main910import (11	"bytes"12	"debug/dwarf"13	"debug/elf"14	"debug/macho"15	"debug/pe"16	"encoding/binary"17	"errors"18	"flag"19	"fmt"20	"go/ast"21	"go/parser"22	"go/token"23	"internal/xcoff"24	"math"25	"os"26	"os/exec"27	"path/filepath"28	"slices"29	"strconv"30	"strings"31	"sync/atomic"32	"unicode"33	"unicode/utf8"3435	"cmd/internal/quoted"36)3738var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")39var debugGcc = flag.Bool("debug-gcc", false, "print gcc invocations")4041var nameToC = map[string]string{42	"schar":         "signed char",43	"uchar":         "unsigned char",44	"ushort":        "unsigned short",45	"uint":          "unsigned int",46	"ulong":         "unsigned long",47	"longlong":      "long long",48	"ulonglong":     "unsigned long long",49	"complexfloat":  "float _Complex",50	"complexdouble": "double _Complex",51}5253var incomplete = "_cgopackage.Incomplete"5455// cname returns the C name to use for C.s.56// The expansions are listed in nameToC and also57// struct_foo becomes "struct foo", and similarly for58// union and enum.59func cname(s string) string {60	if t, ok := nameToC[s]; ok {61		return t62	}6364	if t, ok := strings.CutPrefix(s, "struct_"); ok {65		return "struct " + t66	}67	if t, ok := strings.CutPrefix(s, "union_"); ok {68		return "union " + t69	}70	if t, ok := strings.CutPrefix(s, "enum_"); ok {71		return "enum " + t72	}73	if t, ok := strings.CutPrefix(s, "sizeof_"); ok {74		return "sizeof(" + cname(t) + ")"75	}76	return s77}7879// ProcessCgoDirectives processes the import C preamble:80//  1. discards all #cgo CFLAGS, LDFLAGS, nocallback and noescape directives,81//     so they don't make their way into _cgo_export.h.82//  2. parse the nocallback and noescape directives.83func (f *File) ProcessCgoDirectives() {84	linesIn := strings.Split(f.Preamble, "\n")85	linesOut := make([]string, 0, len(linesIn))86	f.NoCallbacks = make(map[string]bool)87	f.NoEscapes = make(map[string]bool)88	for _, line := range linesIn {89		l := strings.TrimSpace(line)90		if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(rune(l[4])) {91			linesOut = append(linesOut, line)92		} else {93			linesOut = append(linesOut, "")9495			// #cgo (nocallback|noescape) <function name>96			if fields := strings.Fields(l); len(fields) == 3 {97				directive := fields[1]98				funcName := fields[2]99				if directive == "nocallback" {100					f.NoCallbacks[funcName] = true101				} else if directive == "noescape" {102					f.NoEscapes[funcName] = true103				}104			}105		}106	}107	f.Preamble = strings.Join(linesOut, "\n")108}109110// addToFlag appends args to flag.111func (p *Package) addToFlag(flag string, args []string) {112	if flag == "CFLAGS" {113		// We'll also need these when preprocessing for dwarf information.114		// However, discard any -g options: we need to be able115		// to parse the debug info, so stick to what we expect.116		for _, arg := range args {117			if !strings.HasPrefix(arg, "-g") {118				p.GccOptions = append(p.GccOptions, arg)119			}120		}121	}122	if flag == "LDFLAGS" {123		p.LdFlags = append(p.LdFlags, args...)124	}125}126127// splitQuoted splits the string s around each instance of one or more consecutive128// white space characters while taking into account quotes and escaping, and129// returns an array of substrings of s or an empty list if s contains only white space.130// Single quotes and double quotes are recognized to prevent splitting within the131// quoted region, and are removed from the resulting substrings. If a quote in s132// isn't closed err will be set and r will have the unclosed argument as the133// last element. The backslash is used for escaping.134//135// For example, the following string:136//137//	`a b:"c d" 'e''f'  "g\""`138//139// Would be parsed as:140//141//	[]string{"a", "b:c d", "ef", `g"`}142func splitQuoted(s string) (r []string, err error) {143	var args []string144	arg := make([]rune, len(s))145	escaped := false146	quoted := false147	quote := '\x00'148	i := 0149	for _, r := range s {150		switch {151		case escaped:152			escaped = false153		case r == '\\':154			escaped = true155			continue156		case quote != 0:157			if r == quote {158				quote = 0159				continue160			}161		case r == '"' || r == '\'':162			quoted = true163			quote = r164			continue165		case unicode.IsSpace(r):166			if quoted || i > 0 {167				quoted = false168				args = append(args, string(arg[:i]))169				i = 0170			}171			continue172		}173		arg[i] = r174		i++175	}176	if quoted || i > 0 {177		args = append(args, string(arg[:i]))178	}179	if quote != 0 {180		err = errors.New("unclosed quote")181	} else if escaped {182		err = errors.New("unfinished escaping")183	}184	return args, err185}186187// loadDebug runs gcc to load debug information for the File. The debug188// information will be saved to the debugs field of the file, and be189// processed when Translate is called on the file later.190// loadDebug is called concurrently with different files.191func (f *File) loadDebug(p *Package) {192	for _, cref := range f.Ref {193		// Convert C.ulong to C.unsigned long, etc.194		cref.Name.C = cname(cref.Name.Go)195	}196197	ft := fileTypedefs{typedefs: make(map[string]bool)}198	numTypedefs := -1199	for len(ft.typedefs) > numTypedefs {200		numTypedefs = len(ft.typedefs)201		// Also ask about any typedefs we've seen so far.202		for _, info := range ft.typedefList {203			if f.Name[info.typedef] != nil {204				continue205			}206			n := &Name{207				Go: info.typedef,208				C:  info.typedef,209			}210			f.Name[info.typedef] = n211			f.NamePos[n] = info.pos212		}213		needType := p.guessKinds(f)214		if len(needType) > 0 {215			f.debugs = append(f.debugs, p.loadDWARF(f, &ft, needType))216		}217218		// In godefs mode we're OK with the typedefs, which219		// will presumably also be defined in the file, we220		// don't want to resolve them to their base types.221		if *godefs {222			break223		}224	}225}226227// Translate rewrites f.AST, the original Go input, to remove228// references to the imported package C, replacing them with229// references to the equivalent Go types, functions, and variables.230// Preconditions: File.loadDebug must be called prior to translate.231func (p *Package) Translate(f *File) {232	var conv typeConv233	conv.Init(p.PtrSize, p.IntSize)234	for _, d := range f.debugs {235		p.recordTypes(f, d, &conv)236	}237	p.prepareNames(f)238	if p.rewriteCalls(f) {239		// Add `import _cgo_unsafe "unsafe"` after the package statement.240		f.Edit.Insert(f.offset(f.AST.Name.End()), "; import _cgo_unsafe \"unsafe\"")241	}242	p.rewriteRef(f)243}244245// loadDefines coerces gcc into spitting out the #defines in use246// in the file f and saves relevant renamings in f.Name[name].Define.247// Returns true if env:CC is Clang248func (f *File) loadDefines(gccOptions []string) bool {249	var b bytes.Buffer250	b.WriteString(builtinProlog)251	b.WriteString(f.Preamble)252	stdout := gccDefines(b.Bytes(), gccOptions)253254	var gccIsClang bool255	for line := range strings.SplitSeq(stdout, "\n") {256		if len(line) < 9 || line[0:7] != "#define" {257			continue258		}259260		line = strings.TrimSpace(line[8:])261262		var key, val string263		spaceIndex := strings.Index(line, " ")264		tabIndex := strings.Index(line, "\t")265266		if spaceIndex == -1 && tabIndex == -1 {267			continue268		} else if tabIndex == -1 || (spaceIndex != -1 && spaceIndex < tabIndex) {269			key = line[0:spaceIndex]270			val = strings.TrimSpace(line[spaceIndex:])271		} else {272			key = line[0:tabIndex]273			val = strings.TrimSpace(line[tabIndex:])274		}275276		if key == "__clang__" {277			gccIsClang = true278		}279280		if n := f.Name[key]; n != nil {281			if *debugDefine {282				fmt.Fprintf(os.Stderr, "#define %s %s\n", key, val)283			}284			n.Define = val285		}286	}287	return gccIsClang288}289290// guessKinds tricks gcc into revealing the kind of each291// name xxx for the references C.xxx in the Go input.292// The kind is either a constant, type, or variable.293// guessKinds is called concurrently with different files.294func (p *Package) guessKinds(f *File) []*Name {295	// Determine kinds for names we already know about,296	// like #defines or 'struct foo', before bothering with gcc.297	var names, needType []*Name298	optional := map[*Name]bool{}299	for _, key := range nameKeys(f.Name) {300		n := f.Name[key]301		// If we've already found this name as a #define302		// and we can translate it as a constant value, do so.303		if n.Define != "" {304			if i, err := strconv.ParseInt(n.Define, 0, 64); err == nil {305				n.Kind = "iconst"306				// Turn decimal into hex, just for consistency307				// with enum-derived constants. Otherwise308				// in the cgo -godefs output half the constants309				// are in hex and half are in whatever the #define used.310				n.Const = fmt.Sprintf("%#x", i)311			} else if n.Define[0] == '\'' {312				if _, err := parser.ParseExpr(n.Define); err == nil {313					n.Kind = "iconst"314					n.Const = n.Define315				}316			} else if n.Define[0] == '"' {317				if _, err := parser.ParseExpr(n.Define); err == nil {318					n.Kind = "sconst"319					n.Const = n.Define320				}321			}322323			if n.IsConst() {324				continue325			}326		}327328		// If this is a struct, union, or enum type name, no need to guess the kind.329		if strings.HasPrefix(n.C, "struct ") || strings.HasPrefix(n.C, "union ") || strings.HasPrefix(n.C, "enum ") {330			n.Kind = "type"331			needType = append(needType, n)332			continue333		}334335		if (goos == "darwin" || goos == "ios") && strings.HasSuffix(n.C, "Ref") {336			// For FooRef, find out if FooGetTypeID exists.337			s := n.C[:len(n.C)-3] + "GetTypeID"338			n := &Name{Go: s, C: s}339			names = append(names, n)340			optional[n] = true341		}342343		// Otherwise, we'll need to find out from gcc.344		names = append(names, n)345	}346347	// Bypass gcc if there's nothing left to find out.348	if len(names) == 0 {349		return needType350	}351352	// Coerce gcc into telling us whether each name is a type, a value, or undeclared.353	// For names, find out whether they are integer constants.354	// We used to look at specific warning or error messages here, but that tied the355	// behavior too closely to specific versions of the compilers.356	// Instead, arrange that we can infer what we need from only the presence or absence357	// of an error on a specific line.358	//359	// For each name, we generate these lines, where xxx is the index in toSniff plus one.360	//361	//	#line xxx "not-declared"362	//	void __cgo_f_xxx_1(void) { __typeof__(name) *__cgo_undefined__1; }363	//	#line xxx "not-type"364	//	void __cgo_f_xxx_2(void) { name *__cgo_undefined__2; }365	//	#line xxx "not-int-const"366	//	void __cgo_f_xxx_3(void) { enum { __cgo_undefined__3 = (name)*1 }; }367	//	#line xxx "not-num-const"368	//	void __cgo_f_xxx_4(void) { static const double __cgo_undefined__4 = (name); }369	//	#line xxx "not-str-lit"370	//	void __cgo_f_xxx_5(void) { static const char __cgo_undefined__5[] = (name); }371	//372	// If we see an error at not-declared:xxx, the corresponding name is not declared.373	// If we see an error at not-type:xxx, the corresponding name is not a type.374	// If we see an error at not-int-const:xxx, the corresponding name is not an integer constant.375	// If we see an error at not-num-const:xxx, the corresponding name is not a number constant.376	// If we see an error at not-str-lit:xxx, the corresponding name is not a string literal.377	//378	// The specific input forms are chosen so that they are valid C syntax regardless of379	// whether name denotes a type or an expression.380381	var b bytes.Buffer382	b.WriteString(builtinProlog)383	b.WriteString(f.Preamble)384385	for i, n := range names {386		fmt.Fprintf(&b, "#line %d \"not-declared\"\n"+387			"void __cgo_f_%d_1(void) { __typeof__(%s) *__cgo_undefined__1; }\n"+388			"#line %d \"not-type\"\n"+389			"void __cgo_f_%d_2(void) { %s *__cgo_undefined__2; }\n"+390			"#line %d \"not-int-const\"\n"+391			"void __cgo_f_%d_3(void) { enum { __cgo_undefined__3 = (%s)*1 }; }\n"+392			"#line %d \"not-num-const\"\n"+393			"void __cgo_f_%d_4(void) { static const double __cgo_undefined__4 = (%s); }\n"+394			"#line %d \"not-str-lit\"\n"+395			"void __cgo_f_%d_5(void) { static const char __cgo_undefined__5[] = (%s); }\n",396			i+1, i+1, n.C,397			i+1, i+1, n.C,398			i+1, i+1, n.C,399			i+1, i+1, n.C,400			i+1, i+1, n.C,401		)402	}403	fmt.Fprintf(&b, "#line 1 \"completed\"\n"+404		"int __cgo__1 = __cgo__2;\n")405406	// We need to parse the output from this gcc command, so ensure that it407	// doesn't have any ANSI escape sequences in it. (TERM=dumb is408	// insufficient; if the user specifies CGO_CFLAGS=-fdiagnostics-color,409	// GCC will ignore TERM, and GCC can also be configured at compile-time410	// to ignore TERM.)411	stderr := p.gccErrors(b.Bytes(), "-fdiagnostics-color=never")412	if strings.Contains(stderr, "unrecognized command line option") {413		// We're using an old version of GCC that doesn't understand414		// -fdiagnostics-color. Those versions can't print color anyway,415		// so just rerun without that option.416		stderr = p.gccErrors(b.Bytes())417	}418	if stderr == "" {419		fatalf("%s produced no output\non input:\n%s", gccBaseCmd[0], b.Bytes())420	}421422	completed := false423	sniff := make([]int, len(names))424	const (425		notType = 1 << iota426		notIntConst427		notNumConst428		notStrLiteral429		notDeclared430	)431	sawUnmatchedErrors := false432	for line := range strings.SplitSeq(stderr, "\n") {433		// Ignore warnings and random comments, with one434		// exception: newer GCC versions will sometimes emit435		// an error on a macro #define with a note referring436		// to where the expansion occurs. We care about where437		// the expansion occurs, so in that case treat the note438		// as an error.439		isError := strings.Contains(line, ": error:")440		isErrorNote := strings.Contains(line, ": note:") && sawUnmatchedErrors441		if !isError && !isErrorNote {442			continue443		}444445		c1 := strings.Index(line, ":")446		if c1 < 0 {447			continue448		}449		c2 := strings.Index(line[c1+1:], ":")450		if c2 < 0 {451			continue452		}453		c2 += c1 + 1454455		filename := line[:c1]456		i, _ := strconv.Atoi(line[c1+1 : c2])457		i--458		if i < 0 || i >= len(names) {459			if isError {460				sawUnmatchedErrors = true461			}462			continue463		}464465		switch filename {466		case "completed":467			// Strictly speaking, there is no guarantee that seeing the error at completed:1468			// (at the end of the file) means we've seen all the errors from earlier in the file,469			// but usually it does. Certainly if we don't see the completed:1 error, we did470			// not get all the errors we expected.471			completed = true472473		case "not-declared":474			sniff[i] |= notDeclared475		case "not-type":476			sniff[i] |= notType477		case "not-int-const":478			sniff[i] |= notIntConst479		case "not-num-const":480			sniff[i] |= notNumConst481		case "not-str-lit":482			sniff[i] |= notStrLiteral483		default:484			if isError {485				sawUnmatchedErrors = true486			}487			continue488		}489490		sawUnmatchedErrors = false491	}492493	if !completed {494		fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", gccBaseCmd[0], b.Bytes(), stderr)495	}496497	for i, n := range names {498		switch sniff[i] {499		default:500			if sniff[i]&notDeclared != 0 && optional[n] {501				// Ignore optional undeclared identifiers.502				// Don't report an error, and skip adding n to the needType array.503				continue504			}505			error_(f.NamePos[n], "could not determine what C.%s refers to", fixGo(n.Go))506		case notStrLiteral | notType:507			n.Kind = "iconst"508		case notIntConst | notStrLiteral | notType:509			n.Kind = "fconst"510		case notIntConst | notNumConst | notType:511			n.Kind = "sconst"512		case notIntConst | notNumConst | notStrLiteral:513			n.Kind = "type"514		case notIntConst | notNumConst | notStrLiteral | notType:515			n.Kind = "not-type"516		}517		needType = append(needType, n)518	}519	if nerrors > 0 {520		// Check if compiling the preamble by itself causes any errors,521		// because the messages we've printed out so far aren't helpful522		// to users debugging preamble mistakes. See issue 8442.523		preambleErrors := p.gccErrors([]byte(builtinProlog + f.Preamble))524		if len(preambleErrors) > 0 {525			error_(token.NoPos, "\n%s errors for preamble:\n%s", gccBaseCmd[0], preambleErrors)526		}527528		fatalf("unresolved names")529	}530531	return needType532}533534// loadDWARF parses the DWARF debug information generated535// by gcc to learn the details of the constants, variables, and types536// being referred to as C.xxx.537// loadDwarf is called concurrently with different files.538func (p *Package) loadDWARF(f *File, ft *fileTypedefs, names []*Name) *debug {539	// Extract the types from the DWARF section of an object540	// from a well-formed C program. Gcc only generates DWARF info541	// for symbols in the object file, so it is not enough to print the542	// preamble and hope the symbols we care about will be there.543	// Instead, emit544	//	__typeof__(names[i]) *__cgo__i;545	// for each entry in names and then dereference the type we546	// learn for __cgo__i.547	var b bytes.Buffer548	b.WriteString(builtinProlog)549	b.WriteString(f.Preamble)550	b.WriteString("#line 1 \"cgo-dwarf-inference\"\n")551	for i, n := range names {552		fmt.Fprintf(&b, "__typeof__(%s) *__cgo__%d;\n", n.C, i)553		if n.Kind == "iconst" {554			fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C)555		}556	}557558	// We create a data block initialized with the values,559	// so we can read them out of the object file.560	fmt.Fprintf(&b, "long long __cgodebug_ints[] = {\n")561	for _, n := range names {562		if n.Kind == "iconst" {563			fmt.Fprintf(&b, "\t%s,\n", n.C)564		} else {565			fmt.Fprintf(&b, "\t0,\n")566		}567	}568	// for the last entry, we cannot use 0, otherwise569	// in case all __cgodebug_data is zero initialized,570	// LLVM-based gcc will place the it in the __DATA.__common571	// zero-filled section (our debug/macho doesn't support572	// this)573	fmt.Fprintf(&b, "\t1\n")574	fmt.Fprintf(&b, "};\n")575576	// do the same work for floats.577	fmt.Fprintf(&b, "double __cgodebug_floats[] = {\n")578	for _, n := range names {579		if n.Kind == "fconst" {580			fmt.Fprintf(&b, "\t%s,\n", n.C)581		} else {582			fmt.Fprintf(&b, "\t0,\n")583		}584	}585	fmt.Fprintf(&b, "\t1\n")586	fmt.Fprintf(&b, "};\n")587588	// do the same work for strings.589	for i, n := range names {590		if n.Kind == "sconst" {591			fmt.Fprintf(&b, "const char __cgodebug_str__%d[] = %s;\n", i, n.C)592			fmt.Fprintf(&b, "const unsigned long long __cgodebug_strlen__%d = sizeof(%s)-1;\n", i, n.C)593		}594	}595596	d, ints, floats, strs := p.gccDebug(b.Bytes(), len(names))597598	// Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i.599	types := make([]dwarf.Type, len(names))600	r := d.Reader()601	for {602		e, err := r.Next()603		if err != nil {604			fatalf("reading DWARF entry: %s", err)605		}606		if e == nil {607			break608		}609		switch e.Tag {610		case dwarf.TagVariable:611			name, _ := e.Val(dwarf.AttrName).(string)612			// As of https://reviews.llvm.org/D123534, clang613			// now emits DW_TAG_variable DIEs that have614			// no name (so as to be able to describe the615			// type and source locations of constant strings)616			// like the second arg in the call below:617			//618			//     myfunction(42, "foo")619			//620			// If a var has no name we won't see attempts to621			// refer to it via "C.<name>", so skip these vars622			//623			// See issue 53000 for more context.624			if name == "" {625				break626			}627			typOff, _ := e.Val(dwarf.AttrType).(dwarf.Offset)628			if typOff == 0 {629				if e.Val(dwarf.AttrSpecification) != nil {630					// Since we are reading all the DWARF,631					// assume we will see the variable elsewhere.632					break633				}634				fatalf("malformed DWARF TagVariable entry")635			}636			if !strings.HasPrefix(name, "__cgo__") {637				break638			}639			typ, err := d.Type(typOff)640			if err != nil {641				fatalf("loading DWARF type: %s", err)642			}643			t, ok := typ.(*dwarf.PtrType)644			if !ok || t == nil {645				fatalf("internal error: %s has non-pointer type", name)646			}647			i, err := strconv.Atoi(name[7:])648			if err != nil {649				fatalf("malformed __cgo__ name: %s", name)650			}651			types[i] = t.Type652			ft.recordTypedefs(t.Type, f.NamePos[names[i]])653		}654		if e.Tag != dwarf.TagCompileUnit {655			r.SkipChildren()656		}657	}658659	return &debug{names, types, ints, floats, strs}660}661662// debug is the data extracted by running an iteration of loadDWARF on a file.663type debug struct {664	names  []*Name665	types  []dwarf.Type666	ints   []int64667	floats []float64668	strs   []string669}670671func (p *Package) recordTypes(f *File, data *debug, conv *typeConv) {672	names, types, ints, floats, strs := data.names, data.types, data.ints, data.floats, data.strs673674	// Record types and typedef information.675	for i, n := range names {676		if strings.HasSuffix(n.Go, "GetTypeID") && types[i].String() == "func() CFTypeID" {677			conv.getTypeIDs[n.Go[:len(n.Go)-9]] = true678		}679	}680	for i, n := range names {681		if types[i] == nil {682			continue683		}684		pos := f.NamePos[n]685		f, fok := types[i].(*dwarf.FuncType)686		if n.Kind != "type" && fok {687			n.Kind = "func"688			n.FuncType = conv.FuncType(f, pos)689		} else {690			n.Type = conv.Type(types[i], pos)691			switch n.Kind {692			case "iconst":693				if i < len(ints) {694					if _, ok := types[i].(*dwarf.UintType); ok {695						n.Const = fmt.Sprintf("%#x", uint64(ints[i]))696					} else {697						n.Const = fmt.Sprintf("%#x", ints[i])698					}699				}700			case "fconst":701				if i >= len(floats) {702					break703				}704				switch base(types[i]).(type) {705				case *dwarf.IntType, *dwarf.UintType:706					// This has an integer type so it's707					// not really a floating point708					// constant. This can happen when the709					// C compiler complains about using710					// the value as an integer constant,711					// but not as a general constant.712					// Treat this as a variable of the713					// appropriate type, not a constant,714					// to get C-style type handling,715					// avoiding the problem that C permits716					// uint64(-1) but Go does not.717					// See issue 26066.718					n.Kind = "var"719				default:720					n.Const = fmt.Sprintf("%f", floats[i])721				}722			case "sconst":723				if i < len(strs) {724					n.Const = fmt.Sprintf("%q", strs[i])725				}726			}727		}728		conv.FinishType(pos)729	}730}731732type fileTypedefs struct {733	typedefs    map[string]bool // type names that appear in the types of the objects we're interested in734	typedefList []typedefInfo735}736737// recordTypedefs remembers in ft.typedefs all the typedefs used in dtypes and its children.738func (ft *fileTypedefs) recordTypedefs(dtype dwarf.Type, pos token.Pos) {739	ft.recordTypedefs1(dtype, pos, map[dwarf.Type]bool{})740}741742func (ft *fileTypedefs) recordTypedefs1(dtype dwarf.Type, pos token.Pos, visited map[dwarf.Type]bool) {743	if dtype == nil {744		return745	}746	if visited[dtype] {747		return748	}749	visited[dtype] = true750	switch dt := dtype.(type) {751	case *dwarf.TypedefType:752		if strings.HasPrefix(dt.Name, "__builtin") {753			// Don't look inside builtin types. There be dragons.754			return755		}756		if !ft.typedefs[dt.Name] {757			ft.typedefs[dt.Name] = true758			ft.typedefList = append(ft.typedefList, typedefInfo{dt.Name, pos})759			ft.recordTypedefs1(dt.Type, pos, visited)760		}761	case *dwarf.PtrType:762		ft.recordTypedefs1(dt.Type, pos, visited)763	case *dwarf.ArrayType:764		ft.recordTypedefs1(dt.Type, pos, visited)765	case *dwarf.QualType:766		ft.recordTypedefs1(dt.Type, pos, visited)767	case *dwarf.FuncType:768		ft.recordTypedefs1(dt.ReturnType, pos, visited)769		for _, a := range dt.ParamType {770			ft.recordTypedefs1(a, pos, visited)771		}772	case *dwarf.StructType:773		for _, l := range dt.Field {774			ft.recordTypedefs1(l.Type, pos, visited)775		}776	}777}778779// prepareNames finalizes the Kind field of not-type names and sets780// the mangled name of all names.781func (p *Package) prepareNames(f *File) {782	for _, n := range f.Name {783		if n.Kind == "not-type" {784			if n.Define == "" {785				n.Kind = "var"786			} else {787				n.Kind = "macro"788				n.FuncType = &FuncType{789					Result: n.Type,790					Go: &ast.FuncType{791						Results: &ast.FieldList{List: []*ast.Field{{Type: n.Type.Go}}},792					},793				}794			}795		}796		p.mangleName(n)797		if n.Kind == "type" && typedef[n.Mangle] == nil {798			typedef[n.Mangle] = n.Type799		}800	}801}802803// mangleName does name mangling to translate names804// from the original Go source files to the names805// used in the final Go files generated by cgo.806func (p *Package) mangleName(n *Name) {807	// When using gccgo variables have to be808	// exported so that they become global symbols809	// that the C code can refer to.810	prefix := "_C"811	if *gccgo && n.IsVar() {812		prefix = "C"813	}814	n.Mangle = prefix + n.Kind + "_" + n.Go815}816817func (f *File) isMangledName(s string) bool {818	t, ok := strings.CutPrefix(s, "_C")819	if !ok {820		return false821	}822	return slices.ContainsFunc(nameKinds, func(k string) bool {823		return strings.HasPrefix(t, k+"_")824	})825}826827// rewriteCalls rewrites all calls that pass pointers to check that828// they follow the rules for passing pointers between Go and C.829// This reports whether the package needs to import unsafe as _cgo_unsafe.830func (p *Package) rewriteCalls(f *File) bool {831	needsUnsafe := false832	// Walk backward so that in C.f1(C.f2()) we rewrite C.f2 first.833	for _, call := range f.Calls {834		if call.Done {835			continue836		}837		start := f.offset(call.Call.Pos())838		end := f.offset(call.Call.End())839		str, nu := p.rewriteCall(f, call)840		if str != "" {841			f.Edit.Replace(start, end, str)842			if nu {843				needsUnsafe = true844			}845		}846	}847	return needsUnsafe848}849850// rewriteCall rewrites one call to add pointer checks.851// If any pointer checks are required, we rewrite the call into a852// function literal that calls _cgoCheckPointer for each pointer853// argument and then calls the original function.854// This returns the rewritten call and whether the package needs to855// import unsafe as _cgo_unsafe.856// If it returns the empty string, the call did not need to be rewritten.857func (p *Package) rewriteCall(f *File, call *Call) (string, bool) {858	// This is a call to C.xxx; set goname to "xxx".859	// It may have already been mangled by rewriteName.860	var goname string861	switch fun := call.Call.Fun.(type) {862	case *ast.SelectorExpr:863		goname = fun.Sel.Name864	case *ast.Ident:865		goname = strings.TrimPrefix(fun.Name, "_C2func_")866		goname = strings.TrimPrefix(goname, "_Cfunc_")867	}868	if goname == "" || goname == "malloc" {869		return "", false870	}871	name := f.Name[goname]872	if name == nil || name.Kind != "func" {873		// Probably a type conversion.874		return "", false875	}876877	params := name.FuncType.Params878	args := call.Call.Args879	end := call.Call.End()880881	// Avoid a crash if the number of arguments doesn't match882	// the number of parameters.883	// This will be caught when the generated file is compiled.884	if len(args) != len(params) {885		return "", false886	}887888	any := false889	for i, param := range params {890		if p.needsPointerCheck(f, param.Go, args[i]) {891			any = true892			break893		}894	}895	if !any {896		return "", false897	}898899	// We need to rewrite this call.900	//901	// Rewrite C.f(p) to902	//    func() {903	//            _cgo0 := p904	//            _cgoCheckPointer(_cgo0, nil)905	//            C.f(_cgo0)906	//    }()907	// Using a function literal like this lets us evaluate the908	// function arguments only once while doing pointer checks.909	// This is particularly useful when passing additional arguments910	// to _cgoCheckPointer, as done in checkIndex and checkAddr.911	//912	// When the function argument is a conversion to unsafe.Pointer,913	// we unwrap the conversion before checking the pointer,914	// and then wrap again when calling C.f. This lets us check915	// the real type of the pointer in some cases. See issue #25941.916	//917	// When the call to C.f is deferred, we use an additional function918	// literal to evaluate the arguments at the right time.919	//    defer func() func() {920	//            _cgo0 := p921	//            return func() {922	//                    _cgoCheckPointer(_cgo0, nil)923	//                    C.f(_cgo0)924	//            }925	//    }()()926	// This works because the defer statement evaluates the first927	// function literal in order to get the function to call.928929	var sb bytes.Buffer930	sb.WriteString("func() ")931	if call.Deferred {932		sb.WriteString("func() ")933	}934935	needsUnsafe := false936	result := false937	twoResults := false938	if !call.Deferred {939		// Check whether this call expects two results.940		for _, ref := range f.Ref {941			if ref.Expr != &call.Call.Fun {942				continue943			}944			if ref.Context == ctxCall2 {945				sb.WriteString("(")946				result = true947				twoResults = true948			}949			break950		}951952		// Add the result type, if any.953		if name.FuncType.Result != nil {954			rtype := p.rewriteUnsafe(name.FuncType.Result.Go)955			if rtype != name.FuncType.Result.Go {956				needsUnsafe = true957			}958			sb.WriteString(gofmt(rtype))959			result = true960		}961962		// Add the second result type, if any.963		if twoResults {964			if name.FuncType.Result == nil {965				// An explicit void result looks odd but it966				// seems to be how cgo has worked historically.967				sb.WriteString("_Ctype_void")968			}969			sb.WriteString(", error)")970		}971	}972973	sb.WriteString("{ ")974975	// Define _cgoN for each argument value.976	// Write _cgoCheckPointer calls to sbCheck.977	var sbCheck bytes.Buffer978	for i, param := range params {979		origArg := args[i]980		arg, nu := p.mangle(f, &args[i], true)981		if nu {982			needsUnsafe = true983		}984985		// Use "var x T = ..." syntax to explicitly convert untyped986		// constants to the parameter type, to avoid a type mismatch.987		ptype := p.rewriteUnsafe(param.Go)988989		if !p.needsPointerCheck(f, param.Go, args[i]) || param.BadPointer || p.checkUnsafeStringData(args[i]) {990			if ptype != param.Go {991				needsUnsafe = true992			}993			fmt.Fprintf(&sb, "var _cgo%d %s = %s; ", i,994				gofmt(ptype), gofmtPos(arg, origArg.Pos()))995			continue996		}997998		// Check for &a[i].999		if p.checkIndex(&sb, &sbCheck, arg, i) {1000			continue1001		}10021003		// Check for &x.1004		if p.checkAddr(&sb, &sbCheck, arg, i) {1005			continue1006		}10071008		// Check for a[:].1009		if p.checkSlice(&sb, &sbCheck, arg, i) {1010			continue1011		}10121013		fmt.Fprintf(&sb, "_cgo%d := %s; ", i, gofmtPos(arg, origArg.Pos()))1014		fmt.Fprintf(&sbCheck, "_cgoCheckPointer(_cgo%d, nil); ", i)1015	}10161017	if call.Deferred {1018		sb.WriteString("return func() { ")1019	}10201021	// Write out the calls to _cgoCheckPointer.1022	sb.WriteString(sbCheck.String())10231024	if result {1025		sb.WriteString("return ")1026	}10271028	m, nu := p.mangle(f, &call.Call.Fun, false)1029	if nu {1030		needsUnsafe = true1031	}1032	sb.WriteString(gofmtPos(m, end))10331034	sb.WriteString("(")1035	for i := range params {1036		if i > 0 {1037			sb.WriteString(", ")1038		}1039		fmt.Fprintf(&sb, "_cgo%d", i)1040	}1041	sb.WriteString("); ")1042	if call.Deferred {1043		sb.WriteString("}")1044	}1045	sb.WriteString("}")1046	if call.Deferred {1047		sb.WriteString("()")1048	}1049	sb.WriteString("()")10501051	return sb.String(), needsUnsafe1052}10531054// needsPointerCheck reports whether the type t needs a pointer check.1055// This is true if t is a pointer and if the value to which it points1056// might contain a pointer.1057func (p *Package) needsPointerCheck(f *File, t ast.Expr, arg ast.Expr) bool {1058	// An untyped nil does not need a pointer check, and when1059	// _cgoCheckPointer returns the untyped nil the type assertion we1060	// are going to insert will fail. Easier to just skip nil arguments.1061	// TODO: Note that this fails if nil is shadowed.1062	if id, ok := arg.(*ast.Ident); ok && id.Name == "nil" {1063		return false1064	}10651066	return p.hasPointer(f, t, true)1067}10681069// hasPointer is used by needsPointerCheck. If top is true it returns1070// whether t is or contains a pointer that might point to a pointer.1071// If top is false it reports whether t is or contains a pointer.1072// f may be nil.1073func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool {1074	switch t := t.(type) {1075	case *ast.ArrayType:1076		if t.Len == nil {1077			if !top {1078				return true1079			}1080			return p.hasPointer(f, t.Elt, false)1081		}1082		return p.hasPointer(f, t.Elt, top)1083	case *ast.StructType:1084		return slices.ContainsFunc(t.Fields.List, func(field *ast.Field) bool {1085			return p.hasPointer(f, field.Type, top)1086		})1087	case *ast.StarExpr: // Pointer type.1088		if !top {1089			return true1090		}1091		// Check whether this is a pointer to a C union (or class)1092		// type that contains a pointer.1093		if unionWithPointer[t.X] {1094			return true1095		}1096		return p.hasPointer(f, t.X, false)1097	case *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:1098		return true1099	case *ast.Ident:1100		// TODO: Handle types defined within function.1101		for _, d := range p.Decl {1102			gd, ok := d.(*ast.GenDecl)1103			if !ok || gd.Tok != token.TYPE {1104				continue1105			}1106			for _, spec := range gd.Specs {1107				ts, ok := spec.(*ast.TypeSpec)1108				if !ok {1109					continue1110				}1111				if ts.Name.Name == t.Name {1112					return p.hasPointer(f, ts.Type, top)1113				}1114			}1115		}1116		if def := typedef[t.Name]; def != nil {1117			return p.hasPointer(f, def.Go, top)1118		}1119		if t.Name == "string" {1120			return !top1121		}1122		if t.Name == "error" {1123			return true1124		}1125		if t.Name == "any" {1126			return true1127		}1128		if goTypes[t.Name] != nil {1129			return false1130		}1131		// We can't figure out the type. Conservative1132		// approach is to assume it has a pointer.1133		return true1134	case *ast.SelectorExpr:1135		if l, ok := t.X.(*ast.Ident); !ok || l.Name != "C" {1136			// Type defined in a different package.1137			// Conservative approach is to assume it has a1138			// pointer.1139			return true1140		}1141		if f == nil {1142			// Conservative approach: assume pointer.1143			return true1144		}1145		name := f.Name[t.Sel.Name]1146		if name != nil && name.Kind == "type" && name.Type != nil && name.Type.Go != nil {1147			return p.hasPointer(f, name.Type.Go, top)1148		}1149		// We can't figure out the type. Conservative1150		// approach is to assume it has a pointer.1151		return true1152	default:1153		error_(t.Pos(), "could not understand type %s", gofmt(t))1154		return true1155	}1156}11571158// mangle replaces references to C names in arg with the mangled names,1159// rewriting calls when it finds them.1160// It removes the corresponding references in f.Ref and f.Calls, so that we1161// don't try to do the replacement again in rewriteRef or rewriteCall.1162// If addPosition is true, add position info to the idents of C names in arg.1163func (p *Package) mangle(f *File, arg *ast.Expr, addPosition bool) (ast.Expr, bool) {1164	needsUnsafe := false1165	f.walk(arg, ctxExpr, func(f *File, arg any, context astContext) {1166		px, ok := arg.(*ast.Expr)1167		if !ok {1168			return1169		}1170		sel, ok := (*px).(*ast.SelectorExpr)1171		if ok {1172			if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {1173				return1174			}11751176			for _, r := range f.Ref {1177				if r.Expr == px {1178					*px = p.rewriteName(f, r, addPosition)1179					r.Done = true1180					break1181				}1182			}11831184			return1185		}11861187		call, ok := (*px).(*ast.CallExpr)1188		if !ok {1189			return1190		}11911192		for _, c := range f.Calls {1193			if !c.Done && c.Call.Lparen == call.Lparen {1194				cstr, nu := p.rewriteCall(f, c)1195				if cstr != "" {1196					// Smuggle the rewritten call through an ident.1197					*px = ast.NewIdent(cstr)1198					if nu {1199						needsUnsafe = true1200					}1201					c.Done = true1202				}1203			}1204		}1205	})1206	return *arg, needsUnsafe1207}12081209// checkIndex checks whether arg has the form &a[i], possibly inside1210// type conversions. If so, then in the general case it writes1211//1212//	_cgoIndexNN := a1213//	_cgoNN := &cgoIndexNN[i] // with type conversions, if any1214//1215// to sb, and writes1216//1217//	_cgoCheckPointer(_cgoNN, _cgoIndexNN)1218//1219// to sbCheck, and returns true. If a is a simple variable or field reference,1220// it writes1221//1222//	_cgoIndexNN := &a1223//1224// and dereferences the uses of _cgoIndexNN. Taking the address avoids1225// making a copy of an array.1226//1227// This tells _cgoCheckPointer to check the complete contents of the1228// slice or array being indexed, but no other part of the memory allocation.1229func (p *Package) checkIndex(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) bool {1230	// Strip type conversions.1231	x := arg1232	for {1233		c, ok := x.(*ast.CallExpr)1234		if !ok || len(c.Args) != 1 {1235			break1236		}1237		if !p.isType(c.Fun) && !p.isUnsafeData(c.Fun, false) {1238			break1239		}1240		x = c.Args[0]1241	}1242	u, ok := x.(*ast.UnaryExpr)1243	if !ok || u.Op != token.AND {1244		return false1245	}1246	index, ok := u.X.(*ast.IndexExpr)1247	if !ok {1248		return false1249	}12501251	addr := ""1252	deref := ""1253	if p.isVariable(index.X) {1254		addr = "&"1255		deref = "*"1256	}12571258	fmt.Fprintf(sb, "_cgoIndex%d := %s%s; ", i, addr, gofmtPos(index.X, index.X.Pos()))1259	origX := index.X1260	index.X = ast.NewIdent(fmt.Sprintf("_cgoIndex%d", i))1261	if deref == "*" {1262		index.X = &ast.StarExpr{X: index.X}1263	}1264	fmt.Fprintf(sb, "_cgo%d := %s; ", i, gofmtPos(arg, arg.Pos()))1265	index.X = origX12661267	fmt.Fprintf(sbCheck, "_cgoCheckPointer(_cgo%d, %s_cgoIndex%d); ", i, deref, i)12681269	return true1270}12711272// checkAddr checks whether arg has the form &x, possibly inside type1273// conversions. If so, it writes1274//1275//	_cgoBaseNN := &x1276//	_cgoNN := _cgoBaseNN // with type conversions, if any1277//1278// to sb, and writes1279//1280//	_cgoCheckPointer(_cgoBaseNN, true)1281//1282// to sbCheck, and returns true. This tells _cgoCheckPointer to check1283// just the contents of the pointer being passed, not any other part1284// of the memory allocation. This is run after checkIndex, which looks1285// for the special case of &a[i], which requires different checks.1286func (p *Package) checkAddr(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) bool {1287	// Strip type conversions.1288	px := &arg1289	for {1290		c, ok := (*px).(*ast.CallExpr)1291		if !ok || len(c.Args) != 1 {1292			break1293		}1294		if !p.isType(c.Fun) && !p.isUnsafeData(c.Fun, false) {1295			break1296		}1297		px = &c.Args[0]1298	}1299	if u, ok := (*px).(*ast.UnaryExpr); !ok || u.Op != token.AND {1300		return false1301	}13021303	fmt.Fprintf(sb, "_cgoBase%d := %s; ", i, gofmtPos(*px, (*px).Pos()))13041305	origX := *px1306	*px = ast.NewIdent(fmt.Sprintf("_cgoBase%d", i))1307	fmt.Fprintf(sb, "_cgo%d := %s; ", i, gofmtPos(arg, arg.Pos()))1308	*px = origX13091310	// Use "0 == 0" to do the right thing in the unlikely event1311	// that "true" is shadowed.1312	fmt.Fprintf(sbCheck, "_cgoCheckPointer(_cgoBase%d, 0 == 0); ", i)13131314	return true1315}13161317// checkSlice checks whether arg has the form x[i:j], possibly inside1318// type conversions. If so, it writes1319//1320//	_cgoSliceNN := x[i:j]1321//	_cgoNN := _cgoSliceNN // with type conversions, if any1322//1323// to sb, and writes1324//1325//	_cgoCheckPointer(_cgoSliceNN, true)1326//1327// to sbCheck, and returns true. This tells _cgoCheckPointer to check1328// just the contents of the slice being passed, not any other part1329// of the memory allocation.1330func (p *Package) checkSlice(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) bool {1331	// Strip type conversions.1332	px := &arg1333	for {1334		c, ok := (*px).(*ast.CallExpr)1335		if !ok || len(c.Args) != 1 {1336			break1337		}1338		if !p.isType(c.Fun) && !p.isUnsafeData(c.Fun, false) {1339			break1340		}1341		px = &c.Args[0]1342	}1343	if _, ok := (*px).(*ast.SliceExpr); !ok {1344		return false1345	}13461347	fmt.Fprintf(sb, "_cgoSlice%d := %s; ", i, gofmtPos(*px, (*px).Pos()))13481349	origX := *px1350	*px = ast.NewIdent(fmt.Sprintf("_cgoSlice%d", i))1351	fmt.Fprintf(sb, "_cgo%d := %s; ", i, gofmtPos(arg, arg.Pos()))1352	*px = origX13531354	// Use 0 == 0 to do the right thing in the unlikely event1355	// that "true" is shadowed.1356	fmt.Fprintf(sbCheck, "_cgoCheckPointer(_cgoSlice%d, 0 == 0); ", i)13571358	return true1359}13601361// checkUnsafeStringData checks for a call to unsafe.StringData.1362// The result of that call can't contain a pointer so there is1363// no need to call _cgoCheckPointer.1364func (p *Package) checkUnsafeStringData(arg ast.Expr) bool {1365	x := arg1366	for {1367		c, ok := x.(*ast.CallExpr)1368		if !ok || len(c.Args) != 1 {1369			break1370		}1371		if p.isUnsafeData(c.Fun, true) {1372			return true1373		}1374		if !p.isType(c.Fun) {1375			break1376		}1377		x = c.Args[0]1378	}1379	return false1380}13811382// isType reports whether the expression is definitely a type.1383// This is conservative--it returns false for an unknown identifier.1384func (p *Package) isType(t ast.Expr) bool {1385	switch t := t.(type) {1386	case *ast.SelectorExpr:1387		id, ok := t.X.(*ast.Ident)1388		if !ok {1389			return false1390		}1391		if id.Name == "unsafe" && t.Sel.Name == "Pointer" {1392			return true1393		}1394		if id.Name == "C" && typedef["_Ctype_"+t.Sel.Name] != nil {1395			return true1396		}1397		return false1398	case *ast.Ident:1399		// TODO: This ignores shadowing.1400		switch t.Name {1401		case "unsafe.Pointer", "bool", "byte",1402			"complex64", "complex128",1403			"error",1404			"float32", "float64",1405			"int", "int8", "int16", "int32", "int64",1406			"rune", "string",1407			"uint", "uint8", "uint16", "uint32", "uint64", "uintptr":14081409			return true1410		}1411		if strings.HasPrefix(t.Name, "_Ctype_") {1412			return true1413		}1414	case *ast.ParenExpr:1415		return p.isType(t.X)1416	case *ast.StarExpr:1417		return p.isType(t.X)1418	case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType,1419		*ast.MapType, *ast.ChanType:14201421		return true1422	}1423	return false1424}14251426// isUnsafeData reports whether the expression is unsafe.StringData1427// or unsafe.SliceData. We can ignore these when checking for pointers1428// because they don't change whether or not their argument contains1429// any Go pointers. If onlyStringData is true we only check for StringData.1430func (p *Package) isUnsafeData(x ast.Expr, onlyStringData bool) bool {1431	st, ok := x.(*ast.SelectorExpr)1432	if !ok {1433		return false1434	}1435	id, ok := st.X.(*ast.Ident)1436	if !ok {1437		return false1438	}1439	if id.Name != "unsafe" {1440		return false1441	}1442	if !onlyStringData && st.Sel.Name == "SliceData" {1443		return true1444	}1445	return st.Sel.Name == "StringData"1446}14471448// isVariable reports whether x is a variable, possibly with field references.1449func (p *Package) isVariable(x ast.Expr) bool {1450	switch x := x.(type) {1451	case *ast.Ident:1452		return true1453	case *ast.SelectorExpr:1454		return p.isVariable(x.X)1455	case *ast.IndexExpr:1456		return true1457	}1458	return false1459}14601461// rewriteUnsafe returns a version of t with references to unsafe.Pointer1462// rewritten to use _cgo_unsafe.Pointer instead.1463func (p *Package) rewriteUnsafe(t ast.Expr) ast.Expr {1464	switch t := t.(type) {1465	case *ast.Ident:1466		// We don't see a SelectorExpr for unsafe.Pointer;1467		// this is created by code in this file.1468		if t.Name == "unsafe.Pointer" {1469			return ast.NewIdent("_cgo_unsafe.Pointer")1470		}1471	case *ast.ArrayType:1472		t1 := p.rewriteUnsafe(t.Elt)1473		if t1 != t.Elt {1474			r := *t1475			r.Elt = t11476			return &r1477		}1478	case *ast.StructType:1479		changed := false1480		fields := *t.Fields1481		fields.List = nil1482		for _, f := range t.Fields.List {1483			ft := p.rewriteUnsafe(f.Type)1484			if ft == f.Type {1485				fields.List = append(fields.List, f)1486			} else {1487				fn := *f1488				fn.Type = ft1489				fields.List = append(fields.List, &fn)1490				changed = true1491			}1492		}1493		if changed {1494			r := *t1495			r.Fields = &fields1496			return &r1497		}1498	case *ast.StarExpr: // Pointer type.1499		x1 := p.rewriteUnsafe(t.X)1500		if x1 != t.X {1501			r := *t1502			r.X = x11503			return &r1504		}1505	}1506	return t1507}15081509// rewriteRef rewrites all the C.xxx references in f.AST to refer to the1510// Go equivalents, now that we have figured out the meaning of all1511// the xxx. In *godefs mode, rewriteRef replaces the names1512// with full definitions instead of mangled names.1513func (p *Package) rewriteRef(f *File) {1514	// Keep a list of all the functions, to remove the ones1515	// only used as expressions and avoid generating bridge1516	// code for them.1517	functions := make(map[string]bool)15181519	for _, n := range f.Name {1520		if n.Kind == "func" {1521			functions[n.Go] = false1522		}1523	}15241525	// Now that we have all the name types filled in,1526	// scan through the Refs to identify the ones that1527	// are trying to do a ,err call. Also check that1528	// functions are only used in calls.1529	for _, r := range f.Ref {1530		if r.Name.IsConst() && r.Name.Const == "" {1531			error_(r.Pos(), "unable to find value of constant C.%s", fixGo(r.Name.Go))1532		}15331534		if r.Name.Kind == "func" {1535			switch r.Context {1536			case ctxCall, ctxCall2:1537				functions[r.Name.Go] = true1538			}1539		}15401541		expr := p.rewriteName(f, r, false)15421543		if *godefs {1544			// Substitute definition for mangled type name.1545			if r.Name.Type != nil && r.Name.Kind == "type" {1546				expr = r.Name.Type.Go1547			}1548			if id, ok := expr.(*ast.Ident); ok {1549				if t := typedef[id.Name]; t != nil {1550					expr = t.Go1551				}1552				if id.Name == r.Name.Mangle && r.Name.Const != "" {1553					expr = ast.NewIdent(r.Name.Const)1554				}1555			}1556		}15571558		// Copy position information from old expr into new expr,1559		// in case expression being replaced is first on line.1560		// See golang.org/issue/6563.1561		pos := (*r.Expr).Pos()1562		if x, ok := expr.(*ast.Ident); ok {1563			expr = &ast.Ident{NamePos: pos, Name: x.Name}1564		}15651566		// Change AST, because some later processing depends on it,1567		// and also because -godefs mode still prints the AST.1568		old := *r.Expr1569		*r.Expr = expr15701571		// Record source-level edit for cgo output.1572		if !r.Done {1573			// Prepend a space in case the earlier code ends1574			// with '/', which would give us a "//" comment.1575			repl := " " + gofmtPos(expr, old.Pos())1576			end := fset.Position(old.End())1577			// Subtract 1 from the column if we are going to1578			// append a close parenthesis. That will set the1579			// correct column for the following characters.1580			sub := 01581			if r.Name.Kind != "type" {1582				sub = 11583			}1584			if end.Column > sub {1585				repl = fmt.Sprintf("%s /*line :%d:%d*/", repl, end.Line, end.Column-sub)1586			}1587			if r.Name.Kind != "type" {1588				repl = "(" + repl + ")"1589			}1590			f.Edit.Replace(f.offset(old.Pos()), f.offset(old.End()), repl)1591		}1592	}15931594	// Remove functions only used as expressions, so their respective1595	// bridge functions are not generated.1596	for name, used := range functions {1597		if !used {1598			delete(f.Name, name)1599		}1600	}1601}16021603// rewriteName returns the expression used to rewrite a reference.1604// If addPosition is true, add position info in the ident name.1605func (p *Package) rewriteName(f *File, r *Ref, addPosition bool) ast.Expr {1606	getNewIdent := ast.NewIdent1607	if addPosition {1608		getNewIdent = func(newName string) *ast.Ident {1609			mangledIdent := ast.NewIdent(newName)1610			if len(newName) == len(r.Name.Go) {1611				return mangledIdent1612			}1613			p := fset.Position((*r.Expr).End())1614			if p.Column == 0 {1615				return mangledIdent1616			}1617			return ast.NewIdent(fmt.Sprintf("%s /*line :%d:%d*/", newName, p.Line, p.Column))1618		}1619	}1620	var expr ast.Expr = getNewIdent(r.Name.Mangle) // default1621	switch r.Context {1622	case ctxCall, ctxCall2:1623		if r.Name.Kind != "func" {1624			if r.Name.Kind == "type" {1625				r.Context = ctxType1626				if r.Name.Type == nil {1627					error_(r.Pos(), "invalid conversion to C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)1628				}1629				break1630			}1631			error_(r.Pos(), "call of non-function C.%s", fixGo(r.Name.Go))1632			break1633		}1634		if r.Context == ctxCall2 {1635			if builtinDefs[r.Name.Go] != "" {1636				error_(r.Pos(), "no two-result form for C.%s", r.Name.Go)1637				break1638			}1639			// Invent new Name for the two-result function.1640			n := f.Name["2"+r.Name.Go]1641			if n == nil {1642				n = new(Name)1643				*n = *r.Name1644				n.AddError = true1645				n.Mangle = "_C2func_" + n.Go1646				f.Name["2"+r.Name.Go] = n1647			}1648			expr = getNewIdent(n.Mangle)1649			r.Name = n1650			break1651		}1652	case ctxExpr:1653		switch r.Name.Kind {1654		case "func":1655			if builtinDefs[r.Name.C] != "" {1656				error_(r.Pos(), "use of builtin '%s' not in function call", fixGo(r.Name.C))1657			}16581659			// Function is being used in an expression, to e.g. pass around a C function pointer.1660			// Create a new Name for this Ref which causes the variable to be declared in Go land.1661			fpName := "fp_" + r.Name.Go1662			name := f.Name[fpName]1663			if name == nil {1664				name = &Name{1665					Go:   fpName,1666					C:    r.Name.C,1667					Kind: "fpvar",1668					Type: &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*"), Go: ast.NewIdent("unsafe.Pointer")},1669				}1670				p.mangleName(name)1671				f.Name[fpName] = name1672			}1673			r.Name = name1674			// Rewrite into call to _Cgo_ptr to prevent assignments. The _Cgo_ptr1675			// function is defined in out.go and simply returns its argument. See1676			// issue 7757.1677			expr = &ast.CallExpr{1678				Fun:  &ast.Ident{NamePos: (*r.Expr).Pos(), Name: "_Cgo_ptr"},1679				Args: []ast.Expr{getNewIdent(name.Mangle)},1680			}1681		case "type":1682			// Okay - might be new(T), T(x), Generic[T], etc.1683			if r.Name.Type == nil {1684				error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)1685			}1686		case "var":1687			expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}1688		case "macro":1689			expr = &ast.CallExpr{Fun: expr}1690		}1691	case ctxSelector:1692		if r.Name.Kind == "var" {1693			expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}1694		} else {1695			error_(r.Pos(), "only C variables allowed in selector expression %s", fixGo(r.Name.Go))1696		}1697	case ctxType:1698		if r.Name.Kind != "type" {1699			error_(r.Pos(), "expression C.%s used as type", fixGo(r.Name.Go))1700		} else if r.Name.Type == nil {1701			// Use of C.enum_x, C.struct_x or C.union_x without C definition.1702			// GCC won't raise an error when using pointers to such unknown types.1703			error_(r.Pos(), "type C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)1704		}1705	default:1706		if r.Name.Kind == "func" {1707			error_(r.Pos(), "must call C.%s", fixGo(r.Name.Go))1708		}1709	}1710	return expr1711}17121713// gofmtPos returns the gofmt-formatted string for an AST node,1714// with a comment setting the position before the node.1715func gofmtPos(n ast.Expr, pos token.Pos) string {1716	s := gofmt(n)1717	p := fset.Position(pos)1718	if p.Column == 0 {1719		return s1720	}1721	return fmt.Sprintf("/*line :%d:%d*/%s", p.Line, p.Column, s)1722}17231724// checkGCCBaseCmd returns the start of the compiler command line.1725// It uses $CC if set, or else $GCC, or else the compiler recorded1726// during the initial build as defaultCC.1727// defaultCC is defined in zdefaultcc.go, written by cmd/dist.1728//1729// The compiler command line is split into arguments on whitespace. Quotes1730// are understood, so arguments may contain whitespace.1731//1732// checkGCCBaseCmd confirms that the compiler exists in PATH, returning1733// an error if it does not.1734func checkGCCBaseCmd() ([]string, error) {1735	// Use $CC if set, since that's what the build uses.1736	value := os.Getenv("CC")1737	if value == "" {1738		// Try $GCC if set, since that's what we used to use.1739		value = os.Getenv("GCC")1740	}1741	if value == "" {1742		value = defaultCC(goos, goarch)1743	}1744	args, err := quoted.Split(value)1745	if err != nil {1746		return nil, err1747	}1748	if len(args) == 0 {1749		return nil, errors.New("CC not set and no default found")1750	}1751	if _, err := exec.LookPath(args[0]); err != nil {1752		return nil, fmt.Errorf("C compiler %q not found: %v", args[0], err)1753	}1754	return args[:len(args):len(args)], nil1755}17561757// gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm".1758func gccMachine() []string {1759	switch goarch {1760	case "amd64":1761		if goos == "darwin" {1762			return []string{"-arch", "x86_64", "-m64"}1763		}1764		return []string{"-m64"}1765	case "arm64":1766		if goos == "darwin" {1767			return []string{"-arch", "arm64"}1768		}1769	case "386":1770		return []string{"-m32"}1771	case "arm":1772		return []string{"-marm"} // not thumb1773	case "s390":1774		return []string{"-m31"}1775	case "s390x":1776		return []string{"-m64"}1777	case "mips64", "mips64le":1778		if gomips64 == "hardfloat" {1779			return []string{"-mabi=64", "-mhard-float"}1780		} else if gomips64 == "softfloat" {1781			return []string{"-mabi=64", "-msoft-float"}1782		}1783	case "mips", "mipsle":1784		if gomips == "hardfloat" {1785			return []string{"-mabi=32", "-mfp32", "-mhard-float", "-mno-odd-spreg"}1786		} else if gomips == "softfloat" {1787			return []string{"-mabi=32", "-msoft-float"}1788		}1789	case "loong64":1790		return []string{"-mabi=lp64d"}1791	}1792	return nil1793}17941795var n atomic.Int6417961797func gccTmp() string {1798	c := strconv.Itoa(int(n.Add(1)))1799	return filepath.Join(outputDir(), "_cgo_"+c+".o")1800}18011802// gccCmd returns the gcc command line to use for compiling1803// the input.1804// gccCommand is called concurrently for different files.1805func (p *Package) gccCmd(ofile string) []string {1806	c := append(gccBaseCmd,1807		"-w",         // no warnings1808		"-Wno-error", // warnings are not errors1809		"-o"+ofile,   // write object to tmp1810		"-gdwarf-2",  // generate DWARF v2 debugging symbols1811		"-c",         // do not link1812		"-xc",        // input language is C1813	)1814	if p.GccIsClang {1815		c = append(c,1816			"-ferror-limit=0",1817			// Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn)1818			// doesn't have -Wno-unneeded-internal-declaration, so we need yet another1819			// flag to disable the warning. Yes, really good diagnostics, clang.1820			"-Wno-unknown-warning-option",1821			"-Wno-unneeded-internal-declaration",1822			"-Wno-unused-function",1823			"-Qunused-arguments",1824			// Clang embeds prototypes for some builtin functions,1825			// like malloc and calloc, but all size_t parameters are1826			// incorrectly typed unsigned long. We work around that1827			// by disabling the builtin functions (this is safe as1828			// it won't affect the actual compilation of the C code).1829			// See: https://golang.org/issue/6506.1830			"-fno-builtin",1831		)1832	}18331834	c = append(c, p.GccOptions...)1835	c = append(c, gccMachine()...)1836	if goos == "aix" {1837		c = append(c, "-maix64")1838		c = append(c, "-mcmodel=large")1839	}1840	// disable LTO so we get an object whose symbols we can read1841	c = append(c, "-fno-lto")1842	c = append(c, "-") //read input from standard input1843	return c1844}18451846// gccDebug runs gcc -gdwarf-2 over the C program stdin and1847// returns the corresponding DWARF data and, if present, debug data block.1848// gccDebug is called concurrently with different C programs.1849func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int64, floats []float64, strs []string) {1850	ofile := gccTmp()1851	runGcc(stdin, p.gccCmd(ofile))18521853	isDebugInts := func(s string) bool {1854		// Some systems use leading _ to denote non-assembly symbols.1855		return s == "__cgodebug_ints" || s == "___cgodebug_ints"1856	}1857	isDebugFloats := func(s string) bool {1858		// Some systems use leading _ to denote non-assembly symbols.1859		return s == "__cgodebug_floats" || s == "___cgodebug_floats"1860	}1861	indexOfDebugStr := func(s string) int {1862		// Some systems use leading _ to denote non-assembly symbols.1863		if strings.HasPrefix(s, "___") {1864			s = s[1:]1865		}1866		if strings.HasPrefix(s, "__cgodebug_str__") {1867			if n, err := strconv.Atoi(s[len("__cgodebug_str__"):]); err == nil {1868				return n1869			}1870		}1871		return -11872	}1873	indexOfDebugStrlen := func(s string) int {1874		// Some systems use leading _ to denote non-assembly symbols.1875		if strings.HasPrefix(s, "___") {1876			s = s[1:]1877		}1878		if t, ok := strings.CutPrefix(s, "__cgodebug_strlen__"); ok {1879			if n, err := strconv.Atoi(t); err == nil {1880				return n1881			}1882		}1883		return -11884	}18851886	strs = make([]string, nnames)18871888	strdata := make(map[int]string, nnames)1889	strlens := make(map[int]int, nnames)18901891	buildStrings := func() {1892		for n, strlen := range strlens {1893			data := strdata[n]1894			if len(data) <= strlen {1895				fatalf("invalid string literal")1896			}1897			strs[n] = data[:strlen]1898		}1899	}19001901	if f, err := macho.Open(ofile); err == nil {1902		defer f.Close()1903		d, err := f.DWARF()1904		if err != nil {1905			fatalf("cannot load DWARF output from %s: %v", ofile, err)1906		}1907		bo := f.ByteOrder1908		if f.Symtab != nil {1909			for i := range f.Symtab.Syms {1910				s := &f.Symtab.Syms[i]1911				switch {1912				case isDebugInts(s.Name):1913					// Found it. Now find data section.1914					if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {1915						sect := f.Sections[i]1916						if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {1917							if sdat, err := sect.Data(); err == nil {1918								data := sdat[s.Value-sect.Addr:]1919								ints = make([]int64, len(data)/8)1920								for i := range ints {1921									ints[i] = int64(bo.Uint64(data[i*8:]))1922								}1923							}1924						}1925					}1926				case isDebugFloats(s.Name):1927					// Found it. Now find data section.1928					if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {1929						sect := f.Sections[i]1930						if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {1931							if sdat, err := sect.Data(); err == nil {1932								data := sdat[s.Value-sect.Addr:]1933								floats = make([]float64, len(data)/8)1934								for i := range floats {1935									floats[i] = math.Float64frombits(bo.Uint64(data[i*8:]))1936								}1937							}1938						}1939					}1940				default:1941					if n := indexOfDebugStr(s.Name); n != -1 {1942						// Found it. Now find data section.1943						if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {1944							sect := f.Sections[i]1945							if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {1946								if sdat, err := sect.Data(); err == nil {1947									data := sdat[s.Value-sect.Addr:]1948									strdata[n] = string(data)1949								}1950							}1951						}1952						break1953					}1954					if n := indexOfDebugStrlen(s.Name); n != -1 {1955						// Found it. Now find data section.1956						if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {1957							sect := f.Sections[i]1958							if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {1959								if sdat, err := sect.Data(); err == nil {1960									data := sdat[s.Value-sect.Addr:]1961									strlen := bo.Uint64(data[:8])1962									if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt?1963										fatalf("string literal too big")1964									}1965									strlens[n] = int(strlen)1966								}1967							}1968						}1969						break1970					}1971				}1972			}19731974			buildStrings()1975		}1976		return d, ints, floats, strs1977	}19781979	if f, err := elf.Open(ofile); err == nil {1980		defer f.Close()1981		d, err := f.DWARF()1982		if err != nil {1983			fatalf("cannot load DWARF output from %s: %v", ofile, err)1984		}1985		bo := f.ByteOrder1986		symtab, err := f.Symbols()1987		if err == nil {1988			// Check for use of -fsanitize=hwaddress (issue 53285).1989			removeTag := func(v uint64) uint64 { return v }1990			if goarch == "arm64" {1991				for i := range symtab {1992					if symtab[i].Name == "__hwasan_init" {1993						// -fsanitize=hwaddress on ARM1994						// uses the upper byte of a1995						// memory address as a hardware1996						// tag. Remove it so that1997						// we can find the associated1998						// data.1999						removeTag = func(v uint64) uint64 { return v &^ (0xff << (64 - 8)) }2000						break

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.