Minicrt
 

Last time, we bore witness to the drunken boast that if you really want to compile tiny, independent C++ programs with a modern Visual Studio then you have to kick its C Run Time library to the curb. To do that I introduced a replacement library, Minicrt, which worked so well within my contrived example that it escaped examination. Today we'll see how to use Minicrt in more realistic scenarios, but first a brief narration on latter day heroism.

Hero The First is Matt Pietrick, author of Libctiny. Matt wrote about large executable size back in 1996 and again in 2001 in which he fingered Visual Studio's CRT as the cause and offered up Libctiny as the solution. As a drop-in CRT replacement to effortlessly reduce program size, the library worked great and even included source code. One of its premises was that a number of standard C functions could be implemented as thin wrappers over existing Windows API calls, keeping the real work out of the executable by offloading it onto the operating system. For example, printf(), traditionally a meaty CRT function, is implemented in Libctiny simply by calling wsvprintf() to build the output string and WriteFile() to spit that to the standard output stream, and all in about fifteen lines of code! Hero.

Unfortunately, the years weren't kind to Libctiny users. Written in Visual C++ 6.0's heyday, Libctiny became increasingly incompatible with each subsequent version of Visual Studio. Trivial Hello World apps would still compile, but as soon as a program did anything interesting it would run afoul linker errors resulting from incompatibilities with the default libraries. Those could be turned off with the /NODEFAULTLIB linker switch, but that would leave no standard C functions except the poultry few in Libctiny. Even tiny programs need to do something. Sadness ensued. (And yes, poultry.)

Enter Hero The Secondest: Google. Dot com. Look 'em up. Those magnificent bastards went and wrote a software installer named Omaha for Chrome and Google Earth, open sourced the client, and then in order to shave 40kb off of their installer stub not only used Libctiny but dragged it kicking and screaming into the modern world, re-branding it as Minicrt. Minicrt is chock full of standard C functions (making /NODEFAULTLIB less painful), compatible with Visual Studio 2005 and 2008 (as that's what Google uses to build Omaha), and used in a nontrivial program that shows how Minicrt can co-exist with high level ATL classes like CString and CSimpleArray. I don't know the Google programmer responsible for this but I do know his name: Hero.

Thriceroy Of Heroes: Lee Packham. From his UK domain name I'm assuming that he's British. (Look, this is Alaska yo, deal with it.) Lee packaged Minicrt for Visual Studio 2008 to make it compilable by mortals. Google doesn't have Visual Studio project files for Omaha, only build scripts written in Python. Typical. Lee created Visual Studio 2008 project/solution files and also silenced some errors by adding a couple casts and #pragmas to the Minicrt code. Most importantly, as far this text is concerned, it was through his blog post that I learned about Omaha's rebirthening of Libctiny, setting off the chain of events that led to making small programs again, writing this article, and bestowing upon Lee the oft-coveted title: Hero.co.uk.

Now I'm hesitant to call myself a Hero, but if you were to throw a heavily catered banquet in my honor with sufficient austerity and appropriate attendance then I would be honored to appear. It's called Class. Look 'er up. I've taken Lee's Minicrt, made a few light touch-ups for myself, and am making it available here. These are the changes I've made:

  • Added more of the standard C functions. Each was added on an as-needed basis and as a new source file.
  • Added back printf(), which was in the original Libctiny but removed from Google's Minicrt.
  • Added Visual Studio 2005 project/solution files, equivalent to the 2008 ones (which are also included).
  • Set Release Mode to compile the library without debug information and to optimize for size. This makes for a smaller minicrt.lib file.
  • Removed unused source files, including the redundant string.cc which Google's build script doesn't even include.
  • Renamed Minicrt to Libctiny II: Electric Boogaloo, then immediately backpedaled on the decision in an act of equally epic wisdom and cowardice.

So there's the odyssey. Matt Pietrick's Libctiny lives on nearly fifteen years later as Minicrt, which you can compile and use with Visual Studio 2005 and 2008 to drop a simple windowed program to 2.0kb with no dependencies outside of the operating system. Of course, we're talking computers here, so it's not always that simple. As advanced C++ language features and libraries get used, friction occurs in the form of frightening linker errors when you don't have the real CRT in the mix. Several of these are easily remedied by flipping the right switches while other problems require more… extreme measures. I'll go through each of the errors I've encountered and what can be done about them through compiler settings and code. The astute will recall that previously I swore a sacred oath to God to not mention any more compiler settings. Whoops!

Unresolved external symbol const type_info::'vftable' …

