I'm trying to understand how substituting a different entry point for WinMain works in the Microsoft toolchain.
I already found this question and it was super helpful, but one last detail is nagging at me.
The first time I changed the Linker>Advanced>Entry Point option in Visual Studio, I set it to main by mistake and my program compiled and ran fine. I realized it later and rebuilt the program with it set to mainCRTStartup, as the accepted answer in the linked question suggests, and didn't find anything different.
So, my question is: is there any difference at all between main and mainCRTStartup, and if so, what is the difference?
main() is the entrypoint of your C or C++ program. mainCRTStartup() is the entrypoint of the C runtime library. It initializes the CRT, calls any static initializers that you wrote in your code, then calls your main() function.
Clearly it is essential that both the CRT and your own initialization is performed first. You can suffer from pretty hard to diagnose bugs if that doesn't happen. Maybe you won't, it is a crap-shoot. Something you can test by pasting this code in a small C++ program:
class Foo {
public:
Foo() {
std::cout << "init done" << std::endl;
}
} TestInit;
If you change the entrypoint to "main" then you'll see that the constructor never gets called.
This is bad.
In VS2017,create a console C++ application:
#include "pch.h"
#include <iostream>
int func()
{
return 1;
}
int v = func();
int main()
{
}
set a breakpoint in main() and begin debug,then the call stack is like:
testCppConsole.exe!main() Line 8 C++
testCppConsole.exe!invoke_main() Line 78 C++
testCppConsole.exe!__scrt_common_main_seh() Line 288 C++
testCppConsole.exe!__scrt_common_main() Line 331 C++
testCppConsole.exe!mainCRTStartup() Line 17 C++
kernel32.dll!#BaseThreadInitThunk#12() Unknown
ntdll.dll!__RtlUserThreadStart() Unknown
ntdll.dll!__RtlUserThreadStart#8() Unknown
So the program entry point is mainCRTStartup,it finally calls the C entry point main(),and the value of v will be 1.
Now set Linker>Advanced>Entry Point to "main" and begin debug,now the call stack is:
> testCppConsole.exe!main() Line 8 C++
kernel32.dll!#BaseThreadInitThunk#12() Unknown
ntdll.dll!__RtlUserThreadStart() Unknown
ntdll.dll!__RtlUserThreadStart#8() Unknown
So main() become the program entry point,and for this time the value of v will be 0,because CRT init functions are not called at all,so func() won't be called.
Now modify the code to :
#include "pch.h"
#include <iostream>
extern "C" int mainCRTStartup();
extern "C" int entry()
{
return mainCRTStartup();
}
int func()
{
return 1;
}
int v = func();
int main()
{
}
and set Linker>Advanced>Entry Point to "entry" and begin debug,now the call stack is:
> testCppConsole.exe!main() Line 14 C++
testCppConsole.exe!invoke_main() Line 78 C++
testCppConsole.exe!__scrt_common_main_seh() Line 288 C++
testCppConsole.exe!__scrt_common_main() Line 331 C++
testCppConsole.exe!mainCRTStartup() Line 17 C++
testCppConsole.exe!entry() Line 10 C++
kernel32.dll!#BaseThreadInitThunk#12() Unknown
ntdll.dll!__RtlUserThreadStart() Unknown
ntdll.dll!__RtlUserThreadStart#8() Unknown
and v will be 1 again.Program entry point is entry(),it calls mainCRTStartup() which call CRT init funtions which calls func() to init v,and mainCRTStartup() finally calls main().
Related
1.FORTRAN source (main.for)
integer function mysum(a, b)
!DEC$ATTRIBUTES DLLEXPORT,STDCALL :: mysum
!DEC$ATTRIBUTES VALUE :: a, b
integer a,b
mysum = a + b
return
end function mysum
make dll
gfortran main.for -shared -o fordll.dll
call dll
#include <stdio.h>
#include <iostream>
#include <windows.h>
using namespace std;
typedef int(_stdcall * MYSUM)(int a, int b);
int main()
{
int a=10,b=20;
HINSTANCE hLibrary = LoadLibrary("fordll.dll");
if (hLibrary == NULL)
{
cout << "can't find the dll file" << endl;
return -1;
}
MYSUM fact = (MYSUM)GetProcAddress(hLibrary, "mysum");
if (fact == NULL)
{
cout << "can't find the function file." << endl;
return -2;
}
try
{
cout << fact(a,b);
}
catch(...)
{ }
FreeLibrary(hLibrary);
return 0;
}
ERROR
Exception Access Violation reading 0x0000000A
why? if the fortran source file is comppiled by Compad Visual fortran or Inter fortran, it works well. However, it doesn't work with gcc or gfortran. What's wrong?
You are using special directives to alter the calling conventions
!DEC$ATTRIBUTES DLLEXPORT,STDCALL :: mysum
!DEC$ATTRIBUTES VALUE :: a, b
However, these are only valid for the DEC compiler sand its descendant Intel Fortran.
GCC use !GCC$ directives instead. Use them, they are pretty much the same as the DEC ones. See https://gcc.gnu.org/onlinedocs/gfortran/ATTRIBUTES-directive.html#ATTRIBUTES-directive Just change coppy and paste the DEC directives and change DEC to GCC.
Alternatively, change the code to pass-by-reference and ditch the VALUE attribute. The STDCALL attribute is relevant for 32-bit Windows only.
In modern Fortran it is much better to use
integer function mysum(a, b) bind(C,name="mysum")
integer, value :: a, b
(ignoring the stdcall issue, which can be just deleted in the C++ code).
I have the following code in a DLL:
#pragma data_seg("ABC")
__declspec (dllexport) char abc[2000] = { 0 };
#pragma data_seg()
#pragma comment(linker, "-section:ABC,rws")
I have the following code in an executable:
extern "C" __declspec(dllimport) char abc[];
char *abcPtr = abc;
#define iVar0 (*(long *)(abcPtr))
int main()
{
printf("Value: %d %p\n", iVar0, &iVar0);
iVar0 = 66;
printf("Value: %d %p\n", iVar0, &iVar0);
char buffer[256];
scanf_s("%s", buffer, 256);
}
When I run the first instance of the program I get:
Value: 0 0FC2A000
Value: 66 0FC2A000
If I run a second instance I get the following because they are using the same shared section:
Value: 66 0FC2A000 <- Notice the value here is set
Value: 66 0FC2A000
However if I change the value in the first instance using Visual Studio debugger, I can see that it changed in the memory location; however, I cannot see the value change if I rerun the second instance.
Why is the debugger not able to write the actual shared (memory) section?
I got the same result as yours:
My understanding is that certain value was not shared in the shared memory during debugging.
https://blogs.msdn.microsoft.com/oldnewthing/20040804-00/?p=38253/
I am trying to run sample rsa/dsa code using libtomcrypt.
I have installed LibTomMath first as make install, as a result following files are created.
/usr/lib/libtommath.a
/usr/include/tommath.h
After that I installed libtomcrypt with LibTomMath as external library
CFLAGS="-DLTM_DESC -DUSE_LTM -I/usr/include" EXTRALIBS="/usr/lib/libtommath.a " make install
As a result following file is created
/usr/lib/libtomcrypt.a
I am not getting any error while running following command
CFLAGS="-DLTM_DESC -DUSE_LTM -I/usr/include" EXTRALIBS="/usr/lib/libtommath.a " make test
I have gone through this document libtomcrypt_installation and libtomcrypt_resolved to successfully compile using
gcc -DLTM_DESC rsa_make_key_example.c -o rsa -ltomcrypt
or
gcc rsa_make_key_example.c -o rsa -ltomcrypt
no compile error. However when I try to run, I got following error.
./rsa
LTC_ARGCHK 'ltc_mp.name != NULL' failure on line 34 of file src/pk/rsa/rsa_make_key.c
Aborted
Here is my sample rsa code
#include <tomcrypt.h>
#include <stdio.h>
int main(void) {
# ifdef USE_LTM
ltc_mp = ltm_desc;
# elif defined (USE_TFM)
ltc_mp = tfm_desc;
# endif
rsa_key key;
int err;
register_prng(&sprng_desc);
if ((err = rsa_make_key(NULL, find_prng("sprng"), 1024/8, 65537,&key)) != CRYPT_OK) {
printf("make_key error: %s\n", error_to_string(err));
return -1;
}
/* use the key ... */
return 0;
}
Here is my sample dsa code
#include <tomcrypt.h>
#include <stdio.h>
int main(void) {
# ifdef USE_LTM
ltc_mp = ltm_desc;
# elif defined (USE_TFM)
ltc_mp = tfm_desc;
# endif
int err;
register_prng(&sprng_desc);
dsa_key key;
if ((err = dsa_make_key(NULL, find_prng("sprng"), 20, 128,&key)) != CRYPT_OK) {
printf("make_key error: %s\n", error_to_string(err));
return -1;
}
/* use the key ... */
return 0;
}
Here is how I have compiled it successfully,
gcc dsa_make_key_example.c -o dsa -ltomcrypt
When I try to run the code , I am getting following error .
./dsa
segmentation fault
EDIT 1:
I investigated further and found the reason for segmentation fault
#ifdef LTC_MPI
#include <stdarg.h>
int ltc_init_multi(void **a, ...)
{
...
...
if (mp_init(cur) != CRYPT_OK) ---> This line causes segmentation fault
Where am I making mistakes ? How to resolve this problem to run these programs successfully?
I am using linux , gcc. Any help/link will be highly appreciated. Thanks in advance.
It's been a year or so since this was asked, but I have some component of an answer, and a workaround.
The reason mp_init fails is that the "math_descriptor" is uninitialized. mp_init is a defined as
#define mp_init(a) ltc_mp.init(a)
where ltc_mp is a global struct (of type ltc_math_descriptor) that holds pointers to the math routines.
There are several implementations of the math routines available, and a user can choose which they want. For whatever reason, there does not seem to be a default math implementation chosen for certain builds of libtomcrypt. Thus, the init member of ltc_mp is null, and we get the SIGSEGV.
Here is a manual workaround:
You can make your desired ltc_math_descriptor struct available to your main() routine by #defineing one of
LTM_DESC -- built-in math lib
TFM_DESC -- an external fast math package
GMP_DESC -- presumably a GNU MultiPrecision implementation?
Before #include <tomcrypt.h> (or by using -D on the command-line).
Whichever you choose, a corresponding object will be declared:
extern const ltc_math_descriptor ltm_desc;
extern const ltc_math_descriptor tfm_desc;
extern const ltc_math_descriptor gmp_desc;
To use it, manually copy it to the global math descriptor:
E.g., in my case, for the local math imlpementation,
ltc_mp = ltm_desc;
Now libtomcrypt works.
System info: Windows 7, MSVS 2010
The following is a simple program, in which I am testing how Call Stack option in debug works
#include<stdio.h>
#include "stdafx.h"
int main()
{
printf("hello"); //breakpoint
}
When I debug the control hits the break point and the Call Stack is:
testapp.exe!main() Line 10 C++
testapp.exe!__tmainCRTStartup() Line 555 + 0x19 bytes C
testapp.exe!mainCRTStartup() Line 371 C
kernel32.dll!75e7ed6c()
[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]
ntdll.dll!77a537eb()
ntdll.dll!77a537be()
How do I interpret this result? Ad what is __tmainCRTStartup()?
Update
Just checked, the same output in Call Stack even if I am having .c file instead of .cpp file.
The call stack is used to figure out which line of code the debugger is currently at. The top one is the current location.
In your example the relevant line is testapp.exe!main() Line 10 C++ which means it's stopped at a function called main() which is at Line 10 in your file. Normally this contains the filename too.
Paste this code into your file and see if the call stack makes more sense for you when you break:
int main()
{
apple();
}
void apple()
{
banana();
}
void banana()
{
printf("hello"); //breakpoint
}
I want to create a loadable DLL of some of my tcl methods. But I am not getting how to do this. For that I have taken a simple example of tcl api which adds two numbers and prints the sum. Now I want to create a loadable DLL for this to export this tcl functionality.
But I am not understanding how to do it in Visual Studio. I have written a C code which can call this tcl api and get the sum of two integers, but again I don't want it to do this way. I want to create a DLL file to use this tcl functionality. How can I create this DLL on Visual Studio 2010.
Below is my sample tcl program that I am using:
#!/usr/bin/env tclsh8.5
proc add_two_nos { } {
set a 10
set b 20
set c [expr { $a + $b } ]
puts " c is $c ......."
}
And here is the C code which can use this tcl functionality :
#include <tcl.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
Tcl_Interp *interp;
int code;
char *result;
Tcl_FindExecutable(argv[0]);
interp = Tcl_CreateInterp();
code = Tcl_Eval(interp, "source myscript.tcl; add_two_nos");
/* Retrieve the result... */
result = Tcl_GetString(Tcl_GetObjResult(interp));
/* Check for error! If an error, message is result. */
if (code == TCL_ERROR) {
fprintf(stderr, "ERROR in script: %s\n", result);
exit(1);
}
/* Print (normal) result if non-empty; we'll skip handling encodings for now */
if (strlen(result)) {
printf("%s\n", result);
}
/* Clean up */
Tcl_DeleteInterp(interp);
exit(0);
}
I have successfully compiled this code with the below command
gcc simple_addition_wrapper_new.c -I/usr/include/tcl8.5/ -ltcl8.5 -o simple_addition_op
The above code is working with the expected output.
What steps do I need to take to create a loadable dll for this in Visual Studio 2010?
If you look at the answers to this question: here it gives the basic outline of the process you need to go through. There are links from my answer to some Microsoft MSDN articles on creating DLLs.
To go into this in a little more detail for a C++ dll that has Tcl embedded in it.
The first step is to create a new visual studio project with the correct type, one that is going to build a dll that exports symbols. My example project is called TclEmbeddedInDll and that name appears in code in symbols such as TCLEMBEDDEDINDLL_API that are generated by Visual Studio.
The dllmain.cpp look like this:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
allocInterp() ;
break ;
}
case DLL_THREAD_ATTACH:
break ;
case DLL_THREAD_DETACH:
break ;
case DLL_PROCESS_DETACH:
{
destroyInterp() ;
break;
}
}
return TRUE;
}
The allocInterp() and destroyInterp() functions are defined in the TclEmbeddedInDll.h, the reason for using functions here rather than creating the Tcl_Interp directly is that it keeps the details about Tcl away from the DLL interface. If you create the interp here then you have to include tcl.h and then things get complicated when you try and use the DLL in another program.
The TclEmbeddedInDll.h and .cpp are shown next, the function fnTclEmbeddedInDll() is the one that is exported from the DLL - I'm using C linkage for this rather than C++ as it makes it easier to call the function from other languages IMHO.
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the TCLEMBEDDEDINDLL_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// TCLEMBEDDEDINDLL_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef TCLEMBEDDEDINDLL_EXPORTS
#define TCLEMBEDDEDINDLL_API __declspec(dllexport)
#else
#define TCLEMBEDDEDINDLL_API __declspec(dllimport)
#endif
extern "C" {
TCLEMBEDDEDINDLL_API void fnTclEmbeddedInDll(void);
}
void allocInterp() ;
void destroyInterp() ;
// TclEmbeddedInDll.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
extern "C" {
static Tcl_Interp *interp ;
// This is an example of an exported function.
TCLEMBEDDEDINDLL_API void fnTclEmbeddedInDll(void)
{
int code;
const char *result;
code = Tcl_Eval(interp, "source simple_addition.tcl; add_two_nos");
result = Tcl_GetString(Tcl_GetObjResult(interp));
}
}
void allocInterp()
{
Tcl_FindExecutable(NULL);
interp = Tcl_CreateInterp();
}
void destroyInterp()
{
Tcl_DeleteInterp(interp);
}
The implementation of allocInterp() and destroyInterp() is very naive, no error checking is done.
Finally for the Dll the stdafx.h file ties it all together like this:
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
// TODO: reference additional headers your program requires here
#include <tcl.h>
#include "TclEmbeddedInDll.h"