diff options
Diffstat (limited to 'src/pkg/runtime/stack.c')
| -rw-r--r-- | src/pkg/runtime/stack.c | 711 | 
1 files changed, 638 insertions, 73 deletions
| diff --git a/src/pkg/runtime/stack.c b/src/pkg/runtime/stack.c index 634706051..1680f004e 100644 --- a/src/pkg/runtime/stack.c +++ b/src/pkg/runtime/stack.c @@ -6,10 +6,21 @@  #include "arch_GOARCH.h"  #include "malloc.h"  #include "stack.h" +#include "funcdata.h" +#include "typekind.h" +#include "type.h" +#include "../../cmd/ld/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  };  typedef struct StackCacheNode StackCacheNode; @@ -74,22 +85,37 @@ stackcacherelease(void)  }  void* -runtime·stackalloc(uint32 n) +runtime·stackalloc(G *gp, uint32 n)  {  	uint32 pos;  	void *v; +	bool malloced; +	Stktop *top;  	// 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 != 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); + +	gp->stacksize += 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 v; +	} -	// Stacks are usually allocated with a fixed-size free-list allocator, -	// but if we need a stack of non-standard size, we fall back on malloc -	// (assuming that inside malloc and GC all the stack frames are small, +	// Minimum-sized stacks are allocated with a fixed-size free-list allocator, +	// but if we need a stack of a bigger size, we fall back on malloc +	// (assuming that inside malloc all the stack frames are small,  	// so that we do not deadlock). -	if(n == FixedStack || m->mallocing || m->gcing) { +	malloced = true; +	if(n == FixedStack || m->mallocing) {  		if(n != FixedStack) {  			runtime·printf("stackalloc: in malloc, size=%d want %d\n", FixedStack, n);  			runtime·throw("stackalloc"); @@ -102,27 +128,46 @@ runtime·stackalloc(uint32 n)  		m->stackcachepos = pos;  		m->stackcachecnt--;  		m->stackinuse++; -		return v; -	} -	return runtime·mallocgc(n, 0, FlagNoProfiling|FlagNoGC|FlagNoZero|FlagNoInvokeGC); +		malloced = false; +	} else +		v = runtime·mallocgc(n, 0, FlagNoProfiling|FlagNoGC|FlagNoZero|FlagNoInvokeGC); + +	top = (Stktop*)((byte*)v+n-sizeof(Stktop)); +	runtime·memclr((byte*)top, sizeof(*top)); +	top->malloced = malloced; +	return v;  }  void -runtime·stackfree(void *v, uintptr n) +runtime·stackfree(G *gp, void *v, Stktop *top)  {  	uint32 pos; - -	if(n == FixedStack || m->mallocing || m->gcing) { -		if(m->stackcachecnt == StackCacheSize) -			stackcacherelease(); -		pos = m->stackcachepos; -		m->stackcache[pos] = v; -		m->stackcachepos = (pos + 1) % StackCacheSize; -		m->stackcachecnt++; -		m->stackinuse--; +	uintptr n; + +	n = (uintptr)(top+1) - (uintptr)v; +	if(StackDebug >= 1) +		runtime·printf("stackfree %p %d\n", v, (int32)n); +	gp->stacksize -= n; +	if(runtime·debug.efence || StackFromSystem) { +		if(runtime·debug.efence || StackFaultOnFree) +			runtime·SysFault(v, n); +		else +			runtime·SysFree(v, n, &mstats.stacks_sys); +		return; +	} +	if(top->malloced) { +		runtime·free(v);  		return;  	} -	runtime·free(v); +	if(n != FixedStack) +		runtime·throw("stackfree: bad fixed size"); +	if(m->stackcachecnt == StackCacheSize) +		stackcacherelease(); +	pos = m->stackcachepos; +	m->stackcache[pos] = v; +	m->stackcachepos = (pos + 1) % StackCacheSize; +	m->stackcachecnt++; +	m->stackinuse--;  }  // Called from runtime·lessstack when returning from a function which @@ -145,12 +190,12 @@ runtime·oldstack(void)  	sp = (byte*)top;  	argsize = top->argsize; -	if(StackDebug) { +	if(StackDebug >= 1) {  		runtime·printf("runtime: oldstack gobuf={pc:%p sp:%p lr:%p} cret=%p argsize=%p\n", -			top->gobuf.pc, top->gobuf.sp, top->gobuf.lr, m->cret, (uintptr)argsize); +			top->gobuf.pc, top->gobuf.sp, top->gobuf.lr, (uintptr)m->cret, (uintptr)argsize);  	} -	// gp->status is usually Grunning, but it could be Gsyscall if a stack split +	// gp->status is usually Grunning, but it could be Gsyscall if a stack overflow  	// happens during a function call inside entersyscall.  	oldstatus = gp->status; @@ -175,11 +220,7 @@ runtime·oldstack(void)  	gp->stackguard = top->stackguard;  	gp->stackguard0 = gp->stackguard;  	gp->panicwrap = top->panicwrap; - -	if(top->free != 0) { -		gp->stacksize -= top->free; -		runtime·stackfree(old, top->free); -	} +	runtime·stackfree(gp, old, top);  	gp->status = oldstatus;  	runtime·gogo(&gp->sched); @@ -187,6 +228,435 @@ runtime·oldstack(void)  uintptr runtime·maxstacksize = 1<<20; // enough until runtime.main sets it for real +static uint8* +mapnames[] = { +	(uint8*)"---", +	(uint8*)"scalar", +	(uint8*)"ptr", +	(uint8*)"multi", +}; + +// Stack frame layout +// +// (x86) +// +------------------+ +// | args from caller | +// +------------------+ <- frame->argp +// |  return address  | +// +------------------+ <- frame->varp +// |     locals       | +// +------------------+ +// |  args to callee  | +// +------------------+ <- frame->sp +// +// (arm: TODO) + +typedef struct CopyableInfo CopyableInfo; +struct CopyableInfo { +	byte *stk;	// bottom address of segment +	byte *base;	// top address of segment (including Stktop) +	int32 frames;	// count of copyable frames (-1 = not copyable) +}; + +void runtime·main(void); + +static bool +checkframecopy(Stkframe *frame, void *arg) +{ +	CopyableInfo *cinfo; +	Func *f; +	StackMap *stackmap; + +	cinfo = arg; +	f = frame->fn; +	if(StackDebug >= 2) +		runtime·printf("    checking %s frame=[%p,%p] stk=[%p,%p]\n", runtime·funcname(f), frame->sp, frame->fp, cinfo->stk, cinfo->base); +	// if we're not in the segment any more, return immediately. +	if(frame->varp < cinfo->stk || frame->varp >= cinfo->base) { +		if(StackDebug >= 2) +			runtime·printf("    <next segment>\n"); +		return false; // stop traceback +	} +	if(f->entry == (uintptr)runtime·main) { +		// A special routine at the TOS of the main routine. +		// We will allow it to be copied even though we don't +		// have full GC info for it (because it is written in C). +		cinfo->frames++; +		return false; // stop traceback +	} +	if(frame->varp != (byte*)frame->sp) { // not in prologue (and has at least one local or outarg) +		stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps); +		if(stackmap == nil) { +			cinfo->frames = -1; +			if(StackDebug >= 1) +				runtime·printf("copystack: no locals info for %s\n", runtime·funcname(f)); +			return false; +		} +		if(stackmap->n <= 0) { +			cinfo->frames = -1; +			if(StackDebug >= 1) +				runtime·printf("copystack: locals size info only for %s\n", runtime·funcname(f)); +			return false; +		} +	} +	if(frame->arglen != 0) { +		stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps); +		if(stackmap == nil) { +			cinfo->frames = -1; +			if(StackDebug >= 1) +				runtime·printf("copystack: no arg info for %s\n", runtime·funcname(f)); +			return false; +		} +	} +	cinfo->frames++; +	return true; // this frame is ok; keep going +} + +// If the top segment of the stack contains an uncopyable +// frame, return -1.  Otherwise return the number of frames +// in the top segment, all of which are copyable. +static int32 +copyabletopsegment(G *gp) +{ +	CopyableInfo cinfo; +	Defer *d; +	Func *f; +	FuncVal *fn; +	StackMap *stackmap; + +	cinfo.stk = (byte*)gp->stackguard - StackGuard; +	cinfo.base = (byte*)gp->stackbase + sizeof(Stktop); +	cinfo.frames = 0; + +	// Check that each frame is copyable.  As a side effect, +	// count the frames. +	runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, 0x7fffffff, checkframecopy, &cinfo, false); +	if(StackDebug >= 1 && cinfo.frames != -1) +		runtime·printf("copystack: %d copyable frames\n", cinfo.frames); + +	// Check to make sure all Defers are copyable +	for(d = gp->defer; d != nil; d = d->link) { +		if(cinfo.stk <= (byte*)d && (byte*)d < cinfo.base) { +			// Defer is on the stack.  Its copyableness has +			// been established during stack walking. +			// For now, this only happens with the Defer in runtime.main. +			continue; +		} +		if(d->argp < cinfo.stk || cinfo.base <= d->argp) +			break; // a defer for the next segment +		fn = d->fn; +		if(fn == nil) // See issue 8047 +			continue; +		f = runtime·findfunc((uintptr)fn->fn); +		if(f == nil) +			return -1; + +		// Check to make sure we have an args pointer map for the defer's args. +		// We only need the args map, but we check +		// for the locals map also, because when the locals map +		// isn't provided it means the ptr map came from C and +		// C (particularly, cgo) lies to us.  See issue 7695. +		stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps); +		if(stackmap == nil || stackmap->n <= 0) +			return -1; +		stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps); +		if(stackmap == nil || stackmap->n <= 0) +			return -1; + +		if(cinfo.stk <= (byte*)fn && (byte*)fn < cinfo.base) { +			// FuncVal is on the stack.  Again, its copyableness +			// was established during stack walking. +			continue; +		} +		// The FuncVal may have pointers in it, but fortunately for us +		// the compiler won't put pointers into the stack in a +		// heap-allocated FuncVal. +		// One day if we do need to check this, we'll need maps of the +		// pointerness of the closure args.  The only place we have that map +		// right now is in the gc program for the FuncVal.  Ugh. +	} + +	return cinfo.frames; +} + +typedef struct AdjustInfo AdjustInfo; +struct AdjustInfo { +	byte *oldstk;	// bottom address of segment +	byte *oldbase;	// top address of segment (after Stktop) +	uintptr delta;  // ptr distance from old to new stack (newbase - oldbase) +}; + +// 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 = adjinfo->oldstk; +	maxp = adjinfo->oldbase; +	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->data[i / (32 / BitsPerPointer)] >> (i * BitsPerPointer & 31) & 3], scanp[i]); +		switch(bv->data[i / (32 / BitsPerPointer)] >> (i * BitsPerPointer & 31) & 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 || (uintptr)p == PoisonGC || (uintptr)p == PoisonStack)) { +				// Looks like a junk value in a pointer slot. +				// Live analysis wrong? +				m->traceback = 2; +				runtime·printf("runtime: bad pointer in frame %s at %p: %p\n", runtime·funcname(f), &scanp[i], p); +				runtime·throw("bad 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->data[(i+1) / (32 / BitsPerPointer)] >> ((i+1) * BitsPerPointer & 31) & 3) { +			case BitsString: +				// string referents are never on the stack, never need to be adjusted +				i++; // skip len +				break; +			case BitsSlice: +				p = scanp[i]; +				if(minp <= p && p < maxp) { +					if(StackDebug >= 3) +						runtime·printf("adjust slice %p\n", p); +					scanp[i] = p + delta; +				} +				i += 2; // skip len, cap +				break; +			case BitsEface: +				t = (Type*)scanp[i]; +				if(t != nil && (t->size > PtrSize || (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->size > PtrSize || (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; + +	adjinfo = arg; +	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·main) +		return true; +	targetpc = frame->continpc; +	if(targetpc == 0) { +		// Frame is dead. +		return true; +	} +	if(targetpc != f->entry) +		targetpc--; +	pcdata = runtime·pcdatavalue(f, PCDATA_StackMapIndex, targetpc); +	if(pcdata == -1) +		pcdata = 0; // in prologue + +	// adjust local pointers +	if(frame->varp != (byte*)frame->sp) { +		stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps); +		if(stackmap == nil) +			runtime·throw("no locals info"); +		if(stackmap->n <= 0) +			runtime·throw("locals size info only"); +		bv = runtime·stackmapdata(stackmap, pcdata); +		if(StackDebug >= 3) +			runtime·printf("      locals\n"); +		adjustpointers((byte**)frame->varp - bv.n / BitsPerPointer, &bv, adjinfo, f); +	} +	// adjust inargs and outargs +	if(frame->arglen != 0) { +		stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps); +		if(stackmap == nil) +			runtime·throw("no arg info"); +		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) +{ +	if(adjinfo->oldstk <= (byte*)gp->sched.ctxt && (byte*)gp->sched.ctxt < adjinfo->oldbase) +		gp->sched.ctxt = (byte*)gp->sched.ctxt + adjinfo->delta; +} + +static void +adjustdefers(G *gp, AdjustInfo *adjinfo) +{ +	Defer *d, **dp; +	Func *f; +	FuncVal *fn; +	StackMap *stackmap; +	BitVector bv; + +	for(dp = &gp->defer, d = *dp; d != nil; dp = &d->link, d = *dp) { +		if(adjinfo->oldstk <= (byte*)d && (byte*)d < adjinfo->oldbase) { +			// The Defer record is on the stack.  Its fields will +			// get adjusted appropriately. +			// This only happens for runtime.main now, but a compiler +			// optimization could do more of this. +			*dp = (Defer*)((byte*)d + adjinfo->delta); +			continue; +		} +		if(d->argp < adjinfo->oldstk || adjinfo->oldbase <= d->argp) +			break; // a defer for the next segment +		fn = d->fn; +		if(fn == nil) { +			// Defer of nil function.  It will panic when run, and there +			// aren't any args to adjust.  See issue 8047. +			d->argp += adjinfo->delta; +			continue; +		} +		f = runtime·findfunc((uintptr)fn->fn); +		if(f == nil) +			runtime·throw("can't adjust unknown defer"); +		if(StackDebug >= 4) +			runtime·printf("  checking defer %s\n", runtime·funcname(f)); +		// Defer's FuncVal might be on the stack +		if(adjinfo->oldstk <= (byte*)fn && (byte*)fn < adjinfo->oldbase) { +			if(StackDebug >= 3) +				runtime·printf("    adjust defer fn %s\n", runtime·funcname(f)); +			d->fn = (FuncVal*)((byte*)fn + adjinfo->delta); +		} else { +			// deferred function's args might point into the stack. +			if(StackDebug >= 3) +				runtime·printf("    adjust deferred args for %s\n", runtime·funcname(f)); +			stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps); +			if(stackmap == nil) +				runtime·throw("runtime: deferred function has no arg ptr map"); +			bv = runtime·stackmapdata(stackmap, 0); +			adjustpointers(d->args, &bv, adjinfo, f); +		} +		d->argp += adjinfo->delta; +	} +} + +// Copies the top stack segment of gp to a new stack segment of a +// different size.  The top segment must contain nframes frames. +static void +copystack(G *gp, uintptr nframes, uintptr newsize) +{ +	byte *oldstk, *oldbase, *newstk, *newbase; +	uintptr oldsize, used; +	AdjustInfo adjinfo; +	Stktop *oldtop, *newtop; +	bool malloced; + +	if(gp->syscallstack != 0) +		runtime·throw("can't handle stack copy in syscall yet"); +	oldstk = (byte*)gp->stackguard - StackGuard; +	oldbase = (byte*)gp->stackbase + sizeof(Stktop); +	oldsize = oldbase - oldstk; +	used = oldbase - (byte*)gp->sched.sp; +	oldtop = (Stktop*)gp->stackbase; + +	// allocate new stack +	newstk = runtime·stackalloc(gp, newsize); +	newbase = newstk + newsize; +	newtop = (Stktop*)(newbase - sizeof(Stktop)); +	malloced = newtop->malloced; + +	if(StackDebug >= 1) +		runtime·printf("copystack [%p %p]/%d -> [%p %p]/%d\n", oldstk, oldbase, (int32)oldsize, newstk, newbase, (int32)newsize); +	USED(oldsize); +	 +	// adjust pointers in the to-be-copied frames +	adjinfo.oldstk = oldstk; +	adjinfo.oldbase = oldbase; +	adjinfo.delta = newbase - oldbase; +	runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, nframes, adjustframe, &adjinfo, false); +	 +	// adjust other miscellaneous things that have pointers into stacks. +	adjustctxt(gp, &adjinfo); +	adjustdefers(gp, &adjinfo); +	 +	// copy the stack (including Stktop) to the new location +	runtime·memmove(newbase - used, oldbase - used, used); +	newtop->malloced = malloced; +	 +	// Swap out old stack for new one +	gp->stackbase = (uintptr)newtop; +	gp->stackguard = (uintptr)newstk + StackGuard; +	gp->stackguard0 = (uintptr)newstk + StackGuard; // NOTE: might clobber a preempt request +	if(gp->stack0 == (uintptr)oldstk) +		gp->stack0 = (uintptr)newstk; +	gp->sched.sp = (uintptr)(newbase - used); + +	// free old stack +	runtime·stackfree(gp, oldstk, oldtop); +} + +// 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·newstackcall or from runtime·morestack when a new  // stack segment is needed.  Allocate a new stack big enough for  // m->moreframesize bytes, copy m->moreargsize bytes to the new frame, @@ -195,16 +665,18 @@ uintptr runtime·maxstacksize = 1<<20; // enough until runtime.main sets it for  void  runtime·newstack(void)  { -	int32 framesize, argsize, oldstatus; +	int32 framesize, argsize, oldstatus, oldsize, newsize, nframes;  	Stktop *top, *oldtop; -	byte *stk; +	byte *stk, *oldstk, *oldbase;  	uintptr sp;  	uintptr *src, *dst, *dstend;  	G *gp; -	Gobuf label; +	Gobuf label, morebuf; +	void *moreargp;  	bool newstackcall; -	uintptr free; +	if(m->forkstackguard) +		runtime·throw("split stack after fork");  	if(m->morebuf.g != m->curg) {  		runtime·printf("runtime: newstack called from g=%p\n"  			"\tm=%p m->curg=%p m->g0=%p m->gsignal=%p\n", @@ -212,15 +684,21 @@ runtime·newstack(void)  		runtime·throw("runtime: wrong goroutine in newstack");  	} -	// gp->status is usually Grunning, but it could be Gsyscall if a stack split +	// gp->status is usually Grunning, but it could be Gsyscall if a stack overflow  	// happens during a function call inside entersyscall.  	gp = m->curg;  	oldstatus = gp->status;  	framesize = m->moreframesize;  	argsize = m->moreargsize; +	moreargp = m->moreargp; +	m->moreargp = nil; +	morebuf = m->morebuf; +	m->morebuf.pc = (uintptr)nil; +	m->morebuf.lr = (uintptr)nil; +	m->morebuf.sp = (uintptr)nil;  	gp->status = Gwaiting; -	gp->waitreason = "stack split"; +	gp->waitreason = "stack growth";  	newstackcall = framesize==1;  	if(newstackcall)  		framesize = 0; @@ -234,7 +712,7 @@ runtime·newstack(void)  		// The call to morestack cost a word.  		sp -= sizeof(uintptr);  	} -	if(StackDebug || sp < gp->stackguard - StackGuard) { +	if(StackDebug >= 1 || sp < gp->stackguard - StackGuard) {  		runtime·printf("runtime: newstack framesize=%p argsize=%p sp=%p stack=[%p, %p]\n"  			"\tmorebuf={pc:%p sp:%p lr:%p}\n"  			"\tsched={pc:%p sp:%p lr:%p ctxt:%p}\n", @@ -248,8 +726,8 @@ runtime·newstack(void)  	}  	if(argsize % sizeof(uintptr) != 0) { -		runtime·printf("runtime: stack split with misaligned argsize %d\n", argsize); -		runtime·throw("runtime: stack split argsize"); +		runtime·printf("runtime: stack growth with misaligned argsize %d\n", argsize); +		runtime·throw("runtime: stack growth argsize");  	}  	if(gp->stackguard0 == (uintptr)StackPreempt) { @@ -258,7 +736,7 @@ runtime·newstack(void)  		if(oldstatus == Grunning && m->p == nil && m->locks == 0)  			runtime·throw("runtime: g is running but p is not");  		if(oldstatus == Gsyscall && m->locks == 0) -			runtime·throw("runtime: stack split during syscall"); +			runtime·throw("runtime: stack growth during syscall");  		// Be conservative about where we preempt.  		// We are interested in preempting user Go code, not runtime code.  		if(oldstatus != Grunning || m->locks || m->mallocing || m->gcing || m->p->status != Prunning) { @@ -273,46 +751,55 @@ runtime·newstack(void)  		runtime·gosched0(gp);	// never return  	} -	if(newstackcall && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > gp->stackguard) { -		// special case: called from runtime.newstackcall (framesize==1) -		// to call code with an arbitrary argument size, -		// and we have enough space on the current stack. -		// the new Stktop* is necessary to unwind, but -		// we don't need to create a new segment. -		top = (Stktop*)(m->morebuf.sp - sizeof(*top)); -		stk = (byte*)gp->stackguard - StackGuard; -		free = 0; -	} else { -		// allocate new segment. -		framesize += argsize; -		framesize += StackExtra;	// room for more functions, Stktop. -		if(framesize < StackMin) -			framesize = StackMin; -		framesize += StackSystem; -		gp->stacksize += framesize; -		if(gp->stacksize > runtime·maxstacksize) { -			runtime·printf("runtime: goroutine stack exceeds %D-byte limit\n", (uint64)runtime·maxstacksize); -			runtime·throw("stack overflow"); +	// If every frame on the top segment is copyable, allocate a bigger segment +	// and move the segment instead of allocating a new segment. +	if(runtime·copystack) { +		if(!runtime·precisestack) +			runtime·throw("can't copy stacks without precise stacks"); +		nframes = copyabletopsegment(gp); +		if(nframes != -1) { +			oldstk = (byte*)gp->stackguard - StackGuard; +			oldbase = (byte*)gp->stackbase + sizeof(Stktop); +			oldsize = oldbase - oldstk; +			newsize = oldsize * 2; +			copystack(gp, nframes, newsize); +			if(StackDebug >= 1) +				runtime·printf("stack grow done\n"); +			if(gp->stacksize > runtime·maxstacksize) { +				runtime·printf("runtime: goroutine stack exceeds %D-byte limit\n", (uint64)runtime·maxstacksize); +				runtime·throw("stack overflow"); +			} +			gp->status = oldstatus; +			runtime·gogo(&gp->sched);  		} -		stk = runtime·stackalloc(framesize); -		top = (Stktop*)(stk+framesize-sizeof(*top)); -		free = framesize; +		// TODO: if stack is uncopyable because we're in C code, patch return value at +		// end of C code to trigger a copy as soon as C code exits.  That way, we'll +		// have stack available if we get this deep again.  	} -	if(StackDebug) { +	// allocate new segment. +	framesize += argsize; +	framesize += StackExtra;	// room for more functions, Stktop. +	if(framesize < StackMin) +		framesize = StackMin; +	framesize += StackSystem; +	framesize = runtime·round2(framesize); +	stk = runtime·stackalloc(gp, framesize); +	if(gp->stacksize > runtime·maxstacksize) { +		runtime·printf("runtime: goroutine stack exceeds %D-byte limit\n", (uint64)runtime·maxstacksize); +		runtime·throw("stack overflow"); +	} +	top = (Stktop*)(stk+framesize-sizeof(*top)); + +	if(StackDebug >= 1) {  		runtime·printf("\t-> new stack [%p, %p]\n", stk, top);  	}  	top->stackbase = gp->stackbase;  	top->stackguard = gp->stackguard; -	top->gobuf = m->morebuf; -	top->argp = m->moreargp; +	top->gobuf = morebuf; +	top->argp = moreargp;  	top->argsize = argsize; -	top->free = free; -	m->moreargp = nil; -	m->morebuf.pc = (uintptr)nil; -	m->morebuf.lr = (uintptr)nil; -	m->morebuf.sp = (uintptr)nil;  	// copy flag from panic  	top->panic = gp->ispanic; @@ -365,18 +852,96 @@ runtime·newstack(void)  	*(int32*)345 = 123;	// never return  } +#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)  { -	runtime·gostartcall(gobuf, fv->fn, 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∕debug·setMaxStack(intgo in, intgo out) +runtime·shrinkstack(G *gp)  { -	out = runtime·maxstacksize; -	runtime·maxstacksize = in; -	FLUSH(&out); +	int32 nframes; +	byte *oldstk, *oldbase; +	uintptr used, oldsize, newsize; +	MSpan *span; + +	if(!runtime·copystack) +		return; +	oldstk = (byte*)gp->stackguard - StackGuard; +	oldbase = (byte*)gp->stackbase + sizeof(Stktop); +	oldsize = oldbase - oldstk; +	newsize = oldsize / 2; +	if(newsize < FixedStack) +		return; // don't shrink below the minimum-sized stack +	used = oldbase - (byte*)gp->sched.sp; +	if(used >= oldsize / 4) +		return; // still using at least 1/4 of the segment. + +	// To shrink to less than 1/2 a page, we need to copy. +	if(newsize < PageSize/2) { +		if(gp->syscallstack != (uintptr)nil) // TODO: can we handle this case? +			return; +#ifdef GOOS_windows +		if(gp->m != nil && gp->m->libcallsp != 0) +			return; +#endif +		nframes = copyabletopsegment(gp); +		if(nframes == -1) +			return; +		copystack(gp, nframes, newsize); +		return; +	} + +	// To shrink a stack of one page size or more, we can shrink it +	// without copying.  Just deallocate the lower half. +	span = runtime·MHeap_LookupMaybe(&runtime·mheap, oldstk); +	if(span == nil) +		return; // stack allocated outside heap.  Can't shrink it.  Can happen if stack is allocated while inside malloc.  TODO: shrink by copying? +	if(span->elemsize != oldsize) +		runtime·throw("span element size doesn't match stack size"); +	if((uintptr)oldstk != span->start << PageShift) +		runtime·throw("stack not at start of span"); + +	if(StackDebug) +		runtime·printf("shrinking stack in place %p %X->%X\n", oldstk, oldsize, newsize); + +	// new stack guard for smaller stack +	gp->stackguard = (uintptr)oldstk + newsize + StackGuard; +	gp->stackguard0 = (uintptr)oldstk + newsize + StackGuard; +	if(gp->stack0 == (uintptr)oldstk) +		gp->stack0 = (uintptr)oldstk + newsize; +	gp->stacksize -= oldsize - newsize; + +	// Free bottom half of the stack. +	if(runtime·debug.efence || StackFromSystem) { +		if(runtime·debug.efence || StackFaultOnFree) +			runtime·SysFault(oldstk, newsize); +		else +			runtime·SysFree(oldstk, newsize, &mstats.stacks_sys); +		return; +	} +	// First, we trick malloc into thinking +	// we allocated the stack as two separate half-size allocs.  Then the +	// free() call does the rest of the work for us. +	runtime·MSpan_EnsureSwept(span); +	runtime·MHeap_SplitSpan(&runtime·mheap, span); +	runtime·free(oldstk);  } | 
