Saturday, March 20, 2021

Compiling a MinGW application without the C standard library (and minimizing it's size)

 I often wondered if I could build an app without using the C runtime at all. It turns out it is pretty easy to achieve with MinGW. SDL2 doesn't depend on the C standard library too, so you could build tiny useful executables (only 9K in this case) using only SDL. Just ignore the SDL in the example below if you only need the Windows part:

 

// for ExitProcess
#include <windows.h>
#include <SDL2/SDL.h>
SDL_Renderer *x_renderer;
SDL_Window *x_window;
SDL_Texture *x_fontTex;
int nocrt_main( int argc, char *argv[] ) {
if ( SDL_InitSubSystem( SDL_INIT_VIDEO ) < 0 ) {
return -1;
}
x_window = SDL_CreateWindow( NULL,
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
1024,
768,
SDL_WINDOW_RESIZABLE );
if( ! x_window ) {
return -1;
}
#ifdef HINT_OPENGL // if we want to be consistent with linux
SDL_SetHint( SDL_HINT_RENDER_DRIVER, "opengl" );
#endif
x_renderer = SDL_CreateRenderer( x_window, -1, SDL_RENDERER_ACCELERATED );
if ( ! x_renderer ) {
return -1;
}
SDL_SetWindowTitle( x_window, "No CRT" );
int run = 1;
while( run ) {
SDL_Event event;
while ( SDL_PollEvent( &event ) ) {
switch( event.type ) {
case SDL_QUIT:
run = 0;
break;
}
}
SDL_Delay( 20 );
}
// just for completeness, don't see a point
SDL_DestroyRenderer( x_renderer );
SDL_DestroyWindow( x_window );
SDL_Quit();
// the exe never quits without it, SDL bug maybe?
ExitProcess( 0 );
return 0;
}
// ? if mainCRTStartup/WinMainCRTStartup is not defined, the linker will pick the
// ? first function it encounters to be the entry point
#ifdef M_CONSOLE
void Print( int msgSize, const char *msg ) {
HANDLE conOut = GetStdHandle( STD_OUTPUT_HANDLE );
WriteFile( conOut, msg, msgSize, NULL, NULL );
}
int WINAPI mainCRTStartup( void ) {
const char msg[] = "Hello World Console\n";
Print( sizeof( msg ) - 1, msg );
return nocrt_main( 0, NULL );
}
#else
int WINAPI WinMainCRTStartup(void) {
return nocrt_main( 0, NULL );
}
#endif
# Thanks goes to https://nullprogram.com/blog/2016/01/31/ -- Small, Freestanding Windows Executables
no_crt.exe: main.c
x86_64-w64-mingw32-gcc -o no_crt.exe main.c -pedantic-errors \
-std=c99 -Wall -Wextra \
-O3 \
-fno-tree-loop-distribute-patterns \
-fno-stack-protector -mno-stack-arg-probe \
-mconsole -DM_CONSOLE \
-I/usr/local/x86_64-w64-mingw32/include \
`sdl2-config --static-libs` \
-nostdlib -nolibc -nostartfiles \
-lkernel32
# -fno-tree-loop-distribute-patterns -- prevent intoducing memset
# -mconsole -DM_CONSOLE -- remove this for GUI mode. is there a way to detect console link flag without our macro?
# -fno-stack-check -fno-stack-protector -mno-stack-arg-probe -- prevent linking to stack checks, 9k no change in my example
# -ffreestanding -- A freestanding environment is one in which the standard library may not exist, and program startup may not necessarily be at "main". The option -ffreestanding directs the compiler to not assume that standard functions have their usual definition. No difference in size
# -nolibc -nostartfiles -nodefaultlibs -- no difference for size
# -lkernel32 -- for ExitProcess

No comments: