[comp.sys.tandy] Tandy 6000 stack probes - question

stauffer@enme.UCalgary.CA (Ken Stauffer) (06/01/90)

	Does anyone understand how the stack-probes are implemented
	on a Tandy 6000 running Xenix? In particular how does
	Xenix increase the stack space of a process, and how
	does the 'tstb' instruction (see below) help xenix
	accomplish this? Also, is the stack space of a process
	fixed, or can it grow as large as available memory?

	Below is assembly of the header of a C function, generated
	by the C compiler. The highlighted line is the stack probe.
	C functions which are compiled with the -K option
	(no stack probes), do not generate this line.

	The constant LM1, seems to be related to the amount of stack
	space needed by the function for calling other functions.

		        .data
		        .text
		        .globl  _f
		_f:
		        link    a6,#-.LF1
>>>>>>>>	        tstb    -.LM1-132(sp)	<<<<<<<<<<<
		        moveml  #.LS1,-(a7)

	I am writting an alloca() function which increases the
	stack frame size of the caller of alloca(), I am running
	into a problem with large alloca requests which get
	a segmentation violation. I was thinking that I
	could have my alloca routine do the nessesary
	'tstb' equivilant, and thus ensure proper stack growth...

	Thanks,

	Ken Stauffer.

tif@commlab1.austin.ibm.com (/32767) (06/01/90)

In article <1990May31.173301.25856@calgary.uucp> stauffer@enme.UCalgary.CA (Ken Stauffer) writes:
>	Does anyone understand how the stack-probes are implemented
>	on a Tandy 6000 running Xenix?

Perhaps I should have let someone more confident answer but
this is how I understand it:  The stack don't grow.
The size is set by an option (-F?) to ld.
The probes are a waste of time.  In another world,
the operating system would detect the probe (because
it would cause a fault) and grow the stack.  Since the
68000 can't restart an instruction the tstb wouldn't
work but the _real_ stack access would.

--
------		    .--. .- ..- .-..   -.-. .... .- -- -... . .-. .-.. .- .. -.
Paul Chamberlain    IBM VNET: sc30661@ausvm6	     IBM OTHERNET: tif@doorstop
Austin, TX  78758   ...!cs.utexas.edu!ibmaus!auschs!doorstop.austin.ibm.com!tif
(512) 838-7008		Disclaimer: None of the above are official words of IBM

gordon@sneaky.UUCP (Gordon Burditt) (06/01/90)

>	Does anyone understand how the stack-probes are implemented
>	on a Tandy 6000 running Xenix? In particular how does
>	Xenix increase the stack space of a process, and how

Tandy 6000 Xenix doesn't increase the size of a stack segment.  
There are, in fact, two segments, code (if linked with -n) and 
everything else (data+stack+args+environment+malloc space+code(if not 
linked with -n)), which begin at virtual address 0x800000 and 0, 
respectively.  Stack size (in hex) may be specified with the linker -F 
option.  The default is -F 2000 (8k).  Some programs have been known to 
need up to -F 30000 (192k), in particular, one version of the GNU C compiler, 
when compiling certain pieces of itself.  Incidentally, version 1.37 of
GCC just barely compiles itself on a 6000 with 2 meg of memory.

It is possible to sort-of set the stack at the beginning of runtime.
Process the arguments and figure out the stack size.  malloc() a stack.
Call an assembly-language routine to set a new stack pointer and return.
(You just lost argc, argv, and all your auto variables, so if you needed
them, you'd better have them saved).  I invented this kludge for gcc,
which usually needs less than 50k of stack, but sometimes needs a lot more.

>	does the 'tstb' instruction (see below) help xenix
>	accomplish this? Also, is the stack space of a process
>	fixed, or can it grow as large as available memory?

If the 6000 had a third base-limit register for the stack segment,
which operated backwards (e.g. the TOP address is always 0x7fffff,
the bottom address varies with the size), a fault accessing the stack 
would indicate the need to increase the stack size.  Assuming the OS 
handled this properly, it could then grow the stack and restart the 
process.  However, some instructions can't be restarted properly, so 
the tstb is done in the hope that it will catch stack overflow NOW, 
using an instruction that can be restarted, rather than later hitting one 
that can't.  (This grief wouldn't be needed if we were using a 68020, 
which has better capability to restart instructions).  However, this is 
all wishful thinking, because we don't have a third base-limit register.

Stack probes are not entirely useless on the 6000 as-is.  Given that
you went WAY over your stack limit, your program dies a little sooner
before it erases all the evidence of what went wrong.  However, if you're
tight on space, go ahead and use -K.  I also have this program that
looks at object files, and back-computing that .LM1 number, can figure
out how much stack a function needs.

>	I am writting an alloca() function which increases the
>	stack frame size of the caller of alloca(), I am running
>	into a problem with large alloca requests which get
>	a segmentation violation. I was thinking that I
>	could have my alloca routine do the nessesary
>	'tstb' equivilant, and thus ensure proper stack growth...

If the stack pointer gets within a certain amount (132 is a nice slop figure.
Part of it includes what the kernel might push on the stack for an interrupt
or system call.) of _end, print a nasty message and abort.  You can't 
reallocate and move the stack, and you can't grow it.

