summaryrefslogtreecommitdiff
path: root/libgo/go/cmd/cgo/gcc.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/cmd/cgo/gcc.go')
-rw-r--r--libgo/go/cmd/cgo/gcc.go146
1 files changed, 132 insertions, 14 deletions
diff --git a/libgo/go/cmd/cgo/gcc.go b/libgo/go/cmd/cgo/gcc.go
index de64a75f710..02391495bbe 100644
--- a/libgo/go/cmd/cgo/gcc.go
+++ b/libgo/go/cmd/cgo/gcc.go
@@ -23,10 +23,13 @@ import (
"internal/xcoff"
"math"
"os"
+ "os/exec"
"strconv"
"strings"
"unicode"
"unicode/utf8"
+
+ "cmd/internal/quoted"
)
var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
@@ -400,7 +403,7 @@ func (p *Package) guessKinds(f *File) []*Name {
stderr = p.gccErrors(b.Bytes())
}
if stderr == "" {
- fatalf("%s produced no output\non input:\n%s", p.gccBaseCmd()[0], b.Bytes())
+ fatalf("%s produced no output\non input:\n%s", gccBaseCmd[0], b.Bytes())
}
completed := false
@@ -475,7 +478,7 @@ func (p *Package) guessKinds(f *File) []*Name {
}
if !completed {
- fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", p.gccBaseCmd()[0], b.Bytes(), stderr)
+ fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", gccBaseCmd[0], b.Bytes(), stderr)
}
for i, n := range names {
@@ -506,7 +509,7 @@ func (p *Package) guessKinds(f *File) []*Name {
// to users debugging preamble mistakes. See issue 8442.
preambleErrors := p.gccErrors([]byte(f.Preamble))
if len(preambleErrors) > 0 {
- error_(token.NoPos, "\n%s errors for preamble:\n%s", p.gccBaseCmd()[0], preambleErrors)
+ error_(token.NoPos, "\n%s errors for preamble:\n%s", gccBaseCmd[0], preambleErrors)
}
fatalf("unresolved names")
@@ -1521,7 +1524,7 @@ func (p *Package) rewriteName(f *File, r *Ref, addPosition bool) ast.Expr {
Args: []ast.Expr{getNewIdent(name.Mangle)},
}
case "type":
- // Okay - might be new(T)
+ // Okay - might be new(T), T(x), Generic[T], etc.
if r.Name.Type == nil {
error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
}
@@ -1563,20 +1566,37 @@ func gofmtPos(n ast.Expr, pos token.Pos) string {
return fmt.Sprintf("/*line :%d:%d*/%s", p.Line, p.Column, s)
}
-// gccBaseCmd returns the start of the compiler command line.
+// checkGCCBaseCmd returns the start of the compiler command line.
// It uses $CC if set, or else $GCC, or else the compiler recorded
// during the initial build as defaultCC.
// defaultCC is defined in zdefaultcc.go, written by cmd/dist.
-func (p *Package) gccBaseCmd() []string {
+//
+// The compiler command line is split into arguments on whitespace. Quotes
+// are understood, so arguments may contain whitespace.
+//
+// checkGCCBaseCmd confirms that the compiler exists in PATH, returning
+// an error if it does not.
+func checkGCCBaseCmd() ([]string, error) {
// Use $CC if set, since that's what the build uses.
- if ret := strings.Fields(os.Getenv("CC")); len(ret) > 0 {
- return ret
+ value := os.Getenv("CC")
+ if value == "" {
+ // Try $GCC if set, since that's what we used to use.
+ value = os.Getenv("GCC")
+ }
+ if value == "" {
+ value = defaultCC(goos, goarch)
}
- // Try $GCC if set, since that's what we used to use.
- if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 {
- return ret
+ args, err := quoted.Split(value)
+ if err != nil {
+ return nil, err
}
- return strings.Fields(defaultCC(goos, goarch))
+ if len(args) == 0 {
+ return nil, errors.New("CC not set and no default found")
+ }
+ if _, err := exec.LookPath(args[0]); err != nil {
+ return nil, fmt.Errorf("C compiler %q not found: %v", args[0], err)
+ }
+ return args[:len(args):len(args)], nil
}
// gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm".
@@ -1630,7 +1650,7 @@ func gccTmp() string {
// gccCmd returns the gcc command line to use for compiling
// the input.
func (p *Package) gccCmd() []string {
- c := append(p.gccBaseCmd(),
+ c := append(gccBaseCmd,
"-w", // no warnings
"-Wno-error", // warnings are not errors
"-o"+gccTmp(), // write object to tmp
@@ -2030,7 +2050,7 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6
// #defines that gcc encountered while processing the input
// and its included files.
func (p *Package) gccDefines(stdin []byte) string {
- base := append(p.gccBaseCmd(), "-E", "-dM", "-xc")
+ base := append(gccBaseCmd, "-E", "-dM", "-xc")
base = append(base, p.gccMachine()...)
stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-"))
return stdout
@@ -2111,6 +2131,9 @@ type typeConv struct {
// Type names X for which there exists an XGetTypeID function with type func() CFTypeID.
getTypeIDs map[string]bool
+ // badStructs contains C structs that should be marked NotInHeap.
+ notInHeapStructs map[string]bool
+
// Predeclared types.
bool ast.Expr
byte ast.Expr // denotes padding
@@ -2122,6 +2145,7 @@ type typeConv struct {
string ast.Expr
goVoid ast.Expr // _Ctype_void, denotes C's void
goVoidPtr ast.Expr // unsafe.Pointer or *byte
+ goVoidPtrNoHeap ast.Expr // *_Ctype_void_notinheap, like goVoidPtr but marked NotInHeap
ptrSize int64
intSize int64
@@ -2145,6 +2169,7 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
c.m = make(map[string]*Type)
c.ptrs = make(map[string][]*Type)
c.getTypeIDs = make(map[string]bool)
+ c.notInHeapStructs = make(map[string]bool)
c.bool = c.Ident("bool")
c.byte = c.Ident("byte")
c.int8 = c.Ident("int8")
@@ -2163,6 +2188,7 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
c.void = c.Ident("void")
c.string = c.Ident("string")
c.goVoid = c.Ident("_Ctype_void")
+ c.goVoidPtrNoHeap = c.Ident("*_Ctype_void_notinheap")
// Normally cgo translates void* to unsafe.Pointer,
// but for historical reasons -godefs uses *byte instead.
@@ -2543,6 +2569,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
tt.C = &TypeRepr{"struct %s", []interface{}{tag}}
}
tt.Go = g
+ tt.NotInHeap = c.notInHeapStructs[tag]
typedef[name.Name] = &tt
}
@@ -2586,6 +2613,30 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
oldType.BadPointer = true
}
}
+ if c.badVoidPointerTypedef(dt) {
+ // Treat this typedef as a pointer to a NotInHeap void.
+ s := *sub
+ s.Go = c.goVoidPtrNoHeap
+ sub = &s
+ // Make sure we update any previously computed type.
+ if oldType := typedef[name.Name]; oldType != nil {
+ oldType.Go = sub.Go
+ }
+ }
+ // Check for non-pointer "struct <tag>{...}; typedef struct <tag> *<name>"
+ // typedefs that should be marked NotInHeap.
+ if ptr, ok := dt.Type.(*dwarf.PtrType); ok {
+ if strct, ok := ptr.Type.(*dwarf.StructType); ok {
+ if c.badStructPointerTypedef(dt.Name, strct) {
+ c.notInHeapStructs[strct.StructName] = true
+ // Make sure we update any previously computed type.
+ name := "_Ctype_struct_" + strct.StructName
+ if oldType := typedef[name]; oldType != nil {
+ oldType.NotInHeap = true
+ }
+ }
+ }
+ }
t.Go = name
t.BadPointer = sub.BadPointer
t.NotInHeap = sub.NotInHeap
@@ -3035,6 +3086,31 @@ func upper(s string) string {
// so that all fields are exported.
func godefsFields(fld []*ast.Field) {
prefix := fieldPrefix(fld)
+
+ // Issue 48396: check for duplicate field names.
+ if prefix != "" {
+ names := make(map[string]bool)
+ fldLoop:
+ for _, f := range fld {
+ for _, n := range f.Names {
+ name := n.Name
+ if name == "_" {
+ continue
+ }
+ if name != prefix {
+ name = strings.TrimPrefix(n.Name, prefix)
+ }
+ name = upper(name)
+ if names[name] {
+ // Field name conflict: don't remove prefix.
+ prefix = ""
+ break fldLoop
+ }
+ names[name] = true
+ }
+ }
+ }
+
npad := 0
for _, f := range fld {
for _, n := range f.Names {
@@ -3112,6 +3188,48 @@ func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
return false
}
+// badVoidPointerTypedef is like badPointerTypeDef, but for "void *" typedefs that should be NotInHeap.
+func (c *typeConv) badVoidPointerTypedef(dt *dwarf.TypedefType) bool {
+ // Match the Windows HANDLE type (#42018).
+ if goos != "windows" || dt.Name != "HANDLE" {
+ return false
+ }
+ // Check that the typedef is "typedef void *<name>".
+ if ptr, ok := dt.Type.(*dwarf.PtrType); ok {
+ if _, ok := ptr.Type.(*dwarf.VoidType); ok {
+ return true
+ }
+ }
+ return false
+}
+
+// badStructPointerTypedef is like badVoidPointerTypedefs but for structs.
+func (c *typeConv) badStructPointerTypedef(name string, dt *dwarf.StructType) bool {
+ // Windows handle types can all potentially contain non-pointers.
+ // badVoidPointerTypedef handles the "void *" HANDLE type, but other
+ // handles are defined as
+ //
+ // struct <name>__{int unused;}; typedef struct <name>__ *name;
+ //
+ // by the DECLARE_HANDLE macro in STRICT mode. The macro is declared in
+ // the Windows ntdef.h header,
+ //
+ // https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/shared/ntdef.h#L779
+ if goos != "windows" {
+ return false
+ }
+ if len(dt.Field) != 1 {
+ return false
+ }
+ if dt.StructName != name+"__" {
+ return false
+ }
+ if f := dt.Field[0]; f.Name != "unused" || f.Type.Common().Name != "int" {
+ return false
+ }
+ return true
+}
+
// baseBadPointerTypedef reports whether the base of a chain of typedefs is a bad typedef
// as badPointerTypedef reports.
func (c *typeConv) baseBadPointerTypedef(dt *dwarf.TypedefType) bool {