diff options
-rw-r--r-- | libgo/go/runtime/stack.go | 1229 |
1 files changed, 0 insertions, 1229 deletions
diff --git a/libgo/go/runtime/stack.go b/libgo/go/runtime/stack.go deleted file mode 100644 index fd99e4d..0000000 --- a/libgo/go/runtime/stack.go +++ /dev/null @@ -1,1229 +0,0 @@ -// Copyright 2013 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. - -// +build ignore - -package runtime - -import ( - "runtime/internal/atomic" - "runtime/internal/sys" - "unsafe" -) - -/* -Stack layout parameters. -Included both by runtime (compiled via 6c) and linkers (compiled via gcc). - -The per-goroutine g->stackguard is set to point StackGuard bytes -above the bottom of the stack. Each function compares its stack -pointer against g->stackguard to check for overflow. To cut one -instruction from the check sequence for functions with tiny frames, -the stack is allowed to protrude StackSmall bytes below the stack -guard. Functions with large frames don't bother with the check and -always call morestack. The sequences are (for amd64, others are -similar): - - guard = g->stackguard - frame = function's stack frame size - argsize = size of function arguments (call + return) - - stack frame size <= StackSmall: - CMPQ guard, SP - JHI 3(PC) - MOVQ m->morearg, $(argsize << 32) - CALL morestack(SB) - - stack frame size > StackSmall but < StackBig - LEAQ (frame-StackSmall)(SP), R0 - CMPQ guard, R0 - JHI 3(PC) - MOVQ m->morearg, $(argsize << 32) - CALL morestack(SB) - - stack frame size >= StackBig: - MOVQ m->morearg, $((argsize << 32) | frame) - CALL morestack(SB) - -The bottom StackGuard - StackSmall bytes are important: there has -to be enough room to execute functions that refuse to check for -stack overflow, either because they need to be adjacent to the -actual caller's frame (deferproc) or because they handle the imminent -stack overflow (morestack). - -For example, deferproc might call malloc, which does one of the -above checks (without allocating a full frame), which might trigger -a call to morestack. This sequence needs to fit in the bottom -section of the stack. On amd64, morestack's frame is 40 bytes, and -deferproc's frame is 56 bytes. That fits well within the -StackGuard - StackSmall bytes at the bottom. -The linkers explore all possible call traces involving non-splitting -functions to make sure that this limit cannot be violated. -*/ - -const ( - // StackSystem is a number of additional bytes to add - // to each stack below the usual guard area for OS-specific - // purposes like signal handling. Used on Windows, Plan 9, - // and Darwin/ARM because they do not use a separate stack. - _StackSystem = sys.GoosWindows*512*sys.PtrSize + sys.GoosPlan9*512 + sys.GoosDarwin*sys.GoarchArm*1024 - - // The minimum size of stack used by Go code - _StackMin = 2048 - - // The minimum stack size to allocate. - // The hackery here rounds FixedStack0 up to a power of 2. - _FixedStack0 = _StackMin + _StackSystem - _FixedStack1 = _FixedStack0 - 1 - _FixedStack2 = _FixedStack1 | (_FixedStack1 >> 1) - _FixedStack3 = _FixedStack2 | (_FixedStack2 >> 2) - _FixedStack4 = _FixedStack3 | (_FixedStack3 >> 4) - _FixedStack5 = _FixedStack4 | (_FixedStack4 >> 8) - _FixedStack6 = _FixedStack5 | (_FixedStack5 >> 16) - _FixedStack = _FixedStack6 + 1 - - // Functions that need frames bigger than this use an extra - // instruction to do the stack split check, to avoid overflow - // in case SP - framesize wraps below zero. - // This value can be no bigger than the size of the unmapped - // space at zero. - _StackBig = 4096 - - // The stack guard is a pointer this many bytes above the - // bottom of the stack. - _StackGuard = 880*sys.StackGuardMultiplier + _StackSystem - - // After a stack split check the SP is allowed to be this - // many bytes below the stack guard. This saves an instruction - // in the checking sequence for tiny frames. - _StackSmall = 128 - - // The maximum number of bytes that a chain of NOSPLIT - // functions can use. - _StackLimit = _StackGuard - _StackSystem - _StackSmall -) - -// Goroutine preemption request. -// Stored into g->stackguard0 to cause split stack check failure. -// Must be greater than any real sp. -// 0xfffffade in hex. -const ( - _StackPreempt = uintptrMask & -1314 - _StackFork = uintptrMask & -1234 -) - -const ( - // stackDebug == 0: no logging - // == 1: logging of per-stack operations - // == 2: logging of per-frame operations - // == 3: logging of per-word updates - // == 4: logging of per-word reads - stackDebug = 0 - stackFromSystem = 0 // allocate stacks from system memory instead of the heap - stackFaultOnFree = 0 // old stacks are mapped noaccess to detect use after free - stackPoisonCopy = 0 // fill stack that should not be accessed with garbage, to detect bad dereferences during copy - - stackCache = 1 - - // check the BP links during traceback. - debugCheckBP = false -) - -const ( - uintptrMask = 1<<(8*sys.PtrSize) - 1 - - // Goroutine preemption request. - // Stored into g->stackguard0 to cause split stack check failure. - // Must be greater than any real sp. - // 0xfffffade in hex. - stackPreempt = uintptrMask & -1314 - - // Thread is forking. - // Stored into g->stackguard0 to cause split stack check failure. - // Must be greater than any real sp. - stackFork = uintptrMask & -1234 -) - -// Global pool of spans that have free stacks. -// Stacks are assigned an order according to size. -// order = log_2(size/FixedStack) -// There is a free list for each order. -// TODO: one lock per order? -var stackpool [_NumStackOrders]mSpanList -var stackpoolmu mutex - -// Global pool of large stack spans. -var stackLarge struct { - lock mutex - free [_MHeapMap_Bits]mSpanList // free lists by log_2(s.npages) -} - -func stackinit() { - if _StackCacheSize&_PageMask != 0 { - throw("cache size must be a multiple of page size") - } - for i := range stackpool { - stackpool[i].init() - } - for i := range stackLarge.free { - stackLarge.free[i].init() - } -} - -// stacklog2 returns ⌊log_2(n)⌋. -func stacklog2(n uintptr) int { - log2 := 0 - for n > 1 { - n >>= 1 - log2++ - } - return log2 -} - -// Allocates a stack from the free pool. Must be called with -// stackpoolmu held. -func stackpoolalloc(order uint8) gclinkptr { - list := &stackpool[order] - s := list.first - if s == nil { - // no free stacks. Allocate another span worth. - s = mheap_.allocStack(_StackCacheSize >> _PageShift) - if s == nil { - throw("out of memory") - } - if s.allocCount != 0 { - throw("bad allocCount") - } - if s.stackfreelist.ptr() != nil { - throw("bad stackfreelist") - } - for i := uintptr(0); i < _StackCacheSize; i += _FixedStack << order { - x := gclinkptr(s.base() + i) - x.ptr().next = s.stackfreelist - s.stackfreelist = x - } - list.insert(s) - } - x := s.stackfreelist - if x.ptr() == nil { - throw("span has no free stacks") - } - s.stackfreelist = x.ptr().next - s.allocCount++ - if s.stackfreelist.ptr() == nil { - // all stacks in s are allocated. - list.remove(s) - } - return x -} - -// Adds stack x to the free pool. Must be called with stackpoolmu held. -func stackpoolfree(x gclinkptr, order uint8) { - s := mheap_.lookup(unsafe.Pointer(x)) - if s.state != _MSpanStack { - throw("freeing stack not in a stack span") - } - if s.stackfreelist.ptr() == nil { - // s will now have a free stack - stackpool[order].insert(s) - } - x.ptr().next = s.stackfreelist - s.stackfreelist = x - s.allocCount-- - if gcphase == _GCoff && s.allocCount == 0 { - // Span is completely free. Return it to the heap - // immediately if we're sweeping. - // - // If GC is active, we delay the free until the end of - // GC to avoid the following type of situation: - // - // 1) GC starts, scans a SudoG but does not yet mark the SudoG.elem pointer - // 2) The stack that pointer points to is copied - // 3) The old stack is freed - // 4) The containing span is marked free - // 5) GC attempts to mark the SudoG.elem pointer. The - // marking fails because the pointer looks like a - // pointer into a free span. - // - // By not freeing, we prevent step #4 until GC is done. - stackpool[order].remove(s) - s.stackfreelist = 0 - mheap_.freeStack(s) - } -} - -// stackcacherefill/stackcacherelease implement a global pool of stack segments. -// The pool is required to prevent unlimited growth of per-thread caches. -// -//go:systemstack -func stackcacherefill(c *mcache, order uint8) { - if stackDebug >= 1 { - print("stackcacherefill order=", order, "\n") - } - - // Grab some stacks from the global cache. - // Grab half of the allowed capacity (to prevent thrashing). - var list gclinkptr - var size uintptr - lock(&stackpoolmu) - for size < _StackCacheSize/2 { - x := stackpoolalloc(order) - x.ptr().next = list - list = x - size += _FixedStack << order - } - unlock(&stackpoolmu) - c.stackcache[order].list = list - c.stackcache[order].size = size -} - -//go:systemstack -func stackcacherelease(c *mcache, order uint8) { - if stackDebug >= 1 { - print("stackcacherelease order=", order, "\n") - } - x := c.stackcache[order].list - size := c.stackcache[order].size - lock(&stackpoolmu) - for size > _StackCacheSize/2 { - y := x.ptr().next - stackpoolfree(x, order) - x = y - size -= _FixedStack << order - } - unlock(&stackpoolmu) - c.stackcache[order].list = x - c.stackcache[order].size = size -} - -//go:systemstack -func stackcache_clear(c *mcache) { - if stackDebug >= 1 { - print("stackcache clear\n") - } - lock(&stackpoolmu) - for order := uint8(0); order < _NumStackOrders; order++ { - x := c.stackcache[order].list - for x.ptr() != nil { - y := x.ptr().next - stackpoolfree(x, order) - x = y - } - c.stackcache[order].list = 0 - c.stackcache[order].size = 0 - } - unlock(&stackpoolmu) -} - -// stackalloc allocates an n byte stack. -// -// stackalloc must run on the system stack because it uses per-P -// resources and must not split the stack. -// -//go:systemstack -func stackalloc(n uint32) (stack, []stkbar) { - // Stackalloc must be called on scheduler stack, so that we - // never try to grow the stack during the code that stackalloc runs. - // Doing so would cause a deadlock (issue 1547). - thisg := getg() - if thisg != thisg.m.g0 { - throw("stackalloc not on scheduler stack") - } - if n&(n-1) != 0 { - throw("stack size not a power of 2") - } - if stackDebug >= 1 { - print("stackalloc ", n, "\n") - } - - // Compute the size of stack barrier array. - maxstkbar := gcMaxStackBarriers(int(n)) - nstkbar := unsafe.Sizeof(stkbar{}) * uintptr(maxstkbar) - var stkbarSlice slice - - if debug.efence != 0 || stackFromSystem != 0 { - v := sysAlloc(round(uintptr(n), _PageSize), &memstats.stacks_sys) - if v == nil { - throw("out of memory (stackalloc)") - } - top := uintptr(n) - nstkbar - if maxstkbar != 0 { - stkbarSlice = slice{add(v, top), 0, maxstkbar} - } - return stack{uintptr(v), uintptr(v) + top}, *(*[]stkbar)(unsafe.Pointer(&stkbarSlice)) - } - - // Small stacks are allocated with a fixed-size free-list allocator. - // If we need a stack of a bigger size, we fall back on allocating - // a dedicated span. - var v unsafe.Pointer - if stackCache != 0 && n < _FixedStack<<_NumStackOrders && n < _StackCacheSize { - order := uint8(0) - n2 := n - for n2 > _FixedStack { - order++ - n2 >>= 1 - } - var x gclinkptr - c := thisg.m.mcache - if c == nil || thisg.m.preemptoff != "" || thisg.m.helpgc != 0 { - // c == nil can happen in the guts of exitsyscall or - // procresize. Just get a stack from the global pool. - // Also don't touch stackcache during gc - // as it's flushed concurrently. - lock(&stackpoolmu) - x = stackpoolalloc(order) - unlock(&stackpoolmu) - } else { - x = c.stackcache[order].list - if x.ptr() == nil { - stackcacherefill(c, order) - x = c.stackcache[order].list - } - c.stackcache[order].list = x.ptr().next - c.stackcache[order].size -= uintptr(n) - } - v = unsafe.Pointer(x) - } else { - var s *mspan - npage := uintptr(n) >> _PageShift - log2npage := stacklog2(npage) - - // Try to get a stack from the large stack cache. - lock(&stackLarge.lock) - if !stackLarge.free[log2npage].isEmpty() { - s = stackLarge.free[log2npage].first - stackLarge.free[log2npage].remove(s) - } - unlock(&stackLarge.lock) - - if s == nil { - // Allocate a new stack from the heap. - s = mheap_.allocStack(npage) - if s == nil { - throw("out of memory") - } - } - v = unsafe.Pointer(s.base()) - } - - if raceenabled { - racemalloc(v, uintptr(n)) - } - if msanenabled { - msanmalloc(v, uintptr(n)) - } - if stackDebug >= 1 { - print(" allocated ", v, "\n") - } - top := uintptr(n) - nstkbar - if maxstkbar != 0 { - stkbarSlice = slice{add(v, top), 0, maxstkbar} - } - return stack{uintptr(v), uintptr(v) + top}, *(*[]stkbar)(unsafe.Pointer(&stkbarSlice)) -} - -// stackfree frees an n byte stack allocation at stk. -// -// stackfree must run on the system stack because it uses per-P -// resources and must not split the stack. -// -//go:systemstack -func stackfree(stk stack, n uintptr) { - gp := getg() - v := unsafe.Pointer(stk.lo) - if n&(n-1) != 0 { - throw("stack not a power of 2") - } - if stk.lo+n < stk.hi { - throw("bad stack size") - } - if stackDebug >= 1 { - println("stackfree", v, n) - memclrNoHeapPointers(v, n) // for testing, clobber stack data - } - if debug.efence != 0 || stackFromSystem != 0 { - if debug.efence != 0 || stackFaultOnFree != 0 { - sysFault(v, n) - } else { - sysFree(v, n, &memstats.stacks_sys) - } - return - } - if msanenabled { - msanfree(v, n) - } - if stackCache != 0 && n < _FixedStack<<_NumStackOrders && n < _StackCacheSize { - order := uint8(0) - n2 := n - for n2 > _FixedStack { - order++ - n2 >>= 1 - } - x := gclinkptr(v) - c := gp.m.mcache - if c == nil || gp.m.preemptoff != "" || gp.m.helpgc != 0 { - lock(&stackpoolmu) - stackpoolfree(x, order) - unlock(&stackpoolmu) - } else { - if c.stackcache[order].size >= _StackCacheSize { - stackcacherelease(c, order) - } - x.ptr().next = c.stackcache[order].list - c.stackcache[order].list = x - c.stackcache[order].size += n - } - } else { - s := mheap_.lookup(v) - if s.state != _MSpanStack { - println(hex(s.base()), v) - throw("bad span state") - } - if gcphase == _GCoff { - // Free the stack immediately if we're - // sweeping. - mheap_.freeStack(s) - } else { - // If the GC is running, we can't return a - // stack span to the heap because it could be - // reused as a heap span, and this state - // change would race with GC. Add it to the - // large stack cache instead. - log2npage := stacklog2(s.npages) - lock(&stackLarge.lock) - stackLarge.free[log2npage].insert(s) - unlock(&stackLarge.lock) - } - } -} - -var maxstacksize uintptr = 1 << 20 // enough until runtime.main sets it for real - -var ptrnames = []string{ - 0: "scalar", - 1: "ptr", -} - -// Stack frame layout -// -// (x86) -// +------------------+ -// | args from caller | -// +------------------+ <- frame->argp -// | return address | -// +------------------+ -// | caller's BP (*) | (*) if framepointer_enabled && varp < sp -// +------------------+ <- frame->varp -// | locals | -// +------------------+ -// | args to callee | -// +------------------+ <- frame->sp -// -// (arm) -// +------------------+ -// | args from caller | -// +------------------+ <- frame->argp -// | caller's retaddr | -// +------------------+ <- frame->varp -// | locals | -// +------------------+ -// | args to callee | -// +------------------+ -// | return address | -// +------------------+ <- frame->sp - -type adjustinfo struct { - old stack - delta uintptr // ptr distance from old to new stack (newbase - oldbase) - cache pcvalueCache - - // sghi is the highest sudog.elem on the stack. - sghi uintptr -} - -// Adjustpointer checks whether *vpp is in the old stack described by adjinfo. -// If so, it rewrites *vpp to point into the new stack. -func adjustpointer(adjinfo *adjustinfo, vpp unsafe.Pointer) { - pp := (*uintptr)(vpp) - p := *pp - if stackDebug >= 4 { - print(" ", pp, ":", hex(p), "\n") - } - if adjinfo.old.lo <= p && p < adjinfo.old.hi { - *pp = p + adjinfo.delta - if stackDebug >= 3 { - print(" adjust ptr ", pp, ":", hex(p), " -> ", hex(*pp), "\n") - } - } -} - -// Information from the compiler about the layout of stack frames. -type bitvector struct { - n int32 // # of bits - bytedata *uint8 -} - -type gobitvector struct { - n uintptr - bytedata []uint8 -} - -func gobv(bv bitvector) gobitvector { - return gobitvector{ - uintptr(bv.n), - (*[1 << 30]byte)(unsafe.Pointer(bv.bytedata))[:(bv.n+7)/8], - } -} - -func ptrbit(bv *gobitvector, i uintptr) uint8 { - return (bv.bytedata[i/8] >> (i % 8)) & 1 -} - -// bv describes the memory starting at address scanp. -// Adjust any pointers contained therein. -func adjustpointers(scanp unsafe.Pointer, cbv *bitvector, adjinfo *adjustinfo, f *_func) { - bv := gobv(*cbv) - minp := adjinfo.old.lo - maxp := adjinfo.old.hi - delta := adjinfo.delta - num := bv.n - // If this frame might contain channel receive slots, use CAS - // to adjust pointers. If the slot hasn't been received into - // yet, it may contain stack pointers and a concurrent send - // could race with adjusting those pointers. (The sent value - // itself can never contain stack pointers.) - useCAS := uintptr(scanp) < adjinfo.sghi - for i := uintptr(0); i < num; i++ { - if stackDebug >= 4 { - print(" ", add(scanp, i*sys.PtrSize), ":", ptrnames[ptrbit(&bv, i)], ":", hex(*(*uintptr)(add(scanp, i*sys.PtrSize))), " # ", i, " ", bv.bytedata[i/8], "\n") - } - if ptrbit(&bv, i) == 1 { - pp := (*uintptr)(add(scanp, i*sys.PtrSize)) - retry: - p := *pp - if f != nil && 0 < p && p < minLegalPointer && debug.invalidptr != 0 { - // Looks like a junk value in a pointer slot. - // Live analysis wrong? - getg().m.traceback = 2 - print("runtime: bad pointer in frame ", funcname(f), " at ", pp, ": ", hex(p), "\n") - throw("invalid pointer found on stack") - } - if minp <= p && p < maxp { - if stackDebug >= 3 { - print("adjust ptr ", hex(p), " ", funcname(f), "\n") - } - if useCAS { - ppu := (*unsafe.Pointer)(unsafe.Pointer(pp)) - if !atomic.Casp1(ppu, unsafe.Pointer(p), unsafe.Pointer(p+delta)) { - goto retry - } - } else { - *pp = p + delta - } - } - } - } -} - -// Note: the argument/return area is adjusted by the callee. -func adjustframe(frame *stkframe, arg unsafe.Pointer) bool { - adjinfo := (*adjustinfo)(arg) - targetpc := frame.continpc - if targetpc == 0 { - // Frame is dead. - return true - } - f := frame.fn - if stackDebug >= 2 { - print(" adjusting ", funcname(f), " frame=[", hex(frame.sp), ",", hex(frame.fp), "] pc=", hex(frame.pc), " continpc=", hex(frame.continpc), "\n") - } - if f.entry == systemstack_switchPC { - // A special routine at the bottom of stack of a goroutine that does an systemstack call. - // We will allow it to be copied even though we don't - // have full GC info for it (because it is written in asm). - return true - } - if targetpc != f.entry { - targetpc-- - } - pcdata := pcdatavalue(f, _PCDATA_StackMapIndex, targetpc, &adjinfo.cache) - if pcdata == -1 { - pcdata = 0 // in prologue - } - - // Adjust local variables if stack frame has been allocated. - size := frame.varp - frame.sp - var minsize uintptr - switch sys.ArchFamily { - case sys.ARM64: - minsize = sys.SpAlign - default: - minsize = sys.MinFrameSize - } - if size > minsize { - var bv bitvector - stackmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps)) - if stackmap == nil || stackmap.n <= 0 { - print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n") - throw("missing stackmap") - } - // Locals bitmap information, scan just the pointers in locals. - if pcdata < 0 || pcdata >= stackmap.n { - // don't know where we are - print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", targetpc, ")\n") - throw("bad symbol table") - } - bv = stackmapdata(stackmap, pcdata) - size = uintptr(bv.n) * sys.PtrSize - if stackDebug >= 3 { - print(" locals ", pcdata, "/", stackmap.n, " ", size/sys.PtrSize, " words ", bv.bytedata, "\n") - } - adjustpointers(unsafe.Pointer(frame.varp-size), &bv, adjinfo, f) - } - - // Adjust saved base pointer if there is one. - if sys.ArchFamily == sys.AMD64 && frame.argp-frame.varp == 2*sys.RegSize { - if !framepointer_enabled { - print("runtime: found space for saved base pointer, but no framepointer experiment\n") - print("argp=", hex(frame.argp), " varp=", hex(frame.varp), "\n") - throw("bad frame layout") - } - if stackDebug >= 3 { - print(" saved bp\n") - } - if debugCheckBP { - // Frame pointers should always point to the next higher frame on - // the Go stack (or be nil, for the top frame on the stack). - bp := *(*uintptr)(unsafe.Pointer(frame.varp)) - if bp != 0 && (bp < adjinfo.old.lo || bp >= adjinfo.old.hi) { - println("runtime: found invalid frame pointer") - print("bp=", hex(bp), " min=", hex(adjinfo.old.lo), " max=", hex(adjinfo.old.hi), "\n") - throw("bad frame pointer") - } - } - adjustpointer(adjinfo, unsafe.Pointer(frame.varp)) - } - - // Adjust arguments. - if frame.arglen > 0 { - var bv bitvector - if frame.argmap != nil { - bv = *frame.argmap - } else { - stackmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps)) - if stackmap == nil || stackmap.n <= 0 { - print("runtime: frame ", funcname(f), " untyped args ", frame.argp, "+", frame.arglen, "\n") - throw("missing stackmap") - } - if pcdata < 0 || pcdata >= stackmap.n { - // don't know where we are - print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " args stack map entries for ", funcname(f), " (targetpc=", targetpc, ")\n") - throw("bad symbol table") - } - bv = stackmapdata(stackmap, pcdata) - } - if stackDebug >= 3 { - print(" args\n") - } - adjustpointers(unsafe.Pointer(frame.argp), &bv, adjinfo, nil) - } - return true -} - -func adjustctxt(gp *g, adjinfo *adjustinfo) { - adjustpointer(adjinfo, unsafe.Pointer(&gp.sched.ctxt)) - if !framepointer_enabled { - return - } - if debugCheckBP { - bp := gp.sched.bp - if bp != 0 && (bp < adjinfo.old.lo || bp >= adjinfo.old.hi) { - println("runtime: found invalid top frame pointer") - print("bp=", hex(bp), " min=", hex(adjinfo.old.lo), " max=", hex(adjinfo.old.hi), "\n") - throw("bad top frame pointer") - } - } - adjustpointer(adjinfo, unsafe.Pointer(&gp.sched.bp)) -} - -func adjustdefers(gp *g, adjinfo *adjustinfo) { - // Adjust defer argument blocks the same way we adjust active stack frames. - tracebackdefers(gp, adjustframe, noescape(unsafe.Pointer(adjinfo))) - - // Adjust pointers in the Defer structs. - // Defer structs themselves are never on the stack. - for d := gp._defer; d != nil; d = d.link { - adjustpointer(adjinfo, unsafe.Pointer(&d.fn)) - adjustpointer(adjinfo, unsafe.Pointer(&d.sp)) - adjustpointer(adjinfo, unsafe.Pointer(&d._panic)) - } -} - -func adjustpanics(gp *g, adjinfo *adjustinfo) { - // Panics are on stack and already adjusted. - // Update pointer to head of list in G. - adjustpointer(adjinfo, unsafe.Pointer(&gp._panic)) -} - -func adjustsudogs(gp *g, adjinfo *adjustinfo) { - // the data elements pointed to by a SudoG structure - // might be in the stack. - for s := gp.waiting; s != nil; s = s.waitlink { - adjustpointer(adjinfo, unsafe.Pointer(&s.elem)) - adjustpointer(adjinfo, unsafe.Pointer(&s.selectdone)) - } -} - -func adjuststkbar(gp *g, adjinfo *adjustinfo) { - for i := int(gp.stkbarPos); i < len(gp.stkbar); i++ { - adjustpointer(adjinfo, unsafe.Pointer(&gp.stkbar[i].savedLRPtr)) - } -} - -func fillstack(stk stack, b byte) { - for p := stk.lo; p < stk.hi; p++ { - *(*byte)(unsafe.Pointer(p)) = b - } -} - -func findsghi(gp *g, stk stack) uintptr { - var sghi uintptr - for sg := gp.waiting; sg != nil; sg = sg.waitlink { - p := uintptr(sg.elem) + uintptr(sg.c.elemsize) - if stk.lo <= p && p < stk.hi && p > sghi { - sghi = p - } - p = uintptr(unsafe.Pointer(sg.selectdone)) + unsafe.Sizeof(sg.selectdone) - if stk.lo <= p && p < stk.hi && p > sghi { - sghi = p - } - } - return sghi -} - -// syncadjustsudogs adjusts gp's sudogs and copies the part of gp's -// stack they refer to while synchronizing with concurrent channel -// operations. It returns the number of bytes of stack copied. -func syncadjustsudogs(gp *g, used uintptr, adjinfo *adjustinfo) uintptr { - if gp.waiting == nil { - return 0 - } - - // Lock channels to prevent concurrent send/receive. - // It's important that we *only* do this for async - // copystack; otherwise, gp may be in the middle of - // putting itself on wait queues and this would - // self-deadlock. - var lastc *hchan - for sg := gp.waiting; sg != nil; sg = sg.waitlink { - if sg.c != lastc { - lock(&sg.c.lock) - } - lastc = sg.c - } - - // Adjust sudogs. - adjustsudogs(gp, adjinfo) - - // Copy the part of the stack the sudogs point in to - // while holding the lock to prevent races on - // send/receive slots. - var sgsize uintptr - if adjinfo.sghi != 0 { - oldBot := adjinfo.old.hi - used - newBot := oldBot + adjinfo.delta - sgsize = adjinfo.sghi - oldBot - memmove(unsafe.Pointer(newBot), unsafe.Pointer(oldBot), sgsize) - } - - // Unlock channels. - lastc = nil - for sg := gp.waiting; sg != nil; sg = sg.waitlink { - if sg.c != lastc { - unlock(&sg.c.lock) - } - lastc = sg.c - } - - return sgsize -} - -// Copies gp's stack to a new stack of a different size. -// Caller must have changed gp status to Gcopystack. -// -// If sync is true, this is a self-triggered stack growth and, in -// particular, no other G may be writing to gp's stack (e.g., via a -// channel operation). If sync is false, copystack protects against -// concurrent channel operations. -func copystack(gp *g, newsize uintptr, sync bool) { - if gp.syscallsp != 0 { - throw("stack growth not allowed in system call") - } - old := gp.stack - if old.lo == 0 { - throw("nil stackbase") - } - used := old.hi - gp.sched.sp - - // allocate new stack - new, newstkbar := stackalloc(uint32(newsize)) - if stackPoisonCopy != 0 { - fillstack(new, 0xfd) - } - if stackDebug >= 1 { - print("copystack gp=", gp, " [", hex(old.lo), " ", hex(old.hi-used), " ", hex(old.hi), "]/", gp.stackAlloc, " -> [", hex(new.lo), " ", hex(new.hi-used), " ", hex(new.hi), "]/", newsize, "\n") - } - - // Compute adjustment. - var adjinfo adjustinfo - adjinfo.old = old - adjinfo.delta = new.hi - old.hi - - // Adjust sudogs, synchronizing with channel ops if necessary. - ncopy := used - if sync { - adjustsudogs(gp, &adjinfo) - } else { - // sudogs can point in to the stack. During concurrent - // shrinking, these areas may be written to. Find the - // highest such pointer so we can handle everything - // there and below carefully. (This shouldn't be far - // from the bottom of the stack, so there's little - // cost in handling everything below it carefully.) - adjinfo.sghi = findsghi(gp, old) - - // Synchronize with channel ops and copy the part of - // the stack they may interact with. - ncopy -= syncadjustsudogs(gp, used, &adjinfo) - } - - // Copy the stack (or the rest of it) to the new location - memmove(unsafe.Pointer(new.hi-ncopy), unsafe.Pointer(old.hi-ncopy), ncopy) - - // Disallow sigprof scans of this stack and block if there's - // one in progress. - gcLockStackBarriers(gp) - - // Adjust remaining structures that have pointers into stacks. - // We have to do most of these before we traceback the new - // stack because gentraceback uses them. - adjustctxt(gp, &adjinfo) - adjustdefers(gp, &adjinfo) - adjustpanics(gp, &adjinfo) - adjuststkbar(gp, &adjinfo) - if adjinfo.sghi != 0 { - adjinfo.sghi += adjinfo.delta - } - - // copy old stack barriers to new stack barrier array - newstkbar = newstkbar[:len(gp.stkbar)] - copy(newstkbar, gp.stkbar) - - // Swap out old stack for new one - gp.stack = new - gp.stackguard0 = new.lo + _StackGuard // NOTE: might clobber a preempt request - gp.sched.sp = new.hi - used - oldsize := gp.stackAlloc - gp.stackAlloc = newsize - gp.stkbar = newstkbar - gp.stktopsp += adjinfo.delta - - // Adjust pointers in the new stack. - gentraceback(^uintptr(0), ^uintptr(0), 0, gp, 0, nil, 0x7fffffff, adjustframe, noescape(unsafe.Pointer(&adjinfo)), 0) - - gcUnlockStackBarriers(gp) - - // free old stack - if stackPoisonCopy != 0 { - fillstack(old, 0xfc) - } - stackfree(old, oldsize) -} - -// round x up to a power of 2. -func round2(x int32) int32 { - s := uint(0) - for 1<<s < x { - s++ - } - return 1 << s -} - -// Called from runtime·morestack when more stack is needed. -// Allocate larger stack and relocate to new stack. -// Stack growth is multiplicative, for constant amortized cost. -// -// g->atomicstatus will be Grunning or Gscanrunning upon entry. -// If the GC is trying to stop this g then it will set preemptscan to true. -// -// ctxt is the value of the context register on morestack. newstack -// will write it to g.sched.ctxt. -func newstack(ctxt unsafe.Pointer) { - thisg := getg() - // TODO: double check all gp. shouldn't be getg(). - if thisg.m.morebuf.g.ptr().stackguard0 == stackFork { - throw("stack growth after fork") - } - if thisg.m.morebuf.g.ptr() != thisg.m.curg { - print("runtime: newstack called from g=", hex(thisg.m.morebuf.g), "\n"+"\tm=", thisg.m, " m->curg=", thisg.m.curg, " m->g0=", thisg.m.g0, " m->gsignal=", thisg.m.gsignal, "\n") - morebuf := thisg.m.morebuf - traceback(morebuf.pc, morebuf.sp, morebuf.lr, morebuf.g.ptr()) - throw("runtime: wrong goroutine in newstack") - } - - gp := thisg.m.curg - // Write ctxt to gp.sched. We do this here instead of in - // morestack so it has the necessary write barrier. - gp.sched.ctxt = ctxt - - if thisg.m.curg.throwsplit { - // Update syscallsp, syscallpc in case traceback uses them. - morebuf := thisg.m.morebuf - gp.syscallsp = morebuf.sp - gp.syscallpc = morebuf.pc - print("runtime: newstack sp=", hex(gp.sched.sp), " stack=[", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n", - "\tmorebuf={pc:", hex(morebuf.pc), " sp:", hex(morebuf.sp), " lr:", hex(morebuf.lr), "}\n", - "\tsched={pc:", hex(gp.sched.pc), " sp:", hex(gp.sched.sp), " lr:", hex(gp.sched.lr), " ctxt:", gp.sched.ctxt, "}\n") - - traceback(morebuf.pc, morebuf.sp, morebuf.lr, gp) - throw("runtime: stack split at bad time") - } - - morebuf := thisg.m.morebuf - thisg.m.morebuf.pc = 0 - thisg.m.morebuf.lr = 0 - thisg.m.morebuf.sp = 0 - thisg.m.morebuf.g = 0 - - // NOTE: stackguard0 may change underfoot, if another thread - // is about to try to preempt gp. Read it just once and use that same - // value now and below. - preempt := atomic.Loaduintptr(&gp.stackguard0) == stackPreempt - - // Be conservative about where we preempt. - // We are interested in preempting user Go code, not runtime code. - // If we're holding locks, mallocing, or preemption is disabled, don't - // preempt. - // This check is very early in newstack so that even the status change - // from Grunning to Gwaiting and back doesn't happen in this case. - // That status change by itself can be viewed as a small preemption, - // because the GC might change Gwaiting to Gscanwaiting, and then - // this goroutine has to wait for the GC to finish before continuing. - // If the GC is in some way dependent on this goroutine (for example, - // it needs a lock held by the goroutine), that small preemption turns - // into a real deadlock. - if preempt { - if thisg.m.locks != 0 || thisg.m.mallocing != 0 || thisg.m.preemptoff != "" || thisg.m.p.ptr().status != _Prunning { - // Let the goroutine keep running for now. - // gp->preempt is set, so it will be preempted next time. - gp.stackguard0 = gp.stack.lo + _StackGuard - gogo(&gp.sched) // never return - } - } - - if gp.stack.lo == 0 { - throw("missing stack in newstack") - } - sp := gp.sched.sp - if sys.ArchFamily == sys.AMD64 || sys.ArchFamily == sys.I386 { - // The call to morestack cost a word. - sp -= sys.PtrSize - } - if stackDebug >= 1 || sp < gp.stack.lo { - print("runtime: newstack sp=", hex(sp), " stack=[", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n", - "\tmorebuf={pc:", hex(morebuf.pc), " sp:", hex(morebuf.sp), " lr:", hex(morebuf.lr), "}\n", - "\tsched={pc:", hex(gp.sched.pc), " sp:", hex(gp.sched.sp), " lr:", hex(gp.sched.lr), " ctxt:", gp.sched.ctxt, "}\n") - } - if sp < gp.stack.lo { - print("runtime: gp=", gp, ", gp->status=", hex(readgstatus(gp)), "\n ") - print("runtime: split stack overflow: ", hex(sp), " < ", hex(gp.stack.lo), "\n") - throw("runtime: split stack overflow") - } - - if preempt { - if gp == thisg.m.g0 { - throw("runtime: preempt g0") - } - if thisg.m.p == 0 && thisg.m.locks == 0 { - throw("runtime: g is running but p is not") - } - // Synchronize with scang. - casgstatus(gp, _Grunning, _Gwaiting) - if gp.preemptscan { - for !castogscanstatus(gp, _Gwaiting, _Gscanwaiting) { - // Likely to be racing with the GC as - // it sees a _Gwaiting and does the - // stack scan. If so, gcworkdone will - // be set and gcphasework will simply - // return. - } - if !gp.gcscandone { - // gcw is safe because we're on the - // system stack. - gcw := &gp.m.p.ptr().gcw - scanstack(gp, gcw) - if gcBlackenPromptly { - gcw.dispose() - } - gp.gcscandone = true - } - gp.preemptscan = false - gp.preempt = false - casfrom_Gscanstatus(gp, _Gscanwaiting, _Gwaiting) - // This clears gcscanvalid. - casgstatus(gp, _Gwaiting, _Grunning) - gp.stackguard0 = gp.stack.lo + _StackGuard - gogo(&gp.sched) // never return - } - - // Act like goroutine called runtime.Gosched. - casgstatus(gp, _Gwaiting, _Grunning) - gopreempt_m(gp) // never return - } - - // Allocate a bigger segment and move the stack. - oldsize := int(gp.stackAlloc) - newsize := oldsize * 2 - if uintptr(newsize) > maxstacksize { - print("runtime: goroutine stack exceeds ", maxstacksize, "-byte limit\n") - throw("stack overflow") - } - - // The goroutine must be executing in order to call newstack, - // so it must be Grunning (or Gscanrunning). - casgstatus(gp, _Grunning, _Gcopystack) - - // The concurrent GC will not scan the stack while we are doing the copy since - // the gp is in a Gcopystack status. - copystack(gp, uintptr(newsize), true) - if stackDebug >= 1 { - print("stack grow done\n") - } - casgstatus(gp, _Gcopystack, _Grunning) - gogo(&gp.sched) -} - -//go:nosplit -func nilfunc() { - *(*uint8)(nil) = 0 -} - -// adjust Gobuf as if it executed a call to fn -// and then did an immediate gosave. -func gostartcallfn(gobuf *gobuf, fv *funcval) { - var fn unsafe.Pointer - if fv != nil { - fn = unsafe.Pointer(fv.fn) - } else { - fn = unsafe.Pointer(funcPC(nilfunc)) - } - gostartcall(gobuf, fn, unsafe.Pointer(fv)) -} - -// Maybe shrink the stack being used by gp. -// Called at garbage collection time. -// gp must be stopped, but the world need not be. -func shrinkstack(gp *g) { - gstatus := readgstatus(gp) - if gstatus&^_Gscan == _Gdead { - if gp.stack.lo != 0 { - // Free whole stack - it will get reallocated - // if G is used again. - stackfree(gp.stack, gp.stackAlloc) - gp.stack.lo = 0 - gp.stack.hi = 0 - gp.stkbar = nil - gp.stkbarPos = 0 - } - return - } - if gp.stack.lo == 0 { - throw("missing stack in shrinkstack") - } - if gstatus&_Gscan == 0 { - throw("bad status in shrinkstack") - } - - if debug.gcshrinkstackoff > 0 { - return - } - if gp.startpc == gcBgMarkWorkerPC { - // We're not allowed to shrink the gcBgMarkWorker - // stack (see gcBgMarkWorker for explanation). - return - } - - oldsize := gp.stackAlloc - newsize := oldsize / 2 - // Don't shrink the allocation below the minimum-sized stack - // allocation. - if newsize < _FixedStack { - return - } - // Compute how much of the stack is currently in use and only - // shrink the stack if gp is using less than a quarter of its - // current stack. The currently used stack includes everything - // down to the SP plus the stack guard space that ensures - // there's room for nosplit functions. - avail := gp.stack.hi - gp.stack.lo - if used := gp.stack.hi - gp.sched.sp + _StackLimit; used >= avail/4 { - return - } - - // We can't copy the stack if we're in a syscall. - // The syscall might have pointers into the stack. - if gp.syscallsp != 0 { - return - } - if sys.GoosWindows != 0 && gp.m != nil && gp.m.libcallsp != 0 { - return - } - - if stackDebug > 0 { - print("shrinking stack ", oldsize, "->", newsize, "\n") - } - - copystack(gp, newsize, false) -} - -// freeStackSpans frees unused stack spans at the end of GC. -func freeStackSpans() { - lock(&stackpoolmu) - - // Scan stack pools for empty stack spans. - for order := range stackpool { - list := &stackpool[order] - for s := list.first; s != nil; { - next := s.next - if s.allocCount == 0 { - list.remove(s) - s.stackfreelist = 0 - mheap_.freeStack(s) - } - s = next - } - } - - unlock(&stackpoolmu) - - // Free large stack spans. - lock(&stackLarge.lock) - for i := range stackLarge.free { - for s := stackLarge.free[i].first; s != nil; { - next := s.next - stackLarge.free[i].remove(s) - mheap_.freeStack(s) - s = next - } - } - unlock(&stackLarge.lock) -} - -//go:nosplit -func morestackc() { - systemstack(func() { - throw("attempt to execute C code on Go stack") - }) -} |