Click here to Skip to main content
Click here to Skip to main content

Tiny C Runtime Library

By , 25 Mar 2007
 

Introduction

Updated 2007-03-25: See end of article for details.

Ever designed a simple utility program, such as a hex-dump program, only to find your simple program is a full 64K, optimized for size, when all it does is read a file and print to stdout? Ever wonder what happened to those good ol' DOS days where programs had to be small? Where a COM file was limited to 64K? Or when you can write a bare-bones DOS-style protected mode operating system kernel in about 64K?

Well look no further. Here I will examine what causes this code bloat, and what can be done to fix it.

Background

Matt Pietrek wrote an excellent article in the January 2001 MSDN Magazine titled Under the Hood: Reduce EXE and DLL Size with LIBCTINY.LIB. While most of this information remains valid today, I have updated some of his code to work better with Visual Studio 2005. I have also added support for functions that were not included in his article.

Intended Audience

This article is aimed at programmers who like to have control over every little detail. It is also geared towards small portable utility-like programs, where a DLL CRT is undesirable because of the need for a second file and installation program, and where the overhead of a statically linked CRT is much greater than the core program code.

Of course, by replacing the CRT, programs that rely on specifics of the Microsoft CRT will fail. For instance, if you go digging into the FILE structure, or expect a certain header on your memory allocations, or rely on the buffering features of stdio, or use locales, runtime checks, or C++ exception handling, you can't use this library. This library is aimed for use by small, simple programs, such as a hex-dump command line program or the many UNIX-style tools like cat or grep.

Many C/C++ purists will take offence at my suggestions, because the C runtime is, to them, something that shouldn't be tampered with. But bear with me, because although you might never use any of this article's information, it should at least give you an insight into how your program works.

Where's Bloat-o?

(really bad pun, I know...)

The source of this 'code bloat' is very easy to find by looking at a linker-generated map file. Here is a snippet from the demo programs' map file:

 0001:00000000       ?DumpFile@@YAXPAD@Z        00401000 f   hd.obj
 0001:00000152       _main                      00401152 f   hd.obj
 0001:0000021b       _feof                      0040121b f   LIBCMT:feoferr.obj
 0001:0000024a       _fgetc                     0040124a f   LIBCMT:fgetc.obj
 0001:00000381       _printf                    00401381 f   LIBCMT:printf.obj
 0001:00000430       __get_printf_count_output  00401430 f   LIBCMT:printf.obj
 0001:00000446       __fsopen                   00401446 f   LIBCMT:fopen.obj
 0001:0000050a       _fopen                     0040150a f   LIBCMT:fopen.obj
 0001:00000520       _memset                    00401520 f   LIBCMT:memset.obj
 0001:0000059a       __fclose_nolock            0040159a f   LIBCMT:fclose.obj
 0001:0000060d       _fclose                    0040160d f   LIBCMT:fclose.obj
 0001:00000689       __amsg_exit                00401689 f   LIBCMT:crt0dat.obj
 0001:000006ad       ___crtCorExitProcess       004016ad f   LIBCMT:crt0dat.obj
 0001:000006d3       ___crtExitProcess          004016d3 f   LIBCMT:crt0dat.obj
 ...
 0001:0000a590       __allmul                   0040b590 f   LIBCMT:llmul.obj
 0001:0000a5e0       _strchr                    0040b5e0 f   LIBCMT:strchr.obj
 0001:0000a5e6       ___from_strstr_to_strchr   0040b5e6     LIBCMT:strchr.obj

As you can see, it includes "two" functions from my program, and over "two hundred" functions in the C Runtime (CRT).

Notice that one of the functions is even ___crtCorExitProcess, a function that is used by a C++/CLI program! Other gems include multithreading support, locales, and exception handling - none of which are used by my program!

And this is with Eliminate Unreferenced Data and COMDAT Folding on!

Where do I begin?

I will first highlight the various tasks performed by the C Runtime to give the reader a better understanding of the 'magic' that happens in C and C++.

Let's start by configuring the linker to Ignore Default Libraries. Compile. I was greeted with this:

hd.obj : error LNK2001: unresolved external symbol _feof
hd.obj : error LNK2001: unresolved external symbol _fgetc
hd.obj : error LNK2001: unresolved external symbol _printf
hd.obj : error LNK2001: unresolved external symbol _fopen
hd.obj : error LNK2001: unresolved external symbol _memset
hd.obj : error LNK2001: unresolved external symbol _stricmp
hd.obj : error LNK2001: unresolved external symbol _fclose
hd.obj : error LNK2001: unresolved external symbol _exit
LINK : error LNK2001: unresolved external symbol _mainCRTStartup

Not good. Not good at all.

mainCRTStartup

Where does your console program start? Did I hear you say main? If you did, you said what I would have said before journeying into the inner Stationworkings of the C Runtime.

Windows isn't nice enough to provide your app with a ready-made argc and argv. All it does is call a void function() specified in the EXE header. And by default, that function is called mainCRTStartup. Here is a simple example:

extern "C" void __cdecl mainCRTStartup()
{
    int argc = _init_args();
    _init_atexit();
    _initterm(__xc_a, __xc_z);         // Call C++ constructors

    int ret = main(argc, _argv, 0);    // Don't handle environment strings

    _doexit();
    ExitProcess(ret);
}

We start by creating argc and argv, which we later pass to main. But before we do that we have to take care of some things, like calling the constructors for static C++ objects.

