I noticed the EBP pointer on top of the stack frame is not quite right when a bare simple MFC's main application object is instantiated and this happens only in release build. Here is the code.
CMDIDemoApp::CMDIDemoApp()
{
// support Restart Manager
m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_ALL_ASPECTS;
#ifdef _MANAGED
// If the application is built using Common Language Runtime support (/clr):
// 1) This additional setting is needed for Restart Manager support to work properly.
// 2) In your project, you must add a reference to System.Windows.Forms in order to build.
System::Windows::Forms::Application::SetUnhandledExceptionMode(System::Windows::Forms::UnhandledExceptionMode::ThrowException);
#endif
// breakpoint here, EBP is correct
// TODO: replace application ID string below with unique ID string; recommended
// format for string is CompanyName.ProductName.SubProduct.VersionInformation
SetAppID(_T("MDIDemo.AppID.NoVersion"));
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
// The one and only CMDIDemoApp object
CMDIDemoApp theApp; // breakpoint here, EBP is not what I would expect
// CMDIDemoApp initialization
BOOL CMDIDemoApp::InitInstance()
{
// InitCommonControlsEx() is required on Windows XP if an application
// manifest specifies use of ComCtl32.dll version 6 or later to enable
// visual styles. Otherwise, any window creation will fail.
INITCOMMONCONTROLSEX InitCtrls; // breakpoint here again EBP is good
InitCtrls.dwSize = sizeof(InitCtrls);
// Set this to include all the common control classes you want to use
// in your application.
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
// Initialize OLE libraries
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
EnableTaskbarInteraction(FALSE);
// AfxInitRichEdit2() is required to use RichEdit control
// AfxInitRichEdit2();
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need
// Change the registry key under which our settings are stored
// TODO: You should modify this string to be something appropriate
// such as the name of your company or organization
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
LoadStdProfileSettings(4); // Load standard INI file options (including MRU)
// Register the application's document templates. Document templates
// serve as the connection between documents, frame windows and views
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_MDIDemoTYPE,
RUNTIME_CLASS(CMDIDemoDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CMDIDemoView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
{
delete pMainFrame;
return FALSE;
}
m_pMainWnd = pMainFrame;
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line. Will return FALSE if
// app was launched with /RegServer, /Register, /Unregserver or /Unregister.
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The main window has been initialized, so show and update it
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
return TRUE;
}
The stack at WinDbg is the following at breakpoint at line CMDIDemoApp theApp;
0:000> k
# ChildEBP RetAddr
00 0049fbdc 638381cd MDIDemo!`dynamic initializer for 'theApp'' [e:\projects\vs2015 projects\mdidemo\mdidemo\mdidemo.cpp @ 55]
01 0049fbf8 001826c5 ucrtbase!_initterm+0x6d
02 0049fc3c 765d336a MDIDemo!__scrt_common_main_seh+0x7b [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 221]
03 0049fc48 76f59902 kernel32!BaseThreadInitThunk+0xe
04 0049fc88 76f598d5 ntdll!__RtlUserThreadStart+0x70
05 0049fca0 00000000 ntdll!_RtlUserThreadStart+0x1b
0:000> dc 0049fbdc
0049fbdc 00000e00 638381cd 00000000 00000000 .......c........
0049fbec 7efde000 00000005 1c53dd92 0049fc3c ...~......S.<.I.
0049fbfc 001826c5 00184604 00184618 04b2f942 .&...F...F..B...
0049fc0c 00000000 00000000 7efde000 0049fc00 ...........~..I.
0049fc1c 00000000 00000000 0049fc08 000000fd ..........I.....
0049fc2c 0049fc78 00182c2a 04e3677e 00000000 x.I.*,..~g......
0049fc3c 0049fc48 765d336a 7efde000 0049fc88 H.I.j3]v...~..I.
0049fc4c 76f59902 7efde000 6fe6f10a 00000000 ...v...~...o....
0:000> dc 0049fbf8
0049fbf8 0049fc3c 001826c5 00184604 00184618 <.I..&...F...F..
0049fc08 04b2f942 00000000 00000000 7efde000 B..............~
0049fc18 0049fc00 00000000 00000000 0049fc08 ..I...........I.
0049fc28 000000fd 0049fc78 00182c2a 04e3677e ....x.I.*,..~g..
0049fc38 00000000 0049fc48 765d336a 7efde000 ....H.I.j3]v...~
0049fc48 0049fc88 76f59902 7efde000 6fe6f10a ..I....v...~...o
0049fc58 00000000 00000000 7efde000 00000000 ...........~....
0049fc68 00000000 00000000 0049fc54 00000000 ........T.I.....
0:000> dc 0049fc3c
0049fc3c 0049fc48 765d336a 7efde000 0049fc88 H.I.j3]v...~..I.
0049fc4c 76f59902 7efde000 6fe6f10a 00000000 ...v...~...o....
0049fc5c 00000000 7efde000 00000000 00000000 .......~........
0049fc6c 00000000 0049fc54 00000000 ffffffff ....T.I.........
0049fc7c 76f958c5 195bcba2 00000000 0049fca0 .X.v..[.......I.
0049fc8c 76f598d5 001827b2 7efde000 00000000 ...v.'.....~....
0049fc9c 00000000 00000000 00000000 001827b2 .............'..
0049fcac 7efde000 00000000 00000000 00000000 ...~............
Notice that dc 0049fbdc
should have yielded 0049fbf8
but instead it is 00000e00
. Why does this happens and only in release build? It is indeed correct in debug build.
Also the EFP are correct when I set breakpoints in constructor (CMDIDemoApp::CMDIDemoApp()
) or in any other function like CMDIDemoApp::InitInstance()
. So it's like correct in any function but not quite right when the global object is declared!
This is different from my earlier question when EFP is simply incorrect even in regular functions like constructor and InitIntance() in that project. The purpose of this question is to provide this extra information because in this case we pretty know the code is not broken, it's a default mfc application without any change. This also qualifies as a separate question.
Update
The assembly code at line CMDIDemoApp theApp
is:
0:000> u
MDIDemo!`dynamic initializer for 'theApp'' [e:\projects\vs2015 projects\mdidemo\mdidemo\mdidemo.cpp @ 55]:
00ee1000 55 push ebp
00ee1001 8bec mov ebp,esp
00ee1003 68cc000000 push 0CCh
00ee1008 b9a892ee00 mov ecx,offset MDIDemo!theApp (00ee92a8)
00ee100d e86e0e0000 call MDIDemo!CMDIDemoApp::__autoclassinit2 (00ee1e80)
00ee1012 b9a892ee00 mov ecx,offset MDIDemo!theApp (00ee92a8)
00ee1017 e8a4050000 call MDIDemo!CMDIDemoApp::CMDIDemoApp (00ee15c0)
00ee101c 680040ee00 push offset MDIDemo!`dynamic atexit destructor for 'theApp'' (00ee4000)
The constructor assembly is the following
MDIDemo!CMDIDemoApp::CMDIDemoApp:
00ee15c0 55 push ebp
00ee15c1 8bec mov ebp,esp
00ee15c3 6aff push 0FFFFFFFFh
00ee15c5 68a83eee00 push offset MDIDemo!__scrt_stub_for_acrt_initialize+0x4c (00ee3ea8)
00ee15ca 64a100000000 mov eax,dword ptr fs:[00000000h]
00ee15d0 50 push eax
00ee15d1 51 push ecx
00ee15d2 a11490ee00 mov eax,dword ptr [MDIDemo!__security_cookie (00ee9014)]
00ee15d7 33c5 xor eax,ebp
00ee15d9 50 push eax
00ee15da 8d45f4 lea eax,[ebp-0Ch]
00ee15dd 64a300000000 mov dword ptr fs:[00000000h],eax
00ee15e3 894df0 mov dword ptr [ebp-10h],ecx
00ee15e6 6a00 push 0
00ee15e8 8b4df0 mov ecx,dword ptr [ebp-10h]