Im trying to run a function from a dll using cgo. The library is in c++ so I created a C header here are the functions defined and a .cpp file where is the implementation.
lib.h:
#ifdef __cplusplus
extern "C" {
#endif
void* LoadEngine(char*);
#ifdef __cplusplus
}
#endif
lib.cpp:
#include <Engine.h> //the library
void* LoadEngine(char *sn) {
Engine *e;
GetEngineObject(sn, &e); //function from the dll, here it fails
return (void*) e;
}
then calling it with:
package main
/*
#include "lib.h"
*/
import "C"
func main() {
e := C.LoadEngine(C.CString("foobar")
...
}
I have go1.12 windows/amd64
and mingw-w64-posix-seh
tried also mingw-w64-posix-sjlj, mingw-w64-win32-seh, mingw-w64-win32-sjlj, but the result is the same, or it doesn't compile at all
compiling it just with go build and:
#cgo windows CFLAGS: -IC:/Engine/Inc
#cgo windows CPPFLAGS: -IC:/Engine/Inc
#cgo windows LDFLAGS: -LC:/Engine/Bin64 -lEngine -lEngineObj -lole32 -loleaut32 -luuid
On win8 it runs ok, but on win10+ it doesn't work, if the function gets to calling the GetEngineObject which is from the dll, it fails with:
Exception 0x40010006 0x1 0x8deb90 0x7ffbc9ada388
PC=0x7ffbc9ada388
runtime: unknown pc 0x7ffbc9ada388
stack: frame={sp:0x8dea80, fp:0x0} stack=[0x0,0x8dfdf0)
00000000008de980: 0000000002000002 000000002a080321
00000000008de990: 0000000090000191 0000000000000321
00000000008de9a0: 0000000500000008 0000000500000000
00000000008de9b0: feeefeeefeeefeee 00000000024c0150
00000000008de9c0: 0000000000000000 0000000000000000
00000000008de9d0: 00000000024c0000 00007ffbccb730ac
00000000008de9e0: 0000000002680000 00000000024c1e30
00000000008de9f0: 00000000026a2d90 00000000026ad480
00000000008dea00: 0000000000000df1 0000000000000000
00000000008dea10: 0000000000000df1 0000000000000df1
00000000008dea20: 0000000000001bf8 0000000000000000
00000000008dea30: 0000000090000191 0000000000000003
00000000008dea40: 0000bcc3daf1f4cb 00000000026a2d00
00000000008dea50: 0000000000000000 000002fffb442d78
00000000008dea60: 0000000000000000 00000000008dfb00
00000000008dea70: 0000000000000020 00007ffbc9ada388
00000000008dea80: <00000000024c0000 000000002fbe1490
00000000008dea90: 00000000008df970 00000000ffffffff
00000000008deaa0: 0000000040010006 0000000000000000
00000000008deab0: 00007ffbc9ada388 0000000000000002
00000000008deac0: 0000000000000001 00000000008deb90
00000000008dead0: 00000000001b0150 0000000000800000
00000000008deae0: 00000000026ad480 0000000000000000
00000000008deaf0: 00000000001b10b0 0000000000690000
00000000008deb00: 00000000024c0000 00007ffbccbbcafa
00000000008deb10: 00000000024c0000 00007ffbccb76ff8
00000000008deb20: 00000000026a2d90 00000000026a9c30
00000000008deb30: 0000000000000000 0000000000000001
00000000008deb40: 00002c98037a65f6 000000000000001f
00000000008deb50: 00000000026a9430 000000002fd8a367
00000000008deb60: 0000000000006000 00000000024c0000
00000000008deb70: 0000000000000df1 0000000000000000
runtime: unknown pc 0x7ffbc9ada388
stack: frame={sp:0x8dea80, fp:0x0} stack=[0x0,0x8dfdf0)
00000000008de980: 0000000002000002 000000002a080321
00000000008de990: 0000000090000191 0000000000000321
00000000008de9a0: 0000000500000008 0000000500000000
00000000008de9b0: feeefeeefeeefeee 00000000024c0150
00000000008de9c0: 0000000000000000 0000000000000000
00000000008de9d0: 00000000024c0000 00007ffbccb730ac
00000000008de9e0: 0000000002680000 00000000024c1e30
00000000008de9f0: 00000000026a2d90 00000000026ad480
00000000008dea00: 0000000000000df1 0000000000000000
00000000008dea10: 0000000000000df1 0000000000000df1
00000000008dea20: 0000000000001bf8 0000000000000000
00000000008dea30: 0000000090000191 0000000000000003
00000000008dea40: 0000bcc3daf1f4cb 00000000026a2d00
00000000008dea50: 0000000000000000 000002fffb442d78
00000000008dea60: 0000000000000000 00000000008dfb00
00000000008dea70: 0000000000000020 00007ffbc9ada388
00000000008dea80: <00000000024c0000 000000002fbe1490
00000000008dea90: 00000000008df970 00000000ffffffff
00000000008deaa0: 0000000040010006 0000000000000000
00000000008deab0: 00007ffbc9ada388 0000000000000002
00000000008deac0: 0000000000000001 00000000008deb90
00000000008dead0: 00000000001b0150 0000000000800000
00000000008deae0: 00000000026ad480 0000000000000000
00000000008deaf0: 00000000001b10b0 0000000000690000
00000000008deb00: 00000000024c0000 00007ffbccbbcafa
00000000008deb10: 00000000024c0000 00007ffbccb76ff8
00000000008deb20: 00000000026a2d90 00000000026a9c30
00000000008deb30: 0000000000000000 0000000000000001
00000000008deb40: 00002c98037a65f6 000000000000001f
00000000008deb50: 00000000026a9430 000000002fd8a367
00000000008deb60: 0000000000006000 00000000024c0000
00000000008deb70: 0000000000000df1 0000000000000000
goroutine 1 [syscall]:
path/to/package._Cfunc_LoadEngine(0x23a2c20, 0x0)
_cgo_gotypes.go:518 +0x51
path/to/package.Load(0x4d05ad, 0x1d, 0x0, 0x0, 0x0)
C:/Users/microo8/Documents/workspace/src/path/to/package/fre.go:55 +0x89
main.main()
W:/Workspace/src/path/to/package/test/test.go:12 +0x41
rax 0x3e00003e
rbx 0x2fbe1490
rcx 0xfffffffffffffffe
rdi 0xffffffff
rsi 0x8df970
rbp 0x20
rsp 0x8dea80
r8 0x2fca2793
r9 0x3a0f4c0
r10 0x2fca2701
r11 0x26aadb0
r12 0x8dfb00
r13 0x0
r14 0x2fffb442d78
r15 0x0
rip 0x7ffbc9ada388
rflags 0x202
cs 0x33
fs 0x53
gs 0x2b
I've also tried to catch the exception with mingw's __try1 and __except1 but it fails anyways. Tried different mingw versions, using -ldflags="-linkmode internal" but this doesn't compile and -ldflags="-linkmode external" makes also this exception.
EDIT: also tried to call the LoadEngine function (from lib.h) from a C program and it works fine. Compiled the lib.cpp with g++ to lib.o. And linked it to test.c where I just called the LoadEngine just from main function. So maybe there is something wrong how go links the library to cgo?
For some reason on win10+ it would raise the DBG_PRINTEXCEPTION_C. Which is caused by the OutputDebugStringW and no debugger is listening.
I think normally it would be done with microsoft's __try and __except, but in MinGW is just __try1 and __except1, which I think only works on 32bit systems (very little documentation is to that)
But I found, that you can add a Exception Handler
so now lib.cpp:
#include <Engine.h> //the library
#ifdef _WIN32
#include <windows.h>
LONG WINAPI VectoredHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) {
UNREFERENCED_PARAMETER(ExceptionInfo);
return EXCEPTION_CONTINUE_EXECUTION; //just continue
}
#endif
void* LoadEngine(char *sn) {
#ifdef _WIN32
PVOID handler = AddVectoredContinueHandler(1, VectoredHandler);
#endif
Engine *e;
GetEngineObject(sn, &e);
#ifdef _WIN32
RemoveVectoredContinueHandler(handler);
#endif
return (void*) e;
}
Because using the library starts with loading the engine and ends with unloading it, and the engine is a singleton, I've just made the PVOID handler also a global singleton and the AddVectoredContinueHandler is in LoadEngine and RemoveVectoredContinueHandler is in UnloadEngine.
Related
When using [plog][1] on Windows XP. In this case, the code is:
void LogInit(void)
{
static plog::RollingFileAppender<plog::TxtFormatter> fileAppender("log.log");
Using Visual Studio 2019 but the project uses the platform toolset Visual Studio 2017 - Windows XP (v141_XP)
The output assembly is:
; COMDAT _LogInit
_TEXT SEGMENT
_status$1$ = -516 ; size = 4
_appender$66 = -516 ; size = 4
$T65 = -512 ; size = 256
$T64 = -512 ; size = 256
$T62 = -512 ; size = 256
$T60 = -512 ; size = 256
$T58 = -256 ; size = 256
$T57 = -256 ; size = 256
$T41 = -256 ; size = 256
_LogInit PROC ; COMDAT
; 108 : {
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 83 e4 f8 and esp, -8 ; fffffff8H
; 109 : static plog::RollingFileAppender<plog::TxtFormatter> fileAppender("log.log");
00006 64 a1 00 00 00
00 mov eax, DWORD PTR fs:__tls_array
0000c 81 ec 04 02 00
00 sub esp, 516 ; 00000204H
00012 8b 0d 00 00 00
00 mov ecx, DWORD PTR __tls_index
00018 53 push ebx
00019 56 push esi
0001a 8b 34 88 mov esi, DWORD PTR [eax+ecx*4]
The null pointer is because EAX (__tls_array) and ECX (__tls_index) area both null. Output from WinDbg:
TGLOBALFLAG: 70
APPLICATION_VERIFIER_FLAGS: 0
CONTEXT: (.ecxr)
eax=00000000 ebx=00000000 ecx=00000000 edx=7c90e4f4 esi=0012f624 edi=00000000
eip=1000366a esp=001afda4 ebp=001affb4 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010216
LogTest!LogInit+0x1a:
1000366a 8b3488 mov esi,dword ptr [eax+ecx*4] ds:0023:00000000=????????
Resetting default scope
EXCEPTION_RECORD: (.exr -1)
ExceptionAddress: 1000366a (LogTest!LogInit+0x0000001a)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00000000
Attempt to read from address 00000000
PROCESS_NAME: notepad.exe
READ_ADDRESS: 00000000
ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.
EXCEPTION_CODE_STR: c0000005
EXCEPTION_PARAMETER1: 00000000
EXCEPTION_PARAMETER2: 00000000
FAULTING_LOCAL_VARIABLE_NAME: fileAppender
STACK_TEXT:
001affb4 7c80b713 00000000 00000000 0012f624 LogTest!LogInit+0x1a
001affec 00000000 10003650 00000000 00000000 kernel32!BaseThreadStart+0x37
STACK_COMMAND: ~1s; .ecxr ; kb
FAULTING_SOURCE_LINE: d:\test\logtest.cpp
FAULTING_SOURCE_FILE: d:\test\logtest.cpp
FAULTING_SOURCE_LINE_NUMBER: 109
FAULTING_SOURCE_CODE:
105:
106: // This is an example of an exported function.
107: LogInit_API void LogInit(void)
108: {
> 109: static plog::RollingFileAppender<plog::TxtFormatter> fileAppender(";pg.log");
110: plog::init(plog::info, &fileAppender);
111:
112:
113:
114:
SYMBOL_NAME: LogTest!LogInit+1a
MODULE_NAME: LogTest
IMAGE_NAME: LogTest.dll
FAILURE_BUCKET_ID: NULL_POINTER_READ_c0000005_LogTest.dll!LogInit
OS_VERSION: 5.1.2600.5512
BUILDLAB_STR: xpsp
OSPLATFORM_TYPE: x86
OSNAME: Windows XP
FAILURE_ID_HASH: {0218fa42-bce4-328f-5683-a7e3657927fc}
Followup: MachineOwner
---------
Code for affected class is:
namespace plog
{
template<class Formatter, class Converter = NativeEOLConverter<UTF8Converter> >
class PLOG_LINKAGE_HIDDEN RollingFileAppender : public IAppender
{
public:
RollingFileAppender(const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0)
: m_fileSize()
, m_maxFileSize()
, m_maxFiles(maxFiles)
, m_firstWrite(true)
{
setFileName(fileName);
setMaxFileSize(maxFileSize);
}
#ifdef _WIN32
RollingFileAppender(const char* fileName, size_t maxFileSize = 0, int maxFiles = 0)
: m_fileSize()
, m_maxFileSize()
, m_maxFiles(maxFiles)
, m_firstWrite(true)
{
setFileName(fileName);
setMaxFileSize(maxFileSize);
}
#endif
virtual void write(const Record& record)
{
util::MutexLock lock(m_mutex);
if (m_firstWrite)
{
openLogFile();
m_firstWrite = false;
}
else if (m_maxFiles > 0 && m_fileSize > m_maxFileSize && static_cast<size_t>(-1) != m_fileSize)
{
rollLogFiles();
}
size_t bytesWritten = m_file.write(Converter::convert(Formatter::format(record)));
if (static_cast<size_t>(-1) != bytesWritten)
{
m_fileSize += bytesWritten;
}
}
void setFileName(const util::nchar* fileName)
{
util::MutexLock lock(m_mutex);
util::splitFileName(fileName, m_fileNameNoExt, m_fileExt);
m_file.close();
m_firstWrite = true;
}
#ifdef _WIN32
void setFileName(const char* fileName)
{
setFileName(util::toWide(fileName).c_str());
}
#endif
void setMaxFiles(int maxFiles)
{
m_maxFiles = maxFiles;
}
void setMaxFileSize(size_t maxFileSize)
{
m_maxFileSize = (std::max)(maxFileSize, static_cast<size_t>(1000)); // set a lower limit for the maxFileSize
}
void rollLogFiles()
{
m_file.close();
util::nstring lastFileName = buildFileName(m_maxFiles - 1);
util::File::unlink(lastFileName.c_str());
for (int fileNumber = m_maxFiles - 2; fileNumber >= 0; --fileNumber)
{
util::nstring currentFileName = buildFileName(fileNumber);
util::nstring nextFileName = buildFileName(fileNumber + 1);
util::File::rename(currentFileName.c_str(), nextFileName.c_str());
}
openLogFile();
m_firstWrite = false;
}
private:
void openLogFile()
{
util::nstring fileName = buildFileName();
m_fileSize = m_file.open(fileName.c_str());
if (0 == m_fileSize)
{
size_t bytesWritten = m_file.write(Converter::header(Formatter::header()));
if (static_cast<size_t>(-1) != bytesWritten)
{
m_fileSize += bytesWritten;
}
}
}
util::nstring buildFileName(int fileNumber = 0)
{
util::nostringstream ss;
ss << m_fileNameNoExt;
if (fileNumber > 0)
{
ss << '.' << fileNumber;
}
if (!m_fileExt.empty())
{
ss << '.' << m_fileExt;
}
return ss.str();
}
private:
util::Mutex m_mutex;
util::File m_file;
size_t m_fileSize;
size_t m_maxFileSize;
int m_maxFiles;
util::nstring m_fileExt;
util::nstring m_fileNameNoExt;
bool m_firstWrite;
};
}
Is there code or compiler settings that can be modified to fix/remove the references to __tls_array / __tls_index.
This occurs in both debug & release builds.
[1]: https://github.com/SergiusTheBest/plog
Setting compiler option /Zc:threadSafeInit- removes the references to __tls_array and __tls_index and stops the access violation crash.
Microsoft documentation here mentions:
In the C++11 standard, block scope variables with static or thread
storage duration must be zero-initialized before any other
initialization takes place. Initialization occurs when control first
passes through the declaration of the variable. If an exception is
thrown during initialization, the variable is considered
uninitialized, and initialization is re-attempted the next time
control passes through the declaration. If control enters the
declaration concurrently with initialization, the concurrent execution
blocks while initialization is completed. The behavior is undefined if
control re-enters the declaration recursively during initialization.
By default, Visual Studio starting in Visual Studio 2015 implements
this standard behavior. This behavior may be explicitly specified by
setting the /Zc:threadSafeInit compiler option.
The /Zc:threadSafeInit compiler option is on by default. The
/permissive- option does not affect /Zc:threadSafeInit.
Thread-safe initialization of static local variables relies on code
implemented in the Universal C run-time library (UCRT). To avoid
taking a dependency on the UCRT, or to preserve the non-thread-safe
initialization behavior of versions of Visual Studio prior to Visual
Studio 2015, use the /Zc:threadSafeInit- option. If you know that
thread-safety is not required, use this option to generate slightly
smaller, faster code around static local declarations.
Thread-safe static local variables use thread-local storage (TLS)
internally to provide efficient execution when the static has already
been initialized. The implementation of this feature relies on Windows
operating system support functions in Windows Vista and later
operating systems. Windows XP, Windows Server 2003, and older
operating systems do not have this support, so they do not get the
efficiency advantage. These operating systems also have a lower limit
on the number of TLS sections that can be loaded. Exceeding the TLS
section limit can cause a crash. If this is a problem in your code,
especially in code that must run on older operating systems, use
/Zc:threadSafeInit- to disable the thread-safe initialization code.
The background of the issue is in an already answered question, here
I have a symbol that I want to use in a shared library but despite being listed as a T defined symbol via nm, F (function) via objdump, when I try to access it using dlopen() or via Python (ctypes.CDLL), it seemingly does not exist or is not defined.
I was reading here that this could be related to dependencies.
Output of ldd:
linux#host:~$ ldd ./compiled_program
linux-vdso.so.1 (0x00007ffde1fdf000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3381e7c000)
/lib64/ld-linux-x86-64.so.2 (0x00007f338246f000)
Output of dn -DC:
linux#host:~$ nm -DC ./compiled_program
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w __cxa_finalize
w __gmon_start__
U __libc_start_main
U __stack_chk_fail
U printf
U putchar
U puts
U rand
U srand
U time
With my limited knowlege of C, I am unable to tell if any of these might be unresolved dependencies and where to look for which libraries to load, as the answer suggests, to include them. Is it as simple as finding the library and adding an #include <...> or do I have to locate the library and load it with dlopen()? The answer seems to suggest the latter. I appreciate that I am completely flailing in the dark here!
My current dlopen code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
int main(int argc, char** argv) {
void *handle;
void (*func_in_question)(const char*);
handle = dlopen("./compiled_program", RTLD_LAZY);
if (!handle) {
/* fail to load the library */
fprintf(stderr, "Error: %s\n", dlerror());
return EXIT_FAILURE;
}
*(void**)(&func_in_question) = dlsym(handle, "function_I_would_like_to_use");
if (!func_in_question) {
/* no such symbol */
fprintf(stderr, "Error: %s\n", dlerror());
dlclose(handle);
return EXIT_FAILURE;
}
func_in_question(argv[1]);
dlclose(handle);
return EXIT_SUCCESS;
}
comes back with the undefined symbol error.
I would be grateful for any pointers on, a) any other reasons why I might not be able to access this symbol?
and b) if my half-baked suspicion about dependencies is anywhere near to making sense!
EDIT: copying error message from original question linked above:
Error: ./compiled_program: undefined symbol: function_I_would_like_to_use
and the output of nm --defined-only ./compiled_program from original
question:
0000000000200d98 d _DYNAMIC
0000000000200f88 d _GLOBAL_OFFSET_TABLE_
0000000000000ba0 R _IO_stdin_used
0000000000000d64 r __FRAME_END__
0000000000000bfc r __GNU_EH_FRAME_HDR
0000000000201010 D __TMC_END__
0000000000201010 B __bss_start
0000000000201000 D __data_start
00000000000007d0 t __do_global_dtors_aux
0000000000200d90 t __do_global_dtors_aux_fini_array_entry
0000000000201008 D __dso_handle
0000000000200d88 t __frame_dummy_init_array_entry
0000000000200d90 t __init_array_end
0000000000200d88 t __init_array_start
0000000000000b90 T __libc_csu_fini
0000000000000b20 T __libc_csu_init
0000000000201010 D _edata
0000000000201018 B _end
0000000000000b94 T _fini
0000000000000660 T _init
0000000000000710 T _start
0000000000201010 b completed.7696
0000000000201000 W data_start
0000000000000740 t deregister_tm_clones
0000000000000810 t frame_dummy
0000000000000a92 T main
000000000000081a T function_I_would_like_to_use \\ << this one
0000000000000780 t register_tm_clones
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]
I want to know whether it is possible to expose goto label within a function to symbol table from C/C++
For instance, I want to make ret label of the following snippet appeared from the symbol table and can be referred using standard APIs such as dlsym().
Thanks for your help in advance!
#include <stdio.h>
int main () {
void *ret_p = &&ret;
printf("ret: %p\n", ret_p);
goto *ret_p;
return 1;
ret:
return 0;
}
Thanks to Marc Glisse's comment which is about using inline asm that specifies label, I could come up with a workaround for the question. The following example code snippet shows how I solved the problem.
#include <stdio.h>
int main () {
void *ret_p = &&ret;
printf("ret: %p\n", ret_p);
goto *ret_p;
return 1;
ret:
asm("RET:")
return 0;
}
This will add a symbol table entry as follows.
jikk#sos15-32:~$ gcc -Wl,--export-dynamic t.c -ldl
jikk#sos15-32:~$ readelf -s a.out
39: 08048620 0 FUNC LOCAL DEFAULT 13 __do_global_ctors_aux
40: 00000000 0 FILE LOCAL DEFAULT ABS t.c
41: 0804858a 0 NOTYPE LOCAL DEFAULT 13 RET
42: 08048612 0 FUNC LOCAL DEFAULT 13 __i686.get_pc_thunk.bx
43: 08049f20 0 OBJECT LOCAL DEFAULT 19 __DTOR_END__
jikk#sos15-32:~$ ./a.out
ret: 0x804858a
I'll further test this workaround the verify whether this produces any unexpected side effects.
Thanks
I would like to create process dump file at the moment when access violation that will not be handled occurred.
Currently, I have registered mine, unhandled exception callback with:
SetUnhandledExceptionFilter(CustomUnhandledExceptionFilter);
CustomUnhandledExceptionFilter creates dump file and prints call stack.
But this approach has one flaw - it is done when AV has already happened, AV exception is thrown and has not been handled by thread it happened in. Unhandled exception callback is called when exception is about to leave thread scope and dump created at this point has no local variables of function that exception occurred since stack pointer is lost.
Is there an way to over come this? I would like to take a look at stack of thread that got AV at the moment AV happened.
VS
#include "stdafx.h"
#include <windows.h>
#include <dbghelp.h>
LONG WINAPI MyUnhandledExceptionFilter(EXCEPTION_POINTERS *ExceptionInfo)
{
HANDLE hFile = CreateFile(
L"proc.dmp",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
MINIDUMP_EXCEPTION_INFORMATION mei;
mei.ThreadId = GetCurrentThreadId();
mei.ClientPointers = TRUE;
mei.ExceptionPointers = ExceptionInfo;
MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
MiniDumpNormal,
&mei,
NULL,
NULL);
return EXCEPTION_EXECUTE_HANDLER;
}
int _tmain(int argc, _TCHAR* argv[])
{
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
int* p = NULL;
*p = 1;
return 0;
}
WinDbg
Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
Copyright (c) Microsoft Corporation. All rights reserved.
Loading Dump File [D:\Documents\Visual Studio 2012\Projects\Test\Debug\proc.dmp]
User Mini Dump File: Only registers, stack and portions of memory are available
Symbol search path is: SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
Windows 7 Version 7600 UP Free x86 compatible
Product: WinNt, suite: SingleUserTS
Machine Name:
Debug session time: Sat Dec 15 19:29:31.000 2012 (UTC + 4:00)
System Uptime: not available
Process Uptime: not available
......................
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(8d8.1084): Access violation - code c0000005 (first/second chance not available)
eax=fffffffd ebx=005d0d78 ecx=0022f070 edx=778964f4 esi=005d0d38 edi=0022f110
eip=778964f4 esp=0022edd0 ebp=0022ede0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!KiFastSystemCallRet:
778964f4 c3 ret
0:000> .ecxr
eax=00000000 ebx=7ffdf000 ecx=0022fa30 edx=778964f4 esi=0022fcf4 edi=0022fdcc
eip=0124157c esp=0022fcf4 ebp=0022fdcc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
*** WARNING: Unable to verify checksum for dump.exe
dump!wmain+0x3c:
0124157c c70001000000 mov dword ptr [eax],1 ds:0023:00000000=????????
0:000> dv
argc = 0n1
argv = 0x00280e38
p = 0x00000000
0:000> kb
*** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr Args to Child
0022fdcc 01241b19 00000001 00280e38 0027f9c8 dump!wmain+0x3c [d:\documents\visual studio 2012\projects\test\dump\dump.cpp # 35]
0022fe1c 01241d0d 0022fe30 76051194 7ffdf000 dump!__tmainCRTStartup+0x199 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c # 533]
0022fe24 76051194 7ffdf000 0022fe70 778ab495 dump!wmainCRTStartup+0xd [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c # 377]
0022fe30 778ab495 7ffdf000 7676831a 00000000 kernel32!BaseThreadInitThunk+0xe
0022fe70 778ab468 0124107d 7ffdf000 00000000 ntdll!__RtlUserThreadStart+0x70
0022fe88 00000000 0124107d 7ffdf000 00000000 ntdll!_RtlUserThreadStart+0x1b
0:000> u .
dump!wmain+0x3c [d:\documents\visual studio 2012\projects\test\dump\dump.cpp # 35]:
0124157c c70001000000 mov dword ptr [eax],1
01241582 33c0 xor eax,eax
01241584 5f pop edi
01241585 5e pop esi
01241586 5b pop ebx
01241587 81c4cc000000 add esp,0CCh
0124158d 3bec cmp ebp,esp
0124158f e8c0fbffff call dump!ILT+335(__RTC_CheckEsp) (01241154)
0:000> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
GetPageUrlData failed, server returned HTTP status 404
URL requested: http://watson.microsoft.com/StageOne/dump_exe/0_0_0_0/50cc9743/dump_exe/0_0_0_0/50cc9743/c0000005/0001157c.htm?Retriage=1
FAULTING_IP:
dump!wmain+3c [d:\documents\visual studio 2012\projects\test\dump\dump.cpp # 35]
0124157c c70001000000 mov dword ptr [eax],1
EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 0124157c (dump!wmain+0x0000003c)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000001
Parameter[1]: 00000000
Attempt to write to address 00000000
DEFAULT_BUCKET_ID: NULL_POINTER_WRITE
PROCESS_NAME: dump.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.
EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.
EXCEPTION_PARAMETER1: 00000001
EXCEPTION_PARAMETER2: 00000000
WRITE_ADDRESS: 00000000
FOLLOWUP_IP:
dump!wmain+3c [d:\documents\visual studio 2012\projects\test\dump\dump.cpp # 35]
0124157c c70001000000 mov dword ptr [eax],1
MOD_LIST: <ANALYSIS/>
FAULTING_THREAD: 00001084
PRIMARY_PROBLEM_CLASS: NULL_POINTER_WRITE
BUGCHECK_STR: APPLICATION_FAULT_NULL_POINTER_WRITE
LAST_CONTROL_TRANSFER: from 01241b19 to 0124157c
STACK_TEXT:
0022fdcc 01241b19 00000001 00280e38 0027f9c8 dump!wmain+0x3c [d:\documents\visual studio 2012\projects\test\dump\dump.cpp # 35]
0022fe1c 01241d0d 0022fe30 76051194 7ffdf000 dump!__tmainCRTStartup+0x199 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c # 533]
0022fe24 76051194 7ffdf000 0022fe70 778ab495 dump!wmainCRTStartup+0xd [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c # 377]
0022fe30 778ab495 7ffdf000 7676831a 00000000 kernel32!BaseThreadInitThunk+0xe
0022fe70 778ab468 0124107d 7ffdf000 00000000 ntdll!__RtlUserThreadStart+0x70
0022fe88 00000000 0124107d 7ffdf000 00000000 ntdll!_RtlUserThreadStart+0x1b
STACK_COMMAND: ~0s; .ecxr ; kb
FAULTING_SOURCE_CODE:
31: int _tmain(int argc, _TCHAR* argv[])
32: {
33: SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
34: int* p = NULL;
> 35: *p = 1;
36: return 0;
37: }
38:
SYMBOL_STACK_INDEX: 0
SYMBOL_NAME: dump!wmain+3c
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: dump
IMAGE_NAME: dump.exe
DEBUG_FLR_IMAGE_TIMESTAMP: 50cc9743
FAILURE_BUCKET_ID: NULL_POINTER_WRITE_c0000005_dump.exe!wmain
BUCKET_ID: APPLICATION_FAULT_NULL_POINTER_WRITE_dump!wmain+3c
WATSON_STAGEONE_URL: http://watson.microsoft.com/StageOne/dump_exe/0_0_0_0/50cc9743/dump_exe/0_0_0_0/50cc9743/c0000005/0001157c.htm?Retriage=1
Followup: MachineOwner
---------
Try using AddVectoredExceptionHandler. The thread context of the moment of exception is passed as an argument to the callback proc of this method.
To keep the exception context intact you can create the minidump from an outer process, which debugs the process of interest with DebugActiveProcess API and receives exception events as a part of debugging. Minidump creation preceding release of the debugee with ContinueDebugEvent preserves call stack and exception context.