The same thing happens in GUI programs, except the function is called WinMainCRTStartup. And for DLLs, the true entry point is _DllMainCRTStartup. Unicode programs look for wmainCRTStartup and wWinMainCRTStartup respectively. DllMain appears to stay the same.

C++ Magic

The constructors of static objects don't just call themselves. And Windows is certainly not going to call them for us. So we have to do it ourselves. What do I mean?

class StaticClass
{
public:
    StaticClass() {printf("StaticClass constructor\n");};
    ~StaticClass() {printf("StaticClass destructor\n");};
};
StaticClass staticClass;

void main()
{
    printf("main\n");
}

C++ programmers should automatically expect the output of this program to be:

StaticClass constructor
main
StaticClass destructor

Matt Pietrek has a great explanation in his article, mentioned earlier, under the heading "The Dark Underbelly of Constructors", so I will not bother going into that level of detail here. Suffice it to say that the compiler emits pointers to the constructor functions (actually thunks to constructor functions) in a special ".CRT" section in the object file, which is later merged with the ".data" section. By declaring a pointer to the start and the end of this section, the _initterm function is able to iterate over these pointers, calling each constructor in turn.

The constructor thunk function also registers an atexit callback to call the destructor of the object. Thus the mainCRTStartup function above goes to the trouble of creating an atexit table. The _doexit function is responsible for calling these functions.

Standard Functions

So now we have taken care of the program's entry point. What about the other functions?

printf and Family

