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]¬Declared != 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.