C++ exceptions and/or RTTI are the cause of this. Both language features are out of Minicrt's league and both can be disabled. This really should go without saying, but if your program depends on either of these then that kinda disqualifies it from the Minicrt fun.

Configuration Properties \ C/C++ \ Code Generation \ Enable C++ Exceptions
	Yes (/EHsc) => No

Configuration Properties \ C/C++ \ Language \ Enable Run-Time Type Info
	Yes => No (/GR-)

Unresolved external symbol __security_cookie

This error and variants on it are a result of the compiler's buffer security check, which is expecting to find the corresponding Visual Studio 2005 CRT code. Turn it off.

Configuration Properties \ C/C++ \ Code Generation \ Buffer Security Check
	Yes => No (/GS-)

Unresolved external symbol __load_config_used

This is related to structured exception handling, though it is usually prevented simply by turning off C++ exceptions as described above. If that doesn't do it, disabling the safe exception handler table will.

Configuration Properties \ Linker \ Command Line \ Additional Options
	/SAFESEH:NO

Unresolved external symbol ___CxxFrameHandler3

Who even uses exception handling in C++? Once again, turn off C++ exceptions as described above.

Unresolved external symbol __ftol2_sse

Hoo boy. When a floating point number is converted to a long, or really any integer type, Visual Studio 2005 builds applications that call the CRT helper function _ftol2_sse to do the work. In the previous version of Visual Studio this function was named _ftol2, which is a problem for Minicrt because it's built with ftol2.obj from the 2003 CRT and only contains the function _ftol2. From the names involved in this conflict you'd think that you'd just need to disable SSE in the project setting to persuade Visual Studio to use the older function, but that's not the case. _ftol2_sse isn't just an alternate version; it wraps both by first checking for SSE availability and then only calling _ftol2 if SSE is not there. That means that either way, Visual Studio 2005 links programs against _ftol2_sse. It's position on this is intractable and adamantine. That leaves us with only one move to make, but it's drastic: using a depreciated compiler switch to decline calling any ftol conversion functions!

Configuration Properties \ C/C++ \ Command Line \ Additional Options
	/QIfist

This should get you compiling, but there are consequences to this switch. The first is cosmetic and annoying: Visual Studio will always generate a warning about using a depreciated compiler switch, a warning that can't be disabled. The second is serious. ftol is responsible for setting the Floating Point Unit control flag to the correct state before and after each conversion, and it's the FPU control flag that determines how rounding should occur. The ANSI C behavior is to round towards zero (truncate mode) when converting, but now that ftol has gone awol (see what I did there?), who knows what mode the FPU is in? Mine defaults to rounding towards nearest, so this switch potentially changes the result of every floating point conversion or calculation application-wide. That's terrible. Now there's a way out of this… but it involves assembly. Yeah, I know, I wanted this to be an easy one too. But it's the other way.

// set rounding mode to truncate
//  from https://web.archive.org/web/20160304215813/http://www.musicdsp.org/showone.php?id=246
static short control_word;
static short control_word2;

inline void SetFloatingPointRoundingToTruncate()
{
    __asm
    {
        fstcw   control_word                // store fpu control word
        mov     dx, word ptr [control_word]
        or      dx, 0x0C00                  // rounding: truncate
        mov     control_word2, dx
        fldcw   control_word2               // load modfied control word
    }
}

Calling this at the start of your program will set the rounding mode to truncate and keep it there for the duration (unless you manually change it again), which makes float conversions behave the way they would have before /QIfst. Any other floating point routines that require a different mode are going to require that manual change or their results will be off. As you can see, heavy use of floating point math can also be a Minicrt disqualifier. On the other hand, if you can test your results and they come out okay then there's really no problem. In fact, this method of disabling ftol and manually initializing the rounding mode is an established optimization used by speed freaks because conversions go faster without the overhead of setting and re-setting the control state each time.

Unresolved external symbol __purecall

This shows up when you start using virtual functions. _purecall is a placeholder function that lives in the CRT so that a class with a pure virtual function can have its vtable entry pointed to something instead of nothing, which would be illegal. It's a function that doesn't do anything, and it's an error for it to ever be called, so all you need to do is create your own do-nothing _purecall. The only difference between this and the CRT's version (defined in purevirt.c) is that the real one throws an error and aborts the program.

extern "C" int __cdecl _purecall(void) { return 0; }

Unresolved external symbol class ATL::CAtlBaseModule… and everything else ATL related.