One of the more complex tasks performed by the C Runtime is parsing the printf format string. (I'll admit it's not terribly complex; it's just non-trivial compared to strcmp) To save space, we can offload this processing to the Windows function wvsprintf. No, that's not a wide-character version. The w probably stands for Windows.

extern "C" int __cdecl printf(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    int ret = vprintf(fmt, args);
    va_end(args);

    return ret;
}

extern "C" int __cdecl vprintf(const char *fmt, va_list args)
{
    char bfr[2048];                // ugly... but this whole idea of replacing
                                   // the CRT could be called ugly too!
    int ret = wvsprintf(bfr, fmt, args);

    fwrite(bfr, ret, 1, stdout);   // fwrite takes care of CRLF translation
    return ret;
}

File I/O

Originally I had planned to eschew the FILE structure altogether - and instead just use a HANDLE cast to a FILE*. But this would have only given me two bits of information. As I added functionality to the library this ideal solution became less ideal when I needed to store an end-of-file flag, text-mode flag, and possibly other data. And besides, not using the FILE structure means that the stdin, stdout, and stderr identifiers don't work! So now I (ab)use the FILE structure.

Because I cannot change the FILE structure itself (it is defined in stdio.h) I have to use its fields to work with my data. A very ugly solution. But this library isn't intended to be pretty. NOTE however, that this means code that relies on internal fields in the FILE structure will crash. But then again, you shouldn't be messing with internal data structures anyways, right?

Thus, for illustration, here is fopen:

extern "C" FILE *fopen(const char *path, const char *attrs)
{
    DWORD access, disp;
    if (strchr(attrs, 'w'))
    {
        access = GENERIC_WRITE;
        disp = CREATE_ALWAYS;
    }
    else
    {
        access = GENERIC_READ;
        disp = OPEN_EXISTING;
    }

    HANDLE hFile = CreateFileA(path, access, 0, 0, disp, 0, 0);
    if (hFile == INVALID_HANDLE_VALUE)
        return 0;

    _FILE *file = new _FILE;
    memset(file, 0, sizeof(_FILE));
    file->set_handle(hFile);

    if (strchr(attrs, 't'))
        file->_flag |= _FILE_TEXT;

    return file;
}

fread and fwrite are substantially more complicated than this, because they must translate '\r\n' combinations to '\n' only. For brevity, I will not discuss the algorithm - see the source code if you are interested.

String functions

Replacing the CRT means no more strlen, strcmp, or even memset. These must be implemented from scratch. Thankfully, they are not difficult to implement - just tedious. Care should be taken to handle NULL pointers and other special cases described in the MSDN documentation.

Wide Character (Unicode) Support

This is the major new feature in this library. It is still under development and hasn't undergone extensive testing yet.

As suggested by Hans Dietrich I have started to add wide-character support to the library. Basically that means implementing wide-character versions of various functions.

Uppercase and lowercase

When dealing with ASCII, functions like isalpha, toupper, and strlwr are trivial to implement. But as soon as Unicode enters the picture, they become much more complicated. There are different rules for uppercase versus lowercase and alphabetic versus numeric, so some operating system help is in order. To fix this problem, the function GetStringTypeW is used to implement the isXYZ family of functions, and the functions CharUpper and CharLower are used to implement toupper and tolower, respectively.

File encoding

Up until VS2005 even the Unicode file library functions could only write ASCII characters. Output to wprintf, fwprintf, and fwputs in text mode are all translated from Unicode before it is written to the file.

Because adding support for UTF-8, UTF-16, and other forms of file encoding would just add bloat to this library, I have made the decision to not include it. The behavior will remain compatible with the pre-VS2005 CRT. If you need to deal with file encodings, you probably need the full CRT anyway.

Why are you adding all this stuff? Why not keep it simple!

Simple: Only the stuff that you call is included in a release build!

But then why is Microsoft's CRT so bloated if you don't call much stuff? Again - because you do, but don't know it. The CRT startup code itself calls lots of functions that in turn call other functions - and a lot of it is garbage that isn't needed 90% of the time. Locales, exception handling, etc. have their place, but not in all programs. If your program doesn't use it, why should it have to pay the price of Microsoft's startup code using it?

The startup code and various functions in this CRT library are designed to rely on as little functionality as possible. Thus only the essentials are included.

Using the code

Add the tlibc (Tiny Libc) project to your project's solution, and add it as a referenced project. Alternatively, compile the library and add it to your project's linker options.

Because we are replacing the default CRT, C++ exception handling and SEH will not be handled properly. So don't use it! You will also need to turn off Buffer Security Check, set Runtime Checks to default, and disable Runtime Type Information.

Make sure to link with Ignore Default Libraries turned on! And to generate the smallest code, compile with link-time code generation on, optimize for size, turn string pooling on, and enable COMDAT folding and eliminate unreferenced data.

Results

After recompiling the program with libctiny and the method above, the EXE jumped from a giant 64K to a much more reasonable 4K! (4096 bytes to be exact). For comparison, the entire code section of the linker map file is reproduced below:

 0001:00000000       ?DumpFile@@YAXPAD@Z        00401000 f   hd.obj
 0001:0000013d       _main                      0040113d f   hd.obj
 0001:0000021a       _fopen                     0040121a f   libct:file.obj
 0001:000002a7       _fread                     004012a7 f   libct:file.obj
 0001:000003c2       _fwrite                    004013c2 f   libct:file.obj
 0001:0000048b       _fgetc                     0040148b f   libct:file.obj
 0001:000004b6       _printf                    004014b6 f   libct:printf.obj
 0001:000004ef       _memset                    004014ef f   libct:memory.obj
 0001:0000050e       __doexit                   0040150e f   libct:initterm.obj
 0001:0000053a       _mainCRTStartup            0040153a f   libct:crt0tcon.obj
 0001:000005f5       _malloc                    004015f5 f   libct:alloc.obj
 0001:00000607       __init_args                00401607 f   libct:argcargv.obj
 0001:00000705       __ismbcspace               00401705 f   libct:isctype.obj

History

2007-03-25

  • Fixed strnicmp, pointed out by mpj

2006-08-19

  • Wide-character bugfixes (_fgetws)
  • Non-Unicode builds now set to SBCS rather than MBCS
  • Fixed typo bug in stderr, pointed out by Hans

2006-08-13

  • Preliminary wide-character support
  • Fixed memory leak in command-line parsing (existed in original)
  • Fixed memory leak in fread
  • Fixed behavior of feof
  • Fixed a rather embarrassing problem with strncpy
  • Added _DllMainCRTStartup that I accidentally omitted

2006-08-12

  • Submission to CodeProject

Comments, complaints, questions, etc. are welcome. Please let me know if you actually use this for something. If you need a function that is not included in this library, let me know and I will update the code. Comments on my 'comments' are also welcome.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Mike_V
United States United States
Member
Mike_V is currently a student at UCLA.
 
After a few years on the Dark Side, he reformed and now chants "Death to VB." His computer-related interests include C++, C#, and ASP.NET (in C#, of course). He writes operating systems in C++ and assembler as a hobby.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralWeird thing compiling for 64 bit with .rdata sectionmemberChimpy2010 Apr '10 - 22:20 
In order to remove some link warnings (LNK4254) when using this lib with a 64 bit program I am writing, I had to remove the write attribute from the lines
 
#pragma section(".CRT$XCA", read, write )
 
and
 
#pragma section(".CRT$XCZ", read, write ).
 
in the file initterm.cpp. This seems to work alright although I don't quite know what this bit of code is doing!
 
I'm using Visual Studio 2010 RC.
 
Thanks.
GeneralMany thanks!membermonoceres1 Feb '10 - 4:22 
I've saved ~100 kB thanks to your code! Having exe's ~4 kB that are completely standalone is a bliss!
QuestionTiny C problemsmemberT800G13 Oct '09 - 11:16 
I recently "discovered" Tiny C compiler and it's great (leanest non-assembler code I've seen), but.... Frown | :(
It seems that out-of-the-box compiler (from official web site) has a problem with windows-GUI-subsystem CRT startup code (console crt seems to work ok) because even simple "hello world" test is falsely detected as a virus ("Zhelatin" variant). Dead | X|
 
If I leave WinMain empty all is ok, but insert any external function, eg. MessageBox, and it is instantly detected by antivirus.
I tested compiled windows binary at Virustotal.com and nearly all(!) out of 41 antivirus program detected a malware.
Somehow it comes down to 13 (less prominent vendors) out of 41 if I recompile tcc from source and change one if-block condition in tcc-0.9.25.tar.bz2\tcc-0.9.25\win32\lib\wincrt1.c like this:
 
int _winstart(void)
{
char *szCmd; STARTUPINFO startinfo;
 
__set_app_type(__GUI_APP);
_controlfp(0x10000, 0x30000);
 
szCmd = GetCommandLine();
if (szCmd!=NULL)//<----eliminates some AV false positives

 
but clearly problem goes deeper than one code block.
I tried to build libtcc1.a with gcc but the further I go the more problems emerge(mostly function linking related).
Since I can't fix it myself (and don't have so much spare time) I hope maybe someone can help resolve this issue.
AnswerRe: Tiny C problemsmemberfreesaif1 Jan '10 - 13:36 
Hi,
 
I'm currently stop developing my project because i have this problem, Kasper-sky Anti-virus detect my .exe compiled by tcc as a virus, that Anti-virus software is stupid really
 
I test your solution "if (szCmd!=NULL)", but Kasper-Sky detect it again, please if your find a good solution post it here or send my a email ( freeseif [AT] live [DOT] com )
 
Thank You
GeneralRe: Tiny C problemsmemberDavid 'dex' Schwartz9 Dec '10 - 14:49 
Virus checkers may also be looking for code that is missing.
In the case of programs linked with Tiny C, a lot of expected code is missing.
Keep it simple
dex

Generallibct.lib(sprintf.obj) : error LNK2001: unresolved external symbol _vsprintfmemberMember 603819610 May '09 - 1:27 
The following code fragment causes some errors.
UINT step, totsteps;
endpointVolume->GetVolumeStepInfo(&step, &totsteps);
 
char buffer[200];
sprintf(buffer, "Step: %d, Total amount of steps: %d", step, steps);
 
MessageBox(NULL, buffer, "step", MB_OK);
 
itoa(totsteps, buffer, 10);
 
MessageBox(NULL, buffer, "total steps", MB_OK);
 
Output:
1>------ Build started: Project: myProject, Configuration: Release Win32 ------
1>Compiling...
1>code.cpp
1>.\code.cpp(30) : warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
1>        c:\Program\Microsoft Visual Studio 9.0\VC\include\stdio.h(366) : see declaration of 'sprintf'
1>.\code.cpp(34) : warning C4996: 'itoa': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _itoa. See online help for details.
1>        c:\Program\Microsoft Visual Studio 9.0\VC\include\stdlib.h(862) : see declaration of 'itoa'
1>Linking...
1>   Creating library Release/myProject.lib and object Release/myProject.exp
1>code.obj : error LNK2001: unresolved external symbol _itoa
1>libct.lib(sprintf.obj) : error LNK2001: unresolved external symbol _vsprintf
1>Release/myProject.dll : fatal error LNK1120: 2 unresolved externals
1>Build log was saved at "file://xxx\BuildLog.htm"
1>myProject - 3 error(s), 2 warning(s)
========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========
 
What am I doing wrong here?
GeneralRe: libct.lib(sprintf.obj) : error LNK2001: unresolved external symbol _vsprintfmemberMember 603819612 May '09 - 0:05 
Got another one:
 
1>code.obj : error LNK2001: unresolved external symbol __ftol2_sse
 
Might be some relevant info about this one here: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=107716[^]
GeneralMore Windows-API string functions available!memberDmitry Zemskov14 Apr '09 - 5:29 
There are some more Windows-API string functions in Shlwapi.dll (Shlwapi.h is the header). They include StrStr, StrChr and more. Why not use them instead of writing new code?
(I consider you simply didn't know about their existence).
GeneralHere is missing strtok()memberkr.kim7 Feb '09 - 0:07 
Nice lib,but i met link error with strtok,so i wrote strtok in string.cpp,
 
char *strtok(char *_str,const char*delim)
{
static char *ep;
char *bp;
if(_str)
ep = _str;
bp = ep;
if(*ep == 0)
return 0;
 
while(*ep){
if(strchr(delim,*ep)){
*ep++ = 0;
while(*ep && strchr(delim,*ep)) ep++;
break;
}
ep++;
}
return bp;
}
GeneralNice,tiny bug in strstrmemberkr.kim31 Jan '09 - 21:05 
I found tiny bug in strstr line 9,
 
Original:
 
if (!strcmp(&str[i], substr))
 
Change To:
 
if (!strncmp(&str[i], substr,substr_len))
 

Other Question:
is it compatible with STL ?
it was not compatible with STL when i tested( i hope use vector,...etc).
is it correct?
 
Thank you! Laugh | :laugh:
Generalunresolved symbols on vs2008membernicocode31 Jan '08 - 10:46 
Hi
i'm trying to use the lib together with pixeltoaster (http://www.pixeltoaster.com)
i get several unresolved symbols (see below)
 
1) what's it about with the __RTC_* stuff? isn't it something with the realtime clock,
can it be removed?
2) any advice on how to locate the _Direct3DCreate9 function by dynamically loading the
directx9 dynamic link lib?
3) how to disable the usage of the security_check cookie?! (main offender)
4) the vftable is the virtual function table is there any luck to get rid of it /declare it/ support it by tlibc?
 