I had a lot of grief implementing alloca.  The straightforward approach
generated some very difficult-to-track-down bugs in bison, which were
actually the fault of my alloca implementation destroying register variables
in the caller's caller.  So now I use the following complicated mess.  Note 
that alloca(2) takes something like 50 bytes of stack.  

This code also includes _check_stack, which alloca doesn't need,
but it's useful to call from recursive routines that might get themselves
in trouble.


| $Header: /usr/src/gnu/bison/RCS/alloca.s,v 4.1 88/04/10 15:08:24 gordon Exp $
|
|	alloca(size)
|
|		Alloca stack setup
|	At entry	Just before exit
|
|  sp-x-40-->		|---------------| <-- sp	\
|			|  Return addr	| 4		|
|			|---------------|		|  Copied
|			|   Alloca arg	| 4		|  portion
|			|---------------|		|  of the
|			|   Saved regs	| 0-40		|  stack
|			|---------------|		|
|			|  Wasted space	| 40-0		|
|			|---------------| <-- 48(sp)	/
|  sp-->|---------------|		|  old sp could be either side
|     4	|  Return addr	|   Allocated	|  of new 48(sp)
|       |---------------|		| x
|     4	|   Alloca arg	|    Memory	|
|       |---------------|---------------|
|	|    Pushed	|    Pushed	|
|	|    Args	|    Args	|
|       |---------------|---------------|
|  0-40	|   Saved regs	|  Wasted space	| 0-40
|       |---------------|---------------|
|	|    Local	|    Local	|
|	|Stack Variables|Stack Variables|
|  a6-->|---------------|---------------|<-- a6
|     4	| Old frame ptr	| Old frame ptr	| 4
|       |---------------|---------------|
|     4	|  Return addr	|  Return addr	| 4
|       |---------------|---------------|
|	|   Arguments	|   Arguments	|
|       |---------------|---------------|
|	Saved registers may be of length 0-40 because there may be up to 10
|	of them saved:  d2, d3, d4, d5, d6, d7, a2, a3, a4, a5
|	This version of alloca will not work if the saved registers plus
|	pushed args for pending function calls 
|	(e.g. printf(...,strcpy(alloca(x)),...);) exceed 40 bytes.
|
					
	.text
	.globl	_alloca
	.globl	_abort
	.globl	_exit
	.globl	_end
	.globl	_write
_alloca:
	movl	4(sp),d1	| pick up length arg
	addql	#1,d1
	andl	#-2,d1		| round up to next multiple of 2
	
	movl	sp,a0		| start of area to copy from

	lea	-40(sp),a1	| compute new stack pointer
	subl	d1,a1		| - requested length

	cmpl	#_end+132,a1	| range check stack pointer
	ble	.L1		| stack overflow, abort
	movl	a1,sp		| set new stack pointer

	movl	(a0),(sp)	| copy return address
				| skip alloca arg
	movl	8(a0),8(sp)	| copy (possibly) saved registers
	movl	12(a0),12(sp)	| This is an overlapped
	movl	16(a0),16(sp)	| copy so it has to be
	movl	20(a0),20(sp)	| done in the right order.
	movl	24(a0),24(sp)
	movl	28(a0),28(sp)
	movl	32(a0),32(sp)
	movl	36(a0),36(sp)
	movl	40(a0),40(sp)
	movl	44(a0),44(sp)

	lea	48(sp),a0	| fill allocated memory
.L3:	subql	#2,d1		| with 6db6 pattern
	bmi	.L2
	movw	#0x6db6,(a0)+
	bra	.L3
.L2:
	lea	48(sp),a0	| compute address of allocated memory
	movl	a0,d0
	rts

.L1:				| Stack overflow, write error message and quit.

	movl	d1,-(sp)	| length requested
	movl	#msg,-(sp)	| format string
	movl	#$allocabuf,-(sp)	| buffer
	jsr	_sprintf
	addw	#12,sp		| pop args off stack

	movl	#$allocabuf,-(sp)
	jsr	_strlen
	movl	d0,-(sp)	| length
	movl	#$allocabuf,-(sp) | buffer
	movl	#2,-(sp)	| write to standard error
	jsr	_write
	addw	#16,sp		| pop args off stack
.L4:
	jsr	_abort		| abort

	movl	#2,-(sp)	| just in case
	jsr	_exit

msg:	.asciz	"alloca:  stack overflow allocating %d bytes\n"
	.comm	$allocabuf,50
|
|	jsr	stack_overflow
|	stack-overflow handler routine for anything that checks for it
|
	.globl	stack_overflow
	.even
	.text
stack_overflow:
	movl	#15,-(sp)	| length (15 == strlen(msg2))
	movl	#msg2,-(sp)	| buffer
	movl	#2,-(sp)	| write to standard error
	jsr	_write
	addw	#12,sp		| pop args off stack

	jra	.L4		| go abort

msg2:	.ascii	"stack overflow\n"
|
|	_check_stack()
|	verify that stack is in range.  Call stack_overflow if not
|
	.text
	.even
	.globl	_check_stack
_check_stack:
	link	a6,#0		| set up stack frame for adb
	cmpl	#_end+132,sp	| check stack
	bgt	.L10		| OK
	jbsr	stack_overflow	| bad
.L10:	unlk	a6		| exit
	rts



					Gordon L. Burditt
					sneaky.lonestar.org!gordon