Quote:
With regards to developing your own tiny C and C++ standard library replacements, are you minimising executable sizes by eliminating functionality that you, personally, do not use? Or does the lack of bloat stem from optimised internal algorithms and the lack of SEH? Either way, the output sizes that you have achieved are incredible! Do you plan on publishing your libraries at any point or are they only for personal use? How's your character encoding support?
You had asked how my TCLib.likb minimizes program size. Matt Pietrek covers this in detail in his Microsoft Systems Journal Article on LibCTiny.lib, which is still downloadable I think from his website. But I’ll go over it a bit. Try to follow along and I’ll show you how to create a 2,560 byte stand alone (no runtimes required) x64 GUI executable. First, open a Visual Studio command prompt where your build environment is configured to produce x64 binaries. There should be a shortcut on your Start Menu to it. Then put the following two files in whatever directory you want to use for this work, and change the directory to that directory…
Code:// crt_win_a.cpp
//========================================================================================
// Developed As An Addition To Matt Pietrek's LibCTiny.lib
// By Fred Harris, January 2016
//
// cl crt_win_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
extern "C" void __cdecl WinMainCRTStartup(void)
{
int iReturn = WinMain(GetModuleHandle(NULL),NULL,NULL,SW_SHOWDEFAULT);
ExitProcess(iReturn);
}
The top file you can compile to crt_win_a.obj using the command line string listed in the header. After you’ve compiled that to an obj you can build Form1.cpp using the alternate WinMainCRTStartUp() code and the top command line string to a 2,560 byte executable with VC19 from Visual Studio Community 2015. Next thing to do (after you see it works) would be invoke Dumpbin.exe on Form1.exe like so…Code:// Form1.cpp
// cl Form1.cpp /O1 /Os /GS- /link crt_win_a.obj kernel32.lib user32.lib // 2,560 bytes
// cl Form1.cpp /O1 /Os /MT /GS- /link kernel32.lib user32.lib // 38,912 bytes
#include <windows.h>
#include "tchar.h"
LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
if(msg==WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPTSTR lpszArgument, int iShow)
{
WNDCLASSEX wc;
MSG messages;
HWND hWnd;
wc.lpszClassName = _T("Form1");
wc.lpfnWndProc = fnWndProc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.lpszMenuName = NULL;
wc.hIconSm = NULL;
RegisterClassEx(&wc);
hWnd=CreateWindowEx(0,_T("Form1"),_T("Form1"),WS_OVERLAPPEDWINDOW|WS_VISIBLE,200,100,325,300,HWND_DESKTOP,0,hInstance,0);
while(GetMessage(&messages,NULL,0,0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return messages.wParam;
}
Dumpbin.exe /all Form1.exe [ENTER]
A good bit of stuff will come flying out, but among that stuff will be the functions imported into the executable. Here is a list of them…
My count is nine functions imported into the executable. Now lets build the same program loading the C Runtime. Here is the command line string for that, but its listed above in Form1.cpp too…Code:Section contains the following imports:
KERNEL32.dll
140002000 Import Address Table
1400020B8 Import Name Table
0 time date stamp
0 Index of first forwarder reference
1F6 GetModuleHandleA
105 ExitProcess
USER32.dll
140002018 Import Address Table
1400020D0 Import Name Table
0 time date stamp
0 Index of first forwarder reference
95 DefWindowProcA
224 PostQuitMessage
14C GetMessageA
A8 DispatchMessageA
2DD TranslateMessage
67 CreateWindowExA
238 RegisterClassExA
cl Form1.cpp /O1 /Os /MT /GS- /link kernel32.lib user32.lib
I’m seeing 38,912 bytes for that. Actually, I thought it would be a lot bigger. Anyway, do a dumpbin on that now and be prepared for a pretty long wait as everything scrolls by. Buried in all that stuff will be this listing of imported functions…
AS you can see, there are a lot more of them, and by far most of them weren’t used in the app. So that should give you an inkling of why the compile without loading all that extra stuff ended up producing a smaller executable.Code:Section contains the following imports:
USER32.dll
1400071E0 Import Address Table
140008B88 Import Name Table
0 time date stamp
0 Index of first forwarder reference
238 RegisterClassExA
67 CreateWindowExA
2DD TranslateMessage
A8 DispatchMessageA
14C GetMessageA
224 PostQuitMessage
95 DefWindowProcA
KERNEL32.dll
140007000 Import Address Table
1400089A8 Import Name Table
0 time date stamp
0 Index of first forwarder reference
266 GetTickCount
170 GetCommandLineA
239 GetStartupInfoA
419 SetUnhandledExceptionFilter
1F9 GetModuleHandleW
425 Sleep
220 GetProcAddress
105 ExitProcess
491 WriteFile
23B GetStdHandle
1F4 GetModuleFileNameA
396 RtlUnwindEx
14B FreeEnvironmentStringsA
1C0 GetEnvironmentStrings
14C FreeEnvironmentStringsW
47E WideCharToMultiByte
1E6 GetLastError
1C2 GetEnvironmentStringsW
3EC SetHandleCount
1D8 GetFileType
BF DeleteCriticalSection
D6 EncodePointer
B8 DecodePointer
13F FlsGetValue
140 FlsSetValue
13E FlsFree
3F0 SetLastError
1AE GetCurrentThreadId
13D FlsAlloc
2A5 HeapSetInformation
29F HeapCreate
34E QueryPerformanceCounter
1AB GetCurrentProcessId
24F GetSystemTimeAsFileTime
2E9 LeaveCriticalSection
DA EnterCriticalSection
431 TerminateProcess
1AA GetCurrentProcess
442 UnhandledExceptionFilter
2CB IsDebuggerPresent
397 RtlVirtualUnwind
390 RtlLookupFunctionEntry
389 RtlCaptureContext
2EB LoadLibraryA
2B5 InitializeCriticalSectionAndSpinCount
15C GetCPInfo
153 GetACP
213 GetOEMCP
2D5 IsValidCodePage
2A1 HeapFree
2A6 HeapSize
1E8 GetLocaleInfoA
2DB LCMapStringA
314 MultiByteToWideChar
2DD LCMapStringW
23D GetStringTypeA
240 GetStringTypeW
29D HeapAlloc
2A4 HeapReAlloc
I first became familiar with the work of Matt Pietrek on this topic of minimizing the sizes of executables by eliminating the C Runtime in October 2014. Matt was a luminary in software development who used to write the popular “Under The Hood” column in “Microsoft Systems Journal”. His article on this was named…
“Reduce EXE and DLL Size with LIBCTINY.LIB”
…and it was published in the January 2001 issue of “Microsoft Systems Journal”. From Matt’s website…
Matt Pietrek’s Website
Wheaty Productions Inc.
… I was able to download his source and the LibCTiny.lib file. I was able to run nmake on that and build his lib myself. It worked great. Then I wanted to see if there was any possibility of getting it to work in x64. There was. There was only one error that came up and it was easy to fix. I posted my explorations of this topic on my board under “Discussion” at …
Reducing Executable Size In C/C++ Programs By Eliminating The C Runtime (October 16, 2014)
Reducing Executable Size In C/C++ Programs By Eliminating The C Runtime
The topic so interested me that I wanted to find time in my work at some future point to explore it further and see if I couldn’t get the code working in wide character builds, so that I could do both x86 and x64 in ansi and wide. That time didn’t come for about a year and a half but it did come beginning of 2016. Here are links to my postings on it in Jose Roca’s forum…
Reducing Windows Executable File Size With Matt Pietrek’s LibCTiny.Lib Revisited (February 12, 2016)
Reducing Windows Executable File Size With Matt Pietrek's LIBCTINY.LIB Revisited
Reducing Program Size In UNICODE And x64 Builds With Matt Pietrek’s LibCTiny.LIB February 26, 2016)
Reducing Program Size In UNICODE And X64 Builds With Matt Pietrek’s LibCTiny.lib
Minimize Program Size By Eliminating The C Runtime Library (March 23, 2016)
Minimize Program Size By Eliminating The C Runtime Library
Moving TCLib Into Mainstream
http://www.jose.it-berater.org/smffo...p?topic=5132.0
Also, there is a lot more can be found out about this topic. Here is a noteworthy article from Dr. Dobbs Journal by Matthew Wilson…
Avoiding The Visual C++ Runtime Library (Matthew Wilson, February 1, 2003)
http://www.drdobbs.com/avoiding-the-...rary/184416623
The latter article was particularly interesting to me because I found it right after spending like a month wrestling with floating point math issues and having lost a lot of sleep, blood, and hide getting it to work. And in that article Matt Wilson stated that if your app needs floating point support your only alternative is standard linking with the C and C++ Runtimes. So I guess its good I hadn’t read his article before I accomplished it. Maybe I wouldn’t even have tried! I will say though that getting everything to work was easier in x64 than x86 when these foundational papers were written (the issues involve casting of 64 bit quantities to 32 bits and vice versa).
Also noteworthy was a post at CodeProvect by a Mike_V in around 2007…
http://www.codeproject.com/Articles/...untime-Library
Through his work I got a clue on how to modify Matt Pietrek’s work for wide character support. I also got some clues on file routines from him. I did have some problems with his code however, and didn’t use much of it.
Last but by far not least was Martins Mozeiko of HandmadeHero…
https://hero.handmade.network/forums...iscussion/t/94
Martins helped me when I was about to totally give up on floating point math. He is incredible. That’s all I can say about him. I don’t do computer games but in witnessing his know-how I have a new respect for gamers!
But everything is there at those Jose Roca links above. Because my postings of it were a ‘work in progress’ type thing, to put it all together in final finished form pretty much necessitates starting at the beginning and copying the code, creating the libs, etc., and agonizing through it like I did. I suppose I could post what I have now, but its still not done I guess, as I’m still wrestling with printf, sprintf, fprintf, etc., and of course the issues are what they always have been, i.e., floating point support.
I have had some major successes with it though. What elated me to no end, and what was so wonderful I still can’t believe its true, is that after a number of days of struggle I was able to get my ActiveX Grid Control working perfectly! The code and discussion for that is at the above links. That grid control I original wrote about 5 years ago in PowerBASIC. I used all very low level code to create it, i.e., I built the virtual function tables by hand using just struct/types and memory allocations, and it built in PowerBASIC to 49 k which is incredible (and compacts to 22k!). Probably the smallest full featured grid control anywhere. Sometime after that I rewrote it in C++ so I could create 32/64 bit versions. It was a good bit larger in C++ - like about 90 k, but I was able to compact it with UPX to 50 k. However, my PowerBASIC created version was 22 k when compacted. When I finally got my C++ code to build with my TCLib.lib I was at about 22 k in x64! And that compacted with UPX to 17k! So I would call that a mammoth success! And it works perfectly far as I can tell. I’ve tested quite a bit.
For the past couple weeks I have been attempting to re-build a large mission critical enterprise level application with my TCLib.lib. I have not had any difficulties getting it to build, but the app contains literally thousands of fprintf calls with floating point numbers. My TCLib is awkward to use for this because my printfs from Matt Pietrek’s original work didn’t work with floats/doubles. I can use my FltToCh/FltToWch functions, which work perfectly, but I have to modify the original code substantially, use an extra output buffer, and make two function calls whereas there were originally only one. So that’s why I’m presently working on a fully compatible printf that works with floats/doubles in exactly the same manner as the C Runtime.
So yes, the minimization achieved by any such libraries as my TCLib.lib, Matt Pietrek’s LIBCTINY.LIB, etc., come from the elimination of code that isn’t being used. For example, in that simple example I gave above which compiled to 2,560 bytes in x64 there is no support for command line argument processing, and no support for global/static Constructors/Destructors, i.e., classes instantiated outside of any function at global scope. I very, very seldom do either of those things, so I eliminated them. However, in Matt Pietrek’s code he didn’t, but still his executable would have only been 4 k instead of the 2.5 k of mine. So those two things don’t add that much. The big hit with linking with the C Standard Library comes from all the extra functions imported from it that aren’t used, plus its own heap code, startup code, etc. It all adds up.