i'd be glad to get help on this,
the pixeltoaster library has support for floating High Dynamic Range image buffers which
i completely disabled per #define (ie. no floating point calcs possible)
thanks in advance

Error 6 error LNK2019: unresolved external symbol __RTC_CheckEsp referenced in Error 7 error LNK2001: unresolved external symbol __RTC_CheckEsp
Error 8 error LNK2019: unresolved external symbol @_RTC_CheckStackVars@8
Error 9 error LNK2001: unresolved external symbol @_RTC_CheckStackVars@8
Error 10 error LNK2001: unresolved external symbol __RTC_Shutdown
Error 12 error LNK2001: unresolved external symbol __RTC_InitBase
Error 13 error LNK2001: unresolved external symbol __RTC_InitBase
 
Error 15 error LNK2001: unresolved external symbol "const type_info::`vftable'" (??_7type_info@@6B@)
Error 16 error LNK2001: unresolved external symbol "const type_info::`vftable'" (??_7type_info@@6B@)
Error 17 error LNK2019: unresolved external symbol ___security_cookie referenced in function "union PixelToaster::TrueColorPixel * __cdecl load(char const * const,int &,int &)" (?load@@YAPATTrueColorPixel@PixelToaster@@QBDAAH1@Z) main.obj
Error 18 error LNK2001: unresolved external symbol ___security_cookie PixelToaster.obj
Error 19 error LNK2019: unresolved external symbol @__security_check_cookie@4 referenced in function "union PixelToaster::TrueColorPixel * __cdecl load(char const * const,int &,int &)" (?load@@YAPATTrueColorPixel@PixelToaster@@QBDAAH1@Z) main.obj
Error 20 error LNK2001: unresolved external symbol @__security_check_cookie@4 PixelToaster.obj
Error 21 error LNK2019: unresolved external symbol _Direct3DCreate9@4 referenced in function "public: __thiscall PixelToaster::WindowsDisplay::WindowsDisplay(void)" (??0WindowsDisplay@PixelToaster@@QAE@XZ) PixelToaster.obj blubber
Error 22 error LNK2019: unresolved external symbol __imp___wassert referenced in function "public: __thiscall PixelToaster::WindowsWindow::WindowsWindow(class PixelToaster::DisplayInterface *,class PixelToaster::WindowsAdapter *,char const * const,int,int)" (??0WindowsWindow@PixelToaster@@QAE@PAVDisplayInterface@1@PAVWindowsAdapter@1@QBDHH@Z) PixelToaster.obj
Error 23 error LNK2019: unresolved external symbol __ftol2_sse referenced in function "public: void __thiscall PixelToaster::WindowsWindow::zoom(float)" (?zoom@WindowsWindow@PixelToaster@@QAEXM@Z) PixelToaster.obj
 
