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