Reputation: 147
I am trying to create simple program on Windows without any CRT library.
I have two codes:
// compile without -lkernel32
int __stdcall _main() {
return 5;
}
and
// compile with -lkernel32
#include <windows.h>
void __stdcall _main() {
ExitProcess(5);
}
I am compiling them with MinGW-w64 7.1.0 with this bash script:
@echo off
del main.exe 2>nul
C:\Users\Michal\Downloads\mingw64\bin\g++ main.cpp -o main.exe -O3 -s -nostdlib -lkernel32
main.exe
echo %errorlevel%
pause
The output (exit code) is the same. (I am using Windows 7 Pro 64-bit)
What code is better? (Maybe better question is: Why does the "return" variant work?)
Edit:
Entrypoint to the program (defaultly pre-set by linker) is _main (normally CRT lib does some work and then calls the 'main' function and calls ExitProcess (or something like that) with value returned from main function).
In my code I am not using CRT library and _main is still the entry-point to the program (and is not calling 'main' function).
Upvotes: 5
Views: 6627
Reputation: 33716
If you do not use CRT, you need to directly call ExitProcess
. Without this your process cannot terminate at all. So the variant with only return is wrong.
It will work only if your process has a single thread, otherwise the process will not terminate.
You need to understand How Processes are Terminated.
A process executes until one of the following events occurs:
Any thread of the process calls the ExitProcess function
The last thread of the process terminates
Any thread calls the TerminateProcess function with a handle to the process
When we use CRT, it internally calls ExitProcess
. When we do not use CRT, main
is the true entry point of your exe. After returning, you return directly to kernel32 code. kernel32 code calls ExitThread
but not ExitProcess
-- this is very important. So your process will be terminated in this case only if there are no other threads in your process. We can never assume this, however. Beginning with Windows 10, this is usually false. Therefore the only correct way to terminate a process without using CRT is to directly call ExitProcess
.
Upvotes: 9
Reputation: 283634
You are correct that why the return
version works is key to understanding which is better.
The important thing here is that main
is not the entrypoint for a Windows console application. The entrypoint is in library code, which initializes memory, calls constructors on global variables, splits the command line into argc
/argv
format, and then calls main
saving the return value.
If main
does return, it returns to the library code which calls atexit
-registered functions and destructors for static variables, then calls ExitProcess
.
Therefore there's really only one way to set the exit value, by calling ExitProcess
. But doing it yourself will skip the cleanup actions taken by the library -- if you call ExitProcess
then destructors don't get called and you might end up with loss of data stuck in write buffers.
When you build without the standard library, then library actions of construction/destruction aren't relevant, and calling ExitProcess
explicitly is nearly the same as returning. There still is a library-provided call frame which catches your return value and calls ExitThread
if you don't, but it comes from the OS itself (kernel32.dll) when there is no CRT. As RmMb points out, this is an important difference, because other threads won't be killed; the process will exit if all threads exit.
Upvotes: 9