i can avoid the printf however:
Error 14 error LNK2019: unresolved external symbol __imp__printf referenced in function "public: int __thiscall Application::run(void)" (?run@Application@@QAEHXZ)

GeneralRe: unresolved symbols on vs2008memberMike_V31 Jan '08 - 12:53 
So some of these look like VS2008 expands the list of runtime checks it uses. Unfortunately the CRT is very compiler-specific, so it looks like VS2008 expands the list of runtime checks available.
 
Becuase I have virtually no time to work on this, and I intended this to be more of an "under the hood" type of article, I will not be able to update this project. Sorry about that. But it sounds like your project needs the full CRT functionality anyways, so it shouldn't be too bad using that.
 
Mike
GeneralRe: unresolved symbols on vs2008membernicocode1 Feb '08 - 6:04 
Thanks for the answer,
I managed to get down to these two last errors:

error LNK2001: unresolved external symbol "const type_info::`vftable'" (??_7type_info@@6B@) error LNK2001: unresolved external symbol _Direct3DCreate9@4

By simply linking against the DLL Runtime Library instead of the application rtl and
switching off the security cookie feature.
I can understand that you don't have the time (same for me when i'd like to do some fun stuff , ie. small computer graphics animations)
I think i'll get the single directx call done on my own, however,
i'd greatly appreciate any hints in getting the vftable reference resolved somehow.
after that just the directsound stuff is missing - the goal is to keep it smaller than 64k
best regards
nico
AnswerRe: unresolved symbols on vs2008membernicocode1 Feb '08 - 6:37 
the vftable linking error can be omitted by disabling the RTTI generation of the
compiler i found out, no runtime type info - no vftable resolve probs!
so issue solved, thanks anyhow -- perhaps i was just too fast in posting here
have a nice weekend
GeneralBug fix in fgetwsmemberPaul Sanders (AlpineSoft)9 Oct '07 - 4:08 
Hi all,
 
Very handy tiny library. Thanks very much.
 
Small buglet in fgetws. Looks like a simple oversight. Change 1st line to:
    if (s->_flag & _FILE_TEXT)
Also, change:
    fgets(bfr, n, s);
to:
    if (fgets(bfr, n, s) == NULL)
        return NULL;
 
Paul Sanders
http://www.alpinesoft.co.uk[^]
GeneralC compilationmembershguillon19 Sep '07 - 5:30 
I have compiled the code in c with some modifications (removing "extern C") and I've tried to run a small program but it crashes.
 
I tried to compile it with
 
BOOL WINAPI DllMain(HANDLE hInst, DWORD reason, LPVOID imp);
 
BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, DWORD reason, LPVOID imp)
{......
 
specified in the crt0tdll file but it gives me an error that _DllMainCRTStartup is an unresolved external. I have specified my _DllMainCRTStartup in my DEF file as an export.
It only compiles when I code it like this:
 
BOOL WINAPI DllMain(HANDLE hInst, DWORD reason, LPVOID imp)
{
return TRUE;
}
 
BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, DWORD reason, LPVOID imp)
{......
 

And I also have a problem when I link my tyny library to generate an exe. In my testing program I have only
 
int main(int argc, char *argv[])
{
return 0;
}
 
and when I compile it and run it, my program crashes...
 
I would appreciate any advices or comments.
 
Thanks

QuestionDllMainCRTStartup Unresolved External Symbolmembershguillon12 Sep '07 - 9:39 
I am trying to use your article and some of your code to implement an small executable linked to a dll. I get the error Unresolved external symbol '__DllMainCRTStartup@8...at compilation. I am using a DEF file for the exports where I define my DllMainCRTStartup like this:
EXPORTS
"_DllMainCRTStartup" = DllMainCRTStartup@8
 
Is there anything else I need to do, or What am I doing wrong?
 
Many Thanks...
Generalx64memberAbsolute Zero10 Jul '07 - 23:32 
Any chance for a 64bit compilable update?
GeneralRe: x64memberGreenReaper18 Nov '08 - 9:37 
To make this library x64 compatible you can add:
 
#ifndef _M_X64
...
#endif
 
around _CIacos() and _ftol2() in math.cpp
 
To avoid the need for _ftol2() you can use the compilation paramater /QIfist, just read this first:
http://msdn.microsoft.com/en-us/library/6d9xx1d2%28VS.80%29.aspx
It is unlikely to affect people using this for small quick apps.
 
A better way would be to separate out the assembly code into a MASM file but then you'd need to compile it with that.
Generalfew comments about file size and securitymemberamirman6 Apr '07 - 6:05 
1. if you wish to save size:
- you can compile with /align:16
- open your PE file with an hex editor and cut out the null's in the end of the file
- just use a packer (like UPX)
 
2. the implementation of *printf's uses a constant buffer of 1024 bytes.
while this might just end up as a bug in a program (which would be quite hard to detect)
it posses a huge security threat,
using any of the printf's leads almosty every time to an easily exploitable
stack based buffer overflow
 
Amir T.
GeneralRe: few comments about file size and securitymemberMike_V6 Apr '07 - 12:37 
1. /align:16 isn't necessarily supported on all Windows systems. Some (albeit the older ones) require 512 or 4096-byte alignment.
 
Cutting out the nulls at the end of the file is hack-ish at best, and I would _never_ consider doing that even for one of my personal projects.
 
Executable packers make debugging difficult if not impossible, make your program look suspicious (at least in my eyes), and add a step to the build process.
 

2. That would be a "huge security threat" if I just blindly passed the buffer to the underlying function. I do not, and instead truncate the string if it is too large.
 

Keep in mind the stated purpose of this article. This is an advanced trick that will work for some programs to cut their size down significantly, more than any other of the methods in (1) can. It is not intended to be used blindly, but rather to only be used after studying its limitations. It cannot be a drop-in replacement for the Microsoft CRT. If it was, then it would by definition have to implement everything the Microsoft CRT does.
 
Thanks for the feedback,
Michael
Generalsaving a few bytesmemberluc raymond5 Apr '07 - 2:24 
for those who wish to save a few bytes with/without using this library, you can go into your project's option, under Linker->Optimization->Optimize for Windows 98: change it from DEFAULT to NO.
 
You will save around 11k
 
Luc raymond
http://www.virtualshowcar.com
http://www.lucraymond.net
http://www.woxxom.com

QuestionIntrinsic InteroperabilitymemberRob Grainger4 Apr '07 - 2:03 
Great article - I remember spending a little time investigating how the static constructors worked myself. Seems a shame Microsoft didn't adopt this approach with the _ATL_MIN_CRT version of ATL, I've suffered there from avoiding static objects.
 
One question - have you considered/tested interoperation with intrinsic versions of various functions? The VS2005 C++ compiler has option for generating intrinsic versions of memcpy and other functions suitable for tight inlining. Can we still utilise these with the Tiny C Runtime Library?
 
Once again - congrats - excellent article.
AnswerRe: Intrinsic InteroperabilitymemberMike_V4 Apr '07 - 8:01 
I would assume so, although I have not personally tested it myself. Projects I create using this are optimized for size (obviously) which turns off intrinsics.
 
Mike
GeneralA bit scarymembernorm .net25 Mar '07 - 23:38 
I can't image all the black box testing that would have to be done so this could be reiably used in production code, but the idea and article have good value.
 
.net is a box of never ending treasures, every day I get find another gem.

GeneralRe: A bit scarymvpHans Dietrich25 Mar '07 - 23:59 
norm .net wrote:
...all the black box testing that would have to be done so this could be reiably used in production code

That's a good point!
 
Maybe in next update there could be comprehensive set of test cases for each function, with results displayed?

GeneralRe: A bit scarymembernorm .net26 Mar '07 - 0:02 
Then at least on release of each version he could do regression testing. I'd hate to even contemplate how many tests would be required?
 

 
.net is a box of never ending treasures, every day I get find another gem.

GeneralRe: A bit scarymvpHans Dietrich26 Mar '07 - 2:24 
norm .net wrote:
I'd hate to even contemplate how many tests would be required?

In total, there might be a large number. But it seems to me that the tests could be table-driven, with many of the tests having the same (or similar) parameters - only the result code would be different.

GeneralRe: A bit scarymembersk8er_boy28726 Mar '07 - 2:59 
The amount of time it would take to test these tweaks is probably a lot greater than the amount of time it would take you to learn assembly language and create your own program initialization routine. Smile | :)
You could even skip initializing global stuff like argc and argv altogether, if your program doesn't need it. You don't even need WinMain. You could start coding your program (creating windows and the main message loop) directly from the program's entry point. Now that's a real optimization!Smile | :)
Stuff like this (minimizing executable size) always has and always will be easier done in assembly language.
VC++ itself uses assembly code for some runtime functions (I guess whoever did it that way didn't trust VS's optimizer).
The bloating probably comes from trying to keep compatibility with older operating systems (all the way down to Windows 3.1, and who knows, maybe even earlier Smile | :) ) There's also code for some bugs in older processor implementations, where some floating point instructions wouldn't do what they were supposed to. As far as I know, they aren't making those brands of processors anymore.D'Oh! | :doh:
I don't think a completely compatible run-time library will ever be written that will be smaller in code than the one already provided by VC++.
However, the effort is to be appreciated.
 
Gabriel
GeneralRe: A bit scarymemberMike_V26 Mar '07 - 4:36 
It's not looking fun Smile | :)
 
That's not saying I haven't tested this monster. (I write... as the Visual Studio "Defy All Challenges" monster is to the left!) On the contrary, I've used it in quite a few of my personal projects without incident - and some of them have quite abused the C runtime Wink | ;)
 
