KnowledgeBase Archive

An Archive of Early Microsoft KnowledgeBase Articles

View on GitHub

Q58194: How to Write Your Own __chkstk Routine

Article: Q58194
Product(s): See article
Version(s): 5.10
Operating System(s): MS-DOS
Keyword(s): ENDUSER | s_quickasm s_quickc | mspl13_c
Last Modified: 20-FEB-1990

Problem:

I am trying to rewrite the stack checking routine (__chkstk) so that I
can work around some of the assumptions that it makes.

These assumptions are DS==SS, and that Microsoft's internal variables
point to the beginning/end of the stack.

Response:

The stack checking routine actually allocates space on the stack, so
doing a stack checking routine that just does a RET does not work. The
following code demonstrates the bare necessities for writing your own
__chkstk routine:

                ;   a chkstk routine for the small/compact memory models
.MODEL SMALL
.DATA

PUBLIC STKHQQ

STKHQQ dw     dataoffset _end+STACKSLOP

.CODE

PUBLIC __chkstk

__chkstk PROC

        pop     cx      ; grab the return address
        sub     sp, ax
        jmp     cx

__chkstk ENDP

END

                ;   a chkstk routine for the medium/large memory models
.MODEL LARGE
.DATA

PUBLIC STKHQQ

STKHQQ dw    dataoffset _end+STACKSLOP

.CODE

PUBLIC __chkstk

__chkstk PROC

        pop     cx      ; grab the return address
        pop     dx      ; (and its segment)

        sub     sp, ax

        push    dx      ; push the return address
        push    cx
        ret             ; and go back to that address

__chkstk ENDP

END

Currently, STACKSLOP is defined as being 256 bytes for DOS and 512
bytes for OS/2, although this may change with future versions of the
compiler.

It is unsafe to do just a simple return from inside this function
because the stack would not be modified. In one scenario, if a
function assumes that it has X amount of stack space and that the
stack space hasn't been allocated, the function will write over
whatever is currently on the stack (this function could do a memory
move of data onto the stack rather than pushing and popping the
stack). Because of this, you must allocate space on the stack.

You cannot simply turn off stack checking because some functions
(printf() in particular) have a call to __chkstk hardcoded.

To get the above example working, let's assume that it is in a file
called CHKSTK.ASM. Assemble with the /MX switch, to make all symbols
case sensitive, as follows:

   MASM /MX chkstk.asm ;

   or

   QCL /Cx chkstk.asm

You can then link your program with this module in the following way:

   LINK /NOE program chkstk ;

This assumes that your program has been compiled successfully and that
it is named "program." It also assumes that you are using the same
memory model for both modules.

This information is correct for Microsoft C Versions 5.10 and earlier.
It is also correct for QuickC Versions 2.00 and earlier, as well as
QuickAssembler Version 2.01.

THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Copyright Microsoft Corporation 1986-2002.