...
Run Format

Source file src/runtime/cgocall.go

     1	// Copyright 2009 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	// Cgo call and callback support.
     6	//
     7	// To call into the C function f from Go, the cgo-generated code calls
     8	// runtime.cgocall(_cgo_Cfunc_f, frame), where _cgo_Cfunc_f is a
     9	// gcc-compiled function written by cgo.
    10	//
    11	// runtime.cgocall (below) locks g to m, calls entersyscall
    12	// so as not to block other goroutines or the garbage collector,
    13	// and then calls runtime.asmcgocall(_cgo_Cfunc_f, frame).
    14	//
    15	// runtime.asmcgocall (in asm_$GOARCH.s) switches to the m->g0 stack
    16	// (assumed to be an operating system-allocated stack, so safe to run
    17	// gcc-compiled code on) and calls _cgo_Cfunc_f(frame).
    18	//
    19	// _cgo_Cfunc_f invokes the actual C function f with arguments
    20	// taken from the frame structure, records the results in the frame,
    21	// and returns to runtime.asmcgocall.
    22	//
    23	// After it regains control, runtime.asmcgocall switches back to the
    24	// original g (m->curg)'s stack and returns to runtime.cgocall.
    25	//
    26	// After it regains control, runtime.cgocall calls exitsyscall, which blocks
    27	// until this m can run Go code without violating the $GOMAXPROCS limit,
    28	// and then unlocks g from m.
    29	//
    30	// The above description skipped over the possibility of the gcc-compiled
    31	// function f calling back into Go. If that happens, we continue down
    32	// the rabbit hole during the execution of f.
    33	//
    34	// To make it possible for gcc-compiled C code to call a Go function p.GoF,
    35	// cgo writes a gcc-compiled function named GoF (not p.GoF, since gcc doesn't
    36	// know about packages).  The gcc-compiled C function f calls GoF.
    37	//
    38	// GoF calls crosscall2(_cgoexp_GoF, frame, framesize).  Crosscall2
    39	// (in cgo/gcc_$GOARCH.S, a gcc-compiled assembly file) is a two-argument
    40	// adapter from the gcc function call ABI to the 6c function call ABI.
    41	// It is called from gcc to call 6c functions. In this case it calls
    42	// _cgoexp_GoF(frame, framesize), still running on m->g0's stack
    43	// and outside the $GOMAXPROCS limit. Thus, this code cannot yet
    44	// call arbitrary Go code directly and must be careful not to allocate
    45	// memory or use up m->g0's stack.
    46	//
    47	// _cgoexp_GoF calls runtime.cgocallback(p.GoF, frame, framesize, ctxt).
    48	// (The reason for having _cgoexp_GoF instead of writing a crosscall3
    49	// to make this call directly is that _cgoexp_GoF, because it is compiled
    50	// with 6c instead of gcc, can refer to dotted names like
    51	// runtime.cgocallback and p.GoF.)
    52	//
    53	// runtime.cgocallback (in asm_$GOARCH.s) switches from m->g0's
    54	// stack to the original g (m->curg)'s stack, on which it calls
    55	// runtime.cgocallbackg(p.GoF, frame, framesize).
    56	// As part of the stack switch, runtime.cgocallback saves the current
    57	// SP as m->g0->sched.sp, so that any use of m->g0's stack during the
    58	// execution of the callback will be done below the existing stack frames.
    59	// Before overwriting m->g0->sched.sp, it pushes the old value on the
    60	// m->g0 stack, so that it can be restored later.
    61	//
    62	// runtime.cgocallbackg (below) is now running on a real goroutine
    63	// stack (not an m->g0 stack).  First it calls runtime.exitsyscall, which will
    64	// block until the $GOMAXPROCS limit allows running this goroutine.
    65	// Once exitsyscall has returned, it is safe to do things like call the memory
    66	// allocator or invoke the Go callback function p.GoF.  runtime.cgocallbackg
    67	// first defers a function to unwind m->g0.sched.sp, so that if p.GoF
    68	// panics, m->g0.sched.sp will be restored to its old value: the m->g0 stack
    69	// and the m->curg stack will be unwound in lock step.
    70	// Then it calls p.GoF.  Finally it pops but does not execute the deferred
    71	// function, calls runtime.entersyscall, and returns to runtime.cgocallback.
    72	//
    73	// After it regains control, runtime.cgocallback switches back to
    74	// m->g0's stack (the pointer is still in m->g0.sched.sp), restores the old
    75	// m->g0.sched.sp value from the stack, and returns to _cgoexp_GoF.
    76	//
    77	// _cgoexp_GoF immediately returns to crosscall2, which restores the
    78	// callee-save registers for gcc and returns to GoF, which returns to f.
    79	
    80	package runtime
    81	
    82	import (
    83		"runtime/internal/atomic"
    84		"runtime/internal/sys"
    85		"unsafe"
    86	)
    87	
    88	// Addresses collected in a cgo backtrace when crashing.
    89	// Length must match arg.Max in x_cgo_callers in runtime/cgo/gcc_traceback.c.
    90	type cgoCallers [32]uintptr
    91	
    92	// Call from Go to C.
    93	//go:nosplit
    94	func cgocall(fn, arg unsafe.Pointer) int32 {
    95		if !iscgo && GOOS != "solaris" && GOOS != "windows" {
    96			throw("cgocall unavailable")
    97		}
    98	
    99		if fn == nil {
   100			throw("cgocall nil")
   101		}
   102	
   103		if raceenabled {
   104			racereleasemerge(unsafe.Pointer(&racecgosync))
   105		}
   106	
   107		/*
   108		 * Lock g to m to ensure we stay on the same stack if we do a
   109		 * cgo callback. Add entry to defer stack in case of panic.
   110		 */
   111		lockOSThread()
   112		mp := getg().m
   113		mp.ncgocall++
   114		mp.ncgo++
   115		defer endcgo(mp)
   116	
   117		// Reset traceback.
   118		mp.cgoCallers[0] = 0
   119	
   120		/*
   121		 * Announce we are entering a system call
   122		 * so that the scheduler knows to create another
   123		 * M to run goroutines while we are in the
   124		 * foreign code.
   125		 *
   126		 * The call to asmcgocall is guaranteed not to
   127		 * split the stack and does not allocate memory,
   128		 * so it is safe to call while "in a system call", outside
   129		 * the $GOMAXPROCS accounting.
   130		 */
   131		entersyscall(0)
   132		errno := asmcgocall(fn, arg)
   133		exitsyscall(0)
   134	
   135		return errno
   136	}
   137	
   138	//go:nosplit
   139	func endcgo(mp *m) {
   140		mp.ncgo--
   141	
   142		if raceenabled {
   143			raceacquire(unsafe.Pointer(&racecgosync))
   144		}
   145	
   146		unlockOSThread() // invalidates mp
   147	}
   148	
   149	// Call from C back to Go.
   150	//go:nosplit
   151	func cgocallbackg(ctxt uintptr) {
   152		gp := getg()
   153		if gp != gp.m.curg {
   154			println("runtime: bad g in cgocallback")
   155			exit(2)
   156		}
   157	
   158		// Save current syscall parameters, so m.syscall can be
   159		// used again if callback decide to make syscall.
   160		syscall := gp.m.syscall
   161	
   162		// entersyscall saves the caller's SP to allow the GC to trace the Go
   163		// stack. However, since we're returning to an earlier stack frame and
   164		// need to pair with the entersyscall() call made by cgocall, we must
   165		// save syscall* and let reentersyscall restore them.
   166		savedsp := unsafe.Pointer(gp.syscallsp)
   167		savedpc := gp.syscallpc
   168		exitsyscall(0) // coming out of cgo call
   169	
   170		cgocallbackg1(ctxt)
   171	
   172		// going back to cgo call
   173		reentersyscall(savedpc, uintptr(savedsp))
   174	
   175		gp.m.syscall = syscall
   176	}
   177	
   178	func cgocallbackg1(ctxt uintptr) {
   179		gp := getg()
   180		if gp.m.needextram || atomic.Load(&extraMWaiters) > 0 {
   181			gp.m.needextram = false
   182			systemstack(newextram)
   183		}
   184	
   185		if ctxt != 0 {
   186			s := append(gp.cgoCtxt, ctxt)
   187	
   188			// Now we need to set gp.cgoCtxt = s, but we could get
   189			// a SIGPROF signal while manipulating the slice, and
   190			// the SIGPROF handler could pick up gp.cgoCtxt while
   191			// tracing up the stack.  We need to ensure that the
   192			// handler always sees a valid slice, so set the
   193			// values in an order such that it always does.
   194			p := (*slice)(unsafe.Pointer(&gp.cgoCtxt))
   195			atomicstorep(unsafe.Pointer(&p.array), unsafe.Pointer(&s[0]))
   196			p.cap = cap(s)
   197			p.len = len(s)
   198	
   199			defer func(gp *g) {
   200				// Decrease the length of the slice by one, safely.
   201				p := (*slice)(unsafe.Pointer(&gp.cgoCtxt))
   202				p.len--
   203			}(gp)
   204		}
   205	
   206		if gp.m.ncgo == 0 {
   207			// The C call to Go came from a thread not currently running
   208			// any Go. In the case of -buildmode=c-archive or c-shared,
   209			// this call may be coming in before package initialization
   210			// is complete. Wait until it is.
   211			<-main_init_done
   212		}
   213	
   214		// Add entry to defer stack in case of panic.
   215		restore := true
   216		defer unwindm(&restore)
   217	
   218		if raceenabled {
   219			raceacquire(unsafe.Pointer(&racecgosync))
   220		}
   221	
   222		type args struct {
   223			fn      *funcval
   224			arg     unsafe.Pointer
   225			argsize uintptr
   226		}
   227		var cb *args
   228	
   229		// Location of callback arguments depends on stack frame layout
   230		// and size of stack frame of cgocallback_gofunc.
   231		sp := gp.m.g0.sched.sp
   232		switch GOARCH {
   233		default:
   234			throw("cgocallbackg is unimplemented on arch")
   235		case "arm":
   236			// On arm, stack frame is two words and there's a saved LR between
   237			// SP and the stack frame and between the stack frame and the arguments.
   238			cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize))
   239		case "arm64":
   240			// On arm64, stack frame is four words and there's a saved LR between
   241			// SP and the stack frame and between the stack frame and the arguments.
   242			cb = (*args)(unsafe.Pointer(sp + 5*sys.PtrSize))
   243		case "amd64":
   244			// On amd64, stack frame is two words, plus caller PC.
   245			if framepointer_enabled {
   246				// In this case, there's also saved BP.
   247				cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize))
   248				break
   249			}
   250			cb = (*args)(unsafe.Pointer(sp + 3*sys.PtrSize))
   251		case "386":
   252			// On 386, stack frame is three words, plus caller PC.
   253			cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize))
   254		case "ppc64", "ppc64le", "s390x":
   255			// On ppc64 and s390x, the callback arguments are in the arguments area of
   256			// cgocallback's stack frame. The stack looks like this:
   257			// +--------------------+------------------------------+
   258			// |                    | ...                          |
   259			// | cgoexp_$fn         +------------------------------+
   260			// |                    | fixed frame area             |
   261			// +--------------------+------------------------------+
   262			// |                    | arguments area               |
   263			// | cgocallback        +------------------------------+ <- sp + 2*minFrameSize + 2*ptrSize
   264			// |                    | fixed frame area             |
   265			// +--------------------+------------------------------+ <- sp + minFrameSize + 2*ptrSize
   266			// |                    | local variables (2 pointers) |
   267			// | cgocallback_gofunc +------------------------------+ <- sp + minFrameSize
   268			// |                    | fixed frame area             |
   269			// +--------------------+------------------------------+ <- sp
   270			cb = (*args)(unsafe.Pointer(sp + 2*sys.MinFrameSize + 2*sys.PtrSize))
   271		case "mips64", "mips64le":
   272			// On mips64x, stack frame is two words and there's a saved LR between
   273			// SP and the stack frame and between the stack frame and the arguments.
   274			cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize))
   275		}
   276	
   277		// Invoke callback.
   278		// NOTE(rsc): passing nil for argtype means that the copying of the
   279		// results back into cb.arg happens without any corresponding write barriers.
   280		// For cgo, cb.arg points into a C stack frame and therefore doesn't
   281		// hold any pointers that the GC can find anyway - the write barrier
   282		// would be a no-op.
   283		reflectcall(nil, unsafe.Pointer(cb.fn), cb.arg, uint32(cb.argsize), 0)
   284	
   285		if raceenabled {
   286			racereleasemerge(unsafe.Pointer(&racecgosync))
   287		}
   288		if msanenabled {
   289			// Tell msan that we wrote to the entire argument block.
   290			// This tells msan that we set the results.
   291			// Since we have already called the function it doesn't
   292			// matter that we are writing to the non-result parameters.
   293			msanwrite(cb.arg, cb.argsize)
   294		}
   295	
   296		// Do not unwind m->g0->sched.sp.
   297		// Our caller, cgocallback, will do that.
   298		restore = false
   299	}
   300	
   301	func unwindm(restore *bool) {
   302		if !*restore {
   303			return
   304		}
   305		// Restore sp saved by cgocallback during
   306		// unwind of g's stack (see comment at top of file).
   307		mp := acquirem()
   308		sched := &mp.g0.sched
   309		switch GOARCH {
   310		default:
   311			throw("unwindm not implemented")
   312		case "386", "amd64", "arm", "ppc64", "ppc64le", "mips64", "mips64le", "s390x":
   313			sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + sys.MinFrameSize))
   314		case "arm64":
   315			sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 16))
   316		}
   317		releasem(mp)
   318	}
   319	
   320	// called from assembly
   321	func badcgocallback() {
   322		throw("misaligned stack in cgocallback")
   323	}
   324	
   325	// called from (incomplete) assembly
   326	func cgounimpl() {
   327		throw("cgo not implemented")
   328	}
   329	
   330	var racecgosync uint64 // represents possible synchronization in C code
   331	
   332	// Pointer checking for cgo code.
   333	
   334	// We want to detect all cases where a program that does not use
   335	// unsafe makes a cgo call passing a Go pointer to memory that
   336	// contains a Go pointer. Here a Go pointer is defined as a pointer
   337	// to memory allocated by the Go runtime. Programs that use unsafe
   338	// can evade this restriction easily, so we don't try to catch them.
   339	// The cgo program will rewrite all possibly bad pointer arguments to
   340	// call cgoCheckPointer, where we can catch cases of a Go pointer
   341	// pointing to a Go pointer.
   342	
   343	// Complicating matters, taking the address of a slice or array
   344	// element permits the C program to access all elements of the slice
   345	// or array. In that case we will see a pointer to a single element,
   346	// but we need to check the entire data structure.
   347	
   348	// The cgoCheckPointer call takes additional arguments indicating that
   349	// it was called on an address expression. An additional argument of
   350	// true means that it only needs to check a single element. An
   351	// additional argument of a slice or array means that it needs to
   352	// check the entire slice/array, but nothing else. Otherwise, the
   353	// pointer could be anything, and we check the entire heap object,
   354	// which is conservative but safe.
   355	
   356	// When and if we implement a moving garbage collector,
   357	// cgoCheckPointer will pin the pointer for the duration of the cgo
   358	// call.  (This is necessary but not sufficient; the cgo program will
   359	// also have to change to pin Go pointers that cannot point to Go
   360	// pointers.)
   361	
   362	// cgoCheckPointer checks if the argument contains a Go pointer that
   363	// points to a Go pointer, and panics if it does. It returns the pointer.
   364	func cgoCheckPointer(ptr interface{}, args ...interface{}) interface{} {
   365		if debug.cgocheck == 0 {
   366			return ptr
   367		}
   368	
   369		ep := (*eface)(unsafe.Pointer(&ptr))
   370		t := ep._type
   371	
   372		top := true
   373		if len(args) > 0 && (t.kind&kindMask == kindPtr || t.kind&kindMask == kindUnsafePointer) {
   374			p := ep.data
   375			if t.kind&kindDirectIface == 0 {
   376				p = *(*unsafe.Pointer)(p)
   377			}
   378			if !cgoIsGoPointer(p) {
   379				return ptr
   380			}
   381			aep := (*eface)(unsafe.Pointer(&args[0]))
   382			switch aep._type.kind & kindMask {
   383			case kindBool:
   384				if t.kind&kindMask == kindUnsafePointer {
   385					// We don't know the type of the element.
   386					break
   387				}
   388				pt := (*ptrtype)(unsafe.Pointer(t))
   389				cgoCheckArg(pt.elem, p, true, false, cgoCheckPointerFail)
   390				return ptr
   391			case kindSlice:
   392				// Check the slice rather than the pointer.
   393				ep = aep
   394				t = ep._type
   395			case kindArray:
   396				// Check the array rather than the pointer.
   397				// Pass top as false since we have a pointer
   398				// to the array.
   399				ep = aep
   400				t = ep._type
   401				top = false
   402			default:
   403				throw("can't happen")
   404			}
   405		}
   406	
   407		cgoCheckArg(t, ep.data, t.kind&kindDirectIface == 0, top, cgoCheckPointerFail)
   408		return ptr
   409	}
   410	
   411	const cgoCheckPointerFail = "cgo argument has Go pointer to Go pointer"
   412	const cgoResultFail = "cgo result has Go pointer"
   413	
   414	// cgoCheckArg is the real work of cgoCheckPointer. The argument p
   415	// is either a pointer to the value (of type t), or the value itself,
   416	// depending on indir. The top parameter is whether we are at the top
   417	// level, where Go pointers are allowed.
   418	func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) {
   419		if t.kind&kindNoPointers != 0 {
   420			// If the type has no pointers there is nothing to do.
   421			return
   422		}
   423	
   424		switch t.kind & kindMask {
   425		default:
   426			throw("can't happen")
   427		case kindArray:
   428			at := (*arraytype)(unsafe.Pointer(t))
   429			if !indir {
   430				if at.len != 1 {
   431					throw("can't happen")
   432				}
   433				cgoCheckArg(at.elem, p, at.elem.kind&kindDirectIface == 0, top, msg)
   434				return
   435			}
   436			for i := uintptr(0); i < at.len; i++ {
   437				cgoCheckArg(at.elem, p, true, top, msg)
   438				p = add(p, at.elem.size)
   439			}
   440		case kindChan, kindMap:
   441			// These types contain internal pointers that will
   442			// always be allocated in the Go heap. It's never OK
   443			// to pass them to C.
   444			panic(errorString(msg))
   445		case kindFunc:
   446			if indir {
   447				p = *(*unsafe.Pointer)(p)
   448			}
   449			if !cgoIsGoPointer(p) {
   450				return
   451			}
   452			panic(errorString(msg))
   453		case kindInterface:
   454			it := *(**_type)(p)
   455			if it == nil {
   456				return
   457			}
   458			// A type known at compile time is OK since it's
   459			// constant. A type not known at compile time will be
   460			// in the heap and will not be OK.
   461			if inheap(uintptr(unsafe.Pointer(it))) {
   462				panic(errorString(msg))
   463			}
   464			p = *(*unsafe.Pointer)(add(p, sys.PtrSize))
   465			if !cgoIsGoPointer(p) {
   466				return
   467			}
   468			if !top {
   469				panic(errorString(msg))
   470			}
   471			cgoCheckArg(it, p, it.kind&kindDirectIface == 0, false, msg)
   472		case kindSlice:
   473			st := (*slicetype)(unsafe.Pointer(t))
   474			s := (*slice)(p)
   475			p = s.array
   476			if !cgoIsGoPointer(p) {
   477				return
   478			}
   479			if !top {
   480				panic(errorString(msg))
   481			}
   482			if st.elem.kind&kindNoPointers != 0 {
   483				return
   484			}
   485			for i := 0; i < s.cap; i++ {
   486				cgoCheckArg(st.elem, p, true, false, msg)
   487				p = add(p, st.elem.size)
   488			}
   489		case kindString:
   490			ss := (*stringStruct)(p)
   491			if !cgoIsGoPointer(ss.str) {
   492				return
   493			}
   494			if !top {
   495				panic(errorString(msg))
   496			}
   497		case kindStruct:
   498			st := (*structtype)(unsafe.Pointer(t))
   499			if !indir {
   500				if len(st.fields) != 1 {
   501					throw("can't happen")
   502				}
   503				cgoCheckArg(st.fields[0].typ, p, st.fields[0].typ.kind&kindDirectIface == 0, top, msg)
   504				return
   505			}
   506			for _, f := range st.fields {
   507				cgoCheckArg(f.typ, add(p, f.offset), true, top, msg)
   508			}
   509		case kindPtr, kindUnsafePointer:
   510			if indir {
   511				p = *(*unsafe.Pointer)(p)
   512			}
   513	
   514			if !cgoIsGoPointer(p) {
   515				return
   516			}
   517			if !top {
   518				panic(errorString(msg))
   519			}
   520	
   521			cgoCheckUnknownPointer(p, msg)
   522		}
   523	}
   524	
   525	// cgoCheckUnknownPointer is called for an arbitrary pointer into Go
   526	// memory. It checks whether that Go memory contains any other
   527	// pointer into Go memory. If it does, we panic.
   528	// The return values are unused but useful to see in panic tracebacks.
   529	func cgoCheckUnknownPointer(p unsafe.Pointer, msg string) (base, i uintptr) {
   530		if cgoInRange(p, mheap_.arena_start, mheap_.arena_used) {
   531			if !inheap(uintptr(p)) {
   532				// On 32-bit systems it is possible for C's allocated memory
   533				// to have addresses between arena_start and arena_used.
   534				// Either this pointer is a stack or an unused span or it's
   535				// a C allocation. Escape analysis should prevent the first,
   536				// garbage collection should prevent the second,
   537				// and the third is completely OK.
   538				return
   539			}
   540	
   541			b, hbits, span, _ := heapBitsForObject(uintptr(p), 0, 0)
   542			base = b
   543			if base == 0 {
   544				return
   545			}
   546			n := span.elemsize
   547			for i = uintptr(0); i < n; i += sys.PtrSize {
   548				if i != 1*sys.PtrSize && !hbits.morePointers() {
   549					// No more possible pointers.
   550					break
   551				}
   552				if hbits.isPointer() {
   553					if cgoIsGoPointer(*(*unsafe.Pointer)(unsafe.Pointer(base + i))) {
   554						panic(errorString(msg))
   555					}
   556				}
   557				hbits = hbits.next()
   558			}
   559	
   560			return
   561		}
   562	
   563		for datap := &firstmoduledata; datap != nil; datap = datap.next {
   564			if cgoInRange(p, datap.data, datap.edata) || cgoInRange(p, datap.bss, datap.ebss) {
   565				// We have no way to know the size of the object.
   566				// We have to assume that it might contain a pointer.
   567				panic(errorString(msg))
   568			}
   569			// In the text or noptr sections, we know that the
   570			// pointer does not point to a Go pointer.
   571		}
   572	
   573		return
   574	}
   575	
   576	// cgoIsGoPointer returns whether the pointer is a Go pointer--a
   577	// pointer to Go memory. We only care about Go memory that might
   578	// contain pointers.
   579	//go:nosplit
   580	//go:nowritebarrierrec
   581	func cgoIsGoPointer(p unsafe.Pointer) bool {
   582		if p == nil {
   583			return false
   584		}
   585	
   586		if inHeapOrStack(uintptr(p)) {
   587			return true
   588		}
   589	
   590		for datap := &firstmoduledata; datap != nil; datap = datap.next {
   591			if cgoInRange(p, datap.data, datap.edata) || cgoInRange(p, datap.bss, datap.ebss) {
   592				return true
   593			}
   594		}
   595	
   596		return false
   597	}
   598	
   599	// cgoInRange returns whether p is between start and end.
   600	//go:nosplit
   601	//go:nowritebarrierrec
   602	func cgoInRange(p unsafe.Pointer, start, end uintptr) bool {
   603		return start <= uintptr(p) && uintptr(p) < end
   604	}
   605	
   606	// cgoCheckResult is called to check the result parameter of an
   607	// exported Go function. It panics if the result is or contains a Go
   608	// pointer.
   609	func cgoCheckResult(val interface{}) {
   610		if debug.cgocheck == 0 {
   611			return
   612		}
   613	
   614		ep := (*eface)(unsafe.Pointer(&val))
   615		t := ep._type
   616		cgoCheckArg(t, ep.data, t.kind&kindDirectIface == 0, false, cgoResultFail)
   617	}
   618	

View as plain text