Stack checking in the Windows NT application differs from stack checking in the MS-DOS application.
In an MS-DOS application, stack overflow is detected only by software. Bydefault, the compiler inserts a call to the __chkstk()
function in the prologue code for each function. The __chkstk()
function compares the amount of stack space a function requires with the amount of stack space available. The function issues an overflow error if the current stack pointer and requested stack allocation exceeds the maximum stack sizespecified in the EXE header.
In the Microsoft Windows NT operating system, stack overflow is detected byhardware and software working together, using the page protectionmechanisms. Each new Windows NT process has a maximum reserved stack sizeand an initial committed stack allocation. Committed memory is physicallyallocated to the process and is backed by the page file; it is a relatively"expensive" resource. Reserved memory is address space that is not mappedto real memory; it is a relatively "cheap" resource.
As the stack grows, it moves from the committed portion of stack memoryinto the reserved or uncommitted memory. When this happens, a page faultoccurs and the operating system commits another page of memory to thestack. If a page fault occurs when the stack has already grown to itsmaximum specified size, the system reports a stack overflow exception.
This automatic growth method uses a guard page, a reserved, uncommitted,memory page that is contiguous with the committed portion of memory. Whenthe application touches the guard page, the operating system commits thatpage and the next uncommitted page becomes the new guard page. Automaticstack growth works only for the guard page and stack memory must grow in4K, or one page, increments. If the application touches another reservedbut uncommitted page of stack memory before it touches the guard page, anormal page fault exception occurs and unpredictable behavior can result.
If a function included the following statements in its prologue code, thisproblem could occur:
PUSH EBP MOV EBP,ESP SUB ESP,10000 PUSH ESI
In this code, the PUSH EBP and PUSH ESI do not occur in the same or inadjoining 4K stack pages. If the stack must grow to accommodate the 10,000byte local allocation, this program faults.
To prevent the fault, the compiler calls the __chkstk()
function each time the local allocation exceeds 4K. The Windows NT __chkstk()
function does not explicitly check for stack overflow as the MS-DOS version does. It simply touches memory addresses every 4K from the current stack pointer location to the requested allocation. This triggers the guard pages in the proper sequence and commits additional memory to the stack as required.
Therefore, when the compiler command line includes the /Ge
option switch and the prologue code always calls the __chkstk()
function, the application is not running as efficiently as it might because the operating system supports an automatic mechanism to perform stack overflow detection.
compiler option switch does not disable all __chkstk()
calls. Instead, it disables __chkstk()
calls for those functions that require less than 4K of local storage. The /Gs
option is the default behavior of the compiler.
option accepts an optional parameter, the threshold value. If a function's local stack allocation exceeds the specified threshold, thecompiler inserts a __chkstk()
call into the function prologue. For a user-mode application to run correctly in Windows NT, the default threshold 4096 is required. To suppress all __chkstk()
calls, specify an artificially high threshold value such as /Gs999999
. The /Gs0
option has the same function as the /Ge
option and instructs the compiler to call __chkstk()
in every function. It might be advantageous to change the default value if the codeexecutes in an environment where the stack is fully committed or if thestack growth mechanism is otherwise not available.
For more information, refer to the /Gs
compilation option and thecheck_stack preprocessor pragma in the Visual C++ Books Online.