But before I can use it in a real, production product myself, I need to show the results of a detailed unit test of every function. So once I get that completed I will be posting the unit-test code, along with any updates that are needed to the library.
 
After all, if I say it 'just works' then why should anybody trust me? Unless of course I show the tests and their results.
 
Mike
GeneralRe: A bit scarymembernorm .net26 Mar '07 - 4:50 
Mike_V wrote:
It's not looking fun

 
And testing isn't.
 
You could even have levels of functionality identified by a precompiler switch so as you introduce more functions you could decide whether or not they should make it to the lib, just a thought.

 
.net is a box of never ending treasures, every day I get find another gem.

Questionerror LNK2001: unresolved external symbol _wWinMainCRTStartupmemberBugByter4 Mar '07 - 1:22 
Hi,
thank you very much for your effort, your example works great!
I'm having some trouble though and ask for your advice:
 
I'm trying to create EXEs with minimal memory footprint and EXE size. I want to create GUI programs and thus started off with a "win32 project" (all default parameters). I tried to add libct in several ways, including it as a lib or using your referenced project approach. I stepped though each and every setting under project C++ and project linker options and set it like you did in your demo. But whenever i enable "Ignore Default Libraries", i still get the following error:
 
Error 1 error LNK2001: unresolved external symbol _wWinMainCRTStartup TinyC++
Error 2 fatal error LNK1120: 1 unresolved externals \Release\TinyC++.exe
 
Can you point me in the right direction? :/ How can I use libct with a standard "win32 project" gui app?
 
also, are there other (better) ways to get a small memory footprint, as opposed to small EXEs?
 
Thanks a lot in advance and thank you again for your effort,
buggy
AnswerRe: error LNK2001: unresolved external symbol _wWinMainCRTStartupmemberMike_V4 Mar '07 - 10:01 
Are you compiling the c library with unicode support turned on? Or have you tried compiling your program with unicode support turned off?
 
That's just for starters, if it still doesn't work let me know.
 
Michael
GeneralSmall bugmembermpj18 Jan '07 - 1:45 
Hi,
 
Nice article there is just a small mistake in the strnicmp function, at this moement the strnicmp function return 1, 2 or 3.
int strnicmp(const char *s1, const char *s2, size_t n)
{
     return CompareStringA(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
}
 
When you change the function to this
 
int strnicmp(const char *s1, const char *s2, size_t n)
{
     return (CompareStringA( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, s1, (DWORD)n, s2, (DWORD)n) - CSTR_EQUAL );
}
 
Now the function should return < 0, ==0 and >0 as the c runtime library.

GeneralRe: Small bugmemberMike_V25 Mar '07 - 17:04 
Hey, so evidently I missed your comment here. Thanks for pointing that out.
 
The article has been updated.
 
Thanks again,
Mike
QuestionHow big should a default application be?memberMichael Chapman22 Dec '06 - 13:13 
In VS2005, I've generated a default "Win32 Project -> Windows application". I stripped out the about box resource and code, and applied the optimizations (string pooling, etc.) from this article. With a Unicode build, I get an application size of 64K. If I link to a Unicode build of libct.lib, the application size shrinks to 51.5K.
 
I used WTL 7.5 to build a similar default application and stripped out everything to just have a simple window with a "File | Exit" menu. I've enabled minimal use of CRT in ATL. The size of this application is 30.5K.
 
Using Dependency Walker, the WTL app has 4 more dependencies than the LIBCT one -- ADVAPI32.DLL, OLE32.DLL, OLEAUT32.DLL, COMCTL32.DLL -- so maybe that explains the smaller executable size.
 
But I'm wondering if I'm missing something in regards to using LIBCT that could make my application size even smaller. I just want to have an app that sits in the system tray with no GUI, just a message pump that can receive window messages.
AnswerRe: How big should a default application be?memberMike_V22 Dec '06 - 13:30 
Did you use /NODEFAULTLIB in addition to linking to the library. Somehow even 51.5K seems too large for my experience. I can't test it at the moment, but I will after the holiday weekend.
 
Thanks,
Mike
GeneralRe: How big should a default application be?memberMichael Chapman22 Dec '06 - 14:28 
It looks like I've done everything in the "Using the code" section, including /NODEFAULTLIB.
 
I deleted the project and started over. This time I only get a reduction from 64K to 60K. The other project may have been MBCS rather than Unicode.
 
Thanks,
Michael
GeneralAwesome!memberjswoofer19 Oct '06 - 11:43 
I had a dll that compiled to 34kb with all optimizations turned on. Using tlibc I got it down to an incredible 4kb!
 
Thanks for releasing this
GeneralGood work, small typo in codememberHans14 Aug '06 - 9:52 
Nice work, and kudos for keeping this knowledge alive.
Would be really nice to get it working for c++ exceptions, but I appreciate it is hard to do so.
 
Small typo:

void _init_file()
{
// STDIN
__iob[0].set_handle(GetStdHandle(STD_INPUT_HANDLE));
__iob[0]._flag = _FILE_TEXT;
 
// STDOUT
__iob[1].set_handle(GetStdHandle(STD_OUTPUT_HANDLE));
__iob[1]._flag = _FILE_TEXT;
 
// STDERR
__iob[2].set_handle(GetStdHandle(STD_ERROR_HANDLE));
__iob[1]._flag = _FILE_TEXT; <---- should be __iob[2]
}

 
Hans

GeneralRe: Good work, small typo in codememberMike_V14 Aug '06 - 11:54 
Good catch. I never use stderr for anything, so maybe that's why I didn't catch it!
 
Thanks. I will apply it to the next article update.
 
Mike
GeneralExceptions.memberNemanja Trifunovic14 Aug '06 - 1:49 
Really nice article. Congratulations.
 
Just one suggestion. Why don't you add the support for C++ exceptions as well? It has always bothered me that we need to link CRT to be able to use this core language feature.
 

AnswerRe: Exceptions.memberMike_V14 Aug '06 - 3:35 
C++ exceptions rely on runtime support code that Microsoft doesn't provide. Well, actually they do, but it is a precompiled LIB, so I'd have to resort to dumpbin /disasm on it and hope for debug symbols Smile | :)
 
