Share via


/GZ   (Catch Release-Build Errors in Debug Build)

OverviewHow Do ICompiler Options

Some bugs normally arise only when you switch to a Release build (/O1, /O2, /Ox, or /Og). You can use the /GZ option to enable run-time checks to catch these bugs in a Debug (/Od) build. /GZ is not compatible with /O1, /O2, /Ox, or /Og builds.

Note: Use of the /GZ option will disable any #pragma optimize statements in your code.

The /GZ option does the following:

  • Auto-initialization of local variables

  • Function pointer call stack validation

  • Call stack validation

See Creating a Release Build: Overview for more information on going from Debug builds to Release builds.

Auto-initialization of Local Variables

/GZ initializes all local variables not explicitly initialized by the program. It fills all memory used by these variables with 0xCC.

Assuming the stack is zero, code with uninitialized variables may fail with /GZ. Consider the following example. When compiled with /Od or /Ox without /GZ, this code may produce an access-violation exception, or it may appear to run correctly. When compiled with /Gz /Od, it always produces the exception. You can catch the exception in the debugger and get some hints about the exact location of failure.

#include <stdio.h>

void function1(char **pptr)
{
   printf("*pptr = 0x%X\n", *pptr);
   if(*pptr == NULL)
      *pptr = "Hello world\n";
}
void function2()
{
   int padding[10];          // force sub esp frame initialization
   char * p;               // p is uninitialized

   function1(&p);
   puts(p);
}

void main()
{
   int padding[1000];        // Force clean stack page
   function2();
}

Function Pointer Call Stack Validation

The stack pointer (ESP) is checked to make sure it is the same before and after the call through the function pointer. This can catch any mismatch between the calling function’s cleanup (__cdecl) calling convention and called function’s cleanup calling conventions (__sdtcall, __fastcall, __thiscall) when calling through a function pointer.

This example works with /Od, fails with /Ox and raises an exception with /GZ. You get an exception breakpoint pop-up if compiled with a release version of the run-time library and a more meaningful message when compiled with the debug version of the run-time library.

void __stdcall function1(char *p)
{
   puts(p);
}

typedef void (*PFUNC)(char *);

main()
{
   PFUNC pfunction1 = (PFUNC)function1;

   pfunction1("Hello world\n");
}

A more realistic scenario occurs with GetProcAddress:

#include <windows.h>
void function1()
{
   typedef int (*FPTYPE)(int); // should be "int(__stdcall *)(int)"
   FPTYPE fp;
   int ret = 0;
   HINSTANCE hinst = LoadLibrary( "your.dll" );
   fp = (FPTYPE) GetProcAddress( hinst, "SomeFunc");

   ret = (*fp)(22);      // ESP invalid after this call.
                        // but it is caught upon return

   FreeLibrary( hinst );

   // and also caught at function exit
}

Function pointer call stack validation will also catch cases where a function prototype has the incorrect number of parameters if the function uses the __stdcall, __fastcall, or __thiscall calling convention. __cdecl cases are not caught because the caller does both the pushes and the pops of parameters.

Call Stack Validation

The stack pointer (ESP) is checked at the end of the function to see that the stack pointer has not been changed. This can catch cases where ESP is corrupted in inline assembly or a non-pointer function's calling convention is declared incorrectly.