The Active Template Library works great with Minicrt, it just takes a few macro definitions to disable ATL's dependencies on newer CRT functions… and a few more to silence all the depreciation warnings. Finally, the linker needs to be reminded about the ATL libary file since /NODEFAULTLIB excludes it from being automatically included in the list. I've taken all of these settings from Omaha's build script, and it's thanks to them that I've been able to use CString and collections like CAtlArray and CAtlMap.

Configuration Properties \ C/C++ \ Preprocessor \ Preprocessor Definitions
	_ATL_MIN_CRT
	_ATL_SECURE_NO_DEPRECATE
	_CRT_SECURE_NO_WARNINGS
	_SECURE_SCL=0
	_SECURE_ATL=0

Configuration Properties \ Linker \ Input \ Additional Dependencies
	atls.lib

Unresolved external symbol [ insert CRT function here ]

Eventually you'll want to use a CRT function that simply isn't in Minicrt. Yet. The beauty of being able to build Minicrt yourself with Visual Studio is that it's easy to add functions to it. When I said that I added functions on an "as-needed basis", that's what I meant. It wasn't for fun, each one was done because I had a program that depended on it. Thankfully, you only need to add a function once. It's not as daunting as it sounds. Most of the time you can just find an existing implementation and paste it in with little or no modifications. The first place to look is the source for Visual Studio's CRT, which comes with Visual Studio (located in %VSINSTALLDIR%\vc\crt\src). That's where I got the code for strrchr().

//==========================================
// minicrt - Chris Benshoof 2009
// strrchr from MSVCRT
//==========================================
#include "libctiny.h"
#include <stdlib.h>

extern "C" char * __cdecl strrchr (const char * string, int ch)
{
    char *start = (char *)string;

    while (*string++)                       /* find end of string */
        ;
    /* search towards front */
    while (--string != start && *string != (char)ch)
        ;

    if (*string == (char)ch)                /* char found ? */
        return( (char *)string );

    return(NULL);
}

In the case of string functions, the next place to look is Minicrt's very own string.c. There are a number of string functions in there that aren't exported but are used by ones that are. When needed, I've copied these functions into new source files made them exportable, that way I don't make any changes to string.c and it's clear what my rookie additions are. By not touching the original files it's easy for me to periodically diff my evolving Minicrt with the one in Google's Omaha repository so that I can easily merge in their updates. I pulled tolower() from string.c into tolower.cc.

//==========================================
// minicrt - Chris Benshoof 2009
// tolower from string.c (__ascii_tolower)
//==========================================
#include "libctiny.h"

extern "C" int __cdecl tolower(int c)
{
    if (c >= 'A' && c <= 'Z') return (c + ('a' - 'A'));
    return c;
}

The final place to look, of course, is ye olde abandoned Internet. A lot of people have had to implement the C Run Time library over the years, so there's a lot of implementations laying around. That's how I avoided writing my own atoi().

//==========================================
// minicrt - Chris Benshoof 2009
// atoi(), modified from
// https://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/atoi.c.htm
//==========================================
#include "libctiny.h"

extern "C" int __cdecl atoi(const char *String)
{
    int Value = 0, Digit;
    int c;

    while ((c = *String++) != '\0') {

        if (c >= '0' && c <= '9')
            Digit = (c - '0');
        else
            break;

        Value = (Value * 10) + Digit;
    }

    return Value;
}

If all else fails, you can always write the function yer own damn self. I cobbled together sprintf() from Minicrt's printf() code. It's kinda fun (perversely) to be in charge of your own little run time as long as you know the limitations. For example, the printf() implementation uses a 1024 byte buffer from the stack to build the output string, as that's the largest buffer wvsprintf() will accept. If you call printf() to build a string longer than that… uh-oh. Just something to keep in mind.

Thus concludes my panoptica on Minicrt and making small programs. From here on out I can go into pornographic detail about actual programs instead of coming off as an obsessive compulsive over trivial technical attributes… though I have a feeling I'll be doing both. In my defense, many of the programs I'll be examining make use of Minicrt so this topic seems a necessary prerequisite. On the other hand, all you have to do is look at the lengths of the two articles to see that there really is no defense.

Well, what's done is done. Enjoy the source code to Minicrt. As I update my copy I'll update the copy here too. If that happens enough, I may even concoct something fancy like a text file of release notes. In any case, I'll keep this article updated if I survive any more linker issues.

Next time, I'll prove that I've actually written a program before. Though wouldn't it be great if I hadn't?

minicrt.lib (library)
minicrt.zip (source code)