However, there's this article[^] that may or may not have enough information to do it. Only one problem: adding the exception handling code would almost definitely increase executable size. And doesn't it rely on runtime type information? (I could be wrong) RTTI is another feature that is MS-specific and undocumented.
 
And I agree with you - why can't it be more like MSIL and try/catch where it is a built-in feature of the language? Of course, knowing the x86 architecture, it can't be, but we can wish, can't we? Big Grin | :-D
 
Mike
GeneralRe: Exceptions. [modified]memberS.B.16 Aug '06 - 13:05 
Kudos to you, nice article. Smile | :)
 
FYI, exceptions don't require RTTI. In fact the original Win32 exceptions (__try and __except) work in C code. They used an exception filter callback function to determine the correct handler rather than matching the exception type.
 
We always turn off RTTI and we use exceptions extensively. RTTI is just bloat we don't need, plus it has a slight detrimental effect on performance (see http://www.comms.scitech.susx.ac.uk/fft/programming/ANSI-ISOCppProfessionalProgrammersHandbook.pdf)
 
See http://msdn2.microsoft.com/en-us/library/6dekhbbc.aspx for more info on how exceptions are implemented by the Microsoft compiler.
 
IMO, it should be possible to add exception support to your CRT without too much effort.
 

-- modified at 19:06 Wednesday 16th August, 2006
Edit: made the links into links.
Generallibctiny + ATLmemberTzanth13 Aug '06 - 23:51 
Firstly, can I suggest the extern "C" statements in libct.h be surrounded by #ifdef _cplusplus - when running with a .c compile it doesn't work.
 
Anyways, just trying to get this to work with an ATL prog... and having little success.
 
-----------------
stdafx.obj : error LNK2001: unresolved external symbol @__security_check_cookie@4
atls.lib(atlbase.obj) : error LNK2001: unresolved external symbol @__security_check_cookie@4
TestApp.obj : error LNK2001: unresolved external symbol __vscwprintf
TestApp.obj : error LNK2001: unresolved external symbol __vswprintf
atls.lib(atlbase.obj) : error LNK2001: unresolved external symbol _memmove_s
atls.lib(atlbase.obj) : error LNK2001: unresolved external symbol __SEH_epilog4
atls.lib(atlbase.obj) : error LNK2001: unresolved external symbol __except_handler4
atls.lib(atlbase.obj) : error LNK2001: unresolved external symbol __SEH_prolog4
atls.lib(atlbase.obj) : error LNK2001: unresolved external symbol __recalloc
atls.lib(atlbase.obj) : error LNK2001: unresolved external symbol ___security_cookie
-----------------
 
Has anybody else had any success with using ATL and LIBCTINY?

NewsRe: libctiny + ATLmemberMike_V14 Aug '06 - 3:29 
Good suggestion about #ifdef _cplusplus... I always compile as C++ so that has never been a problem Smile | :)
 
It looks like ATL is using structured exception handling and runtime checks. Unfortunately, those are two features this library is not going to support - otherwise it turns into another version of Microsoft's CRT. IIRC, there is an ATL_MIN_CRT option or something like that, but my knowledge of ATL is so limited I'd be better off keeping my mouth shut Smile | :)
 
_vswprintf and _vscwprintf I should be able to support, along with _recalloc. memmove_s is Microsoft's "secure" version of memmove, another function I don't plan to support.
 
Sorry about that.
 
Mike
GeneralRe: libctiny + ATLmemberTzanth14 Aug '06 - 14:34 
Not a problem.
 
Curious that the "memmove_s" was used in the previous post though - I had explicitly told the test app not to link to the secure libraries, so I decided to look at rebuilding the static ATL library without SEH and runtime checks.
Will post results later on - there are a few things that need to be changed in order for the compile to work. (Removal of deprecated flags, etc. etc.)
 
ATL_MIN_CRT does pretty much the same thing as LIBCTINY, so I doubt it's a good idea to use both Smile | :)

Generalmemcmp cannot workmemberghu2212 Aug '06 - 23:46 
Hi
 
good article, but I noticed the wrong implementation of memcmp:
 
extern "C" int memcmp(const void *b1, const void *b2, size_t n)
{
return strncmp((const char *)b1, (const char *)b2, n);
}

 
Ususally strncmp stops at first occurence of character '0'. You have to do a loop over all memory as in memcpy.
 
ghu
GeneralRe: memcmp cannot workmemberMike_V13 Aug '06 - 5:31 
Doh! Blush | :O
 
That's what happens when I try to get something done too fast...
 
And in that case, strncmp wasn't implemented properly, but memcmp was, since strncmp was really memcmp.
 
Thanks. I will fix this in an update.
AnswerRe: memcmp cannot workmemberMike_V14 Aug '06 - 11:55 
Fixed!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 25 Mar 2007
Article Copyright 2006 by Mike_V
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid