// Copyright 2015 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// Code to check that pointer writes follow the cgo rules.// These functions are invoked when GOEXPERIMENT=cgocheck2 is enabled.package runtimeimport ()const cgoWriteBarrierFail = "unpinned Go pointer stored into non-Go memory"// cgoCheckPtrWrite is called whenever a pointer is stored into memory.// It throws if the program is storing an unpinned Go pointer into non-Go// memory.//// This is called from generated code when GOEXPERIMENT=cgocheck2 is enabled.////go:nosplit//go:nowritebarrierfunc cgoCheckPtrWrite( *unsafe.Pointer, unsafe.Pointer) {if !mainStarted {// Something early in startup hates this function. // Don't start doing any actual checking until the // runtime has set itself up.return }if !cgoIsGoPointer() {return }ifcgoIsGoPointer(unsafe.Pointer()) {return }// If we are running on the system stack then dst might be an // address on the stack, which is OK. := getg()if == .m.g0 || == .m.gsignal {return }// Allocating memory can write to various mfixalloc structs // that look like they are non-Go memory.if .m.mallocing != 0 {return }// If the object is pinned, it's safe to store it in C memory. The GC // ensures it will not be moved or freed.ifisPinned() {return }// It's OK if writing to memory allocated by persistentalloc. // Do this check last because it is more expensive and rarely true. // If it is false the expense doesn't matter since we are crashing.ifinPersistentAlloc(uintptr(unsafe.Pointer())) {return }systemstack(func() {println("write of unpinned Go pointer", hex(uintptr()), "to non-Go memory", hex(uintptr(unsafe.Pointer())))throw(cgoWriteBarrierFail) })}// cgoCheckMemmove is called when moving a block of memory.// It throws if the program is copying a block that contains an unpinned Go// pointer into non-Go memory.//// This is called from generated code when GOEXPERIMENT=cgocheck2 is enabled.////go:nosplit//go:nowritebarrierfunc cgoCheckMemmove( *_type, , unsafe.Pointer) {cgoCheckMemmove2(, , , 0, .Size_)}// cgoCheckMemmove2 is called when moving a block of memory.// dst and src point off bytes into the value to copy.// size is the number of bytes to copy.// It throws if the program is copying a block that contains an unpinned Go// pointer into non-Go memory.////go:nosplit//go:nowritebarrierfunc cgoCheckMemmove2( *_type, , unsafe.Pointer, , uintptr) {if !.Pointers() {return }if !cgoIsGoPointer() {return }ifcgoIsGoPointer() {return }cgoCheckTypedBlock(, , , )}// cgoCheckSliceCopy is called when copying n elements of a slice.// src and dst are pointers to the first element of the slice.// typ is the element type of the slice.// It throws if the program is copying slice elements that contain unpinned Go// pointers into non-Go memory.////go:nosplit//go:nowritebarrierfunc cgoCheckSliceCopy( *_type, , unsafe.Pointer, int) {if !.Pointers() {return }if !cgoIsGoPointer() {return }ifcgoIsGoPointer() {return } := for := 0; < ; ++ {cgoCheckTypedBlock(, , 0, .Size_) = add(, .Size_) }}// cgoCheckTypedBlock checks the block of memory at src, for up to size bytes,// and throws if it finds an unpinned Go pointer. The type of the memory is typ,// and src is off bytes into that type.////go:nosplit//go:nowritebarrierfunc cgoCheckTypedBlock( *_type, unsafe.Pointer, , uintptr) {// Anything past typ.PtrBytes is not a pointer.if .PtrBytes <= {return }if := .PtrBytes - ; > { = }if .Kind_&abi.KindGCProg == 0 {cgoCheckBits(, .GCData, , )return }// The type has a GC program. Try to find GC bits somewhere else.for , := rangeactiveModules() {ifcgoInRange(, .data, .edata) { := uintptr() - .datacgoCheckBits(add(, -), .gcdatamask.bytedata, +, )return }ifcgoInRange(, .bss, .ebss) { := uintptr() - .bsscgoCheckBits(add(, -), .gcbssmask.bytedata, +, )return } } := spanOfUnchecked(uintptr())if .state.get() == mSpanManual {// There are no heap bits for value stored on the stack. // For a channel receive src might be on the stack of some // other goroutine, so we can't unwind the stack even if // we wanted to. // We can't expand the GC program without extra storage // space we can't easily get. // Fortunately we have the type information.systemstack(func() {cgoCheckUsingType(, , , ) })return }// src must be in the regular heap. := .typePointersOf(uintptr(), )for {varuintptrif , = .next(uintptr() + ); == 0 {break } := *(*unsafe.Pointer)(unsafe.Pointer())ifcgoIsGoPointer() && !isPinned() {throw(cgoWriteBarrierFail) } }}// cgoCheckBits checks the block of memory at src, for up to size// bytes, and throws if it finds an unpinned Go pointer. The gcbits mark each// pointer value. The src pointer is off bytes into the gcbits.////go:nosplit//go:nowritebarrierfunc cgoCheckBits( unsafe.Pointer, *byte, , uintptr) { := / goarch.PtrSize / 8 := * goarch.PtrSize * 8 := addb(, ) = add(, ) -= += varuint32for := uintptr(0); < ; += goarch.PtrSize {if &(goarch.PtrSize*8-1) == 0 { = uint32(*) = addb(, 1) } else { >>= 1 }if > 0 { -= goarch.PtrSize } else {if &1 != 0 { := *(*unsafe.Pointer)(add(, ))ifcgoIsGoPointer() && !isPinned() {throw(cgoWriteBarrierFail) } } } }}// cgoCheckUsingType is like cgoCheckTypedBlock, but is a last ditch// fall back to look for pointers in src using the type information.// We only use this when looking at a value on the stack when the type// uses a GC program, because otherwise it's more efficient to use the// GC bits. This is called on the system stack.////go:nowritebarrier//go:systemstackfunc cgoCheckUsingType( *_type, unsafe.Pointer, , uintptr) {if !.Pointers() {return }// Anything past typ.PtrBytes is not a pointer.if .PtrBytes <= {return }if := .PtrBytes - ; > { = }if .Kind_&abi.KindGCProg == 0 {cgoCheckBits(, .GCData, , )return }switch .Kind_ & abi.KindMask {default:throw("can't happen")caseabi.Array: := (*arraytype)(unsafe.Pointer())for := uintptr(0); < .Len; ++ {if < .Elem.Size_ { (.Elem, , , ) } = add(, .Elem.Size_) := if > .Elem.Size_ { = .Elem.Size_ } := .Elem.Size_ - -= if <= {return } -= }caseabi.Struct: := (*structtype)(unsafe.Pointer())for , := range .Fields {if < .Typ.Size_ { (.Typ, , , ) } = add(, .Typ.Size_) := if > .Typ.Size_ { = .Typ.Size_ } := .Typ.Size_ - -= if <= {return } -= } }}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.