// 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. #include "runtime.h" #include "arch_GOARCH.h" #include "malloc.h" #include "stack.h" #include "funcdata.h" #include "typekind.h" #include "type.h" #include "race.h" #include "mgc0.h" #include "textflag.h" enum { // 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, }; // 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. MSpan runtime·stackpool[NumStackOrders]; Mutex runtime·stackpoolmu; // TODO: one lock per order? static Stack stackfreequeue; void runtime·stackinit(void) { int32 i; if((StackCacheSize & PageMask) != 0) runtime·throw("cache size must be a multiple of page size"); for(i = 0; i < NumStackOrders; i++) runtime·MSpanList_Init(&runtime·stackpool[i]); } // Allocates a stack from the free pool. Must be called with // stackpoolmu held. static MLink* poolalloc(uint8 order) { MSpan *list; MSpan *s; MLink *x; uintptr i; list = &runtime·stackpool[order]; s = list->next; if(s == list) { // no free stacks. Allocate another span worth. s = runtime·MHeap_AllocStack(&runtime·mheap, StackCacheSize >> PageShift); if(s == nil) runtime·throw("out of memory"); if(s->ref != 0) runtime·throw("bad ref"); if(s->freelist != nil) runtime·throw("bad freelist"); for(i = 0; i < StackCacheSize; i += FixedStack << order) { x = (MLink*)((s->start << PageShift) + i); x->next = s->freelist; s->freelist = x; } runtime·MSpanList_Insert(list, s); } x = s->freelist; if(x == nil) runtime·throw("span has no free stacks"); s->freelist = x->next; s->ref++; if(s->freelist == nil) { // all stacks in s are allocated. runtime·MSpanList_Remove(s); } return x; } // Adds stack x to the free pool. Must be called with stackpoolmu held. static void poolfree(MLink *x, uint8 order) { MSpan *s; s = runtime·MHeap_Lookup(&runtime·mheap, x); if(s->state != MSpanStack) runtime·throw("freeing stack not in a stack span"); if(s->freelist == nil) { // s will now have a free stack runtime·MSpanList_Insert(&runtime·stackpool[order], s); } x->next = s->freelist; s->freelist = x; s->ref--; if(s->ref == 0) { // span is completely free - return to heap runtime·MSpanList_Remove(s); s->freelist = nil; runtime·MHeap_FreeStack(&runtime·mheap, s); } } // stackcacherefill/stackcacherelease implement a global pool of stack segments. // The pool is required to prevent unlimited growth of per-thread caches. static void stackcacherefill(MCache *c, uint8 order) { MLink *x, *list; uintptr size; if(StackDebug >= 1) runtime·printf("stackcacherefill order=%d\n", order); // Grab some stacks from the global cache. // Grab half of the allowed capacity (to prevent thrashing). list = nil; size = 0; runtime·lock(&runtime·stackpoolmu); while(size < StackCacheSize/2) { x = poolalloc(order); x->next = list; list = x; size += FixedStack << order; } runtime·unlock(&runtime·stackpoolmu); c->stackcache[order].list = list; c->stackcache[order].size = size; } static void stackcacherelease(MCache *c, uint8 order) { MLink *x, *y; uintptr size; if(StackDebug >= 1) runtime·printf("stackcacherelease order=%d\n", order); x = c->stackcache[order].list; size = c->stackcache[order].size; runtime·lock(&runtime·stackpoolmu); while(size > StackCacheSize/2) { y = x->next; poolfree(x, order); x = y; size -= FixedStack << order; } runtime·unlock(&runtime·stackpoolmu); c->stackcache[order].list = x; c->stackcache[order].size = size; } void runtime·stackcache_clear(MCache *c) { uint8 order; MLink *x, *y; if(StackDebug >= 1) runtime·printf("stackcache clear\n"); runtime·lock(&runtime·stackpoolmu); for(order = 0; order < NumStackOrders; order++) { x = c->stackcache[order].list; while(x != nil) { y = x->next; poolfree(x, order); x = y; } c->stackcache[order].list = nil; c->stackcache[order].size = 0; } runtime·unlock(&runtime·stackpoolmu); } Stack runtime·stackalloc(uint32 n) { uint8 order; uint32 n2; void *v; MLink *x; MSpan *s; MCache *c; // 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). if(g != g->m->g0) runtime·throw("stackalloc not on scheduler stack"); if((n & (n-1)) != 0) runtime·throw("stack size not a power of 2"); if(StackDebug >= 1) runtime·printf("stackalloc %d\n", n); if(runtime·debug.efence || StackFromSystem) { v = runtime·sysAlloc(ROUND(n, PageSize), &mstats.stacks_sys); if(v == nil) runtime·throw("out of memory (stackalloc)"); return (Stack){(uintptr)v, (uintptr)v+n}; } // 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. if(StackCache && n < FixedStack << NumStackOrders && n < StackCacheSize) { order = 0; n2 = n; while(n2 > FixedStack) { order++; n2 >>= 1; } c = g->m->mcache; if(c == nil || g->m->gcing || g->m->helpgc) { // 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. runtime·lock(&runtime·stackpoolmu); x = poolalloc(order); runtime·unlock(&runtime·stackpoolmu); } else { x = c->stackcache[order].list; if(x == nil) { stackcacherefill(c, order); x = c->stackcache[order].list; } c->stackcache[order].list = x->next; c->stackcache[order].size -= n; } v = (byte*)x; } else { s = runtime·MHeap_AllocStack(&runtime·mheap, ROUND(n, PageSize) >> PageShift); if(s == nil) runtime·throw("out of memory"); v = (byte*)(s->start<= 1) runtime·printf(" allocated %p\n", v); return (Stack){(uintptr)v, (uintptr)v+n}; } void runtime·stackfree(Stack stk) { uint8 order; uintptr n, n2; MSpan *s; MLink *x; MCache *c; void *v; n = stk.hi - stk.lo; v = (void*)stk.lo; if(n & (n-1)) runtime·throw("stack not a power of 2"); if(StackDebug >= 1) { runtime·printf("stackfree %p %d\n", v, (int32)n); runtime·memclr(v, n); // for testing, clobber stack data } if(runtime·debug.efence || StackFromSystem) { if(runtime·debug.efence || StackFaultOnFree) runtime·SysFault(v, n); else runtime·SysFree(v, n, &mstats.stacks_sys); return; } if(StackCache && n < FixedStack << NumStackOrders && n < StackCacheSize) { order = 0; n2 = n; while(n2 > FixedStack) { order++; n2 >>= 1; } x = (MLink*)v; c = g->m->mcache; if(c == nil || g->m->gcing || g->m->helpgc) { runtime·lock(&runtime·stackpoolmu); poolfree(x, order); runtime·unlock(&runtime·stackpoolmu); } else { if(c->stackcache[order].size >= StackCacheSize) stackcacherelease(c, order); x->next = c->stackcache[order].list; c->stackcache[order].list = x; c->stackcache[order].size += n; } } else { s = runtime·MHeap_Lookup(&runtime·mheap, v); if(s->state != MSpanStack) { runtime·printf("%p %p\n", s->start<argp // | return address | // +------------------+ <- 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 void runtime·main(void); void runtime·switchtoM(void(*)(void)); typedef struct AdjustInfo AdjustInfo; struct AdjustInfo { Stack old; uintptr delta; // ptr distance from old to new stack (newbase - oldbase) }; // Adjustpointer checks whether *vpp is in the old stack described by adjinfo. // If so, it rewrites *vpp to point into the new stack. static void adjustpointer(AdjustInfo *adjinfo, void *vpp) { byte **pp, *p; pp = vpp; p = *pp; if(StackDebug >= 4) runtime·printf(" %p:%p\n", pp, p); if(adjinfo->old.lo <= (uintptr)p && (uintptr)p < adjinfo->old.hi) { *pp = p + adjinfo->delta; if(StackDebug >= 3) runtime·printf(" adjust ptr %p: %p -> %p\n", pp, p, *pp); } } // bv describes the memory starting at address scanp. // Adjust any pointers contained therein. static void adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f) { uintptr delta; int32 num, i; byte *p, *minp, *maxp; Type *t; Itab *tab; minp = (byte*)adjinfo->old.lo; maxp = (byte*)adjinfo->old.hi; delta = adjinfo->delta; num = bv->n / BitsPerPointer; for(i = 0; i < num; i++) { if(StackDebug >= 4) runtime·printf(" %p:%s:%p\n", &scanp[i], mapnames[bv->bytedata[i / (8 / BitsPerPointer)] >> (i * BitsPerPointer & 7) & 3], scanp[i]); switch(bv->bytedata[i / (8 / BitsPerPointer)] >> (i * BitsPerPointer & 7) & 3) { case BitsDead: if(runtime·debug.gcdead) scanp[i] = (byte*)PoisonStack; break; case BitsScalar: break; case BitsPointer: p = scanp[i]; if(f != nil && (byte*)0 < p && (p < (byte*)PageSize && runtime·invalidptr || (uintptr)p == PoisonGC || (uintptr)p == PoisonStack)) { // Looks like a junk value in a pointer slot. // Live analysis wrong? g->m->traceback = 2; runtime·printf("runtime: bad pointer in frame %s at %p: %p\n", runtime·funcname(f), &scanp[i], p); runtime·throw("invalid stack pointer"); } if(minp <= p && p < maxp) { if(StackDebug >= 3) runtime·printf("adjust ptr %p %s\n", p, runtime·funcname(f)); scanp[i] = p + delta; } break; case BitsMultiWord: switch(bv->bytedata[(i+1) / (8 / BitsPerPointer)] >> ((i+1) * BitsPerPointer & 7) & 3) { default: runtime·throw("unexpected garbage collection bits"); case BitsEface: t = (Type*)scanp[i]; if(t != nil && ((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0)) { p = scanp[i+1]; if(minp <= p && p < maxp) { if(StackDebug >= 3) runtime·printf("adjust eface %p\n", p); if(t->size > PtrSize) // currently we always allocate such objects on the heap runtime·throw("large interface value found on stack"); scanp[i+1] = p + delta; } } i++; break; case BitsIface: tab = (Itab*)scanp[i]; if(tab != nil) { t = tab->type; //runtime·printf(" type=%p\n", t); if((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0) { p = scanp[i+1]; if(minp <= p && p < maxp) { if(StackDebug >= 3) runtime·printf("adjust iface %p\n", p); if(t->size > PtrSize) // currently we always allocate such objects on the heap runtime·throw("large interface value found on stack"); scanp[i+1] = p + delta; } } } i++; break; } break; } } } // Note: the argument/return area is adjusted by the callee. static bool adjustframe(Stkframe *frame, void *arg) { AdjustInfo *adjinfo; Func *f; StackMap *stackmap; int32 pcdata; BitVector bv; uintptr targetpc, size, minsize; adjinfo = arg; targetpc = frame->continpc; if(targetpc == 0) { // Frame is dead. return true; } f = frame->fn; if(StackDebug >= 2) runtime·printf(" adjusting %s frame=[%p,%p] pc=%p continpc=%p\n", runtime·funcname(f), frame->sp, frame->fp, frame->pc, frame->continpc); if(f->entry == (uintptr)runtime·switchtoM) { // A special routine at the bottom of stack of a goroutine that does an onM 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 = runtime·pcdatavalue(f, PCDATA_StackMapIndex, targetpc); if(pcdata == -1) pcdata = 0; // in prologue // Adjust local variables if stack frame has been allocated. size = frame->varp - frame->sp; if(thechar != '6' && thechar != '8') minsize = sizeof(uintptr); else minsize = 0; if(size > minsize) { stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps); if(stackmap == nil || stackmap->n <= 0) { runtime·printf("runtime: frame %s untyped locals %p+%p\n", runtime·funcname(f), (byte*)(frame->varp-size), size); runtime·throw("missing stackmap"); } // Locals bitmap information, scan just the pointers in locals. if(pcdata < 0 || pcdata >= stackmap->n) { // don't know where we are runtime·printf("runtime: pcdata is %d and %d locals stack map entries for %s (targetpc=%p)\n", pcdata, stackmap->n, runtime·funcname(f), targetpc); runtime·throw("bad symbol table"); } bv = runtime·stackmapdata(stackmap, pcdata); size = (bv.n * PtrSize) / BitsPerPointer; if(StackDebug >= 3) runtime·printf(" locals\n"); adjustpointers((byte**)(frame->varp - size), &bv, adjinfo, f); } // Adjust arguments. if(frame->arglen > 0) { if(frame->argmap != nil) { bv = *frame->argmap; } else { stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps); if(stackmap == nil || stackmap->n <= 0) { runtime·printf("runtime: frame %s untyped args %p+%p\n", runtime·funcname(f), frame->argp, (uintptr)frame->arglen); runtime·throw("missing stackmap"); } if(pcdata < 0 || pcdata >= stackmap->n) { // don't know where we are runtime·printf("runtime: pcdata is %d and %d args stack map entries for %s (targetpc=%p)\n", pcdata, stackmap->n, runtime·funcname(f), targetpc); runtime·throw("bad symbol table"); } bv = runtime·stackmapdata(stackmap, pcdata); } if(StackDebug >= 3) runtime·printf(" args\n"); adjustpointers((byte**)frame->argp, &bv, adjinfo, nil); } return true; } static void adjustctxt(G *gp, AdjustInfo *adjinfo) { adjustpointer(adjinfo, &gp->sched.ctxt); } static void adjustdefers(G *gp, AdjustInfo *adjinfo) { Defer *d; bool (*cb)(Stkframe*, void*); // Adjust defer argument blocks the same way we adjust active stack frames. cb = adjustframe; runtime·tracebackdefers(gp, &cb, 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, &d->fn); adjustpointer(adjinfo, &d->argp); adjustpointer(adjinfo, &d->panic); } } static void adjustpanics(G *gp, AdjustInfo *adjinfo) { // Panics are on stack and already adjusted. // Update pointer to head of list in G. adjustpointer(adjinfo, &gp->panic); } static void adjustsudogs(G *gp, AdjustInfo *adjinfo) { SudoG *s; // 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, &s->elem); adjustpointer(adjinfo, &s->selectdone); } } // Copies gp's stack to a new stack of a different size. static void copystack(G *gp, uintptr newsize) { Stack old, new; uintptr used; AdjustInfo adjinfo; uint32 oldstatus; bool (*cb)(Stkframe*, void*); byte *p, *ep; if(gp->syscallsp != 0) runtime·throw("stack growth not allowed in system call"); old = gp->stack; if(old.lo == 0) runtime·throw("nil stackbase"); used = old.hi - gp->sched.sp; // allocate new stack new = runtime·stackalloc(newsize); if(StackPoisonCopy) { p = (byte*)new.lo; ep = (byte*)new.hi; while(p < ep) *p++ = 0xfd; } if(StackDebug >= 1) runtime·printf("copystack gp=%p [%p %p %p]/%d -> [%p %p %p]/%d\n", gp, old.lo, old.hi-used, old.hi, (int32)(old.hi-old.lo), new.lo, new.hi-used, new.hi, (int32)newsize); // adjust pointers in the to-be-copied frames adjinfo.old = old; adjinfo.delta = new.hi - old.hi; cb = adjustframe; runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, 0x7fffffff, &cb, &adjinfo, 0); // adjust other miscellaneous things that have pointers into stacks. adjustctxt(gp, &adjinfo); adjustdefers(gp, &adjinfo); adjustpanics(gp, &adjinfo); adjustsudogs(gp, &adjinfo); // copy the stack to the new location if(StackPoisonCopy) { p = (byte*)new.lo; ep = (byte*)new.hi; while(p < ep) *p++ = 0xfb; } runtime·memmove((byte*)new.hi - used, (byte*)old.hi - used, used); oldstatus = runtime·casgcopystack(gp); // cas from Gwaiting or Grunnable to Gcopystack, return old status // 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; runtime·casgstatus(gp, Gcopystack, oldstatus); // oldstatus is Gwaiting or Grunnable // free old stack if(StackPoisonCopy) { p = (byte*)old.lo; ep = (byte*)old.hi; while(p < ep) *p++ = 0xfc; } if(newsize > old.hi-old.lo) { // growing, free stack immediately runtime·stackfree(old); } else { // shrinking, queue up free operation. We can't actually free the stack // just yet because we might run into the following situation: // 1) GC starts, scans a SudoG but does not yet mark the SudoG.elem pointer // 2) The stack that pointer points to is shrunk // 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. runtime·lock(&runtime·stackpoolmu); *(Stack*)old.lo = stackfreequeue; stackfreequeue = old; runtime·unlock(&runtime·stackpoolmu); } } // round x up to a power of 2. int32 runtime·round2(int32 x) { int32 s; s = 0; while((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. void runtime·newstack(void) { int32 oldsize, newsize; uintptr sp; G *gp; Gobuf morebuf; if(g->m->morebuf.g->stackguard0 == (uintptr)StackFork) runtime·throw("stack growth after fork"); if(g->m->morebuf.g != g->m->curg) { runtime·printf("runtime: newstack called from g=%p\n" "\tm=%p m->curg=%p m->g0=%p m->gsignal=%p\n", g->m->morebuf.g, g->m, g->m->curg, g->m->g0, g->m->gsignal); morebuf = g->m->morebuf; runtime·traceback(morebuf.pc, morebuf.sp, morebuf.lr, morebuf.g); runtime·throw("runtime: wrong goroutine in newstack"); } if(g->m->curg->throwsplit) runtime·throw("runtime: stack split at bad time"); // The goroutine must be executing in order to call newstack, // so it must be Grunning or Gscanrunning. gp = g->m->curg; morebuf = g->m->morebuf; g->m->morebuf.pc = (uintptr)nil; g->m->morebuf.lr = (uintptr)nil; g->m->morebuf.sp = (uintptr)nil; g->m->morebuf.g = (G*)nil; runtime·casgstatus(gp, Grunning, Gwaiting); gp->waitreason = runtime·gostringnocopy((byte*)"stack growth"); runtime·rewindmorestack(&gp->sched); if(gp->stack.lo == 0) runtime·throw("missing stack in newstack"); sp = gp->sched.sp; if(thechar == '6' || thechar == '8') { // The call to morestack cost a word. sp -= sizeof(uintreg); } if(StackDebug >= 1 || sp < gp->stack.lo) { runtime·printf("runtime: newstack sp=%p stack=[%p, %p]\n" "\tmorebuf={pc:%p sp:%p lr:%p}\n" "\tsched={pc:%p sp:%p lr:%p ctxt:%p}\n", sp, gp->stack.lo, gp->stack.hi, g->m->morebuf.pc, g->m->morebuf.sp, g->m->morebuf.lr, gp->sched.pc, gp->sched.sp, gp->sched.lr, gp->sched.ctxt); } if(sp < gp->stack.lo) { runtime·printf("runtime: gp=%p, gp->status=%d\n ", (void*)gp, runtime·readgstatus(gp)); runtime·printf("runtime: split stack overflow: %p < %p\n", sp, gp->stack.lo); runtime·throw("runtime: split stack overflow"); } if(gp->stackguard0 == (uintptr)StackPreempt) { if(gp == g->m->g0) runtime·throw("runtime: preempt g0"); if(g->m->p == nil && g->m->locks == 0) runtime·throw("runtime: g is running but p is not"); if(gp->preemptscan) { runtime·gcphasework(gp); runtime·casgstatus(gp, Gwaiting, Grunning); gp->stackguard0 = gp->stack.lo + StackGuard; gp->preempt = false; gp->preemptscan = false; // Tells the GC premption was successful. runtime·gogo(&gp->sched); // never return } // Be conservative about where we preempt. // We are interested in preempting user Go code, not runtime code. if(g->m->locks || g->m->mallocing || g->m->gcing || g->m->p->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; runtime·casgstatus(gp, Gwaiting, Grunning); runtime·gogo(&gp->sched); // never return } // Act like goroutine called runtime.Gosched. runtime·casgstatus(gp, Gwaiting, Grunning); runtime·gosched_m(gp); // never return } // Allocate a bigger segment and move the stack. oldsize = gp->stack.hi - gp->stack.lo; newsize = oldsize * 2; if(newsize > runtime·maxstacksize) { runtime·printf("runtime: goroutine stack exceeds %D-byte limit\n", (uint64)runtime·maxstacksize); runtime·throw("stack overflow"); } // Note that the concurrent GC might be scanning the stack as we try to replace it. // copystack takes care of the appropriate coordination with the stack scanner. copystack(gp, newsize); if(StackDebug >= 1) runtime·printf("stack grow done\n"); runtime·casgstatus(gp, Gwaiting, Grunning); runtime·gogo(&gp->sched); } #pragma textflag NOSPLIT void runtime·nilfunc(void) { *(byte*)0 = 0; } // adjust Gobuf as if it executed a call to fn // and then did an immediate gosave. void runtime·gostartcallfn(Gobuf *gobuf, FuncVal *fv) { void *fn; if(fv != nil) fn = fv->fn; else fn = runtime·nilfunc; runtime·gostartcall(gobuf, fn, fv); } // Maybe shrink the stack being used by gp. // Called at garbage collection time. void runtime·shrinkstack(G *gp) { uintptr used, oldsize, newsize; if(runtime·readgstatus(gp) == Gdead) { if(gp->stack.lo != 0) { // Free whole stack - it will get reallocated // if G is used again. runtime·stackfree(gp->stack); gp->stack.lo = 0; gp->stack.hi = 0; } return; } if(gp->stack.lo == 0) runtime·throw("missing stack in shrinkstack"); oldsize = gp->stack.hi - gp->stack.lo; newsize = oldsize / 2; if(newsize < FixedStack) return; // don't shrink below the minimum-sized stack used = gp->stack.hi - gp->sched.sp; if(used >= oldsize / 4) return; // still using at least 1/4 of the segment. // 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; #ifdef GOOS_windows if(gp->m != nil && gp->m->libcallsp != 0) return; #endif if(StackDebug > 0) runtime·printf("shrinking stack %D->%D\n", (uint64)oldsize, (uint64)newsize); copystack(gp, newsize); } // Do any delayed stack freeing that was queued up during GC. void runtime·shrinkfinish(void) { Stack s, t; runtime·lock(&runtime·stackpoolmu); s = stackfreequeue; stackfreequeue = (Stack){0,0}; runtime·unlock(&runtime·stackpoolmu); while(s.lo != 0) { t = *(Stack*)s.lo; runtime·stackfree(s); s = t; } } static void badc(void); #pragma textflag NOSPLIT void runtime·morestackc(void) { void (*fn)(void); fn = badc; runtime·onM(&fn); } static void badc(void) { runtime·throw("attempt to execute C code on Go stack"); }