| // 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. | |
| 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 | |
| ) | |
| 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 < _PageSize && 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") | |
| } | |
| 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)) | |
| } | |
| 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") | |
| }) | |
| } |